CSS in TypeScript met vanille-extract

Bronknooppunt: 1121024

vanille-extract is een nieuwe framework-agnostische CSS-in-TypeScript-bibliotheek. Het is een lichtgewicht, robuuste en intuïtieve manier om uw stijlen te schrijven. vanilla-extract is geen prescriptief CSS-framework, maar een flexibel stukje ontwikkelaarstool. CSS-tooling is de afgelopen jaren een relatief stabiele ruimte geweest PostCSS, Sass, CSS-modules en gestileerde componenten ze komen allemaal uit vóór 2017 (sommige lang daarvoor) en zij populair blijven <p></p> Tailwind is een van de weinige tools die de afgelopen jaren de boel op het gebied van CSS-tooling heeft opgeschud.

vanille-extract heeft als doel de boel weer op te schudden. Het werd dit jaar uitgebracht en heeft het voordeel dat het gebruik kan maken van enkele recente trends, waaronder:

  • JavaScript-ontwikkelaars schakelen over naar TypeScript
  • Browserondersteuning voor aangepaste CSS-eigenschappen
  • Utility-first styling

Er zijn een hele reeks slimme innovaties in vanille-extract waarvan ik denk dat ze er een groot probleem van maken.

Nul looptijd

CSS-in-JS-bibliotheken injecteren gewoonlijk tijdens runtime stijlen in het document. Dit heeft voordelen, waaronder kritische CSS extractie en dynamische styling.

Maar als algemene vuistregel geldt een afzonderlijk CSS-bestand zal beter presteren. Dat komt omdat JavaScript-code duurdere parsing/compilatie moet ondergaan, terwijl een afzonderlijk CSS-bestand in de cache kan worden opgeslagen, terwijl het HTTP2-protocol de kosten van het extra verzoek verlaagt. Ook, aangepaste eigenschappen kan nu gratis veel dynamische styling bieden.

Dus in plaats van stijlen tijdens runtime te injecteren, doet vanille-extract erna Linaria en astroturf. Met deze bibliotheken kunt u stijlen schrijven met behulp van JavaScript-functies die tijdens het bouwen worden verwijderd en worden gebruikt om een ​​CSS-bestand samen te stellen. Hoewel u vanille-extract in TypeScript schrijft, heeft dit geen invloed op de totale grootte van uw productie-JavaScript-bundel.

getypte tekst

Een grote waardepropositie van vanille-extract is dat je aan het typen bent. Als het belangrijk genoeg is om de rest van uw codebase typeveilig te houden, waarom zou u dan niet hetzelfde doen met uw stijlen?

TypeScript biedt een aantal voordelen. Ten eerste is er automatisch aanvullen. Als u 'fo' typt, krijgt u in een TypeScript-vriendelijke editor een lijst met lettertype-opties in een vervolgkeuzelijst - fontFamily, fontKerning, fontWeight, of wat dan ook, om uit te kiezen. Hierdoor kunnen CSS-eigenschappen vanuit het comfort van uw editor worden ontdekt. Als je de naam niet meer weet fontVariant maar weet dat het gaat beginnen met het woord 'lettertype', typ het en blader door de opties. In VS Code hoeft u geen extra tools te downloaden om dit te laten gebeuren.

Dit versnelt het schrijven van stijlen echt:

Het betekent ook dat uw redacteur over uw schouder meekijkt om er zeker van te zijn dat u geen spelfouten maakt die frustrerende bugs kunnen veroorzaken.

vanille-extract-typen bieden ook een uitleg van de syntaxis in hun typedefinitie en een link naar de MDN-documentatie voor de CSS-eigenschap die u bewerkt. Dit elimineert een stap van verwoed Googlen wanneer stijlen zich onverwacht gedragen.

Afbeelding van VSCode met de cursor over de eigenschap fontKerning en een pop-up waarin wordt beschreven wat de eigenschap doet met een link naar de Mozilla-documentatie voor de eigenschap

Schrijven in TypeScript betekent dat u camel-case-namen gebruikt voor CSS-eigenschappen, zoals backgroundColor. Dit kan een kleine verandering zijn voor ontwikkelaars die de reguliere CSS-syntaxis gebruiken, zoals background-color.

Integraties

vanilla-extract biedt eersteklas integraties voor alle nieuwste bundelaars. Hier is een volledige lijst van integraties het ondersteunt momenteel:

  • webpack
  • esbouwen
  • schroef
  • snowpack
  • VolgendeJS
  • Gatsby

Het is ook volledig raamwerk-agnostisch. Het enige wat u hoeft te doen is klassenamen importeren uit vanilla-Extract, die tijdens het bouwen worden omgezet in een string.

Gebruik

Om vanille-extract te gebruiken, schrijf je een .css.ts bestand dat uw componenten kunnen importeren. Aanroepen naar deze functies worden tijdens de bouwstap geconverteerd naar gehashte en bereikbare klassenaamreeksen. Dit klinkt misschien hetzelfde als CSS-modules, en dit is geen toeval: de maker van vanille-extract, Mark Dalgleish, is ook mede-maker van CSS-modules.

style()

U kunt een automatisch bereikbare CSS-klasse maken met behulp van de style() functie. U geeft de stijlen van het element door en exporteert vervolgens de geretourneerde waarde. Importeer deze waarde ergens in uw gebruikerscode en deze wordt omgezet in een bereikbare klassenaam.

// 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>`;

Mediaquery's en pseudo-selectors kunnen ook worden opgenomen in stijldeclaraties:

// 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%)"
}

Deze style functieaanroepen zijn een dunne abstractie van CSS: alle namen en waarden van eigenschappen verwijzen naar de CSS-eigenschappen en -waarden waarmee u bekend bent. Een verandering waar u aan moet wennen is dat waarden soms als een getal kunnen worden gedeclareerd (bijv padding: 30) die standaard wordt ingesteld op een pixeleenheidswaarde, terwijl sommige waarden als een tekenreeks moeten worden gedeclareerd (bijv padding: "10px 20px 15px 15px").

De eigenschappen die binnen de stijlfunctie vallen, kunnen slechts één HTML-knooppunt beïnvloeden. Dit betekent dat je nesten niet kunt gebruiken om stijlen voor de onderliggende elementen van een element te declareren, iets waar je misschien wel aan gewend bent Sass or PostCSS. In plaats daarvan moet je kinderen apart stylen. Als een kindelement verschillende stijlen nodig heeft gebaseerde op de ouder kunt u de selectors eigenschap om stijlen toe te voegen die afhankelijk zijn van de ouder:

// 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>`;

Of u kunt ook de Theming API gebruiken (waar we hierna op ingaan) om aangepaste eigenschappen in het bovenliggende element te maken die door de onderliggende knooppunten worden gebruikt. Dit klinkt misschien beperkend, maar het is opzettelijk zo gelaten om de onderhoudbaarheid in grotere codebases te vergroten. Het betekent dat u precies weet waar de stijlen voor elk element in uw project zijn gedeclareerd.

Theming

U kunt gebruik maken van de createTheme functie om variabelen uit te bouwen in een TypeScript-object:

// 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,
});

Dan kun je met vanille-extract een variant van je thema maken. TypeScript zorgt ervoor dat uw variant allemaal dezelfde eigenschapsnamen gebruikt, zodat u een waarschuwing krijgt als u vergeet de background eigendom bij het thema.

Afbeelding van VS Code waarin een thema wordt weergegeven dat wordt gedeclareerd, maar waarbij de achtergrondeigenschap ontbreekt, waardoor een groot aantal rode kronkelige lijnen ontstaat om te waarschuwen dat de eigenschap is vergeten

Zo kun je een normaal thema en een donkere modus maken:

// 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,
});

Vervolgens kunt u met JavaScript de klassenamen die door vanilla-extract worden geretourneerd dynamisch toepassen om van thema te wisselen:

// 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>`

Hoe werkt dit onder de motorkap? De objecten die u declareert in het createTheme functie worden omgezet in aangepaste CSS-eigenschappen die aan de klasse van het element zijn gekoppeld. Deze aangepaste eigenschappen worden gehasht om conflicten te voorkomen. De uitvoer-CSS voor onze mainTheme voorbeeld ziet er als volgt uit:

.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;
}

En de CSS-uitvoer van onze darkMode thema ziet er als volgt uit:

.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;
}

Het enige dat we dus in onze gebruikerscode moeten veranderen, is de klassenaam. Pas de darkmode klassenaam toe aan het bovenliggende element, en de mainTheme aangepaste eigenschappen worden vervangen darkMode degenen.

Recepten-API

De style en createTheme functies bieden voldoende kracht om een ​​website op zichzelf te stylen, maar vanille-extract biedt een paar extra API's om herbruikbaarheid te bevorderen. Met de Recipes API kunt u een aantal varianten voor een element maken, waaruit u kunt kiezen in uw opmaak of gebruikerscode.

Eerst moet het afzonderlijk worden geïnstalleerd:

npm install @vanilla-extract/recipes

Dit is hoe het werkt. Je importeert de recipe functie en geef een object met de eigenschappen door base en 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 }
});

Binnenkant base, kunt u de stijlen declareren waarop wordt toegepast allen varianten. Binnen variants, kunt u verschillende manieren opgeven om het element aan te passen:

// 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, }, }, },
});

Vervolgens kunt u in de opmaak aangeven welke variant u wilt gebruiken:

// button.ts
import { buttonStyles } from "./button.css"; <button class=`${buttonStyles({color: "normal",size: "medium",})}`>Click me</button>

En vanille-extract maakt gebruik van TypeScript en zorgt voor automatisch aanvullen je eigen variant namen!

U kunt uw varianten een naam geven die u maar wilt, en er de gewenste eigenschappen in plaatsen, zoals:

// button.css.ts
export const buttonStyles = recipe({ variants: { animal: { dog: { backgroundImage: 'url("./dog.png")', }, cat: { backgroundImage: 'url("./cat.png")', }, rabbit: { backgroundImage: 'url("./rabbit.png")', }, }, },
});

Je ziet hoe dit ongelooflijk nuttig zou zijn voor het bouwen van een ontwerpsysteem, omdat je herbruikbare componenten kunt maken en de manier kunt bepalen waarop ze variëren. Deze variaties zijn gemakkelijk te ontdekken met TypeScript; u hoeft alleen maar te typen CMD/CTRL + Space (op de meeste editors) en u krijgt een vervolgkeuzelijst met de verschillende manieren om uw component aan te passen.

Utility-eerst met hagelslag

Sprinkles is een utility-first-framework gebouwd bovenop vanille-extract. Dit is hoe het vanille-extract werkt beschrijf het:

Kortom, het is alsof je je eigen zero-runtime, typeveilige versie bouwt van Tailwind, Gestyled systeem, Etc.

Dus als je geen fan bent van het benoemen van dingen (we hebben allemaal nachtmerries van het creëren van een outer-wrapper div en dan beseffen we dat we het moeten omwikkelen met een . . . outer-outer-wrapper ) Hagelslag is wellicht de beste manier om vanille-extract te gebruiken.

De Sprinkles API moet ook afzonderlijk worden geïnstalleerd:

npm install @vanilla-extract/sprinkles

Nu kunnen we enkele bouwstenen maken die we kunnen gebruiken voor onze nutsfuncties. Laten we een lijst met kleuren en lengtes maken door een aantal objecten te declareren. De JavaScript-sleutelnamen kunnen zijn wat we willen. De waarden moeten geldige CSS-waarden zijn voor de CSS-eigenschappen waarvoor we ze willen gebruiken:

// 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"
};

We kunnen aangeven op welke CSS-eigenschappen deze waarden van toepassing zijn door de defineProperties functie:

  • Geef het een object door met een properties eigendom.
  • In properties, verklaren we een object waarbij de toetsen zijn de CSS-eigenschappen die de gebruiker kan instellen (deze moeten geldige CSS-eigenschappen zijn) en de waarden zijn de objecten die we eerder hebben gemaakt (onze lijsten met colors en lengths).
// 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, },
});

Vervolgens is de laatste stap het doorgeven van de retourwaarde van defineProperties aan de createSprinkles functie en exporteer de geretourneerde waarde:

// 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);

Vervolgens kunnen we beginnen met het inline stylen van onze componenten door de sprinkles functie in het class-attribuut en kiezen welke opties we voor elk element willen.

// index.ts
import { sprinkles } from "./sprinkles.css";
document.getElementById("root").innerHTML = `<button class="${sprinkles({ color: "blue200", backgroundColor: "blue800", padding: "large",
})}">Click me</button>
</div>`;

De JavaScript-uitvoer bevat een klassenaamreeks voor elke stijleigenschap. Deze klassenamen komen overeen met een enkele regel in het uitgevoerde CSS-bestand.

<button class="src_color_blue200__ohrzop1 src_backgroundColor_blue800__ohrzopg src_padding_large__ohrzopk">Click me</button>

Zoals u kunt zien, kunt u met deze API elementen binnen uw opmaak vormgeven met behulp van een reeks vooraf gedefinieerde beperkingen. Je vermijdt ook de moeilijke taak om voor elk element namen van klassen te bedenken. Het resultaat is iets dat veel op Tailwind lijkt, maar ook profiteert van alle infrastructuur die rond TypeScript is gebouwd.

Met de Sprinkles API kun je ook schrijven voorwaarden en steno om responsieve stijlen te maken met behulp van hulpprogrammaklassen.

Afsluiten

vanille-extract voelt als een grote nieuwe stap in CSS-tooling. Er is veel aandacht besteed aan het ombouwen ervan tot een intuïtieve, robuuste oplossing voor styling die gebruikmaakt van alle kracht die statisch typen biedt.

Verdere lezing

Bron: https://css-tricks.com/css-in-typescript-with-vanilla-extract/

Tijdstempel:

Meer van CSS-trucs