Het overbruggen van de Kloof Tussen CSS en JavaScript: CSS Modules, PostCSS en de Toekomst van CSS

0
8

In de vorige post in deze twee-delige serie, verkenden we de CSS-JS landschap en bleek niet alleen dat de CSS-JS kan produceren kritische stijlen, maar ook dat sommige bibliotheken hebben niet eens een runtime. We zagen dat de gebruikerservaring aanzienlijk kan verbeteren door het toevoegen van slimme optimalisaties, dat is de reden waarom deze serie richt zich op de developer – ervaring (de ervaring van het schrijven van stijlen).

In dit deel bespreken we de instrumenten voor “normaal ol’ CSS” door refactoring de Foto onderdeel van onze bestaande voorbeeld.

Controverse en #hotdrama

Een van de meest beroemde CSS debatten is de vraag of de taal is prima maar de manier waarop die het is. Ik denk dat dit debat blijft in leven omdat er een kern van waarheid is aan beide zijden. Bijvoorbeeld, hoewel het waar is dat de CSS werd in eerste instantie ontworpen om de stijl van een document, in plaats van de componenten van een applicatie, het is ook waar dat de komende CSS eigenschappen drastisch zal veranderen, en dat veel CSS fouten komen voort uit de behandeling van styling als een bijzaak in plaats van het nemen van de tijd om te leren het goed of het inhuren van iemand die goed in zijn.

Ik denk niet dat CSS tools zelf aan de bron van de controverse; we zullen waarschijnlijk altijd gebruik maken van hen in bepaalde mate op zijn minst. Maar benaderingen zoals CSS-JS zijn verschillend in die zin dat zij patch-up van de tekortkomingen van CSS en client-side JavaScript. Echter, CSS-JS is niet de enige benadering van hier; het is alleen maar het nieuwste van het nieuwste. Vergeet niet, wanneer we gebruikt hebben soortgelijke debatten over preprocessors, zoals Sass? Sass heeft functies, zoals mixins, die niet zijn gebaseerd op een CSS-voorstel (en niet te vergeten de hele ingesprongen syntaxis). Echter, Sass is geboren in een heel andere tijd en een punt heeft bereikt waar het niet langer eerlijk te zijn in het debat, omdat het debat zelf heeft veranderd — zo zijn we begonnen met het bekritiseren van CSS-JS, want het is een makkelijker doelwit.

Ik denk dat we moeten gebruiken gereedschappen die het laat ons beoogde gebruik van de syntaxis van vandaag. Laten we gebruik maken van JavaScript Beloften als een analogie. Deze functie wordt niet ondersteund door Internet Explorer, dus veel mensen hebben een polyfill. De punt van polyfills is om ons om te doen alsof de functie is overal wordt ondersteund door het vervangen van de native browser implementaties met een patch. Hetzelfde geldt voor transpiling nieuwe syntaxis met hulpmiddelen, zoals Babel. We kunnen het gebruiken van vandaag, omdat de code wordt gecompileerd naar een oudere, goed ondersteund syntaxis. Dit is een goede benadering, omdat het ons in staat stelt om met toekomstige functies van vandaag, terwijl het duwen van JavaScript vooruit de weg pre-processing tools, zoals Sass, hebben geduwd CSS vooruit.

Mijn kijk op de CSS-controverse is dat moeten we gebruik maken van tools die ons in staat stellen te gebruiken toekomst CSS vandaag.

Preprocessors

We hebben al gesproken over CSS preprocessors, dus het is de moeite waard te bespreken in een beetje meer details en hoe ze passen in de CSS-JS gesprek. We hebben Sass, Minder en PostCSS (onder andere) dat kan leiden onze CSS-code met allerlei nieuwe functies.

Voor ons voorbeeld, we zijn alleen maar bezig met nesten, een van de meest voorkomende en krachtige functies van preprocessors. Ik stel met behulp van PostCSS want het geeft ons fijnmazige controle over de functies die we toevoegen, en dat is precies wat we nodig hebben in dit geval. De PostCSS plugin die we gaan gebruiken is postcss-nesten, want het volgt de eigenlijke voorstel voor native CSS nesten.

De beste manier om het gebruik van PostCSS met het samenstellen van onze tool, webpack, is het toevoegen van postcss-loader na de css-lader configuratie. Bij het toevoegen van laders na de css-loader, is het belangrijk om rekening te houden hen in de css-lader opties door het instellen van importLoaders om het aantal slagen laders, die in dit geval 1:

{
test: /.css$/,
gebruik: [
‘stijl-loader’
{
loader: ‘css-loader’
opties: {
importLoaders: 1,
},
},
‘postcss-loader’
],
}

Dit zorgt ervoor dat CSS-bestanden geïmporteerd uit andere CSS-bestanden verwerkt zullen worden met postcss-loader.

Na het instellen van postcss-loader, we installeren postcss nesten en op te nemen in de PostCSS configuratie:

garen toevoegen postcss nesten

Er zijn vele manieren om te configureren PostCSS. In dit geval gaan we voor het toevoegen van een postcss.config.js bestand in de hoofdmap van ons project:

de module.export = {
plugins: {
“postcss-nesten”: {},
},
}

Nu kunnen we schrijven een CSS-bestand voor de Foto ‘ component. We noemen het de Foto.css:

.foto {
breedte: 200px;
&.afgeronde {
border-radius: 1rem;
}
}

@media (min-width: 30rem) {
.foto {
width: 400px;
}
}

Laten we ook het toevoegen van een bestand aan met de naam utils.css bevat een klasse voor visueel verbergen van elementen, die we in het eerste deel van deze serie:

.visuallyHidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
marge: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}

Sinds onze component is gebaseerd op dit programma, we zijn utils.css om Foto.css door het toevoegen van een @import instructie aan de bovenkant:

@import url(‘utils.css’);

Dit zal ervoor zorgen dat webpack vereist utils.css, dankzij de css-loader. We kunnen utils.css overal waar we zijn willen en passen de @import-pad. In dit specifieke geval, het is een broertje van de Foto.css.

Daarna laten we u Foto importeren.css in onze JavaScript-bestand en gebruik de klassen om de stijl van onze component:

importeren Reageren van ‘reageren’
importeren { getSrc, getSrcSet }’. /utils’
importeren ‘./De foto.css’

const Foto = ({ publicId, alt, afgerond }) => (
<afbeelding>
<img
className={afgerond ? ‘foto ‘ afgerond’ : de foto ‘s}
src={getSrc({ publicId, breedte: 200 })}
srcSet={getSrcSet({ publicId, breedtes: [200, 400, 800] })}
maten=”(min-width: 30rem) 400px, 200px”
/>
<figcaption className=”visuallyHidden”>{alt}</figcaption>
</figure>
)

De foto.defaultProps = {
afgerond: false,
}

export standaard Foto

Hoewel dit werk, onze klasse namen zijn veel te simpel en zij zullen zeker botsen met anderen volledig los van ons .foto-klasse. Een van de manieren om dit met behulp van een naamgeving methodologie, zoals BEM, om de naam van onze lessen (bijv. photo_rounded en foto__wat-is-dat-ik-kan-zelfs) om te helpen voorkomen dat conflicten uit de hand is, maar onderdelen snel complex en class namen hebben de neiging om lange, afhankelijk van de complexiteit van het project.

Aan CSS Modules.

CSS Modules

Simpel gezegd, CSS Modules zijn CSS-bestanden waarin alle namen van de klassen en de animaties zijn afgestemd lokaal standaard. Ze lijken op gewone CSS. Bijvoorbeeld, kunnen wij gebruik maken van onze Foto.css en utils.css bestanden (zoals CSS Modules zonder het aan te passen, simpelweg door het passeren van de modules: waar te css-lader opties:

{
loader: ‘css-loader’
opties: {
importLoaders: 1,
modules: true,
},
}

CSS Modules zijn een zich ontwikkelende functie en kunnen worden besproken met zelfs grotere lengte. Robin de drie-delige serie over het is een goed overzicht en inleiding.

Terwijl CSS Modules zelf erg lijken op de gewone CSS, de manier waarop we ze gebruiken, is nogal verschillend. Ze worden geïmporteerd in JavaScript als objecten waar de toetsen komen overeen met de auteur namen van de klassen en waarden van de unieke klasse namen die automatisch zijn gegenereerd voor ons die houden van de scope beperkt tot een onderdeel:

importeren Reageren van ‘reageren’
importeren { getSrc, getSrcSet }’. /utils’
import styles ‘./De foto.css’
importeren stylesUtils uit ‘./utils.css’

const Foto = ({ publicId, alt, afgerond }) => (
<afbeelding>
<img
className={afgerond
? `${stijlen.foto} ${stijlen.afgeronde}`
: stijlen.foto}
src={getSrc({ publicId, breedte: 200 })}
srcSet={getSrcSet({ publicId, breedtes: [200, 400, 800] })}
maten=”(min-width: 30rem) 400px, 200px”
/>
<figcaption className={stylesUtils.visuallyHidden}>{alt}</figcaption>
</figure>
)

De foto.defaultProps = {
afgerond: false,
}

export standaard Foto

Sinds we met behulp van utils.css CSS-Module, we kunnen verwijderen van de @import-instructie aan de bovenkant van de Foto.css. Let ook op dat het gebruik van camelCase te formatteren, worden klasse namen, waardoor ze eenvoudiger te gebruiken in JavaScript. Als we hadden gebruikt streepjes, we zouden hebben om dingen op te schrijven in de volle, zoals stylesUtils[‘visueel verborgen’].

CSS Modules hebben extra functies, zoals de samenstelling. Nu, we importeren utils.css in Photo.js om onze component stijlen, maar laten we zeggen dat we willen op een verschuiving van de verantwoordelijkheid van de styling van het bijschrift van de Foto.css in plaats daarvan. Op die manier, zo ver als onze JSX code is betrokken, stijlen.bijschrift is gewoon een klasse naam; het is gewoon zo gebeurt het om visueel te verbergen element, maar het kan gestyled worden in de toekomst anders. Één van beide manier, Foto.css zal het maken van deze beslissingen.

Dus laten we een bijschrift toevoegen stijl op de Foto.css voor het verlengen van de eigenschappen van de visuallyHidden utility met behulp van componeert:

.caption {
componeert: visuallyHidden uit ‘./utils.css’;
}

We kunnen net zo goed door het toevoegen van meer regels die klasse, maar dit is alles wat we nodig hebben in dit geval. Nu hoeven we niet meer te importeren utils.css in Photo.js we kunnen gewoon gebruik maken van stijlen.bijschrift in de plaats:

<figcaption className={stijlen.bijschrift}>{alt}</figcaption>

Hoe werkt dit? Doe de stijlen van visuallyHidden gekopieerd naar bijschrift? Laten we eens kijken naar de waarde van stijlen.bijschrift — whoa, twee klassen! Dat klopt: een van visuallyHidden en de andere zal de toepassing van elke andere stijlen die we toevoegen om een bijschrift. CSS-JS maakt het te gemakkelijk om dubbele stijlen met bibliotheken, als gepolijst, maar CSS Modules raden u aan hergebruik van bestaande stijlen. Geen behoefte om een nieuwe VisuallyHidden Reageren component slechts van toepassing op verschillende CSS-regels.

Laten we nog een stap verder door het onderzoeken van dit ongemakkelijk klasse samenstelling:

afgerond
? `${stijlen.foto} ${stijlen.afgeronde}`
: stijlen.foto

Er zijn bibliotheken voor deze situaties, zoals klassenamen aan, die nuttig zijn voor de meer complexe klasse samenstelling. In ons voorbeeld, hoewel, we kunnen blijven gebruiken, componeert en wijzigen .afgerond op .roundedPhoto:

.foto {
breedte: 200px;
}

.roundedPhoto {
componeert: foto;
border-radius: 1rem;
}

@media (min-width: 30rem) {
.foto {
width: 400px;
}
}

.caption {
componeert: visuallyHidden uit ‘./utils.css’;
}

Nu kunnen we de klasse namen onze component in een meer leesbare wijze:

afgeronde ? stijlen.roundedPhoto : stijlen.foto

Maar wacht, wat als we per ongeluk plaats .roundedPhoto regelset voor .foto en enkele regels uit .foto eindigen dwingende regels uit .roundedPhoto vanwege de specificiteit? Maak je geen zorgen, CSS Modules voorkomen dat we van het samenstellen van klassen gedefinieerd na de huidige klasse door het gooien van een fout als deze:

verwezen klasse de naam “photo” in componeert niet gevonden (2:3)

1 | .roundedPhoto {
> 2 | componeert: foto;
| ^
3 | border-radius: 1rem;
4 | }

Merk op dat het over het algemeen een goed idee om een bestand naamgevingsconventie voor CSS Modules, bijvoorbeeld voor het gebruik van de extensie .de module.css, want het is normaal om te willen om enkele globale stijlen.

Dynamische stijlen

We hebben het voorwaardelijk toepassen van vooraf gedefinieerde sets van stijlen, dat heet voorwaardelijke styling. Wat doen we als we willen ook in staat zijn om te fine-tunen van de grens straal van de afgeronde foto ‘ s? Dit is de zogenaamde dynamische styling , omdat we niet weten wat de waarde is van plan om van tevoren; het kan veranderen, terwijl de toepassing wordt uitgevoerd.

Er zijn niet veel gebruik van de gevallen voor dynamische styling — meestal zijn we styling voorwaardelijk, maar in gevallen als deze hebben wij nodig, hoe zouden we dit aanpakken? Terwijl we door met het inline-stijlen, een native oplossing voor dit soort problemen is aangepaste eigenschappen (een.k.een. CSS-variabelen). Een zeer waardevol aspect van deze functie is dat browsers update stijlen met behulp van aangepaste eigenschappen als JavaScript veranderingen. Kunnen We stellen een aangepaste woning op een element met inline stijlen, wat betekent dat het zal worden binnen het bereik van dat element en dat element alleen:

style={typeof borderRadius !== ‘undefined’ ? {
‘–border-radius’: borderRadius,
} : null}

In De Foto.css, kunnen we gebruik maken van deze aangepaste eigenschap met behulp van (var) en het passeren van de standaard waarde als het tweede argument:

.roundedPhoto {
componeert: foto;
border-radius: var(–border-radius, 1rem);
}

Zo ver als JavaScript is betrokken, het is alleen het passeren van een dynamische parameter CSS, dan wanneer CSS neemt, kan de waarde zoals die is, het berekenen van een nieuwe waarde uit het gebruik van calc(), etc.

Fallback

Op het moment van dit schrijven is de browser ondersteuning voor aangepaste eigenschappen is… nou ja, bepaal je zelf. Het niet ondersteunen van deze browsers is (waarschijnlijk) uit de vraag voor een real-world-toepassing, maar in gedachten houden dat sommige stijlen zijn minder belangrijk dan anderen. In dit geval is het niet een big deal als de grens straal op IE altijd 1rem. De toepassing hoeft niet te kijken op dezelfde manier op elke browser.

De manier waarop we automatisch uitwijkmogelijkheden voor alle aangepaste eigenschappen te installeren postcss-aangepaste-eigenschappen en voeg het toe aan onze PostCSS configuratie:

garen toevoegen postcss-aangepaste-eigenschappen
de module.export = {
plugins: {
‘postcss nesten’: {},
‘postcss-aangepaste-eigenschappen”: {},
},
}

Dit zal leiden tot een terugval voor onze border-radius regel:

.roundedPhoto {
componeert: foto;
border-radius: 1rem;
border-radius: var(–border-radius, 1rem);
}

Browsers die dat niet begrijpen (var) zal negeren deze regel en gebruik dan de vorige. Laat niet de naam van de plugin je gek; al was het maar gedeeltelijk verbetert de ondersteuning voor aangepaste eigenschappen door het verstrekken van statische uitwijkmogelijkheden. Het dynamische aspect kan niet worden polyfilled.

Het blootstellen van waarden JavaScript

In het vorige deel van deze serie, hebben we onderzocht hoe CSS-JS stelt ons in staat om te delen bijna alles tussen CSS en JavaScript, met behulp van media queries als een voorbeeld. Er is geen manier om dit te bereiken, hier?

Met dank aan Jonathan Neal, je kunt!

Ten eerste, voldoen aan postcss-preset-env, de opvolger van de cssnext. Het is een PostCSS plugin die fungeert als een preset vergelijkbaar met @babel/preset-env. Het bevat plug-ins zoals postcss-nesten, postcss-aangepaste-eigenschappen, autoprefixer enz. zo kunnen we gebruik maken van de toekomst CSS vandaag. Het splitst de plug-ins over de vier stadia van standaardisatie. Enkele van de functies die ik zou u graag laten zien, zijn niet opgenomen in het standaard bereik (fase 2+), dus zullen we dat expliciet inschakelen, die moeten we hebben:

garen toevoegen postcss-preset-env
de module.export = {
plugins: {
‘postcss-preset-env’: {
kenmerken: {
‘nesting-regels’: true,
‘custom properties’: true, // al is opgenomen in etappe 2+
‘custom-media-query ‘s’: true, // oooh, wat is dit? 🙂
},
},
},
}

Merk op dat we vervangen onze bestaande plug-ins omdat dit postcss-preset-env configuratie omvat hen, wat betekent dat onze bestaande code moet werken op dezelfde manier als voorheen.

Met behulp van aangepaste eigenschappen in de media query is ongeldig omdat dat niet is wat ze zijn ontworpen voor. In plaats daarvan gebruiken we aangepaste media query ‘ s:

@custom-media –foto-breakpoint (min-width: 30em);

.foto {
breedte: 200px;
}

@media (–foto-breekpunt) {
.foto {
width: 400px;
}
}

Hoewel deze functie is in het experimentele stadium en daarom niet ondersteund in elke browser, dankzij postcss-preset-env-het werkt gewoon! Een addertje onder het gras is dat PostCSS werkt op een per-bestand, dus op deze manier alleen de Foto.css kan gebruiken –foto-breekpunt. Laten we daar iets aan doen.

Jonathan Neal implementeerde onlangs een importFrom optie in postcss-preset-env, die wordt doorgegeven aan andere plugins die dit ondersteunen, zoals postcss-aangepaste-eigenschappen en postcss-aangepaste-media. De waarde kan vele dingen, maar voor het doel van ons voorbeeld, het is een pad naar een bestand dat moet worden geïmporteerd naar de bestanden PostCSS processen. We noemen dit een globale.css en verplaatsen onze custom media query is er:

@custom-media –foto-breakpoint (min-width: 30em);

…en laten we definiëren importFrom, het pad naar global.css:

de module.export = {
plugins: {
‘postcss-preset-env’: {
importFrom: ‘src/global.css’,
kenmerken: {
‘nesting-regels’: true,
‘custom properties’: true,
‘custom-media-query ‘s’: true,
},
},
},
}

Nu kunnen we verwijderen de @custom-media-lijn aan de bovenkant van de Foto.css en onze-foto-breekpunt waarde zal nog steeds werken, omdat postcss-preset-env zal gebruik maken van één van de wereldwijde.css te compileren. Hetzelfde geldt voor aangepaste eigenschappen en aangepaste kiezers.

Nu, hoe bloot te JavaScript? Wanneer experimentele functies, zoals aangepaste media query ‘ s voor gestandaardiseerde en geïmplementeerd in de belangrijkste browsers, zullen we in staat zijn om ze op te halen native van CSS. Dit is bijvoorbeeld hoe we zouden de toegang tot een aangepaste eigenschap genaamd –font-family gedefinieerd op :root:

const rootStyles = getComputedStyle(document.lichaam)
const fontFamily = rootStyles.getPropertyValue(‘–font-family’)

Als custom media query ‘ s krijgen gestandaardiseerde waarschijnlijk zullen we in staat zijn om toegang te krijgen op een vergelijkbare manier, maar in de tussentijd hebben we een alternatief te vinden. We konden gebruik maken van de exportTo optie voor het genereren van een JavaScript-of JSON-bestand, importeren in JavaScript. Het probleem is echter dat webpack zou proberen te verlangen, voordat deze is gegenereerd. Zelfs als wij gegenereerd voordat u webpack elke update wereldwijd.css zou veroorzaken webpack opnieuw compileren twee keer, een keer voor het genereren van de output bestand, en eens te meer om het te importeren. Ik wilde een oplossing die niet gehinderd door de uitvoering ervan.

Voor deze serie, ik heb een nieuwe webpack loader genaamd css-douane-lader voor jou! Het maakt deze taak eenvoudig: alles wat we nodig hebben, is dit in ons webpack-configuratie voordat css-lader:

{
test: /.css$/,
gebruik: [
‘stijl-loader’
‘css-douane-loader’
{
loader: ‘css-loader’
opties: {
importLoaders: 1,
},
},
‘postcss-loader’
],
}

Dit geeft custom media query ‘ s, evenals aangepaste eigenschappen, JavaScript. Kunnen We toegang krijgen tot hen door het importeren van wereldwijde.css:

importeren Reageren van ‘reageren’
importeren { getSrc, getSrcSet }’. /utils’
import styles ‘./de foto.de module.css’
importeren { customMedia }’. /global.css’

const Foto = ({ publicId, alt, afgerond, borderRadius }) => (
<afbeelding>
<img
className={afgerond ? stijlen.roundedPhoto : stijlen.foto}
style={
typeof borderRadius !== ‘undefined’
? { [‘–border-radius’]: borderRadius }
: null
}
src={getSrc({ publicId, breedte: 200 })}
srcSet={getSrcSet({ publicId, breedtes: [200, 400, 800] })}
maten={`${customMedia[‘–foto-breekpunt’]} 400px, 200px`}
/>
<figcaption className={stijlen.bijschrift}>{alt}</figcaption>
</figure>
)

De foto.defaultProps = {
afgerond: false,
}

export standaard Foto

Dat is het!

Ik heb een archief te tonen van alle begrippen die in deze serie. Het leesmij-bestand bevat ook een aantal geavanceerde tips over de aanpak zoals beschreven in dit bericht.

Bekijk Repo

Conclusie

Het is veilig om te zeggen dat tools zoals CSS Modules en PostCSS en aankomende CSS-functies zijn om de taak van het omgaan met de vele uitdagingen van CSS. Welke kant van de CSS debat je ook bent, deze aanpak is het onderzoeken waard.

Ik heb een sterke CSS-JS achtergrond, maar ik ben erg gevoelig voor hypes, dus houden met die wereld is het heel moeilijk voor mij. Terwijl het hebben van stijlen naast het probleem kan wees beknopt, het is ook de vermenging van twee zeer verschillende talen — CSS is zeer uitgebreid ten opzichte van JavaScript. Deze me aangezet tot het schrijven minder CSS, omdat ik wilde voorkomen om het bestand te druk. Dit kan een kwestie van persoonlijke voorkeur, maar ik wilde niet dat het een probleem. Met behulp van een apart bestand voor CSS uiteindelijk gaf mijn code weer wat lucht.

Terwijl het beheersen van deze aanpak is misschien niet zo eenvoudig als het CSS-JS, ik geloof dat het meer de moeite waard op de lange termijn. Het verbeteren van uw CSS-skills en maak je beter voor te bereiden op haar toekomst.

Serie Artikelen:

  1. CSS-JS
  2. CSS Modules, PostCSS en de Toekomst van CSS (Deze post)