Ikon

icon() mengembalikan sebuah <svg> sebaris. Setiap glif digambar pada kisi 24ร—24 yang sama sebagai goresan tanpa isian dalam currentColor, jadi ia mewarisi warna dan ukuran fon dari apa pun yang ditumpanginya dan tidak butuh penataan sendiri.

stack({ direction: 'row', gap: 'md', align: 'center' },
  icon('check'),
  icon('search'),
  icon('trash'),
  icon('settings'),
)

Di dalam sebuah komponen

Ikon adalah anak seperti yang lain. Karena ia mengukur dirinya dalam em, ia cocok dengan label di sebelahnya tanpa diberi tahu seberapa besar label itu:

stack({ direction: 'row', gap: 'sm', align: 'center', wrap: true },
  button({ color: 'primary' }, icon('download'), 'Unduh'),
  button({ variant: 'outline' }, icon('external-link'), 'Buka'),
  button({ size: 'sm', variant: 'soft', color: 'danger' }, icon('trash'), 'Hapus'),
  iconButton({ label: 'Cari', variant: 'soft', icon: icon('search') }),
)

Ukuran

Bawaannya 1em โ€” ukuran teks di sekitarnya. size menerima sebuah token atau panjang CSS apa pun ketika Anda ingin lepas darinya:

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' }),
)

Warna

Tidak ada props warna. Ikon digambar dalam currentColor, jadi ia mengambil warna konteksnya โ€” dan itulah yang membuat satu kumpulan bekerja di dalam lima palet:

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' })),
)

Nama yang dapat diakses

Ikon ber-aria-hidden secara bawaan, dan itu jauh lebih sering benar daripada tidak: ikon di samping kata โ€œHapusโ€ tidak boleh diumumkan untuk kedua kalinya. Beri ia sebuah label hanya ketika ikonnya membawa seluruh maknanya, dan ia menjadi role="img" dengan nama itu.

icon('trash')                      // hiasan โ€” disembunyikan
button(icon('trash'), 'Hapus')     // katanya yang berbicara

icon('trash', { label: 'Hapus' })  // diumumkan sebagai sebuah gambar

// Tombol yang hanya berikon memberi label pada tombolnya, bukan glif di dalamnya
iconButton({ label: 'Hapus', icon: icon('trash') })

Terisi

filled mengecat sebuah glif alih-alih menggariskannya. Jalurnya sama dalam kedua keadaan โ€” hanya atribut fill-nya yang berubah โ€” jadi kedua bentuknya berbagi tepi luar yang persis sama dan tidak bisa saling melenceng.

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' }),
)

Dan nama-nama yang sama dalam keadaan terisi:

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' }),
)

Ia berupa props alih-alih kumpulan nama kedua karena keadaan terisi hampir selalu merupakan sebuah keadaan โ€” tersimpan, disukai, dinilai โ€” jadi ia menginginkan sebuah boolean, bukan string yang berbeda:

icon('heart', { filled: liked })
icon('bookmark', { filled: saved, label: saved ? 'Tersimpan' : 'Simpan' })

// alih-alih
icon(liked ? 'heart-filled' : 'heart')

Glif status terisi dengan cara berbeda, karena tandanya berada di dalam bentuknya. Mengecat lingkarannya akan menelan centangnya, jadi tandanya justru dilubangkan keluar darinya:

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' }),
)

Yang itu membawa gambar kedua โ€” bentuk padat dengan tandanya dipotong keluar oleh fill-rule: evenodd โ€” karena pelubangan tidak bisa diperoleh dari jalur garisnya dengan mengubah sebuah atribut. Bentuk luarnya digambar pada tepi luar garisnya, jadi kedua bentuknya tetap berakhir pada siluet yang sama. Props-nya sama dalam kedua keadaan; mekanisme mana yang dipakai sebuah glif adalah urusannya sendiri.

Sebuah ok tidak punya bagian dalam untuk dicat sama sekali โ€” ia adalah garis terbuka โ€” jadi ia terisi menjadi segitiga yang digambarkan ketiga titiknya sendiri, sambil mempertahankan goresan yang membulatkan sudutnya:

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() mendaftar semua yang menanggapi filled. Glif tanpa bentuk terisi mengabaikannya dan tetap bergaris โ€” mengisi eye akan menghilangkan pupilnya dan tag lubangnya, jadi keduanya tidak berpura-pura bisa.

Berputar

spin memutar glifnya โ€” dimaksudkan untuk spinner, meski tidak ada yang menghalangi Anda memutar refresh selagi sesuatu dimuat ulang. Ia melambat sampai merayap alih-alih berhenti di bawah prefers-reduced-motion, karena pemutar yang berhenti tampak rusak.

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 }), 'Menyimpanโ€ฆ'),
)

Kumpulannya

Namanya menjelaskan gambarnya alih-alih pekerjaan yang dilakukannya โ€” x-circle, bukan error โ€” karena gambar yang sama dipakai untuk pekerjaan yang tidak berkaitan, dan nama yang menjelaskan gambarnya tetap benar ketika itu terjadi. Takaran alias di bawah ini mencakup maksud-maksud yang umum.

alert-triangle
arrow-down
arrow-left
arrow-right
arrow-up
banknote
bell
bold
bookmark
calendar
camera
check
check-circle
chevron-down
chevron-left
chevron-right
chevron-up
chevrons-left
chevrons-right
clock
close
code
coins
comment-bubble
copy
credit-card
database
download
edit
external-link
eye
eye-off
facebook
feather
file
filter
folder
gear
gift
globe
google
heart
help
home
image
info
instagram
italic
key
layers
link
linkedin
location
lock
log-in
log-out
mail
menu
minus
moon
more-horizontal
more-vertical
package
percent
pin
plus
print
receipt
refresh
search
settings
share
shopping-bag
shopping-cart
smartphone
sparkles
spinner
star
store
sun
tag
terminal
thumbs-down
thumbs-up
tiktok
trash
truck
universal-access
unlock
upload
user
users
video
wallet
whatsapp
x-circle
x-twitter
youtube
zap

Merek

Delapan tanda merek hadir bersama kumpulannya โ€” facebook, google, instagram, linkedin, tiktok, whatsapp, x-twitter, dan youtube. Mereka tetap menerima size dan label serta tetap digambar dalam 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'), 'Bagikan'),
)

Mereka adalah reproduksi tanda milik orang lain alih-alih gambar bergaya pustaka ini, jadi mereka sengaja melanggar dua aturannya: mereka berupa bentuk padat alih-alih goresan, dan memang begitulah sebuah logo, dan proporsinya adalah proporsi mereknya alih-alih kisi ini. filled tidak berarti apa pun bagi mereka โ€” mereka memang sudah begitu.

Karyanya berasal dari Simple Icons, yang merilisnya di bawah CC0. Itu mencakup gambarnya, bukan mereknya: pakai ini untuk menunjuk hal yang mereka namai โ€” tautan profil, tombol berbagi โ€” dan bukan pada produk Anda sendiri.

Namanya x-twitter, bukan x, karena x sudah menjadi alias close dan tombol tutup yang berubah menjadi logo akan menjadi kejutan yang tidak menyenangkan. twitter juga mengarah ke sana.

Alias

Masing-masing dari ini merender glif yang terdaftar di atas, dengan nama yang lebih mungkin Anda raih:

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

Ikon Anda sendiri

registerIcons() menambahkan sebuah glif, atau mengganti salah satu bawaannya. Markup-nya adalah isi dari <svg>-nya โ€” bentuk pada kisi 24ร—24 yang sama, dibiarkan tanpa isian agar currentColor menjangkaunya. Panggil sekali dari modul yang diimpor halaman Anda:

import { registerIcons } from 'sitelo/ui'

registerIcons({
  logo: '<path d="M4 20 12 4l8 16z"/>',
  // Nama yang sudah ada akan menggantikannya di mana-mana, dan begitulah
  // cara menata ulang bawaan tanpa mencabangkan pustakanya.
  check: '<path d="m5 13 4 4 10-11"/>',
  // Satu bentuk tertutup, agar ia bisa menanggapi `filled` seperti bawaannya.
  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')                  // glif Anda
icon('check')                 // sekarang ini pun milik Anda

registerIcons({ check: null }) // dan kembali ke bawaannya

Mengapa sebaris, dan bukan sprite

Ikon dirender ke dalam halamannya alih-alih ditarik dari sebuah icons.svg dengan <use>. Sprite menghemat sekitar seratus bita HTML ter-gzip per halaman dan menukarnya dengan satu perjalanan bolak-balik โ€” markup yang berulang justru kasus yang paling dikuasai gzip, jadi sebagian besar yang ingin diringkas sprite sudah diringkas lebih dulu. Sebaris juga berarti tidak ada berkas yang perlu dihasilkan, tidak ada jalur dasar yang perlu dikonfigurasi, dan tidak ada yang bisa hilang dari dist โ€” takaran yang sama dengan yang diambil styles({ inline: true }).

Props

PropTipeBawaanDeskripsi
namestringโ€”Glif yang mana. Bisa diberikan sebagai argumen pertama sebagai gantinya.
size'sm' | 'md' | 'lg' | string'md'Sebuah token, atau panjang CSS apa pun. Bawaannya 1em.
labelstringโ€”Mengumumkannya sebagai gambar dengan nama ini, alih-alih menyembunyikannya.
spinbooleanfalseMemutarnya terus-menerus.
filledbooleanfalseMengecat glifnya alih-alih menggariskannya. Diabaikan glif yang tidak bisa diisi.

Nama yang tidak dikenal tidak merender apa pun alih-alih melempar galat โ€” props yang sifatnya kosmetik seharusnya tidak bisa menggagalkan build. hasIcon(name) memberi tahu apakah salah satunya ada, iconNames() mendaftar semuanya, dan fillableIcons() yang menerima filled.