Motywy
Na tej stronie
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.
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.
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():
| Prop | Typ | Domyślnie | Opis |
|---|---|---|---|
preset | 'neumorphism' | 'neubrutalism' | 'superneon' | — | Zmienia wygląd każdego komponentu presetem, dołączonym albo wstawionym po głównym arkuszu. |
inline | boolean | false | Wypuść sam CSS zamiast odnośnika do niego. |
hash | boolean | true | Wstaw skrót treści w nazwę pliku. Tylko postać z odnośnikiem. |
base | string | '/su/' | Skieruj adres gdzie indziej; tamtą kopię hostujesz Ty. Tylko postać z odnośnikiem. |
minify | boolean | true | Usuń komentarze i białe znaki. Tylko postać wbudowana. |
nonce | string | — | Nonce CSP dla wypuszczonego elementu. |
stylesUrl() przyjmuje base i hash; stylesheet() przyjmuje minify i preset.
theme(tokens, options):
| Prop | Typ | Domyślnie | Opis |
|---|---|---|---|
selector | string | ':root' | Ogranicza nadpisania do poddrzewa. |
dark | object | — | Nadpisania stosowane tylko w trybie ciemnym. |
nonce | string | — | Nonce CSP. |