Icone
In questa pagina
icon() restituisce un <svg> inline. Ogni glifo è disegnato sulla stessa griglia 24×24 come tratti non riempiti in currentColor, quindi eredita il colore e la dimensione del carattere di ciò in cui sta e non ha bisogno di stile proprio.
stack({ direction: 'row', gap: 'md', align: 'center' },
icon('check'),
icon('search'),
icon('trash'),
icon('settings'),
)Dentro un componente
Un’icona è un figlio come tutti gli altri. Poiché si dimensiona in em, si accorda all’etichetta che le sta accanto senza che le venga detto quanto è grande:
stack({ direction: 'row', gap: 'sm', align: 'center', wrap: true },
button({ color: 'primary' }, icon('download'), 'Scarica'),
button({ variant: 'outline' }, icon('external-link'), 'Apri'),
button({ size: 'sm', variant: 'soft', color: 'danger' }, icon('trash'), 'Elimina'),
iconButton({ label: 'Cerca', variant: 'soft', icon: icon('search') }),
)Dimensione
Il valore predefinito è 1em — la dimensione del testo circostante. size accetta un token o qualunque lunghezza CSS quando vuoi staccartene:
stack({ direction: 'row', gap: 'md', align: 'center' },
icon('star', { size: 'sm' }),
icon('star'),
icon('star', { size: 'lg' }),
icon('star', { size: '2rem' }),
icon('star', { size: '3rem' }),
)Colore
Non esiste una prop per il colore. Un’icona è disegnata in currentColor, quindi prende il colore del proprio contesto — ed è questo che fa funzionare un solo insieme dentro cinque palette:
stack({ direction: 'row', gap: 'md', align: 'center' },
text({ style: 'color: var(--su-primary)' }, icon('heart', { size: 'lg' })),
text({ style: 'color: var(--su-success)' }, icon('check-circle', { size: 'lg' })),
text({ style: 'color: var(--su-warning)' }, icon('alert-triangle', { size: 'lg' })),
text({ style: 'color: var(--su-danger)' }, icon('x-circle', { size: 'lg' })),
text({ tone: 'muted' }, icon('info', { size: 'lg' })),
)Nomi accessibili
Un’icona è aria-hidden per impostazione predefinita, cosa giusta molto più spesso che no: un’icona accanto alla parola “Elimina” non dovrebbe essere annunciata una seconda volta. Dalle una label solo quando è l’icona a portare tutto il significato, e diventa role="img" con quel nome.
icon('trash') // decorativa — nascosta
button(icon('trash'), 'Elimina') // è la parola a parlare
icon('trash', { label: 'Elimina' }) // annunciata come immagine
// Un pulsante di sola icona dà il nome al pulsante, non al glifo dentro
iconButton({ label: 'Elimina', icon: icon('trash') })Riempita
filled dipinge un glifo invece di delinearlo. Il tracciato è lo stesso nei due casi — cambia solo l’attributo fill — quindi le due forme condividono esattamente il bordo esterno e non possono divergere.
stack({ direction: 'row', gap: 'md', align: 'center' },
icon('bell', { size: 'lg' }),
icon('bookmark', { size: 'lg' }),
icon('folder', { size: 'lg' }),
icon('heart', { size: 'lg' }),
icon('star', { size: 'lg' }),
)E gli stessi nomi riempiti:
stack({ direction: 'row', gap: 'md', align: 'center' },
icon('bell', { filled: true, size: 'lg' }),
icon('bookmark', { filled: true, size: 'lg' }),
icon('folder', { filled: true, size: 'lg' }),
icon('heart', { filled: true, size: 'lg' }),
icon('star', { filled: true, size: 'lg' }),
)È una prop e non un secondo insieme di nomi perché lo stato riempito è quasi sempre uno stato — salvato, piaciuto, valutato — quindi vuole un booleano, non una stringa diversa:
icon('heart', { filled: liked })
icon('bookmark', { filled: saved, label: saved ? 'Salvato' : 'Salva' })
// invece che
icon(liked ? 'heart-filled' : 'heart')I glifi di stato si riempiono in modo diverso, perché il loro segno sta dentro la forma. Dipingere il cerchio inghiottirebbe la spunta, quindi il segno viene invece ritagliato fuori:
stack({ direction: 'row', gap: 'md', align: 'center' },
icon('check-circle', { filled: true, size: 'lg' }),
icon('x-circle', { filled: true, size: 'lg' }),
icon('info', { filled: true, size: 'lg' }),
icon('help', { filled: true, size: 'lg' }),
icon('alert-triangle', { filled: true, size: 'lg' }),
)Quelli portano un secondo disegno — la forma piena con il segno ritagliato da fill-rule: evenodd — perché un ritaglio non si può ottenere dal tracciato del contorno cambiando un attributo. La forma esterna è disegnata sul bordo esterno del contorno, così le due forme finiscono comunque sulla stessa sagoma. La prop è la stessa in entrambi i casi; quale meccanismo usi un glifo sono affari suoi.
Un chevron non ha alcun interno da dipingere — è una linea aperta — quindi si riempie fino al triangolo che descrivono i suoi tre punti, mantenendo il tratto che arrotonda gli angoli:
stack({ direction: 'row', gap: 'md', align: 'center' },
icon('chevron-up', { filled: true, size: 'lg' }),
icon('chevron-down', { filled: true, size: 'lg' }),
icon('chevron-left', { filled: true, size: 'lg' }),
icon('chevron-right', { filled: true, size: 'lg' }),
)fillableIcons() elenca tutto ciò che risponde a filled. Un glifo senza forma riempita lo ignora e resta delineato — riempire eye perderebbe la pupilla e tag il suo foro, quindi nessuno dei due fa finta di niente.
Rotazione
spin fa ruotare il glifo — pensato per spinner, anche se nulla ti impedisce di far girare refresh mentre qualcosa si ricarica. Rallenta fino quasi a fermarsi invece di arrestarsi sotto prefers-reduced-motion, perché una rotella che si ferma sembra rotta.
stack({ direction: 'row', gap: 'md', align: 'center' },
icon('spinner', { spin: true, size: 'lg' }),
icon('refresh', { spin: true, size: 'lg' }),
button({ variant: 'soft' }, icon('spinner', { spin: true }), 'Salvataggio…'),
)L’insieme
I nomi descrivono il disegno più che il compito che svolge — x-circle, non error — perché lo stesso disegno viene usato per compiti senza relazione fra loro, e un nome che descrive l’immagine resta vero anche allora. Gli alias qui sotto coprono le intenzioni più comuni.
Marchi
Con l’insieme arrivano otto marchi — facebook, google, instagram, linkedin, tiktok, whatsapp, x-twitter e youtube. Accettano comunque size e label e si disegnano comunque in currentColor:
stack({ direction: 'row', gap: 'md', align: 'center', wrap: true },
icon('facebook', { size: 'lg' }),
icon('instagram', { size: 'lg' }),
icon('x-twitter', { size: 'lg' }),
icon('youtube', { size: 'lg' }),
icon('whatsapp', { size: 'lg' }),
button({ variant: 'soft', color: 'neutral' }, icon('linkedin'), 'Condividi'),
)Sono riproduzioni di marchi altrui e non disegni nello stile di questa libreria, quindi ne infrangono di proposito due regole: sono forme piene invece che tratti, che è quello che è un logo, e le loro proporzioni sono quelle del marchio e non di questa griglia. filled per loro non significa nulla — lo sono già.
Il disegno viene da Simple Icons, che lo rilascia sotto CC0. Questo copre il disegno, non il marchio registrato: usali per indicare la cosa che nominano — un link a un profilo, un pulsante di condivisione — e non su un prodotto tuo.
È x-twitter, non x, perché x è già un alias di close e un pulsante di chiusura che si trasforma in un logo sarebbe una brutta sorpresa. Anche twitter si risolve su di esso.
Alias
Ognuno di questi renderizza un glifo elencato qui sopra, sotto il nome a cui più probabilmente penseresti:
success → check-circle
warning → alert-triangle
danger, error → x-circle
x, cross → close
question → help
loading → spinner
cog, gears → gear
delete, trash-can → trash
pencil → edit
notification → bell
dots → more-horizontal
bolt, lightning → zap
arrow-back → arrow-left
arrow-forward → arrow-right
cart → shopping-cart
bag → shopping-bag
card → credit-card
cash, money → banknote
delivery, shipping → truck
shop → store
discount, sale → percent
login, sign-in → log-in
logout, sign-out → log-out
map-pin, marker → location
mobile → smartphone
like → thumbs-up
dislike → thumbs-down
comment, message, chat → comment-bubble
ai, magic → sparkles
printer → print
accessibility, a11y → universal-access
twitter → x-twitter
Icone tue
registerIcons() aggiunge un glifo, oppure ne sostituisce uno incorporato. Il markup è il contenuto dell’<svg> — forme sulla stessa griglia 24×24, lasciate non riempite così che currentColor le raggiunga. Chiamala una volta sola da un modulo che le tue pagine importano:
import { registerIcons } from 'sitelo/ui'
registerIcons({
logo: '<path d="M4 20 12 4l8 16z"/>',
// Un nome che esiste già lo sostituisce ovunque, ed è così che si
// ridisegna un glifo incorporato senza forkare la libreria.
check: '<path d="m5 13 4 4 10-11"/>',
// Una sola forma chiusa, così può rispondere a `filled` come gli incorporati.
pin: { markup: '<path d="M12 21s7-6.3 7-11a7 7 0 1 0-14 0c0 4.7 7 11 7 11z"/>', fillable: true },
})import { icon } from 'sitelo/ui'
icon('logo') // il tuo glifo
icon('check') // ora anche questo è tuo
registerIcons({ check: null }) // e si torna a quello incorporatoPerché inline, e non uno sprite
Le icone vengono renderizzate dentro la pagina invece di essere prese da un icons.svg con <use>. Uno sprite fa risparmiare nell’ordine del centinaio di byte gzippati di HTML per pagina e costa un viaggio di rete per farlo — il markup ripetuto è esattamente il caso in cui gzip dà il meglio, quindi la maggior parte di ciò che uno sprite esiste per deduplicare è già stata deduplicata. Inline significa anche che non c’è alcun file da emettere, nessun percorso base da configurare, e niente che possa sparire da dist — lo stesso compromesso che fa styles({ inline: true }).
Props
| Prop | Tipo | Predefinito | Descrizione |
|---|---|---|---|
name | string | — | Quale glifo. Si può passare come primo argomento anziché come prop. |
size | 'sm' | 'md' | 'lg' | string | 'md' | Un token, o qualunque lunghezza CSS. Il predefinito è 1em. |
label | string | — | Annunciala come immagine con questo nome, invece di nasconderla. |
spin | boolean | false | Falla ruotare di continuo. |
filled | boolean | false | Dipingi il glifo invece di delinearlo. Ignorata dai glifi che non si possono riempire. |
Un nome sconosciuto non renderizza proprio nulla invece di sollevare un errore — una prop estetica non deve poter far fallire una build. hasIcon(name) ti dice se ne esiste uno, iconNames() li elenca tutti, e fillableIcons() quelli che accettano filled.