Motywy

Każdy komponent czyta te same właściwości własne, więc motyw to zestaw nadpisań na :root — bez kroku budowania, bez pliku konfiguracyjnego i bez komponentu, któremu trzeba by o tym powiedzieć.

Wprowadzenie stylów

styles() zwraca <link> do jednego pliku, który przeglądarka buforuje na wszystkich stronach witryny. Nie ma czego konfigurować ani kopiować: wtyczka sitelo serwuje go w dev i zapisuje do buildu, pod tą samą bazą co runtime komponentów.

import { styles } from 'sitelo/ui'

head(
  title('Moja strona'),
  styles(),
)
// <link rel="stylesheet" href="/su/ui-c9428b65.css">

Nazwa niesie skrót treści, więc możesz serwować go jako immutable i wciąż wypuścić zmianę. Podaj { hash: false } dla zwykłego /su/ui.css albo base, żeby skierować odnośnik na kopię, którą hostujesz sam.

{ inline: true } wkłada zamiast tego cały arkusz w <style> — około 11 kB po gzipie na każdej stronie, ale bez dodatkowego żądania i bez niczego, co mogłoby zginąć z dist/. To lepszy kompromis dla pojedynczej strony; odnośnik odrabia swoje żądanie na drugiej stronie, którą odwiedzający przeczyta.

head(
  title('Moja strona'),
  styles({ inline: true }),
)

Reszta rodziny podaje Ci części. stylesheet() zwraca surowy CSS jako ciąg znaków — do hostowania arkusza tam, gdzie sitelo nie sięgnie, albo do zapisania go gdzieś samodzielnie — a stylesUrl() sam adres, do własnego elementu link.

Presety

Preset zmienia cały wygląd za jednym razem. styles({ preset: 'neumorphism' }) dołącza drugi arkusz zaraz po głównym — serwowany, hashowany i buforowany na tych samych zasadach — a każdy komponent na stronie za nim podąża, bez żadnej zmiany w znacznikach.

import { styles } from 'sitelo/ui'

head(
  title('Moja strona'),
  styles({ preset: 'neumorphism' }),
)
// <link rel="stylesheet" href="/su/ui-c9428b65.css">
// <link rel="stylesheet" href="/su/neumorphism-5d0e7b91.css">

Każdy preset ma własną stronę, na której wszystkie komponenty są przestylowane na żywo: Neumorfizm, Neobrutalizm, Superneon.

inline wstawia oba arkusze inline, stylesheet({ preset }) zwraca je jako jeden ciąg, a nazwa, która nie jest presetem, rzuca błąd z listą tych, które istnieją.

Nadpisywanie tokenów

theme() zapisuje nadpisania. Klucze to nazwy tokenów w camelCase, obiekty palet albo dosłowne właściwości własne — i idzie po styles(), więc wygrywa.

import { styles, theme } from 'sitelo/ui'

head(
  styles(),
  theme({
    primary: { base: '#5b5bd6', hover: '#4a4ac4', active: '#3f3fb0', fg: '#ffffff' },
    radiusMd: '2px',
    fontSans: '"Inter", system-ui, sans-serif',
  }),
)

Motywy ograniczone zakresem

selector ogranicza nadpisania do poddrzewa zamiast do całej strony. Robią tak trzy panele poniżej — te same komponenty, trzy różne palety, jedna strona.

indygo
różowy
zaokrąglony
fragment(
  theme({ primary: { base: '#5b5bd6', hover: '#4a4ac4', fg: '#ffffff', soft: '#e6e6fa', softFg: '#33338f', border: '#b9b9ee' } }, { selector: '.theme-indigo' }),
  theme({ primary: { base: '#b0357a', hover: '#962e68', fg: '#ffffff', soft: '#fbe4f0', softFg: '#7d1f53', border: '#f0a9ce' } }, { selector: '.theme-pink' }),
  theme({ radiusMd: '999px', radiusLg: '1.5rem' }, { selector: '.theme-round' }),
  grid({ min: '11rem' },
    div({ class: 'theme-indigo' },
      card(cardBody(stack({ gap: 'sm' },
        text({ variant: 'caption', tone: 'muted' }, 'indygo'),
        button({ block: true }, 'Primary'),
        button({ variant: 'soft', block: true }, 'Soft'),
      ))),
    ),
    div({ class: 'theme-pink' },
      card(cardBody(stack({ gap: 'sm' },
        text({ variant: 'caption', tone: 'muted' }, 'różowy'),
        button({ block: true }, 'Primary'),
        button({ variant: 'soft', block: true }, 'Soft'),
      ))),
    ),
    div({ class: 'theme-round' },
      card(cardBody(stack({ gap: 'sm' },
        text({ variant: 'caption', tone: 'muted' }, 'zaokrąglony'),
        button({ block: true }, 'Primary'),
        button({ variant: 'soft', block: true }, 'Soft'),
      ))),
    ),
  ),
)

Tryb ciemny

Ciemny rozstrzyga się sam z prefers-color-scheme. Jawny data-theme albo data-su-theme o wartości light lub dark na dowolnym przodku to nadpisuje — i tak właśnie dema na tej witrynie idą za przełącznikiem w górnym pasku.

Podaj dark dla nadpisań, które mają działać tylko tam. Obejmuje naraz atrybut i zapytanie medialne.

theme({
  primary: { base: '#5b5bd6' },
}, {
  dark: { primary: { base: '#8f8ff0' } },
})

Co da się nadpisać

Pięć palet po dziewięć miejsc każda, skala odstępów, krój, promienie, cienie i kolory powierzchni. Każde z nich jest właściwością własną — otwórz arkusz stylów albo inspektora przeglądarki, a wszystkie są na :root.

primary
neutral
success
warning
danger
xs
sm
md
lg
xl
stack({ gap: 'md' },
  stack({ direction: 'row', gap: 'sm', wrap: true },
    ...['primary', 'neutral', 'success', 'warning', 'danger'].map((color) =>
      stack({ gap: 'xs', align: 'center' },
        div({ style: 'width: 3.5rem; height: 2rem; border-radius: 0.4rem; background: var(--su-' + color + ')' }),
        text({ variant: 'caption', tone: 'muted' }, color),
      ),
    ),
  ),
  stack({ direction: 'row', gap: 'sm', wrap: true, align: 'flex-end' },
    ...['xs', 'sm', 'md', 'lg', 'xl'].map((step) =>
      stack({ gap: 'xs', align: 'center' },
        div({ style: 'width: var(--su-space-' + step + '); height: 2rem; border-radius: 0.2rem; background: var(--su-neutral)' }),
        text({ variant: 'caption', tone: 'muted' }, step),
      ),
    ),
  ),
)

Nazewnictwo

Klucz w camelCase staje się właściwością w kebab-case: radiusMd to --su-radius-md, a fontSans to --su-font-sans. Zagnieżdżony obiekt rozwija się tak samo — { primary: { softFg: … } } ustawia --su-primary-soft-fg — a klucz zaczynający się już od -- używany jest dokładnie tak, jak go zapisano, co jest wyjściem awaryjnym na wszystko, czego odwzorowanie nie obejmuje.

Paleta ma dziewięć miejsc: base, hover, active, fg, soft, softHover, softFg, border i ring. Ustaw tylko te, które zmieniasz.

Kontrast

Dołączone palety spełniają WCAG AA względem powierzchni, na których siedzą, w obu motywach, a w repozytorium jest test, który przerywa build, gdy przestanie to być prawdą. Twój własny motyw nie jest nim objęty — sprawdź swoje fg względem swojego base, zanim go opublikujesz.

Propsy

styles():

PropTypDomyślnieOpis
preset'neumorphism' | 'neubrutalism' | 'superneon'—Zmienia wygląd każdego komponentu presetem, dołączonym albo wstawionym po głównym arkuszu.
inlinebooleanfalseWypuść sam CSS zamiast odnośnika do niego.
hashbooleantrueWstaw skrót treści w nazwę pliku. Tylko postać z odnośnikiem.
basestring'/su/'Skieruj adres gdzie indziej; tamtą kopię hostujesz Ty. Tylko postać z odnośnikiem.
minifybooleantrueUsuń komentarze i białe znaki. Tylko postać wbudowana.
noncestring—Nonce CSP dla wypuszczonego elementu.

stylesUrl() przyjmuje base i hash; stylesheet() przyjmuje minify i preset.

theme(tokens, options):

PropTypDomyślnieOpis
selectorstring':root'Ogranicza nadpisania do poddrzewa.
darkobject—Nadpisania stosowane tylko w trybie ciemnym.
noncestring—Nonce CSP.