
vanilla-extract es una nueva biblioteca CSS-in-TypeScript independiente del marco. Es una forma liviana, robusta e intuitiva de escribir sus estilos. vanilla-extract no es un marco CSS prescriptivo, sino una pieza flexible de herramientas para desarrolladores. Las herramientas de CSS han sido un espacio relativamente estable en los últimos años con PostCSS, Sass, módulos CSS y componentes con estilo que salieron antes de 2017 (algunos mucho antes) y siguen siendo populares en la actualidad. Tailwind es una de las pocas herramientas que ha revolucionado las herramientas de CSS en los últimos años.
extracto de vainilla tiene como objetivo sacudir las cosas de nuevo. Fue lanzado este año y tiene la ventaja de poder aprovechar algunas tendencias recientes, que incluyen:
- Desarrolladores de JavaScript cambiando a TypeScript
- Compatibilidad del navegador con propiedades personalizadas de CSS
- Estilo utilitario primero
Hay un montón de innovaciones inteligentes en el extracto de vainilla que creo que lo convierten en un gran problema.
Tiempo de ejecución cero
Las bibliotecas CSS-in-JS generalmente inyectan estilos en el documento en tiempo de ejecución. Esto tiene beneficios, incluida la extracción de CSS crítica y el estilo dinámico.
Pero como regla general, un archivo CSS separado será más eficaz. Esto se debe a que el código JavaScript necesita pasar por un análisis/compilación más costoso, mientras que un archivo CSS separado se puede almacenar en caché mientras que el protocolo HTTP2 reduce el costo de la solicitud adicional. Además, las propiedades personalizadas ahora pueden proporcionar mucho estilo dinámico de forma gratuita.
Entonces, en lugar de inyectar estilos en el tiempo de ejecución, el extracto de vainilla se asemeja a Linaria y astroturf. Estas bibliotecas le permiten crear estilos usando funciones de JavaScript que se extraen en el momento de la compilación y se usan para construir un archivo CSS. Aunque escribe extracto de vainilla en TypeScript, no afecta el tamaño general de su paquete de JavaScript de producción.
Mecanografiado
Una gran propuesta de valor del extracto de vainilla es que te pones a escribir. Si es lo suficientemente importante como para mantener el resto de su base de código segura, ¿por qué no hacer lo mismo con sus estilos?
TypeScript proporciona una serie de beneficios. Primero, hay autocompletar. Si escribe “fo”, entonces, en un editor compatible con TypeScript, obtendrá una lista de opciones de fuente en un menú desplegable: fontFamily
, fontKerning
, fontWeight
, o cualquier otra cosa que coincida, para elegir. Esto hace que las propiedades CSS sean detectables desde la comodidad de su editor. Si no puede recordar el nombre de fontVariant
pero sepa que comenzará con la palabra “fuente”, la escribe y se desplaza por las opciones. En VS Code, no necesita descargar ninguna herramienta adicional para que esto suceda.
Esto realmente acelera la creación de estilos:
También significa que su editor está mirando por encima de su hombro para asegurarse de que no esté cometiendo errores de ortografía que puedan causar errores frustrantes.
Los tipos de extracto de vainilla también brindan una explicación de la sintaxis en su definición de tipo y un enlace a la documentación de MDN para la propiedad CSS que está editando. Esto elimina un paso de buscar frenéticamente en Google cuando los estilos se comportan de forma inesperada.
Escribir en TypeScript significa que está usando nombres de mayúsculas y minúsculas para las propiedades CSS, como backgroundColor
. Esto podría ser un pequeño cambio para los desarrolladores que usan sintaxis CSS regular, como background-color
.
integraciones
vanilla-extract proporciona integraciones de primera clase para todos los paquetes más nuevos. Aquí hay una lista completa de las integraciones que admite actualmente:
- paquete web
- esconstruir
- Vite
- manto de nieve
- SiguienteJS
- gatsby
También es completamente independiente del marco. Todo lo que necesita hacer es importar nombres de clases de vanilla-Extract, que se convierten en una cadena en el momento de la compilación.
Uso
Para usar extracto de vainilla, escribe un .css.ts
archivo que sus componentes pueden importar. Las llamadas a estas funciones se convierten en cadenas de nombre de clase con hash y ámbito en el paso de compilación. Esto puede sonar similar a los módulos CSS, y no es una coincidencia: uno de los creadores de vanilla-Extract, Mark Dalgleish, también es cocreador de los módulos CSS.
style()
Puede crear una clase CSS de alcance automático utilizando el style()
función. Pasa los estilos del elemento y luego exporta el valor devuelto. Importe este valor en algún lugar de su código de usuario y se convertirá en un nombre de clase con ámbito.
// title.css.ts
import {style} from "@vanilla-extract/css";
export const titleStyle = style({
backgroundColor: "hsl(210deg,30%,90%)",
fontFamily: "helvetica, Sans-Serif",
color: "hsl(210deg,60%,25%)",
padding: 30,
borderRadius: 20,
});
// title.ts
import {titleStyle} from "./title.css";
document.getElementById("root").innerHTML = `<h1 class="${titleStyle}">Vanilla Extract</h1>`;
Las consultas de medios y los pseudoselectores también se pueden incluir dentro de las declaraciones de estilo:
// title.css.ts
backgroundColor: "hsl(210deg,30%,90%)",
fontFamily: "helvetica, Sans-Serif",
color: "hsl(210deg,60%,25%)",
padding: 30,
borderRadius: 20,
"@media": {
"screen and (max-width: 700px)": {
padding: 10
}
},
":hover":{
backgroundColor: "hsl(210deg,70%,80%)"
}
Estos style
las llamadas a funciones son una ligera abstracción sobre CSS: todos los nombres y valores de propiedades se asignan a las propiedades y valores de CSS con los que está familiarizado. Un cambio al que hay que acostumbrarse es que los valores a veces se pueden declarar como un número (p. ej. padding: 30
) que por defecto es un valor de unidad de píxel, mientras que algunos valores deben declararse como una cadena (p. ej. padding: "10px 20px 15px 15px"
).
Las propiedades que van dentro de la función de estilo solo pueden afectar a un único nodo HTML. Esto significa que no puede usar el anidamiento para declarar estilos para los hijos de un elemento, algo a lo que podría estar acostumbrado en Sass o PostCSS. En su lugar, debe diseñar a los niños por separado. Si un elemento hijo necesita diferentes estilos basados en el padre, puede usar el selectors
propiedad para agregar estilos que dependen del padre:
// title.css.ts
export const innerSpan = style({
selectors:{[`${titleStyle} &`]:{
color: "hsl(190deg,90%,25%)",
fontStyle: "italic",
textDecoration: "underline"
}}
});
// title.ts
import {titleStyle,innerSpan} from "./title.css";
document.getElementById("root").innerHTML =
`<h1 class="${titleStyle}">Vanilla <span class="${innerSpan}">Extract</span></h1>
<span class="${innerSpan}">Unstyled</span>`;
O también puede usar la API de creación de temas (que veremos a continuación) para crear propiedades personalizadas en el elemento principal que consumen los nodos secundarios. Esto puede parecer restrictivo, pero se ha dejado intencionalmente de esta manera para aumentar la capacidad de mantenimiento en bases de código más grandes. Significa que sabrá exactamente dónde se han declarado los estilos para cada elemento de su proyecto.
Tematización
Puedes usar el createTheme
función para construir variables en un objeto TypeScript:
// title.css.ts
import {style,createTheme } from "@vanilla-extract/css";
// Creating the theme
export const [mainTheme,vars] = createTheme({
color:{
text: "hsl(210deg,60%,25%)",
background: "hsl(210deg,30%,90%)"
},
lengths:{
mediumGap: "30px"
}
})
// Using the theme
export const titleStyle = style({
backgroundColor:vars.color.background,
color: vars.color.text,
fontFamily: "helvetica, Sans-Serif",
padding: vars.lengths.mediumGap,
borderRadius: 20,
});
Luego, el extracto de vainilla le permite hacer una variante de su tema. TypeScript lo ayuda a garantizar que su variante use todos los mismos nombres de propiedad, por lo que recibe una advertencia si olvida agregar el background
propiedad al tema.
Así es como podrías crear un tema normal y un modo oscuro:
// title.css.ts
import {style,createTheme } from "@vanilla-extract/css";
export const [mainTheme,vars] = createTheme({
color:{
text: "hsl(210deg,60%,25%)",
background: "hsl(210deg,30%,90%)"
},
lengths:{
mediumGap: "30px"
}
})
// Theme variant - note this part does not use the array syntax
export const darkMode = createTheme(vars,{
color:{
text:"hsl(210deg,60%,80%)",
background: "hsl(210deg,30%,7%)",
},
lengths:{
mediumGap: "30px"
}
})
// Consuming the theme
export const titleStyle = style({
backgroundColor: vars.color.background,
color: vars.color.text,
fontFamily: "helvetica, Sans-Serif",
padding: vars.lengths.mediumGap,
borderRadius: 20,
});
Luego, usando JavaScript, puede aplicar dinámicamente los nombres de clase devueltos por vanilla-extract para cambiar de tema:
// title.ts
import {titleStyle,mainTheme,darkMode} from "./title.css";
document.getElementById("root").innerHTML =
`<div class="${mainTheme}" id="wrapper">
<h1 class="${titleStyle}">Vanilla Extract</h1>
<button onClick="document.getElementById('wrapper').className="${darkMode}"">Dark mode</button>
</div>`
¿Cómo funciona esto debajo del capó? Los objetos que declaras en el createTheme
se convierten en propiedades personalizadas de CSS adjuntas a la clase del elemento. Estas propiedades personalizadas se codifican para evitar conflictos. El CSS de salida para nuestro mainTheme
ejemplo se ve así:
.src__ohrzop0 {
--color-brand__ohrzop1: hsl(210deg,80%,25%);
--color-text__ohrzop2: hsl(210deg,60%,25%);
--color-background__ohrzop3: hsl(210deg,30%,90%);
--lengths-mediumGap__ohrzop4: 30px;
}
Y la salida CSS de nuestro darkMode
el tema se ve así:
.src__ohrzop5 {
--color-brand__ohrzop1: hsl(210deg,80%,60%);
--color-text__ohrzop2: hsl(210deg,60%,80%);
--color-background__ohrzop3: hsl(210deg,30%,10%);
--lengths-mediumGap__ohrzop4: 30px;
}
Entonces, todo lo que necesitamos cambiar en nuestro código de usuario es el nombre de la clase. Aplica el darkmode
nombre de clase al elemento padre, y el mainTheme
las propiedades personalizadas se intercambian por darkMode
unos.
API de recetas
El style
y createTheme
Las funciones brindan suficiente poder para diseñar un sitio web por sí mismas, pero vanilla-extract proporciona algunas API adicionales para promover la reutilización. La API de Recetas le permite crear un montón de variantes para un elemento, que puede elegir en su marcado o código de usuario.
Primero, debe instalarse por separado:
npm install @vanilla-extract/recipes
Así es como funciona. importas el recipe
función y pasar un objeto con las propiedades base
y variants
:
// button.css.ts
import { recipe } from '@vanilla-extract/recipes';
export const buttonStyles = recipe({
base:{
// Styles that get applied to ALL buttons go in here
},
variants:{
// Styles that we choose from go in here
}
});
Dentro base
, puede declarar los estilos que se aplicarán a todas las variantes. Dentro variants
, puede proporcionar diferentes formas de personalizar el elemento:
// button.css.ts
import { recipe } from '@vanilla-extract/recipes';
export const buttonStyles = recipe({
base: {
fontWeight: "bold",
},
variants: {
color: {
normal: {
backgroundColor: "hsl(210deg,30%,90%)",
},
callToAction: {
backgroundColor: "hsl(210deg,80%,65%)",
},
},
size: {
large: {
padding: 30,
},
medium: {
padding: 15,
},
},
},
});
Luego puede declarar qué variante desea usar en el marcado:
// button.ts
import { buttonStyles } from "./button.css";
<button class=`${buttonStyles({color: "normal",size: "medium",})}`>Click me</button>
¡Y vanilla-extract aprovecha TypeScript dando autocompletado para sus propios nombres de variantes!
Puede nombrar sus variantes como desee y poner las propiedades que desee en ellas, así:
// button.css.ts
export const buttonStyles = recipe({
variants: {
animal: {
dog: {
backgroundImage: 'url("./dog.png")',
},
cat: {
backgroundImage: 'url("./cat.png")',
},
rabbit: {
backgroundImage: 'url("./rabbit.png")',
},
},
},
});
Puede ver cómo esto sería increíblemente útil para construir un sistema de diseño, ya que puede crear componentes reutilizables y controlar las formas en que varían. Estas variaciones se vuelven fácilmente detectables con TypeScript: todo lo que necesita escribir es CMD/CTRL + Space
(en la mayoría de los editores) y obtiene una lista desplegable de las diferentes formas de personalizar su componente.
Utilidad primero con Sprinkles
Sprinkles es un marco de utilidad primero construido sobre extracto de vainilla. Así es como lo describen los documentos de extracto de vainilla:
Básicamente, es como crear su propia versión de Tailwind, Styled System, etc., sin tiempo de ejecución y con seguridad de tipos.
Entonces, si no eres fanático de nombrar cosas (todos tenemos pesadillas de crear un outer-wrapper
div y luego nos damos cuenta de que necesitamos envolverlo con un . . . outer-outer-wrapper
) Las chispas pueden ser su forma preferida de usar extracto de vainilla.
La API de Sprinkles también debe instalarse por separado:
npm install @vanilla-extract/sprinkles
Ahora podemos crear algunos bloques de construcción para que los usen nuestras funciones de utilidad. Vamos a crear una lista de colores y longitudes declarando un par de objetos. Los nombres de las claves de JavaScript pueden ser los que queramos. Los valores deberán ser valores CSS válidos para las propiedades CSS para las que planeamos usarlos:
// sprinkles.css.ts
const colors = {
blue100: "hsl(210deg,70%,15%)",
blue200: "hsl(210deg,60%,25%)",
blue300: "hsl(210deg,55%,35%)",
blue400: "hsl(210deg,50%,45%)",
blue500: "hsl(210deg,45%,55%)",
blue600: "hsl(210deg,50%,65%)",
blue700: "hsl(207deg,55%,75%)",
blue800: "hsl(205deg,60%,80%)",
blue900: "hsl(203deg,70%,85%)",
};
const lengths = {
small: "4px",
medium: "8px",
large: "16px",
humungous: "64px"
};
Podemos declarar a qué propiedades CSS se aplicarán estos valores usando el defineProperties
función:
- Pasarle un objeto con un
properties
propiedad. - En
properties
, declaramos un objeto donde las claves son las propiedades CSS que el usuario puede configurar (estas deben ser propiedades CSS válidas) y los valores son los objetos que creamos anteriormente (nuestras listas decolors
ylengths
).
// sprinkles.css.ts
import { defineProperties } from "@vanilla-extract/sprinkles";
const colors = {
blue100: "hsl(210deg,70%,15%)"
// etc.
}
const lengths = {
small: "4px",
// etc.
}
const properties = defineProperties({
properties: {
// The keys of this object need to be valid CSS properties
// The values are the options we provide the user
color: colors,
backgroundColor: colors,
padding: lengths,
},
});
Luego, el paso final es pasar el valor de retorno de defineProperties
al createSprinkles
y exporte el valor devuelto:
// sprinkles.css.ts
import { defineProperties, createSprinkles } from "@vanilla-extract/sprinkles";
const colors = {
blue100: "hsl(210deg,70%,15%)"
// etc.
}
const lengths = {
small: "4px",
// etc.
}
const properties = defineProperties({
properties: {
color: colors,
// etc.
},
});
export const sprinkles = createSprinkles(properties);
Luego podemos comenzar a diseñar dentro de nuestros componentes en línea llamando al sprinkles
en el atributo class y eligiendo que opciones queremos para cada elemento.
// index.ts
import { sprinkles } from "./sprinkles.css";
document.getElementById("root").innerHTML = `<button class="${sprinkles({
color: "blue200",
backgroundColor: "blue800",
padding: "large",
})}">Click me</button>
</div>`;
La salida de JavaScript contiene una cadena de nombre de clase para cada propiedad de estilo. Estos nombres de clase coinciden con una sola regla en el archivo CSS de salida.
<button class="src_color_blue200__ohrzop1 src_backgroundColor_blue800__ohrzopg src_padding_large__ohrzopk">Click me</button>
Como puede ver, esta API le permite diseñar elementos dentro de su marcado usando un conjunto de restricciones predefinidas. También evita la difícil tarea de encontrar nombres de clases para cada elemento. El resultado es algo que se parece mucho a Tailwind, pero que también se beneficia de toda la infraestructura que se ha construido alrededor de TypeScript.
La API de Sprinkles también le permite escribir condiciones y abreviaturas para crear estilos receptivos usando clases de utilidad.
Terminando
vanilla-extract se siente como un gran paso nuevo en las herramientas de CSS. Se ha pensado mucho en convertirlo en una solución robusta e intuitiva para el estilo que utiliza toda la potencia que proporciona la escritura estática.
Otras lecturas
- documentación de extracto de vainilla
- Charla de Mark sobre el extracto de vainilla
- extracto de vainilla Discord
- Ejemplos de código en CodeSandbox