Sass
Ved å bruke Sass får du et kompileringssteg for CSS, som gir ekstra trygghet og fleksibilitet.
Namespacing
I CSS deler alle stilark det samme globale namespacet. Det får konsekvenser for hvordan vi navngir:
- klasser
- CSS-variabler
- animasjoner
I Jøkul skal disse starte med jkl
for å synliggjøre hvor CSSen kommer fra og unngå navnekollisjoner.
@include jkl.light-mode-variables {--jkl-link-color: #{jkl.$color-granitt};}@include jkl.dark-mode-variables {--jkl-link-color: #{jkl.$color-snohvit};}.jkl-link {--_jkl-link-private: #{jkl.$color-stein};color: var(--jkl-color);}
Block Element Modifier
Vi bruker metodikken Block Element Modifier (BEM) som navnekonvensjon i Jøkul. BEM og namespacing hjelper oss med å unngå navnekollisjoner både innad i Jøkul, og med brukerne av Jøkul.
Nøsting og parent selector (&
) i Sass gjør det enklere å skrive CSS på denne måten.
.jkl-block {/* ... */&__element {/* ... */&--element-modifier {/* ... */}}&--block-modifier {/* ... */}}
Unngå flere elementnivåer
Dersom du er i ferd med å skrive en block__element__another-element
-selector, stopp og tenk om element
egentlig er en annen block
.
Moduler
I Jøkul bruker vi modul-syntaks med @use og @forward, ikke @import
. Hver fil er en modul, på samme måte som du kanskje kjenner til fra TypeScript.
Moduler bør holdes til ett tema. Et tema kan for eksempel være CSSen for en komponent, mixins som har å gjøre med skjermstørrelser, eller funksjoner for konvertering av data.
Vi bruker ikke prefixes sammen med @forward
. Grunnen er at vi ønsker at det skal være enkelt å navigere i Jøkul-koden. Man skal kunne finne igjen variabelen innad i Jøkul med det samme navnet som man kjenner fra det offentlige APIet.
Vanlig forwarding uten å endre navn
Forwarding med prefix endrer navnet utad
Partials
Vi bruker partials (filer hvor navnet starter med _
) for alle filer som ikke skal kompileres til sin egen CSS-fil, men brukes som en part i andre CSS-filer.Eksempler er moduler som består utelukkende av variabler, funksjoner og mixins. Det kan også være en modul med CSS for en komponent, men hvor alt skal samles opp i én enkelt CSS-fil til slutt (se for eksempel packages/table).
Mappestruktur
Hver mappe med Sass skal ha en _index.scss
hvor det listes opp @forward
for alle filer i mappa, litt som en index.ts
kan gjøre i TypeScript.
For eksempel, hvis du har en mappe med to partials _one.scss
og _two.scss
må den samme mappen ha en _index.scss
som ser slik ut:
// _index.scss@forward "one";@forward "two";
Et unntak er på rotnivå i komponentpakkene. Her skal _index.scss
ha én @forward
som peker på hovedfila (for eksempel table.scss
), og så har den fila ansvaret for å hente inn alt som skal være med.
// packages/table/_index.scss@forward "table";// packages/table/table.scss@use "table-caption";@use "table-cell";@use "table-head";@use "table-header";@use "table-row";
Grunnen til dette unntaket er at vi ønsker at Sass skal lage en table.css
fra koden vår. Det er enklest dersom vi har en table.scss
som henter inn alt den trenger.
Hvorfor ha en index?
Index-fila er der for å gjøre det litt enklere å ta i bruk koden andre steder. Sammenlign for eksempel disse to kodesnuttene.
@forward "variables";@forward "mixins";@forward "functions";
I koden over har vi tre mapper: variables
, mixins
og functions
. Jeg som bruker disse mappene trenger ikke forholde meg til hva som er i disse mappene. Jeg vil bare ha det som finnes av variabler, mixins og funksjoner.
@forward "variables/breakpoints";@forward "variables/colors" as color-* hide varslingsfarge;@forward "variables/shadow";@forward "variables/shadows" as shadow-*;@forward "variables/spacing" hide $spacing;@forward "variables/typography" as typography-*;@forward "variables/z-index";@forward "mixins/helpers";@forward "mixins/screens";@forward "mixins/text-style";@forward "mixins/ornaments";@forward "mixins/motion";@forward "mixins/screenreader";@forward "mixins/underline";@forward "mixins/navigation";@forward "functions/convert";@forward "functions/easing";@forward "functions/timing";@forward "functions/responsive-units";
I dette eksempelet har vi ingen index. Jeg som bruker er nødt til å hente inn alle enkeltfiler som finnes. Dersom noen legger til en ny fil må jeg sørge for å oppdatere koden min til å også hente inn denne. Koden min må også endres dersom én av filene endrer navn. Om jeg bare hadde forholdt meg til mappenavn hadde jeg ungått dette.
Variabler
Det er to typer variabler du må forholde deg til:
- Sass-variabler som er tilgjengelige build time
- CSS-variabler som er tilgjengelige run time
Vi starter med CSS-variabler.
CSS-variabler
Vi bruker CSS-variabler for verdier som kan tenkes å endres når brukeren ser på siden. Det vanligste er å bruke CSS-variabler for farger, siden de kan endres hvis brukeren bytter tema.
Alle CSS-variabler prefixes med jkl
for å synliggjøre at en variabel kommer fra Jøkul og for å unngå navnekollisjoner.
Noen ganger trengs CSS-variabler internt i komponenten, uten intensjon om at de skal kunne endres utenfra. Om navnet starter med underscore betyr det at variabelen er privat. Disse er ikke en del av APIet og kan endres uten forvarsel.
@use "@fremtind/jkl-core/jkl";@include jkl.light-mode-variables {--jkl-table-row-border-color: #{jkl.$color-svaberg};}@include jkl.dark-mode-variables {--jkl-table-row-border-color: #{jkl.$color-stein};}.jkl-table-row {--_jkl-table-secret: "jeg kan endres uten forvarsel siden jeg starter med _";border-color: var(--jkl-table-row-border-color);}
Sass-variabler
Sass-variabler behandler vi som konstanter som har en fast verdi. Denne verdien kan gi mening å skille ut i en variabel for å unngå duplisering, eller for å tydeliggjøre en relasjon mellom verdier.
@use "@fremtind/jkl-core/jkl";.jkl-table-row {$_border-width: jkl.rem(1px);border-width: $_border-width;thead > & {border-width: $_border-width;}}
Med unntak av i packages/core
skal variabler som regel være private. Private variabler kan ikke brukes av andre moduler. Varibler er private om navnet starter med _
(underscore).
I motsetning til CSS-variabler skal public variabler ikke prefikses med jkl
. De fleste variabler er uansett private. For public variabler lener vi oss på Sass sitt modulsystem med innebygget namespacing, så et prefiks er unødvendig.
Variabler bør som regel være definert nærmest mulig der den brukes. Hvis en variabel bare er relevant innefor én selector, definer den der.
Variabler som er definert utenfor en CSS selector anses som globale innenfor sin modul. Globale variabler bør være definert øverst i fila, under @use
-statements.
Mixins
Med unntak av i packages/core
skal mixins som regel være private. Private mixins kan ikke brukes av andre moduler. Mixins er private om navnet starter med _
(underscore).
Animasjoner
CSS-animasjoner er ansett som private, ikke som en del av det offisielle APIet til en pakke. Vi bruker et mønster basert på interpolation og string.unique-id()for å få garantert unike IDer per bygg.
Vi prefikser likevel animasjonsnavnet med jkl
for å synliggjøre at animasjonen kommer fra Jøkul.
$_dismiss-animation-name: jkl-dismiss-#{string.unique-id()};.jkl-alert-message {/* ... */animation: $_dismiss-animation-name jkl.timing("lazy") jkl.easing("exit") forwards;/* ... */}@keyframes #{$_dismiss-animation-name} {from {/* ... */}to {/* ... */}}
Offentlig API
Mappenavn og filnavn er ikke en del av APIet til en pakke og kan endres uten forvarsel, med unntak av:
- Filnavnet som matcher pakkens navn, for eksempel
core.scss
ogtable.scss
. Disse navnene brukes av prosjekter som importerer CSS. packages/core/_jkl.scss
, som vi ser på som "hovedinngangen" til Sass-verktøy som mixins, funksjoner og variabler icore
Stabilt
@use "@fremtind/jkl-core/jkl";@use "@fremtind/jkl-table/table";
Ikke stabilt
@use "@fremtind/jkl-core/functions";@use "@fremtind/jkl-core/mixins";@use "@fremtind/jkl-core/variables";@use "@fremtind/jkl-table/table-row";
Variabler
Sass-variabler som er public er en del av pakkens API. En variabel er public dersom navnet ikke starter med _
(underscore) og variabelen er definert i det globale scopet (utenfor en CSS selector).
Sass-variabler som er public skal dokumenteres med SassDoc.
Navnet til CSS- og Sass-variabelen og typen verdi (for eksempel String
) er å anse som stabilt.
/* $_example-padding er brukt av to selectors, og hører hjemme som en global variabel */$_example-padding: jkl.$spacing-l;$_code-padding: jkl.$spacing-s;/// Variabler som ikke starter med _ og som er definert i globalt scope er en del av pakkens API og kan brukes av andre/// @type String$i-am-public: "Hello, world!";.jkl-example-1 {padding: $_example-padding;&__design {$_design-padding: jkl.$spacing-s;padding: $_design-padding;}}.jkl-example-2 {padding: $_example-padding;&__code {/* $_code-padding er bare brukt her, og burde vært definert her, slik som $_design-padding */padding: $_code-padding: jkl.$spacing-s;}}
Mixins og funksjoner
Public mixins og funksjoner er en del av pakkens API. De er public dersom navnet ikke starter med _
(underscore). Mixins og funksjoner som er public skal dokumenteres med SassDoc.
I public mixins og funksjoner er endring i navnet på parametere å anse som en breaking change. Det er fordi utviklere kan ha brukt keyword arguments.
Om du vil endre navnet på en bakoverkompatibel måte kan du beholdet det gamle navnet som et optional argument og håndtere begge parametere internt.
// Keyword arguments i bruk. Hvis navnet// på parameternavnet endres vil det brekke koden.@include jkl.forced-colors-svg-fallback($stroke: ButtonText, $fill: ButtonText);
SassDoc
All Sass-kode som er en del av pakkens offentlige API skal dokumenteres med SassDoc.
/// Kalkuler riktig rem-verdi basert på en gitt pixelverdi/// @param {Number} $px-size - Pixelverdi, helst med unit/// @return {Number} - Pixelverdien konvertert til rem/// @example/// jkl.rem(16px);@function rem($px-size) {// ...}
De viktigste annotasjonene
Det er typisk bare et utvalg dem du trenger å vite om, men se gjerne over listen med tilgjengelige annotasjoner.
Her er en oppsummering av de viktigste:
@content
bruker vi til å beskrive hvordan mixins som har en@content
bruker den.@deprecated
bruker vi for å gi hint om at noe ikke bør brukes lenger, helst med forklaring om hva som bør brukes i stedet. Ting merket@deprecated
vil typisk bli fjernet fra Jøkul etter hvert.@example
er fint for å gi et kodeeksempel.
@parameter
beskriver hvert enkelt parameter i funksjonen/mixinen.@return
beskriver kort hva funksjonen returnerer.
@type
gir et typehint om variabler som kan brukes i SassDoc. Har ingen faktisk typesjekking.
Du trenger ikke alltid bruke alle annotasjonene. Dokumenter det du synes gir mening, så kan det heller finpusses under code review.
Under er noen eksempler på hvordan du kan bruke de ulike annotasjonene.
/// Tilsvarer 16px/// @type Number$spacing-16: 1rem;/// Forenkler media queries som skal gjelde mellom to skjermbredder./// Maksverdien er _eksklusiv_ (verdien vil bli $max - 1px)./// @content Plasseres i et media query med min-width lik $min og max-width lik $max - 1px/// @example/// .class {/// @include jkl.screen-between(42px, 420px) {/// display: none;/// }/// }@mixin screen-between($min, $max) {@media (min-width: $min) and (max-width: #{$max - 1px}) {@content;}}/// @deprecated Bruk heller .jkl-nav-card, .jkl-task-card eller .jkl-info-card.jkl-card {// ...}/// Hjelper for å sette riktig farge på SVGer i Chrome for brukere med Forced Color-modus./// Se https://melanie-richards.com/blog/currentcolor-svg-hcm/ for detaljer/// @param {"Canvas" | "CanvasText" | "LinkText" | "GrayText" | "Highlight" | "HighlightText" | "ButtonFace" | "ButtonText"} $stroke - Definer hvilken systemfarge som skal brukes til stroke. Fargene har en forventet betydning for brukeren. Følg den semantiske betydningen til fargen, ikke velg fargen du selv synes "ser best ut"./// @param {"Canvas" | "CanvasText" | "LinkText" | "GrayText" | "Highlight" | "HighlightText" | "ButtonFace" | "ButtonText"} $fill [null] - Som $stroke, bare for fill. Valgfri./// @output Setter angitte verdier på nåværende selector og dens svg og path children, inni et media query som treffer dersom forced-colors er aktiv.@mixin forced-colors-svg-fallback($stroke, $fill: null) {// ...}