Compare commits

...

234 Commits

Author SHA1 Message Date
Dave Gandy
f278ce1d6d evening up line breaks 2024-03-12 12:12:08 -05:00
lindsaym-fa
0bbf9bb275 hide astro dev toolbar for layouts 2024-03-12 13:09:15 -04:00
lindsaym-fa
4deb928682 add configurable drawer background color 2024-03-12 13:04:49 -04:00
lindsaym-fa
4066e8c591 Merge remote-tracking branch 'origin/konnorrogers/layout-with-astro' into demo-layout 2024-03-12 12:33:25 -04:00
konnorrogers
8eb6ee6609 fix grid for navigation 2024-03-12 12:19:51 -04:00
lindsaym-fa
620525891d add nav toggle to advanced layout 2024-03-12 11:41:56 -04:00
konnorrogers
384147502f rename to pagE 2024-03-12 10:36:55 -04:00
lindsaym-fa
730e20bd38 remove extraneous classes 2024-03-12 10:03:32 -04:00
konnorrogers
9b3669c428 Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers/layout-with-astro 2024-02-28 15:08:03 -05:00
konnorrogers
b003273175 remove vercel.json 2024-02-27 16:09:35 -05:00
konnorrogers
faae5c21fd remove sandbox-settings 2024-02-27 15:51:44 -05:00
konnorrogers
af89149939 maybe now? 2024-02-27 15:40:40 -05:00
konnorrogers
9b5ceb989c add sandbox-settings 2024-02-27 15:31:35 -05:00
konnorrogers
7862c374dc add sandbox-settings 2024-02-27 15:28:43 -05:00
konnorrogers
03508ec523 add sandbox-settings 2024-02-27 15:21:00 -05:00
konnorrogers
aadf4830c6 add vercel.json 2024-02-27 14:36:40 -05:00
konnorrogers
cd282e50ef add vercel.json 2024-02-27 14:30:35 -05:00
konnorrogers
57ddd25662 add vercel.json 2024-02-27 14:23:25 -05:00
konnorrogers
a37f2d594e add vercel.json 2024-02-27 14:18:25 -05:00
konnorrogers
6fbf921f4b add vercel.json 2024-02-27 14:14:39 -05:00
konnorrogers
c11f3d468b prettier 2024-02-27 13:41:55 -05:00
konnorrogers
306eefa44b rename layout to page 2024-02-27 13:39:55 -05:00
konnorrogers
cd237d3057 light-pen 3 2024-02-26 16:24:34 -05:00
konnorrogers
c7757b8cbe working on astro layout 2024-02-22 14:33:49 -05:00
Cory LaViska
b53c1d940a backport 1880 2024-02-21 13:33:00 -05:00
Cory LaViska
edd62490f8 backport 1839 2024-02-20 15:01:35 -05:00
Cory LaViska
96a381d3a3 backport 1879 2024-02-20 14:18:48 -05:00
Cory LaViska
a2a72de2cf backport 1788 2024-02-20 14:13:23 -05:00
Cory LaViska
9a51e69320 backport 1878 2024-02-20 14:00:49 -05:00
Cory LaViska
07be57847d backport 1874 2024-02-20 13:48:57 -05:00
Cory LaViska
0095ca5fe7 backport 1877 2024-02-20 12:53:38 -05:00
konnorrogers
4b8a86f0e2 continued work on layouts 2024-02-13 18:40:37 -05:00
konnorrogers
41de947779 working on playgrounds 2024-02-09 16:32:45 -05:00
Cory LaViska
cb1c423aea backport 1862 2024-02-09 10:05:14 -05:00
Cory LaViska
93306c99ce backport 1800, 1860 2024-02-09 09:58:13 -05:00
Cory LaViska
5f8c69064c backport 1852 2024-02-09 09:46:46 -05:00
Cory LaViska
f51a09ddf0 backport 1840 2024-02-08 12:46:08 -05:00
konnorrogers
11337197d7 first layout converted to sportawesome 2024-02-07 13:41:12 -05:00
konnorrogers
f1739309eb fix buildS 2024-02-07 12:46:46 -05:00
konnorrogers
5007924dbd working on layouts 2024-02-06 16:24:25 -05:00
Konnor Rogers
92533c0297 Convert to Starlight (#22)
* first pass at starlight

* converting to starlight

* working on converting to starlight

* working on data

* watch custom-elements.json

* turn on pagefind

* add component meta data

* fix renderings / overrides.

* fix mdx logo

* continue starlight work

* building site

* get global styles + reloads working

* themer fixes

* adding additional headings

* working on dynamic content

* have TableOfContents.astro push to TOC

* working on code stuff

* remove code preview

* deploy

* add patch package

* patch in build

* patch in build

* remove {% raw %} calls

* convert to starlight...complete

* prettier

* update lockfile

* merge main

* fix index.mdx

* prettier'

* fix small things

* docs updates

* add dark mode shortcut

* prettier

* prettier

* prettier

* remove pagefind from public

* add twitteR

* prettier

* fix tests

* prettier
2024-02-05 11:02:14 -05:00
Cory LaViska
94558e6ea5 Merge pull request #19 from shoelace-style/backport-menu-item-loading
backport `<sl-menu-item loading>`
2024-01-31 16:13:59 -05:00
lindsaym-fa
bab673fbdc optional class for alternating table row colors 2024-01-31 15:27:39 -05:00
lindsaym-fa
7b20f9c87a deprecate global toggle-size properties 2024-01-30 16:06:09 -05:00
lindsaym-fa
ddbd91ad89 update radio with custom properties, svg circle 2024-01-30 15:45:27 -05:00
lindsaym-fa
130844df1c add custom properties to carousel 2024-01-25 14:08:31 -05:00
lindsaym-fa
d6cfa1ab24 add custom properties to tag 2024-01-24 16:40:25 -05:00
Cory LaViska
ec613f8d32 backport 1797 2024-01-24 13:31:42 -05:00
Cory LaViska
52e2518365 backport 1818 2024-01-24 13:24:29 -05:00
Cory LaViska
9b7aad71a9 backport 1831 2024-01-24 13:23:41 -05:00
Cory LaViska
b7541d240b backport 1828 2024-01-24 13:21:27 -05:00
Cory LaViska
c67da1e818 backport 1826 2024-01-24 13:20:36 -05:00
Cory LaViska
265e523a56 backport 1822 2024-01-24 13:16:38 -05:00
Cory LaViska
bfe05d0692 backport 1821 2024-01-24 12:25:13 -05:00
Cory LaViska
651eae8cb6 backport 1820 2024-01-24 12:21:41 -05:00
lindsaym-fa
8c8b3f1853 add custom properties to badge 2024-01-23 11:27:01 -05:00
lindsaym-fa
d1ed504dd8 update custom properties on checkbox doc 2024-01-18 08:46:51 -06:00
lindsaym-fa
5335c9421a add depth to checkbox 2024-01-17 16:44:34 -06:00
lindsaym-fa
1b380f3f1d document custom properties for checkbox 2024-01-17 09:33:11 -06:00
lindsaym-fa
d166bc0e48 add custom properties to checkbox 2024-01-17 09:08:26 -06:00
lindsaym-fa
595cc303e7 add custom property for progress bar shadows 2024-01-16 14:08:11 -06:00
lindsaym-fa
4260b27fd2 tweak spacing for playful theme 2024-01-16 13:55:59 -06:00
lindsaym-fa
7c6f018c5b rename border tokens with s/m/l convention 2024-01-16 13:50:30 -06:00
lindsaym-fa
b892f1f86a re-integrate borders into main theme stylesheets 2024-01-16 13:42:32 -06:00
lindsaym-fa
a0e9125d61 add custom properties to details component 2024-01-16 13:13:32 -06:00
lindsaym-fa
5b741006a1 tweak playful theme font weights 2024-01-16 12:57:12 -06:00
lindsaym-fa
30bfabc397 add link decoration tokens; undo dark mode borders 2024-01-16 10:02:49 -06:00
lindsaym-fa
4e1bea7d94 update fonts, remove font imports from themes 2024-01-15 09:18:34 -06:00
lindsaym-fa
f2bb9fefee dark theme adjustments 2024-01-08 23:19:14 -05:00
lindsaym-fa
9987ce8d4f simplify themer logic 2024-01-08 22:49:48 -05:00
lindsaym-fa
4ace1efbe0 minor themer tweaks 2024-01-08 18:06:02 -05:00
Konnor Rogers
d7920f2e75 Add logo uploader (#18)
* fix tabbable, add logo uploader

* Add icon chooser and project name to themer

* Add icon chooser and project name to themer

* make theme swapping less jarring

* add depth to themer

* update font families / font weights on theme change

* fix prettier

* update themer to reset values on theme change

* remove custom-elements.mjs

* prettier

* update comments in isVisible

* add PR #

* prettier

* update changelog

* prettier

* update changelog

* fix themer

* ensure target exists

* update icon search

* prettier

* fix select test

* prettier

* fix sprite svg

* remove custom-elements.mjs

* Add small validation to themer

* prettier

* fix conflicts

* prettier

* remove cem-manifest.mjs

---------

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
2024-01-08 16:43:57 -05:00
Kelsey Jackson
4198cf0f15 Merge pull request #24 from shoelace-style/theming-ui/dark-mode-for-all-themes
add the ability to toggle light and dark mode for themes
2024-01-08 14:03:16 -06:00
Kelsey Jackson
7562905bbf made updates 2024-01-05 15:20:33 -06:00
Kelsey Jackson
4914cdb352 Merge pull request #23 from shoelace-style/theming-ui/add-curated-font-list
add list of curated fonts
2024-01-04 18:08:21 -06:00
Kelsey Jackson
0c95c70192 updated spacing issue 2024-01-03 11:30:16 -06:00
Kelsey Jackson
430730f24a Merge branch 'next' into theming-ui/add-curated-font-list 2024-01-03 11:01:05 -06:00
Kelsey Jackson
ab0c615e10 updated text 2024-01-03 10:46:53 -06:00
Kelsey Jackson
716ab94069 Merge branch 'next' into theming-ui/dark-mode-for-all-themes 2024-01-03 10:26:08 -06:00
lindsaym-fa
ccc6f1aa23 adjust applied code background color 2023-12-13 17:44:25 -05:00
lindsaym-fa
893f8b2740 fix sandbox icons 2023-12-13 17:10:08 -05:00
lindsaym-fa
00d5164912 touch up icons 2023-12-13 17:02:24 -05:00
Cory LaViska
fc9151e573 backport 1787 2023-12-13 12:04:53 -05:00
Kelsey Jackson
cf97bc3c6c add the ability to toggle light and dark mode for themes 2023-12-12 10:58:29 -06:00
Cory LaViska
eb9dbf097c prettier 2023-12-11 16:53:11 -05:00
konnorrogers
5422e6431c fix copying cdn build to _site after its been built 2023-12-11 16:25:24 -05:00
Cory LaViska
f3a921022e Merge branch 'icon' into next 2023-12-11 16:02:12 -05:00
Cory LaViska
be1440aee0 update settings 2023-12-11 16:01:44 -05:00
Kelsey Jackson
fe23a7ddb8 add list of curated fonts 2023-12-11 11:51:53 -06:00
lindsaym-fa
f53a643cf3 improve typescale 2023-12-08 17:53:33 -05:00
Konnor Rogers
3f604fcee1 prettier (#20) 2023-12-08 15:09:34 -05:00
lindsaym-fa
d8b6db8c5b darken background in themer sample UI 2023-12-08 14:19:54 -05:00
lindsaym-fa
31215dbda4 define font size at root in applied styles 2023-12-08 14:18:24 -05:00
Cory LaViska
f00e8c3a65 prettier ci output 2023-12-07 16:30:37 -05:00
Cory LaViska
a4f8bf94ee fix 2023-12-07 16:26:44 -05:00
Cory LaViska
8ae1303188 sigh 2023-12-07 16:26:07 -05:00
Cory LaViska
ffc0248e4c fix build 2023-12-07 15:59:53 -05:00
konnorrogers
81d3f22da6 fix dev server output for errors / logging 2023-12-07 15:18:56 -05:00
Cory LaViska
0fa8e6f550 update build 2023-12-07 14:58:12 -05:00
Cory LaViska
a67d1df89a fix error 2023-12-07 14:57:53 -05:00
Cory LaViska
0fe400c6f4 Merge branch 'next' into icon 2023-12-07 10:09:54 -05:00
Cory LaViska
349aa45d2b backport 2023-12-06 17:18:12 -05:00
Cory LaViska
fcf0a136f2 backport 1771 2023-12-06 16:26:15 -05:00
Cory LaViska
8acfc4c9de backport 1767 2023-12-06 16:19:01 -05:00
Cory LaViska
4f8417806c backport 1764 2023-12-06 12:02:20 -05:00
Cory LaViska
65cb3175af update soooo many icons 2023-12-05 17:37:06 -05:00
Cory LaViska
06135e686b fic code bg color 2023-12-05 13:55:31 -05:00
lindsaym-fa
340351ca4b improve variance between depth levels 2023-12-01 12:12:49 -05:00
Cory LaViska
5701bef6e9 backport 1749 2023-12-01 10:29:37 -05:00
Cory LaViska
62417ed1d1 backport PR 1752 2023-12-01 10:15:20 -05:00
Cory LaViska
545162eaae data-web-awesome instead of data-webawesome 2023-11-28 14:19:08 -05:00
lindsaym-fa
77a8c418ea add Font Awesome theme 2023-11-22 14:55:37 -05:00
lindsaym-fa
641e92a340 improve shadow calculations 2023-11-22 14:35:17 -05:00
lindsaym-fa
3f8535e7b8 remove border color change for alerts 2023-11-22 14:33:41 -05:00
konnorrogers
81a66df7e4 add exportConditions for tests 2023-11-21 11:07:46 -05:00
Lindsay M
ae2480dfe2 Theme revisions (#12)
* remove square, stretch, and squish spacing tokens

* remove units from base tokens

* rename corner tokens with t-shirt size scale

* rename 'font-size' tokens to 'size'

* rename 'neutral' primitives to 'base'

* remove black and white tokens

* improve 'form-controls' tokens

* reintroduce granular focus ring tokens

* fix themer styles

* tweak shadow styles

* improve naming and scope of foundational colors

* overhaul color naming and add new themes

* more classic sl component styles

* make 'chic' theme dark by default

* adjust table row colors

* remove deprecated properties from 'classic' theme

* remove mistakenly committed stylesheets

* revert adjustment to space properties

* delete web-types.json

* revert "rename 'font-size' tokens to 'size'"
2023-11-15 11:43:40 -05:00
Cory LaViska
c95b0b6c66 backport PR 1722 2023-11-14 13:15:57 -05:00
Konnor Rogers
dee01269ad Konnorrogers/backport 1711 and 1714 (#11)
* backport #1711 & #1714

* remove custom-elements.mjs

* prettier
2023-11-14 12:22:18 -05:00
Cory LaViska
e11eb363aa Merge branch 'next' of https://github.com/shoelace-style/webawesome into next 2023-11-13 15:57:49 -05:00
Cory LaViska
0d33cabec4 add back two-way binding info 2023-11-13 15:57:47 -05:00
Konnor Rogers
b5d9b49b27 backport #1707 & #1708 (#10)
* backport #1707 & #1708

* prettier

* fix prettier log level

* fix test

* backport #1707 & #1708
2023-11-08 15:20:06 -05:00
Lindsay M
1b654c7c85 Support theme customizations for depth and borders
* Move custom properties related to shadows and borders to separate stylesheets
* Change base values and themer calculations related to shadows and borders to be unitless
* Add low-level custom properties for alert, button, card, input, select, switch, and textarea
* Add generic guidance for authoring custom properties to the Contributing docs
2023-11-03 13:46:00 -04:00
Cory LaViska
4e53ce870d backport PR 1702 2023-11-03 10:27:03 -04:00
Cory LaViska
932e2e7566 update jet brains plugin and stop writing to package.json 2023-11-02 08:53:17 -04:00
Konnor Rogers
e76a1dc1f6 fix no translation error (#8)
* fix no translation error

* prettier
2023-11-01 16:49:24 -04:00
Cory LaViska
38302a7c28 fix bad port 2023-10-25 13:38:21 -04:00
Cory LaViska
71e5b10f3b backport PR 1684 2023-10-25 13:07:59 -04:00
Cory LaViska
3eda5510c3 fix tests 2023-10-23 12:33:14 -04:00
Cory LaViska
32494e783c backport PR 1605 2023-10-23 12:03:18 -04:00
Cory LaViska
cdf38fe147 remove ts-check 2023-10-23 11:35:32 -04:00
Cory LaViska
302c174055 backport PR 1671 2023-10-23 10:48:37 -04:00
Cory LaViska
fb044aae89 backport PR 1670 2023-10-23 10:19:34 -04:00
Cory LaViska
bf299d8234 backport PR 1670 2023-10-20 09:04:15 -04:00
Cory LaViska
30a3164a96 backport PR 1663 2023-10-19 10:31:24 -04:00
Cory LaViska
2a22fb683c backport PR 1667 2023-10-19 09:43:32 -04:00
Cory LaViska
325ddafb13 backport docs fix 2023-10-19 09:41:39 -04:00
Cory LaViska
fcb2c7868c backport PR 1661 2023-10-18 13:44:20 -04:00
Cory LaViska
46b198866d backport PR 1656 2023-10-18 13:26:39 -04:00
Cory LaViska
9a4da9b763 backport PR 1655 2023-10-16 13:05:53 -04:00
Cory LaViska
7869144f5e backport changelog for 2.10.0 2023-10-16 13:05:37 -04:00
Cory LaViska
2d7d400040 backport PR 1614 2023-10-16 12:47:36 -04:00
Cory LaViska
6f5e5a2433 update default 2023-10-16 12:41:37 -04:00
Cory LaViska
e59a4659d8 fix import 2023-10-16 12:39:21 -04:00
Cory LaViska
2f9732fc3d use discussions for features 2023-10-16 11:37:17 -04:00
Cory LaViska
fdede79155 backport PR 1615 2023-10-13 13:53:41 -04:00
Cory LaViska
3277284473 backport PR 1608 2023-10-12 16:28:19 -04:00
Cory LaViska
b2b8d0d941 backport PR 1583 2023-10-12 15:52:31 -04:00
Cory LaViska
91bfd38a9a update deps to stay in sync with sl 2023-10-12 15:03:38 -04:00
Cory LaViska
f4971456d0 backport PR 1599 2023-10-12 12:18:08 -04:00
Cory LaViska
cc18a90a86 backport PR 1600 2023-10-12 12:11:20 -04:00
Cory LaViska
60b6803437 Merge branch 'themer' into next 2023-10-12 12:01:36 -04:00
Cory LaViska
2b57157502 backport PR 1604 2023-10-12 12:01:10 -04:00
Cory LaViska
967208d69b backport PR 1586 2023-10-02 09:06:58 -05:00
Cory LaViska
3bd13cd7cb Merge branch 'next' into themer 2023-09-29 11:32:24 -04:00
lindsaym-fa
ebe1904479 update and add themes 2023-09-28 22:47:25 -04:00
Cory LaViska
23356f6e39 adjust help text 2023-09-27 16:48:06 -04:00
Cory LaViska
4958ee41ae add favicon 2023-09-27 16:41:35 -04:00
Cory LaViska
9784faa32a remove tooltip for demo 2023-09-27 16:35:21 -04:00
Cory LaViska
a913c22200 cap border radius for checkboxes 2023-09-27 16:35:16 -04:00
Cory LaViska
95dce95183 fix copy button 2023-09-27 16:28:26 -04:00
Cory LaViska
53f9230354 update logo and add form control examples 2023-09-27 16:17:34 -04:00
Cory LaViska
946f08db4b update logo 2023-09-27 16:05:44 -04:00
Cory LaViska
4b0ee8907f more themes for the demo 2023-09-27 14:16:25 -04:00
Cory LaViska
62bb58dc09 backport localize fix 2023-09-27 13:10:21 -04:00
Cory LaViska
1d903fab38 ignore package.json 2023-09-27 13:09:57 -04:00
Cory LaViska
a458f2a6f0 add style guide to themer demo 2023-09-27 13:09:12 -04:00
Cory LaViska
f66e8cec69 remove shadow 2023-09-26 16:45:41 -04:00
Cory LaViska
9fd070639c more knobs 2023-09-26 16:37:56 -04:00
Cory LaViska
528748155a remove sidebar 2023-09-26 12:44:59 -04:00
Cory LaViska
474ffb98d6 retro changelog 2023-09-26 10:15:34 -04:00
Cory LaViska
cb2d5e4eb4 update to 2.9.0 changelog 2023-09-26 09:33:19 -04:00
Cory LaViska
3c51262a37 Merge branch 'next' into themer 2023-09-26 09:11:03 -04:00
Cory LaViska
7e4dba7af1 backport PR 1572 2023-09-26 09:10:53 -04:00
Cory LaViska
319705106b Merge branch 'next' into themer 2023-09-26 08:54:05 -04:00
Cory LaViska
2416f93a79 backport PR #1575 2023-09-26 08:53:55 -04:00
Cory LaViska
e398091a36 backport issue 1576 2023-09-25 09:09:53 -04:00
Cory LaViska
d836bcebbc fix words 2023-09-25 09:00:31 -04:00
Cory LaViska
d08f928818 early early early themer concept 2023-09-22 11:09:34 -04:00
Cory LaViska
c3e74ada39 sample page 2023-09-21 11:58:40 -04:00
Cory LaViska
a2e9a3de96 add form validation classes 2023-09-21 09:41:36 -04:00
Cory LaViska
b2a99c83e3 don't break 2023-09-20 14:48:17 -04:00
Cory LaViska
a4185bc926 fix focus-visible styles 2023-09-20 14:47:58 -04:00
Cory LaViska
8e09db9d40 Merge branch 'next' into applied-styles 2023-09-18 15:17:15 -04:00
Cory LaViska
07ca5a45ae initial 2023-09-18 15:16:08 -04:00
lindsaym-fa
88a8173178 add mellow theme 2023-09-14 15:51:16 -04:00
lindsaym-fa
5a8c6912dc fix switch focus 2023-09-14 12:36:44 -04:00
Cory LaViska
a7c786987d backport fix for 1548 2023-09-14 12:08:33 -04:00
Cory LaViska
1179e48955 sync 2023-09-14 11:48:06 -04:00
Cory LaViska
07f0884462 fix 2023-09-14 11:45:18 -04:00
Cory LaViska
ef3575358e temp dark mode toggle 2023-09-14 11:36:00 -04:00
Cory LaViska
5e2762cbc6 backport PR 1564 2023-09-14 11:19:10 -04:00
Cory LaViska
7e165fa8bd hold 2023-09-14 11:17:31 -04:00
Cory LaViska
33706e0f27 Merge branch 'next' of https://github.com/shoelace-style/webawesome into next 2023-09-14 11:15:52 -04:00
Cory LaViska
e262ed14b0 backport PR 1565 2023-09-14 11:15:17 -04:00
lindsaym-fa
545eb467fc add theme tokens, update usage, add dark mode 2023-09-13 17:51:21 -04:00
Cory LaViska
2848ab68ef backport PR 1563 2023-09-13 11:54:53 -04:00
Cory LaViska
fc1aa42c26 more style updates 2023-09-12 15:58:45 -04:00
Cory LaViska
8baa32d8c9 Merge branch 'next' of https://github.com/shoelace-style/webawesome into next 2023-09-12 15:06:47 -04:00
Cory LaViska
a519077112 inherit font sizes/line-heights 2023-09-12 15:05:53 -04:00
Konnor Rogers
5219188690 Reduce time it takes to replace strings (#6)
* reduce replacer time from 9 seconds to 3 seconds

* prettier

* prettier
2023-09-12 14:46:39 -04:00
lindsaym-fa
267b9eba20 correct shadow usage 2023-09-12 13:12:37 -04:00
lindsaym-fa
9c343ef3fd update color primitives and fix comments 2023-09-12 13:02:59 -04:00
Cory LaViska
e82b076981 fix tag height/spacing 2023-09-12 12:32:28 -04:00
Cory LaViska
97bd88f904 fix link buttons 2023-09-12 12:25:08 -04:00
Cory LaViska
21d8cdbb5c Merge branch 'next' of https://github.com/shoelace-style/webawesome into next 2023-09-12 12:09:12 -04:00
Cory LaViska
a5f8c51904 update bootstrap icons 2023-09-12 12:09:10 -04:00
lindsaym-fa
94ad43e130 improved color mixing for hover and active states 2023-09-11 16:54:20 -04:00
Cory LaViska
1f04cd2a50 prettier 2023-09-11 13:53:21 -04:00
Cory LaViska
5a55c240ee Merge pull request #4 from shoelace-style/theming
Theming API + SL => WA
2023-09-11 13:50:28 -04:00
Lindsay M
e02b36873e Merge pull request #5 from shoelace-style/lindsaym/theming-tune-up
Theming tune up
2023-09-11 13:31:41 -04:00
lindsaym-fa
f89ef95d65 updated global properties and improved usage 2023-09-11 13:21:35 -04:00
Lindsay M
a65db66005 improved naming and usage of --wa-form-controls-* properties 2023-09-08 17:00:09 -04:00
Lindsay M
a6e19d0710 renamed CSS properties for semantic color variants 2023-09-08 16:23:48 -04:00
Cory LaViska
df02aeef89 remove sponsor links 2023-09-08 14:43:15 -04:00
Cory LaViska
2d03f60c70 lint 2023-09-08 14:35:29 -04:00
Cory LaViska
e45b44ad03 stop making shoes 2023-09-08 14:32:23 -04:00
Cory LaViska
015429e05d sl => wa 2023-09-08 13:45:49 -04:00
Cory LaViska
5628381449 backport PR 1558 2023-09-08 08:33:27 -04:00
Cory LaViska
0bf3cf2535 remove webtypes 2023-09-08 08:33:04 -04:00
Cory LaViska
d4aa9ff99e backport PR 1557 2023-09-08 08:31:26 -04:00
Cory LaViska
0229c315bb remaining components 2023-09-07 12:54:44 -04:00
Cory LaViska
67d4458e69 more stylez 2023-09-06 16:13:49 -04:00
Cory LaViska
67bfbed308 input, textarea, and more button styles 2023-09-06 12:32:57 -04:00
Cory LaViska
2c053b6fd3 more components 2023-09-05 16:39:38 -04:00
Cory LaViska
6156e38a34 dialog, drawer, details, alert, divider 2023-09-05 15:16:24 -04:00
Cory LaViska
631df0293c copy button 2023-09-05 14:53:49 -04:00
Cory LaViska
b86a6a54ab carousel 2023-09-05 14:53:45 -04:00
Cory LaViska
201b32f3fb alerts 2023-09-05 14:45:33 -04:00
Cory LaViska
ebed8daee6 buttons, primary, and more 2023-09-05 12:01:19 -04:00
Cory LaViska
55be0a557f more retheming 2023-08-31 16:50:31 -04:00
Cory LaViska
b4c45b480b update docs and theme tokens to use wa 2023-08-31 12:06:32 -04:00
Cory LaViska
8b9df9871a resolve instead of reject 2023-08-30 15:12:13 -04:00
Cory LaViska
af7682aaca update setting 2023-08-30 11:35:19 -04:00
Konnor Rogers
883cb161ec show errors in dev server (#1547)
* show errors in dev server

* fix build

* prettier
2023-08-30 09:42:34 -04:00
593 changed files with 43825 additions and 40179 deletions

View File

@@ -1,4 +1,7 @@
contact_links:
- name: Feature Requests
url: https://github.com/shoelace-style/shoelace/discussions/categories/ideas
about: All requests for new features should go here.
- name: Help & Support
url: https://github.com/shoelace-style/shoelace/discussions/categories/help
about: Please don't create issues for personal help requests. Instead, ask your question on the discussion forum.

View File

@@ -1,15 +0,0 @@
---
name: Feature Request
about: Suggest an idea for this project.
title: ''
labels: feature
---
### What issue are you having?
Provide a clear and concise description of the problem you're facing.
### Describe the solution you'd like
How would you like to see the library solve it?
### Describe alternatives you've considered
In what ways have you tried to solve this with the current version?

4
.github/SECURITY.md vendored
View File

@@ -1,7 +1,7 @@
# Reporting Security Issues
We take security issues in Shoelace very seriously and appreciate your efforts to disclose your findings responsibly.
We take security issues in Web Awesome very seriously and appreciate your efforts to disclose your findings responsibly.
To report a security issue, email [cory@abeautifulsite.net](mailto:cory@abeautifulsite.net) and include "SHOELACE SECURITY" in the subject line.
To report a security issue, email [cory@fontawesome.com](mailto:cory@abeautifulsite.net) and include "WEB AWESOME SECURITY" in the subject line.
We'll respond as soon as possible and keep you updated throughout the process.

5
.gitignore vendored
View File

@@ -1,9 +1,12 @@
_site
.cache
.DS_Store
package.json
package-lock.json
dist
docs/assets/images/sprite.svg
docs/public/pagefind
node_modules
src/react
cdn
web-types.json
.astro

View File

@@ -2,6 +2,7 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
"source.fixAll.eslint": "explicit"
},
"debug.enableStatusBarColor": false
}

View File

@@ -1,4 +1,4 @@
# Contributing to Shoelace
# Contributing to Web Awesome
Before contributing, please review the contributions guidelines at:

View File

@@ -1,4 +1,4 @@
Copyright (c) 2020 A Beautiful Site, LLC
Copyright (c) 2023 Fonticons, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,4 +1,4 @@
# Shoelace
# Web Awesome
A forward-thinking library of web components.
@@ -9,7 +9,7 @@ A forward-thinking library of web components.
- Built with accessibility in mind ♿️
- Open source 😸
Designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska).
Built by the folks behind [Font Awesome](https://fontawesome.com/).
---
@@ -21,15 +21,15 @@ Twitter: [@shoelace_style](https://twitter.com/shoelace_style)
---
## Shoemakers 🥾
## Developers
Shoemakers, or "Shoelace developers," can use this documentation to learn how to build Shoelace from source. You will need Node >= 14.17 to build and run the project locally.
Developers can use this documentation to learn how to build Web Awesome from source. You will need Node >= 14.17 to build and run the project locally.
**You don't need to do any of this to use Shoelace!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Shoelace.
**You don't need to do any of this to use Web Awesome!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Web Awesome.
If that's not what you're trying to do, the [documentation website](https://shoelace.style) is where you want to be.
### What are you using to build Shoelace?
### What are you using to build Web Awesome?
Components are built with [LitElement](https://lit-element.polymer-project.org/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
@@ -38,8 +38,8 @@ Components are built with [LitElement](https://lit-element.polymer-project.org/)
Start by [forking the repo](https://github.com/shoelace-style/shoelace/fork) on GitHub, then clone it locally and install dependencies.
```bash
git clone https://github.com/YOUR_GITHUB_USERNAME/shoelace
cd shoelace
git clone https://github.com/YOUR_GITHUB_USERNAME/webawesome
cd webawesome
npm install
```
@@ -63,30 +63,18 @@ npm run build
### Creating New Components
To scaffold a new component, run the following command, replacing `sl-tag-name` with the desired tag name.
To scaffold a new component, run the following command, replacing `wa-tag-name` with the desired tag name.
```bash
npm run create sl-tag-name
npm run create wa-tag-name
```
This will generate a source file, a stylesheet, and a docs page for you. When you start the dev server, you'll find the new component in the "Components" section of the sidebar.
### Contributing
Shoelace is an open source project and contributions are encouraged! If you're interesting in contributing, please review the [contribution guidelines](CONTRIBUTING.md) first.
Web Awesome is an open source project and contributions are encouraged! If you're interesting in contributing, please review the [contribution guidelines](CONTRIBUTING.md) first.
## License
Shoelace is designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska). Its available under the terms of the MIT license.
Designing, developing, and supporting this library requires a lot of time, effort, and skill. Id like to keep it open source so everyone can use it, but that doesnt provide me with any income.
**Therefore, if youre using my software to make a profit,** I respectfully ask that you help [fund its development](https://github.com/sponsors/claviska) by becoming a sponsor. There are multiple tiers to choose from with benefits at every level, including prioritized support, bug fixes, feature requests, and advertising.
👇 Your support is very much appreciated! 👇
- [Become a sponsor](https://github.com/sponsors/claviska)
- [Star on GitHub](https://github.com/shoelace-style/shoelace/stargazers)
- [Follow on Twitter](https://twitter.com/shoelace_style)
Whether you're building Shoelace or building something _with_ Shoelace — have fun creating! 🥾
Web Awesome is available under the terms of the MIT license.

View File

@@ -29,6 +29,7 @@
"colour",
"combobox",
"Commonmark",
"compat",
"Composability",
"Consolas",
"contenteditable",
@@ -46,6 +47,7 @@
"dropdowns",
"easings",
"endraw",
"endregion",
"enterkeyhint",
"eqeqeq",
"erroneou",
@@ -89,6 +91,7 @@
"listbox",
"listitem",
"litelement",
"longform",
"lowercasing",
"Lucide",
"maxlength",
@@ -100,6 +103,7 @@
"monospace",
"mousedown",
"mousemove",
"mouseout",
"mouseup",
"multiselectable",
"nextjs",
@@ -109,10 +113,14 @@
"novalidate",
"npmdir",
"Numberish",
"oklab",
"oklch",
"onscrollend",
"outdir",
"ParamagicDev",
"peta",
"petabit",
"Preact",
"prismjs",
"progressbar",
"radiogroup",
@@ -165,9 +173,13 @@
"valpha",
"valuenow",
"valuetext",
"viewports",
"Vuejs",
"WCAG",
"webawesome",
"WEBP",
"Webpacker",
"wordmark"
"xmark"
],
"ignorePaths": [
"package.json",

View File

@@ -1,6 +1,7 @@
import * as path from 'path';
import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';
import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
import { customElementVuejsPlugin } from 'custom-element-vuejs-integration';
import { parse } from 'comment-parser';
import { pascalCase } from 'pascal-case';
import commandLineArgs from 'command-line-args';
@@ -33,14 +34,15 @@ export default {
plugins: [
// Append package data
{
name: 'shoelace-package-data',
name: 'wa-package-data',
packageLinkPhase({ customElementsManifest }) {
customElementsManifest.package = { name, description, version, author, homepage, license };
}
},
// Infer tag names because we no longer use @customElement decorators.
{
name: 'shoelace-infer-tag-names',
name: 'wa-infer-tag-names',
analyzePhase({ ts, node, moduleDoc }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
@@ -55,7 +57,7 @@ export default {
}
const tagNameWithoutPrefix = path.basename(importPath, '.component.ts');
const tagName = 'sl-' + tagNameWithoutPrefix;
const tagName = 'wa-' + tagNameWithoutPrefix;
classDoc.tagNameWithoutPrefix = tagNameWithoutPrefix;
classDoc.tagName = tagName;
@@ -66,9 +68,10 @@ export default {
}
}
},
// Parse custom jsDoc tags
{
name: 'shoelace-custom-tags',
name: 'wa-custom-tags',
analyzePhase({ ts, node, moduleDoc }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
@@ -137,8 +140,9 @@ export default {
}
}
},
{
name: 'shoelace-react-event-names',
name: 'wa-react-event-names',
analyzePhase({ ts, node, moduleDoc }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
@@ -155,8 +159,9 @@ export default {
}
}
},
{
name: 'shoelace-translate-module-paths',
name: 'wa-translate-module-paths',
packageLinkPhase({ customElementsManifest }) {
customElementsManifest?.modules?.forEach(mod => {
//
@@ -191,6 +196,7 @@ export default {
});
}
},
// Generate custom VS Code data
customElementVsCodePlugin({
outdir,
@@ -198,18 +204,27 @@ export default {
referencesTemplate: (_, tag) => [
{
name: 'Documentation',
url: `https://shoelace.style/components/${tag.replace('sl-', '')}`
url: `https://shoelace.style/components/${tag.replace('wa-', '')}`
}
]
}),
customElementJetBrainsPlugin({
outdir: './dist',
excludeCss: true,
packageJson: false,
referencesTemplate: (_, tag) => {
return {
name: 'Documentation',
url: `https://shoelace.style/components/${tag.replace('sl-', '')}`
url: `https://shoelace.style/components/${tag.replace('wa-', '')}`
};
}
}),
customElementVuejsPlugin({
outdir: './dist/types/vue',
fileName: 'index.d.ts',
componentTypePath: (_, tag) => `../../components/${tag.replace('wa-', '')}/${tag.replace('wa-', '')}.component.js`
})
]
};

4
docs/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
docs/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View File

@@ -1,349 +0,0 @@
{% extends "default.njk" %}
{# Find the component based on the `tag` front matter #}
{% set component = getComponent('sl-' + page.fileSlug) %}
{% block content %}
{# Determine the badge variant #}
{% if component.status == 'stable' %}
{% set badgeVariant = 'primary' %}
{% elseif component.status == 'experimental' %}
{% set badgeVariant = 'warning' %}
{% elseif component.status == 'planned' %}
{% set badgeVariant = 'neutral' %}
{% elseif component.status == 'deprecated' %}
{% set badgeVariant = 'danger' %}
{% else %}
{% set badgeVariant = 'neutral' %}
{% endif %}
{# Header #}
<header class="component-header">
<h1>{{ component.name | classNameToComponentName }}</h1>
<div class="component-header__tag">
<code>&lt;{{ component.tagName }}&gt; | {{ component.name }}</code>
</div>
<div class="component-header__info">
<sl-badge variant="neutral" pill>
Since {{component.since or '?' }}
</sl-badge>
<sl-badge variant="{{ badgeVariant }}" pill style="text-transform: capitalize;">
{{ component.status }}
</sl-badge>
</div>
</header>
<p class="component-summary">
{% if component.summary %}
{{ component.summary | markdownInline | safe }}
{% endif %}
</p>
{# Markdown content #}
{{ content | safe }}
{# Importing #}
<h2>Importing</h2>
<p>
If you're using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use
any of the following snippets to <a href="/getting-started/installation#cherry-picking">cherry pick</a> this component.
</p>
<sl-tab-group>
<sl-tab slot="nav" panel="script">Script</sl-tab>
<sl-tab slot="nav" panel="import">Import</sl-tab>
<sl-tab slot="nav" panel="bundler">Bundler</sl-tab>
<sl-tab slot="nav" panel="react">React</sl-tab>
<sl-tab-panel name="script">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a script tag:
</p>
<pre><code class="language-html">&lt;script type=&quot;module&quot; src=&quot;https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}&quot;&gt;&lt;/script&gt;</code></pre>
</sl-tab-panel>
<sl-tab-panel name="import">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a JavaScript import:
</p>
<pre><code class="language-js">import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}';</code></pre>
</sl-tab-panel>
<sl-tab-panel name="bundler">
<p>
To import this component using <a href="{{ rootUrl('/getting-started/installation#bundling') }}">a bundler</a>:
</p>
<pre><code class="language-js">import '@shoelace-style/shoelace/{{ meta.npmdir }}/{{ component.path }}';</code></pre>
</sl-tab-panel>
<sl-tab-panel name="react">
<p>
To import this component as a <a href="/frameworks/react">React component</a>:
</p>
<pre><code class="language-js">import {{ component.name }} from '@shoelace-style/shoelace/{{ meta.npmdir }}/react/{{ component.tagNameWithoutPrefix }}';</code></pre>
</sl-tab-panel>
</sl-tab-group>
{# Slots #}
{% if component.slots.length %}
<h2>Slots</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for slot in component.slots %}
<tr>
<td class="nowrap">
{% if slot.name %}
<code>{{ slot.name }}</code>
{% else %}
(default)
{% endif %}
</td>
<td>{{ slot.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#slots') }}">using slots</a>.</em></p>
{% endif %}
{# Properties #}
{% if component.properties.length %}
<h2>Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-reflects">Reflects</th>
<th class="table-type">Type</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for prop in component.properties %}
<tr>
<td>
<code class="nowrap">{{ prop.name }}</code>
{% if prop.attribute | length > 0 %}
{% if prop.attribute != prop.name %}
<br>
<sl-tooltip content="This attribute is different from its property">
<small>
<code class="nowrap">
{{ prop.attribute }}
</code>
</small>
</sl-tooltip>
{% endif %}
{% endif %}
</td>
<td>
{{ prop.description | markdownInline | safe }}
</td>
<td style="text-align: center;">
{% if prop.reflects %}
<sl-icon label="yes" name="check-lg"></sl-icon>
{% endif %}
</td>
<td>
{% if prop.type.text %}
<code>{{ prop.type.text | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
<td>
{% if prop.default %}
<code>{{ prop.default | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
<tr>
<td class="nowrap"><code>updateComplete</code></td>
<td>
A read-only promise that resolves when the component has
<a href="/getting-started/usage?#component-rendering-and-updating">finished updating</a>.
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#attributes-and-properties') }}">attributes and properties</a>.</em></p>
{% endif %}
{# Events #}
{% if component.events.length %}
<h2>Events</h2>
<table>
<thead>
<tr>
<th class="table-name" data-flavor="html">Name</th>
<th class="table-name" data-flavor="react">React Event</th>
<th class="table-description">Description</th>
<th class="table-event-detail">Event Detail</th>
</tr>
</thead>
<tbody>
{% for event in component.events %}
<tr>
<td data-flavor="html"><code class="nowrap">{{ event.name }}</code></td>
<td data-flavor="react"><code class="nowrap">{{ event.reactName }}</code></td>
<td>{{ event.description | markdownInline | safe }}</td>
<td>
{% if event.type.text %}
<code>{{ event.type.text }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#events') }}">events</a>.</em></p>
{% endif %}
{# Methods #}
{% if component.methods.length %}
<h2>Methods</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-arguments">Arguments</th>
</tr>
</thead>
<tbody>
{% for method in component.methods %}
<tr>
<td class="nowrap"><code>{{ method.name }}()</code></td>
<td>{{ method.description | markdownInline | safe }}</td>
<td>
{% if method.parameters.length %}
<code>
{% for param in method.parameters %}
{{ param.name }}: {{ param.type.text }}{% if not loop.last %},{% endif %}
{% endfor %}
</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#methods') }}">methods</a>.</em></p>
{% endif %}
{# Custom Properties #}
{% if component.cssProperties.length %}
<h2>Custom Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for cssProperty in component.cssProperties %}
<tr>
<td class="nowrap"><code>{{ cssProperty.name }}</code></td>
<td>{{ cssProperty.description | markdownInline | safe }}</td>
<td>{{ cssProperty.default }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
{% endif %}
{# CSS Parts #}
{% if component.cssParts.length %}
<h2>Parts</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for cssPart in component.cssParts %}
<tr>
<td class="nowrap"><code>{{ cssPart.name }}</code></td>
<td>{{ cssPart.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing/#css-parts') }}">customizing CSS parts</a>.</em></p>
{% endif %}
{# Animations #}
{% if component.animations.length %}
<h2>Animations</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for animation in component.animations %}
<tr>
<td class="nowrap"><code>{{ animation.name }}</code></td>
<td>{{ animation.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing#animations') }}">customizing animations</a>.</em></p>
{% endif %}
{# Dependencies #}
{% if component.dependencies.length %}
<h2>Dependencies</h2>
<p>This component automatically imports the following dependencies.</p>
<ul>
{% for dependency in component.dependencies %}
<li><code>&lt;{{ dependency }}&gt;</code></li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@@ -1,150 +0,0 @@
<!DOCTYPE html>
<html
lang="en"
data-layout="{{ layout }}"
data-shoelace-version="{{ meta.version }}"
>
<head>
{# Metadata #}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{ meta.description }}" />
<title>{{ meta.title }}</title>
{# Opt out of Turbo caching #}
<meta name="turbo-cache-control">
{# Stylesheets #}
<link rel="stylesheet" href="{{ assetUrl('styles/docs.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/code-previews.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/search.css') }}" />
{# Favicons #}
<link rel="icon" href="{{ assetUrl('images/logo.svg') }}" type="image/x-icon" />
{# Twitter Cards #}
<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content="shoelace_style" />
<meta name="twitter:image" content="{{ assetUrl(meta.image, true) }}" />
{# OpenGraph #}
<meta property="og:url" content="{{ rootUrl(page.url, true) }}" />
<meta property="og:title" content="{{ meta.title }}" />
<meta property="og:description" content="{{ meta.description }}" />
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
{# Shoelace #}
<link rel="stylesheet" href="/dist/themes/light.css" />
<link rel="stylesheet" href="/dist/themes/dark.css" />
<script type="module" src="/dist/shoelace-autoloader.js"></script>
{# Set the initial theme and menu states here to prevent flashing #}
<script>
(() => {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = localStorage.getItem('theme') || 'auto';
document.documentElement.classList.toggle('sl-theme-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
})();
</script>
{# Turbo + Scroll positioning #}
<script src="{{ assetUrl('scripts/turbo.js') }}" type="module"></script>
<script src="{{ assetUrl('scripts/docs.js') }}" defer></script>
<script src="{{ assetUrl('scripts/code-previews.js') }}" defer></script>
<script src="{{ assetUrl('scripts/lunr.js') }}" defer></script>
<script src="{{ assetUrl('scripts/search.js') }}" defer></script>
</head>
<body>
<a id="skip-to-main" class="visually-hidden" href="#main-content" data-smooth-link="false">
Skip to main content
</a>
{# Menu toggle #}
<button id="menu-toggle" type="button" aria-label="Menu">
<svg width="148" height="148" viewBox="0 0 148 148" xmlns="http://www.w3.org/2000/svg">
<g stroke="currentColor" stroke-width="18" fill="none" fill-rule="evenodd" stroke-linecap="round">
<path d="M9.5 125.5h129M9.5 74.5h129M9.5 23.5h129"></path>
</g>
</svg>
</button>
{# Icon toolbar #}
<div id="icon-toolbar">
{# GitHub #}
<a href="https://github.com/shoelace-style/shoelace" title="View Shoelace on GitHub">
<sl-icon name="github"></sl-icon>
</a>
{# Twitter #}
<a href="https://twitter.com/shoelace_style" title="Follow Shoelace on Twitter">
<sl-icon name="twitter"></sl-icon>
</a>
{# Theme selector #}
<sl-dropdown id="theme-selector" placement="bottom-end" distance="3">
<sl-button slot="trigger" size="small" variant="text" caret title="Press \ to toggle">
<sl-icon class="only-light" name="sun-fill"></sl-icon>
<sl-icon class="only-dark" name="moon-fill"></sl-icon>
</sl-button>
<sl-menu>
<sl-menu-item type="checkbox" value="light">Light</sl-menu-item>
<sl-menu-item type="checkbox" value="dark">Dark</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item type="checkbox" value="auto">System</sl-menu-item>
</sl-menu>
</sl-dropdown>
</div>
<aside id="sidebar" data-preserve-scroll>
<header>
<a href="/">
<img src="{{ assetUrl('images/wordmark.svg') }}" alt="Shoelace" />
</a>
<div class="sidebar-version">
{{ meta.version }}
</div>
</header>
<div class="sidebar-buttons">
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace" target="_blank">
<sl-icon slot="prefix" name="github"></sl-icon> Code
</sl-button>
<sl-button size="small" class="repo-button repo-button--star" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<sl-icon slot="prefix" name="star-fill"></sl-icon> Star
</sl-button>
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
</sl-button>
</div>
<button class="search-box" type="button" title="Press / to search" aria-label="Search" data-plugin="search">
<sl-icon name="search"></sl-icon>
<span>Search</span>
</button>
<nav>
{% include 'sidebar.njk' %}
</nav>
</aside>
{# Content #}
<main>
<a id="main-content"></a>
<article id="content" class="content{% if toc %} content--with-toc{% endif %}">
{% if toc %}
<div class="content__toc">
<ul>
<li class="top"><a href="#">{{ meta.title }}</a></li>
</ul>
</div>
{% endif %}
<div class="content__body">
{% block content %}
{{ content | safe }}
{% endblock %}
</div>
</article>
</main>
</body>
</html>

View File

@@ -1,65 +0,0 @@
<ul>
<li>
<h2>Getting Started</h2>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/getting-started/installation">Installation</a></li>
<li><a href="/getting-started/usage">Usage</a></li>
<li><a href="/getting-started/themes">Themes</a></li>
<li><a href="/getting-started/customizing">Customizing</a></li>
<li><a href="/getting-started/form-controls">Form Controls</a></li>
<li><a href="/getting-started/localization">Localization</a></li>
</ul>
</li>
<li>
<h2>Frameworks</h2>
<ul>
<li><a href="/frameworks/react">React</a></li>
<li><a href="/frameworks/vue">Vue</a></li>
<li><a href="/frameworks/angular">Angular</a></li>
</ul>
</li>
<li>
<h2>Resources</h2>
<ul>
<li><a href="/resources/community">Community</a></li>
<li><a href="https://github.com/shoelace-style/shoelace/discussions">Help &amp; Support</a></li>
<li><a href="/resources/accessibility">Accessibility</a></li>
<li><a href="/resources/contributing">Contributing</a></li>
<li><a href="/resources/changelog">Changelog</a></li>
</ul>
</li>
<li>
<h2>Components</h2>
<ul>
{% for component in meta.components %}
<li>
<a href="/components/{{ component.tagName | removeSlPrefix }}">
{{ component.name | classNameToComponentName }}
</a>
</li>
{% endfor %}
</ul>
</li>
<li>
<h2>Design Tokens</h2>
<ul>
<li><a href="/tokens/typography">Typography</a></li>
<li><a href="/tokens/color">Color</a></li>
<li><a href="/tokens/spacing">Spacing</a></li>
<li><a href="/tokens/elevation">Elevation</a></li>
<li><a href="/tokens/border-radius">Border Radius</a></li>
<li><a href="/tokens/transition">Transition</a></li>
<li><a href="/tokens/z-index">Z-index</a></li>
<li><a href="/tokens/more">More Tokens</a></li>
</ul>
</li>
<li>
<h2>Tutorials</h2>
<ul>
<li><a href="/tutorials/integrating-with-laravel">Integrating with Laravel</a></li>
<li><a href="/tutorials/integrating-with-nextjs">Integrating with NextJS</a></li>
<li><a href="/tutorials/integrating-with-rails">Integrating with Rails</a></li>
</ul>
</li>
</ul>

View File

@@ -1,35 +0,0 @@
function normalizePathname(pathname) {
// Remove /index.html
if (pathname.endsWith('/index.html')) {
pathname = pathname.replace(/\/index\.html/, '');
}
// Remove trailing slashes
return pathname.replace(/\/$/, '');
}
/**
* Adds a class name to links that are currently active.
*/
module.exports = function (doc, options) {
options = {
className: 'active-link', // the class to add to active links
pathname: undefined, // the current pathname to compare
within: 'body', // element containing the target links
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('a').forEach(link => {
if (normalizePathname(options.pathname) === normalizePathname(link.pathname)) {
link.classList.add(options.className);
}
});
return doc;
};

View File

@@ -1,64 +0,0 @@
const { createSlug } = require('./strings.cjs');
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
levels: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], // the headings to convert
className: 'anchor-heading', // the class name to add
within: 'body', // the element containing the target headings
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
const hasAnchor = heading.querySelector('a');
const anchor = doc.createElement('a');
let id = heading.textContent ?? '';
let suffix = 0;
// Skip heading levels we don't care about
if (!options.levels?.includes(heading.tagName.toLowerCase())) {
return;
}
// Convert dots to underscores
id = id.replace(/\./g, '_');
// Turn it into a slug
id = createSlug(id);
// Make sure it starts with a letter
if (!/^[a-z]/i.test(id)) {
id = `id_${id}`;
}
// Make sure the id is unique
const originalId = id;
while (doc.getElementById(id) !== null) {
id = `${originalId}-${++suffix}`;
}
if (hasAnchor || !id) return;
heading.setAttribute('id', id);
anchor.setAttribute('href', `#${encodeURIComponent(id)}`);
anchor.setAttribute('aria-label', `Direct link to "${heading.textContent}"`);
if (options.className) {
heading.classList.add(options.className);
}
// Append the anchor
heading.append(anchor);
});
return doc;
};

View File

@@ -1,138 +0,0 @@
let count = 1;
function escapeHtml(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
/**
* Turns code fields with the :preview suffix into interactive code previews.
*/
module.exports = function (doc, options) {
options = {
within: 'body', // the element containing the code fields to convert
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('[class*=":preview"]').forEach(code => {
const pre = code.closest('pre');
if (!pre) {
return;
}
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
const sourceGroupId = `code-preview-source-group-${count}`;
const isExpanded = code.getAttribute('class').includes(':expanded');
const noCodePen = code.getAttribute('class').includes(':no-codepen');
count++;
const htmlButton = `
<button type="button"
title="Show HTML code"
class="code-preview__button code-preview__button--html"
>
HTML
</button>
`;
const reactButton = `
<button type="button" title="Show React code" class="code-preview__button code-preview__button--react">
React
</button>
`;
const codePenButton = `
<button type="button" class="code-preview__button code-preview__button--codepen" title="Edit on CodePen">
<svg
width="138"
height="26"
viewBox="0 0 138 26"
fill="none"
stroke="currentColor"
stroke-width="2.3"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
</svg>
</button>
`;
const codePreview = `
<div class="code-preview ${isExpanded ? 'code-preview--expanded' : ''}">
<div class="code-preview__preview">
${code.textContent}
<div class="code-preview__resizer">
<sl-icon name="grip-vertical"></sl-icon>
</div>
</div>
<div class="code-preview__source-group" id="${sourceGroupId}">
<div class="code-preview__source code-preview__source--html" ${reactCode ? 'data-flavor="html"' : ''}>
<pre><code class="language-html">${escapeHtml(code.textContent)}</code></pre>
</div>
${
reactCode
? `
<div class="code-preview__source code-preview__source--react" data-flavor="react">
<pre><code class="language-jsx">${escapeHtml(reactCode.textContent)}</code></pre>
</div>
`
: ''
}
</div>
<div class="code-preview__buttons">
<button
type="button"
class="code-preview__button code-preview__toggle"
aria-expanded="${isExpanded ? 'true' : 'false'}"
aria-controls="${sourceGroupId}"
>
Source
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
${reactCode ? ` ${htmlButton} ${reactButton} ` : ''}
${noCodePen ? '' : codePenButton}
</div>
</div>
`;
pre.insertAdjacentHTML('afterend', codePreview);
pre.remove();
if (adjacentPre) {
adjacentPre.remove();
}
});
// Wrap code preview scripts in anonymous functions so they don't run in the global scope
doc.querySelectorAll('.code-preview__preview script').forEach(script => {
if (script.type === 'module') {
// Modules are already scoped
script.textContent = script.innerHTML;
} else {
// Wrap non-modules in an anonymous function so they don't run in the global scope
script.textContent = `(() => { ${script.innerHTML} })();`;
}
});
return doc;
};

View File

@@ -1,23 +0,0 @@
let codeBlockId = 0;
/**
* Adds copy code buttons to code fields. The provided doc should be a document object provided by JSDOM. The same
* document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
const button = doc.createElement('sl-copy-button');
if (!code.id) {
code.id = `code-block-${++codeBlockId}`;
}
button.classList.add('copy-code-button');
button.setAttribute('from', code.id);
pre.append(button);
});
return doc;
};

View File

@@ -1,41 +0,0 @@
const { isExternalLink } = require('./strings.cjs');
/**
* Transforms external links to make them safer and optionally add a target. The provided doc should be a document
* object provided by JSDOM. The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
className: 'external-link', // the class name to add to links
noopener: true, // sets rel="noopener"
noreferrer: true, // sets rel="noreferrer"
ignore: () => false, // callback function to filter links that should be ignored
within: 'body', // element that contains the target links
target: '', // sets the target attribute
...options
};
const within = doc.querySelector(options.within);
if (within) {
within.querySelectorAll('a').forEach(link => {
if (isExternalLink(link) && !options.ignore(link)) {
link.classList.add(options.className);
const rel = [];
if (options.noopener) rel.push('noopener');
if (options.noreferrer) rel.push('noreferrer');
if (rel.length) {
link.setAttribute('rel', rel.join(' '));
}
if (options.target) {
link.setAttribute('target', options.target);
}
}
});
}
return doc;
};

View File

@@ -1,63 +0,0 @@
const Prism = require('prismjs');
const PrismLoader = require('prismjs/components/index.js');
PrismLoader('diff');
PrismLoader.silent = true;
/** Highlights a code string. */
function highlight(code, language) {
const alias = language.replace(/^diff-/, '');
const isDiff = /^diff-/i.test(language);
// Auto-load the target language
if (!Prism.languages[alias]) {
PrismLoader(alias);
if (!Prism.languages[alias]) {
throw new Error(`Unsupported language for code highlighting: "${language}"`);
}
}
// Register diff-* languages to use the diff grammar
if (isDiff) {
Prism.languages[language] = Prism.languages.diff;
}
return Prism.highlight(code, Prism.languages[language], language);
}
/**
* Highlights all code fields that have a language parameter. If the language has a colon in its name, the first chunk
* will be the language used and additional chunks will be applied as classes to the `<pre>`. For example, a code field
* tagged with "html:preview" will be rendered as `<pre class="language-html preview">`.
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code[class]').forEach(code => {
// Look for class="language-*" and split colons into separate classes
code.classList.forEach(className => {
if (className.startsWith('language-')) {
//
// We use certain suffixes to indicate code previews, expanded states, etc. The class might look something like
// this:
//
// class="language-html:preview:expanded"
//
// The language will always come first, so we need to drop the "language-" prefix and everything after the first
// color to get the highlighter language.
//
const language = className.replace(/^language-/, '').split(':')[0];
try {
code.innerHTML = highlight(code.textContent ?? '', language);
} catch (err) {
// Language not found, skip it
}
}
});
});
return doc;
};

View File

@@ -1,67 +0,0 @@
const MarkdownIt = require('markdown-it');
const markdownItContainer = require('markdown-it-container');
const markdownItIns = require('markdown-it-ins');
const markdownItKbd = require('markdown-it-kbd');
const markdownItMark = require('markdown-it-mark');
const markdownItReplaceIt = require('markdown-it-replace-it');
const markdown = MarkdownIt({
html: true,
xhtmlOut: false,
breaks: false,
langPrefix: 'language-',
linkify: false,
typographer: false
});
// Third-party plugins
markdown.use(markdownItContainer);
markdown.use(markdownItIns);
markdown.use(markdownItKbd);
markdown.use(markdownItMark);
markdown.use(markdownItReplaceIt);
// Callouts
['tip', 'warning', 'danger'].forEach(type => {
markdown.use(markdownItContainer, type, {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<div role="alert" class="callout callout--${type}">`;
}
return '</div>\n';
}
});
});
// Asides
markdown.use(markdownItContainer, 'aside', {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<aside>`;
}
return '</aside>\n';
}
});
// Details
markdown.use(markdownItContainer, 'details', {
validate: params => params.trim().match(/^details\s+(.*)$/),
render: (tokens, idx) => {
const m = tokens[idx].info.trim().match(/^details\s+(.*)$/);
if (tokens[idx].nesting === 1) {
return `<details>\n<summary><span>${markdown.utils.escapeHtml(m[1])}</span></summary>\n`;
}
return '</details>\n';
}
});
// Replace [#1234] with a link to GitHub issues
markdownItReplaceIt.replacements.push({
name: 'github-issues',
re: /\[#([0-9]+)\]/gs,
sub: '<a href="https://github.com/shoelace-style/shoelace/issues/$1">#$1</a>',
html: true,
default: true
});
module.exports = markdown;

View File

@@ -1,26 +0,0 @@
const { format } = require('prettier');
/** Formats markup using prettier. */
module.exports = function (content, options) {
options = {
arrowParens: 'avoid',
bracketSpacing: true,
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
bracketSameLine: false,
jsxSingleQuote: false,
parser: 'html',
printWidth: 120,
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'none',
useTabs: false,
...options
};
return format(content, options);
};

View File

@@ -1,19 +0,0 @@
/**
* @typedef {object} Replacement
* @property {string | RegExp} pattern
* @property {string} replacement
*/
/**
* @typedef {Array<Replacement>} Replacements
*/
/**
* @param {Document} content
* @param {Replacements} replacements
*/
module.exports = function (content, replacements) {
replacements.forEach(replacement => {
content.body.innerHTML = content.body.innerHTML.replaceAll(replacement.pattern, replacement.replacement);
});
};

View File

@@ -1,21 +0,0 @@
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
const tables = [...doc.querySelectorAll('table')];
options = {
className: 'table-scroll', // the class name to add to the table's container
...options
};
tables.forEach(table => {
const div = doc.createElement('div');
div.classList.add(options.className);
table.insertAdjacentElement('beforebegin', div);
div.append(table);
});
return doc;
};

View File

@@ -1,16 +0,0 @@
const slugify = require('slugify');
/** Creates a slug from an arbitrary string of text. */
module.exports.createSlug = function (text) {
return slugify(String(text), {
remove: /[^\w|\s]/g,
lower: true
});
};
/** Determines whether or not a link is external. */
module.exports.isExternalLink = function (link) {
// We use the "internal" hostname when initializing JSDOM so we know that those are local links
if (!link.hostname || link.hostname === 'internal') return false;
return true;
};

View File

@@ -1,42 +0,0 @@
/**
* Generates an in-page table of contents based on headings.
*/
module.exports = function (doc, options) {
options = {
levels: ['h2'], // headings to include (they must have an id)
container: 'nav', // the container to append links to
listItem: true, // if true, links will be wrapped in <li>
within: 'body', // the element containing the headings to summarize
...options
};
const container = doc.querySelector(options.container);
const within = doc.querySelector(options.within);
const headingSelector = options.levels.map(h => `${h}[id]`).join(', ');
if (!container || !within) {
return doc;
}
within.querySelectorAll(headingSelector).forEach(heading => {
const listItem = doc.createElement('li');
const link = doc.createElement('a');
const level = heading.tagName.slice(1);
link.href = `#${heading.id}`;
link.textContent = heading.textContent;
if (options.listItem) {
// List item + link
listItem.setAttribute('data-level', level);
listItem.append(link);
container.append(listItem);
} else {
// Link only
link.setAttribute('data-level', level);
container.append(link);
}
});
return doc;
};

View File

@@ -1,23 +0,0 @@
const smartquotes = require('smartquotes');
smartquotes.replacements.push([/---/g, '\u2014']); // em dash
smartquotes.replacements.push([/--/g, '\u2013']); // en dash
smartquotes.replacements.push([/\.\.\./g, '\u2026']); // ellipsis
smartquotes.replacements.push([/\(c\)/gi, '\u00A9']); // copyright
smartquotes.replacements.push([/\(r\)/gi, '\u00AE']); // registered trademark
smartquotes.replacements.push([/\?!/g, '\u2048']); // ?!
smartquotes.replacements.push([/!!/g, '\u203C']); // !!
smartquotes.replacements.push([/\?\?/g, '\u2047']); // ??
smartquotes.replacements.push([/([0-9]\s?)-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
/**
* Improves typography by adding smart quotes and similar corrections within the specified element(s).
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc, selector = 'body') {
const elements = [...doc.querySelectorAll(selector)];
elements.forEach(el => smartquotes.element(el));
return doc;
};

View File

@@ -1,7 +0,0 @@
<svg viewBox="0 0 127 141" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g fill-rule="nonzero" fill="#0ea5e9">
<path d="M102.375,90.85 C102.979,90.557 103.57,90.215 104.15,89.826 L106.425,88.501 C106.848,88.19 107.64,87.573 108.8,86.65 L108.95,86.501 C109.883,85.567 110.916,85.117 112.05,85.15 C112.55,85.15 113.133,85.284 113.8,85.55 L122.3,78 C122.533,77.8 122.767,77.633 123,77.5 L123.05,77.5 C123.95,76.967 124.7,77 125.3,77.6 C126.367,78.166 126.45,79.133 125.55,80.5 L116.65,88.399 C116.717,88.533 116.75,88.666 116.75,88.799 C116.883,89.399 116.833,89.999 116.6,90.599 C116.4,90.999 116.117,91.382 115.75,91.749 C115.379,92.145 114.996,92.528 114.6,92.898 L109.45,96.648 C108.99,96.97 108.523,97.27 108.05,97.548 C107.46,97.906 106.86,98.232 106.25,98.523 C105.985,98.644 105.718,98.76 105.45,98.874 C103.841,99.559 102.174,100.017 100.45,100.249 C99.65,106.982 97.35,113.183 93.55,118.849 C88.75,125.915 82.183,131.299 73.85,134.999 C65.55,138.699 56.567,140.549 46.9,140.549 C33.567,140.415 22.75,137.415 14.45,131.549 C4.817,124.75 0,115.7 0,104.399 L0,102.849 C0.333,95.149 2.6,87.849 6.8,80.95 C10.933,74.116 16.983,69.5 24.95,67.1 C29.05,65.9 33.166,65.3 37.3,65.3 C41.567,65.3 45.967,66.083 50.5,67.65 C55.033,69.216 60.15,71.916 65.85,75.75 L80.7,85.7 C84.833,88.399 88.6,90.233 92,91.2 C91.3,84.3 88.8,78.399 84.5,73.5 C80.2,68.533 74.717,64.9 68.05,62.6 L61.65,60.4 C55.783,58.333 51.417,56.3 48.55,54.3 C40.817,49.067 36.517,41.883 35.65,32.75 L35.5,30.05 C35.5,21.25 39.067,13.883 46.2,7.95 C52.567,2.65 60.133,0 68.9,0 C75.5,0 81.417,1.9 86.65,5.7 C91.917,9.533 94.9,14.967 95.6,22 L95.75,24.75 C95.75,29.85 94.433,34.216 91.8,37.85 C89.1,41.483 86.717,43.3 84.65,43.3 C84.21,43.269 83.802,43.21 83.425,43.125 L74.1,51.9 C72.6,52.733 71.583,52.583 71.05,51.45 C70.517,50.85 70.567,50.1 71.2,49.2 L71.25,49.15 C71.383,48.95 71.567,48.733 71.8,48.5 L80.475,40.275 C80.376,39.872 80.318,39.431 80.3,38.95 C80.3,37.817 80.75,36.867 81.65,36.1 C85.45,32.7 87.35,28.784 87.35,24.35 C87.35,19.95 85.683,16.25 82.35,13.25 C79.017,10.25 74.467,8.716 68.7,8.65 C61.5,8.65 55.583,10.817 50.95,15.15 C46.317,19.483 44,24.683 44,30.75 C44,35.65 45.883,40.066 49.65,44 C53.383,47.9 59.15,50.966 66.95,53.2 C77.883,56.367 86.233,61.7 92,69.2 C97.133,75.833 100,83.283 100.6,91.55 C101.199,91.365 101.791,91.132 102.375,90.85 Z M71.95,49.05 C71.95,49.35 72.117,49.5 72.45,49.5 C72.483,49.5 75.117,47.066 80.35,42.2 C80.35,41.533 78.95,42.45 76.15,44.95 C73.35,47.483 71.95,48.85 71.95,49.05 Z M74.15,50.8 C74.15,50.533 74.017,50.4 73.75,50.4 C73.45,50.4 73.183,50.55 72.95,50.85 C72.416,50.817 72.033,50.884 71.8,51.05 C71.7,51.117 71.65,51.183 71.65,51.25 C71.65,51.45 71.783,51.6 72.05,51.7 L72.95,51.7 C73.75,51.4 74.15,51.1 74.15,50.8 Z M80.35,45.35 C80.35,44.583 79.9,44.583 79,45.35 C78.567,45.75 78.017,46.317 77.35,47.05 C77.117,47.217 76.633,47.667 75.9,48.4 C75.133,49.2 74.75,49.683 74.75,49.85 L74.8,50.2 C75,50.267 75.133,50.3 75.2,50.3 C75.233,50.3 76.1,49.5 77.8,47.9 C79.5,46.3 80.35,45.45 80.35,45.35 Z M124.2,78.3 L115.8,85.7 C116.3,85.967 116.667,86.349 116.9,86.849 L125.2,79.45 C125.266,79.116 125.217,78.849 125.05,78.649 C124.883,78.517 124.6,78.399 124.2,78.3 Z M123.75,78.05 L123.55,77.85 L116.15,83.85 L116.6,84.4 L123.75,78.05 Z M91.85,99.899 C89.65,99.333 87.617,98.649 85.75,97.849 C81.55,96.149 76.333,93.183 70.1,88.95 L59.85,82 C55.517,79.233 51.567,77.166 48,75.8 C44.4,74.467 40.867,73.8 37.4,73.8 L35.9,73.8 C27.067,74.267 20.2,77.583 15.3,83.75 C10.734,89.45 8.45,96.184 8.45,103.95 C8.45,112.683 11.95,119.516 18.95,124.45 C25.916,129.416 35.467,131.899 47.6,131.899 C57.133,131.899 65.166,130.2 71.7,126.799 C78.2,123.399 83.25,118.899 86.85,113.299 C89.55,109.033 91.217,104.566 91.85,99.899 Z"></path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,272 +0,0 @@
//
// Sidebar
//
// When the sidebar is hidden, we apply the inert attribute to prevent focus from reaching it. Due to the many states
// the sidebar can have (e.g. static, hidden, expanded), we test for visibility by checking to see if it's placed
// offscreen or not. Then, on resize/transition we make sure to update the attribute accordingly.
//
(() => {
function getSidebar() {
return document.getElementById('sidebar');
}
function isSidebarOpen() {
return document.documentElement.classList.contains('sidebar-open');
}
function isSidebarVisible() {
return getSidebar().getBoundingClientRect().x >= 0;
}
function toggleSidebar(force) {
const isOpen = typeof force === 'boolean' ? force : !isSidebarOpen();
return document.documentElement.classList.toggle('sidebar-open', isOpen);
}
function updateInert() {
getSidebar().inert = !isSidebarVisible();
}
// Toggle the menu
document.addEventListener('click', event => {
const menuToggle = event.target.closest('#menu-toggle');
if (!menuToggle) return;
toggleSidebar();
});
// Update the sidebar's inert state when the window resizes and when the sidebar transitions
window.addEventListener('resize', () => toggleSidebar(false));
document.addEventListener('transitionend', event => {
const sidebar = event.target.closest('#sidebar');
if (!sidebar) return;
updateInert();
});
// Close when a menu item is selected on mobile
document.addEventListener('click', event => {
const sidebar = event.target.closest('#sidebar');
const link = event.target.closest('a');
if (!sidebar || !link) return;
if (isSidebarOpen()) {
toggleSidebar();
}
});
// Close when open and escape is pressed
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && isSidebarOpen()) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
// Close when clicking outside of the sidebar
document.addEventListener('mousedown', event => {
if (isSidebarOpen() & !event.target?.closest('#sidebar, #menu-toggle')) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
updateInert();
})();
//
// Theme selector
//
(() => {
function getTheme() {
return localStorage.getItem('theme') || 'auto';
}
function isDark() {
if (theme === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
return theme === 'dark';
}
function setTheme(newTheme) {
theme = newTheme;
localStorage.setItem('theme', theme);
// Update the UI
updateSelection();
// Toggle the dark mode class
document.documentElement.classList.toggle('sl-theme-dark', isDark());
}
function updateSelection() {
const menu = document.querySelector('#theme-selector sl-menu');
if (!menu) return;
[...menu.querySelectorAll('sl-menu-item')].map(item => (item.checked = item.getAttribute('value') === theme));
}
let theme = getTheme();
// Selection is not preserved when changing page, so update when opening dropdown
document.addEventListener('sl-show', event => {
const themeSelector = event.target.closest('#theme-selector');
if (!themeSelector) return;
updateSelection();
});
// Listen for selections
document.addEventListener('sl-select', event => {
const menu = event.target.closest('#theme-selector sl-menu');
if (!menu) return;
setTheme(event.detail.item.value);
});
// Update the theme when the preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTheme(theme));
// Toggle with backslash
document.addEventListener('keydown', event => {
if (
event.key === '\\' &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
setTheme(isDark() ? 'light' : 'dark');
}
});
// Set the initial theme and sync the UI
setTheme(theme);
})();
//
// Open details when printing
//
(() => {
const detailsOpenOnPrint = new Set();
window.addEventListener('beforeprint', () => {
detailsOpenOnPrint.clear();
document.querySelectorAll('details').forEach(details => {
if (details.open) {
detailsOpenOnPrint.add(details);
}
details.open = true;
});
});
window.addEventListener('afterprint', () => {
document.querySelectorAll('details').forEach(details => {
details.open = detailsOpenOnPrint.has(details);
});
detailsOpenOnPrint.clear();
});
})();
//
// Smooth links
//
(() => {
document.addEventListener('click', event => {
const link = event.target.closest('a');
const id = (link?.hash ?? '').substr(1);
const isFragment = link?.hasAttribute('href') && link?.getAttribute('href').startsWith('#');
if (!link || !isFragment || link.getAttribute('data-smooth-link') === 'false') {
return;
}
// Scroll to the top
if (link.hash === '') {
event.preventDefault();
window.scroll({ top: 0, behavior: 'smooth' });
history.pushState(undefined, undefined, location.pathname);
}
// Scroll to an id
if (id) {
const target = document.getElementById(id);
if (target) {
event.preventDefault();
window.scroll({ top: target.offsetTop, behavior: 'smooth' });
history.pushState(undefined, undefined, `#${id}`);
}
}
});
})();
//
// Table of Contents scrollspy
//
(() => {
// This will be stale if its not a function.
const getLinks = () => [...document.querySelectorAll('.content__toc a')];
const linkTargets = new WeakMap();
const visibleTargets = new WeakSet();
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '0px 0px' });
let debounce;
function handleIntersect(entries) {
entries.forEach(entry => {
// Remember which targets are visible
if (entry.isIntersecting) {
visibleTargets.add(entry.target);
} else {
visibleTargets.delete(entry.target);
}
});
updateActiveLinks();
}
function updateActiveLinks() {
const links = getLinks();
// Find the first visible target and activate the respective link
links.find(link => {
const target = linkTargets.get(link);
if (target && visibleTargets.has(target)) {
links.forEach(el => el.classList.toggle('active', el === link));
return true;
}
return false;
});
}
// Observe link targets
function observeLinks() {
getLinks().forEach(link => {
const hash = link.hash.slice(1);
const target = hash ? document.querySelector(`.content__body #${hash}`) : null;
if (target) {
linkTargets.set(link, target);
observer.observe(target);
}
});
}
observeLinks();
document.addEventListener('turbo:load', updateActiveLinks);
document.addEventListener('turbo:load', observeLinks);
})();
//
// Show custom versions in the sidebar
//
(() => {
function updateVersion() {
const el = document.querySelector('.sidebar-version');
if (!el) return;
if (location.hostname === 'next.shoelace.style') el.textContent = 'Next';
if (location.hostname === 'localhost') el.textContent = 'Development';
}
updateVersion();
document.addEventListener('turbo:load', updateVersion);
})();

View File

@@ -1,384 +0,0 @@
(() => {
// Append the search dialog to the body
const siteSearch = document.createElement('div');
const scrollbarWidth = Math.abs(window.innerWidth - document.documentElement.clientWidth);
siteSearch.classList.add('search');
siteSearch.innerHTML = `
<div class="search__overlay"></div>
<dialog id="search-dialog" class="search__dialog">
<div class="search__content">
<div class="search__header">
<div id="search-combobox" class="search__input-wrapper">
<sl-icon name="search"></sl-icon>
<input
id="search-input"
class="search__input"
type="search"
placeholder="Search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
enterkeyhint="go"
spellcheck="false"
maxlength="100"
role="combobox"
aria-autocomplete="list"
aria-expanded="true"
aria-controls="search-listbox"
aria-haspopup="listbox"
aria-activedescendant
>
<button type="button" class="search__clear-button" aria-label="Clear entry" tabindex="-1" hidden>
<sl-icon name="x-circle-fill"></sl-icon>
</button>
</div>
</div>
<div class="search__body">
<ul
id="search-listbox"
class="search__results"
role="listbox"
aria-label="Search results"
></ul>
<div class="search__empty">No matching pages</div>
</div>
<footer class="search__footer">
<small><kbd>↑</kbd> <kbd>↓</kbd> Navigate</small>
<small><kbd>↲</kbd> Select</small>
<small><kbd>Esc</kbd> Close</small>
</footer>
</div>
</dialog>
`;
const overlay = siteSearch.querySelector('.search__overlay');
const dialog = siteSearch.querySelector('.search__dialog');
const input = siteSearch.querySelector('.search__input');
const clearButton = siteSearch.querySelector('.search__clear-button');
const results = siteSearch.querySelector('.search__results');
const version = document.documentElement.getAttribute('data-shoelace-version');
const key = `search_${version}`;
const searchDebounce = 50;
const animationDuration = 150;
let isShowing = false;
let searchTimeout;
let searchIndex;
let map;
const loadSearchIndex = new Promise(resolve => {
const cache = localStorage.getItem(key);
const wait = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame;
// Cleanup older search indices (everything before this version)
try {
const items = { ...localStorage };
Object.keys(items).forEach(k => {
if (key > k) {
localStorage.removeItem(k);
}
});
} catch {
/* do nothing */
}
// Look for a cached index
try {
if (cache) {
const data = JSON.parse(cache);
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
return resolve();
}
} catch {
/* do nothing */
}
// Wait until idle to fetch the index
wait(() => {
fetch('/assets/search.json')
.then(res => res.json())
.then(data => {
if (!window.lunr) {
console.error('The Lunr search client has not yet been loaded.');
}
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
// Cache the search index for this version
if (version) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (err) {
console.warn(`Unable to cache the search index: ${err}`);
}
}
resolve();
});
});
});
async function show() {
isShowing = true;
document.body.append(siteSearch);
document.body.classList.add('search-visible');
document.body.style.setProperty('--docs-search-scroll-lock-size', `${scrollbarWidth}px`);
clearButton.hidden = true;
requestAnimationFrame(() => input.focus());
updateResults();
dialog.showModal();
await Promise.all([
dialog.animate(
[
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' },
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
]);
dialog.addEventListener('mousedown', handleMouseDown);
dialog.addEventListener('keydown', handleKeyDown);
}
async function hide() {
isShowing = false;
await Promise.all([
dialog.animate(
[
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' },
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
]);
dialog.close();
input.blur(); // otherwise Safari will scroll to the bottom of the page on close
input.value = '';
document.body.classList.remove('search-visible');
document.body.style.removeProperty('--docs-search-scroll-lock-size');
siteSearch.remove();
updateResults();
dialog.removeEventListener('mousedown', handleMouseDown);
dialog.removeEventListener('keydown', handleKeyDown);
}
function handleInput() {
clearButton.hidden = input.value === '';
// Debounce search queries
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
}
function handleClear() {
clearButton.hidden = true;
input.value = '';
input.focus();
updateResults();
}
function handleMouseDown(event) {
if (!event.target.closest('.search__content')) {
hide();
}
}
function handleKeyDown(event) {
// Close when pressing escape
if (event.key === 'Escape') {
event.preventDefault(); // prevent <dialog> from closing immediately so it can animate
event.stopImmediatePropagation();
hide();
return;
}
// Handle keyboard selections
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
event.preventDefault();
const currentEl = results.querySelector('[data-selected="true"]');
const items = [...results.querySelectorAll('li')];
const index = items.indexOf(currentEl);
let nextEl;
if (items.length === 0) {
return;
}
switch (event.key) {
case 'ArrowUp':
nextEl = items[Math.max(0, index - 1)];
break;
case 'ArrowDown':
nextEl = items[Math.min(items.length - 1, index + 1)];
break;
case 'Home':
nextEl = items[0];
break;
case 'End':
nextEl = items[items.length - 1];
break;
case 'Enter':
currentEl?.querySelector('a')?.click();
break;
}
// Update the selected item
items.forEach(item => {
if (item === nextEl) {
input.setAttribute('aria-activedescendant', item.id);
item.setAttribute('data-selected', 'true');
nextEl.scrollIntoView({ block: 'nearest' });
} else {
item.setAttribute('data-selected', 'false');
}
});
}
}
async function updateResults(query = '') {
try {
await loadSearchIndex;
const hasQuery = query.length > 0;
const searchTerms = query
.split(' ')
.map((term, index, arr) => {
// Search API: https://lunrjs.com/guides/searching.html
if (index === arr.length - 1) {
// The last term is not mandatory and 1x fuzzy. We also duplicate it with a wildcard to match partial words
// as the user types.
return `${term}~1 ${term}*`;
} else {
// All other terms are mandatory and 1x fuzzy
return `+${term}~1`;
}
})
.join(' ');
const matches = hasQuery ? searchIndex.search(searchTerms) : [];
const hasResults = hasQuery && matches.length > 0;
siteSearch.classList.toggle('search--has-results', hasQuery && hasResults);
siteSearch.classList.toggle('search--no-results', hasQuery && !hasResults);
input.setAttribute('aria-activedescendant', '');
results.innerHTML = '';
matches.forEach((match, index) => {
const page = map[match.ref];
const li = document.createElement('li');
const a = document.createElement('a');
const displayTitle = page.title ?? '';
const displayDescription = page.description ?? '';
const displayUrl = page.url.replace(/^\//, '').replace(/\/$/, '');
let icon = 'file-text';
a.setAttribute('role', 'option');
a.setAttribute('id', `search-result-item-${match.ref}`);
if (page.url.includes('getting-started/')) {
icon = 'lightbulb';
}
if (page.url.includes('resources/')) {
icon = 'book';
}
if (page.url.includes('components/')) {
icon = 'puzzle';
}
if (page.url.includes('tokens/')) {
icon = 'palette2';
}
if (page.url.includes('utilities/')) {
icon = 'wrench';
}
if (page.url.includes('tutorials/')) {
icon = 'joystick';
}
li.classList.add('search__result');
li.setAttribute('role', 'option');
li.setAttribute('id', `search-result-item-${match.ref}`);
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
a.href = page.url;
a.innerHTML = `
<div class="search__result-icon" aria-hidden="true">
<sl-icon name="${icon}"></sl-icon>
</div>
<div class="search__result__details">
<div class="search__result-title"></div>
<div class="search__result-description"></div>
<div class="search__result-url"></div>
</div>
`;
a.querySelector('.search__result-title').textContent = displayTitle;
a.querySelector('.search__result-description').textContent = displayDescription;
a.querySelector('.search__result-url').textContent = displayUrl;
li.appendChild(a);
results.appendChild(li);
});
} catch {
// Ignore query errors as the user types
}
}
// Show the search dialog when clicking on data-plugin="search"
document.addEventListener('click', event => {
const searchButton = event.target.closest('[data-plugin="search"]');
if (searchButton) {
show();
}
});
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
document.addEventListener('keydown', event => {
if (
!isShowing &&
(event.key === '/' || (event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
show();
}
});
// Purge cache when we press CMD+CTRL+R
document.addEventListener('keydown', event => {
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'r') {
localStorage.clear();
}
});
input.addEventListener('input', handleInput);
clearButton.addEventListener('click', handleClear);
// Close when a result is selected
results.addEventListener('click', event => {
if (event.target.closest('a')) {
hide();
}
});
// We're using Turbo, so when a user searches for something, visits a result, and presses the back button, the search
// UI will still be visible but not interactive. This removes the search UI when Turbo renders a page so they don't
// get trapped.
window.addEventListener('turbo:render', () => {
document.body.classList.remove('search-visible');
document.querySelectorAll('.search__overlay, .search__dialog').forEach(el => el.remove());
});
})();

View File

@@ -1,29 +0,0 @@
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm';
(() => {
if (!window.scrollPositions) {
window.scrollPositions = {};
}
function preserveScroll() {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
scrollPositions[element.id] = element.scrollTop;
});
}
function restoreScroll(event) {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
if (event.detail && event.detail.newBody) {
event.detail.newBody.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
}
}
window.addEventListener('turbo:before-cache', preserveScroll);
window.addEventListener('turbo:before-render', restoreScroll);
window.addEventListener('turbo:render', restoreScroll);
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,29 @@
/* Search plugin */
:root,
:root.sl-theme-dark {
--docs-search-box-background: var(--sl-color-neutral-0);
--docs-search-box-border-width: 1px;
--docs-search-box-border-color: var(--sl-color-neutral-300);
--docs-search-box-color: var(--sl-color-neutral-600);
--docs-search-dialog-background: var(--sl-color-neutral-0);
--docs-search-border-width: var(--docs-border-width);
--docs-search-border-color: var(--docs-border-color);
--docs-search-text-color: var(--sl-color-neutral-900);
--docs-search-text-color-muted: var(--sl-color-neutral-500);
--docs-search-font-weight-normal: var(--sl-font-weight-normal);
--docs-search-font-weight-semibold: var(--sl-font-weight-semibold);
--docs-search-border-radius: calc(2 * var(--docs-border-radius));
--docs-search-accent-color: var(--sl-color-primary-600);
--docs-search-icon-color: var(--sl-color-neutral-500);
--docs-search-icon-color-active: var(--sl-color-neutral-600);
--docs-search-shadow: var(--docs-shadow-x-large);
--docs-search-result-background-hover: var(--sl-color-neutral-100);
--docs-search-result-color-hover: var(--sl-color-neutral-900);
--docs-search-result-background-active: var(--sl-color-primary-600);
--docs-search-result-color-active: var(--sl-color-neutral-0);
--docs-search-focus-ring: var(--sl-focus-ring);
--docs-search-overlay-background: rgb(0 0 0 / 0.33);
}
:root {
--docs-search-box-background: var(--wa-form-controls-background);
--docs-search-box-border-width: var(--wa-form-controls-border-width);
--docs-search-box-border-color: var(--wa-form-controls-resting-color);
--docs-search-box-color: var(--wa-form-controls-placeholder-color);
:root.sl-theme-dark {
--docs-search-overlay-background: rgb(71 71 71 / 0.33);
--docs-search-dialog-background: var(--wa-color-surface-raised);
--docs-search-border-width: var(--wa-border-width-s);
--docs-search-border-color: var(--wa-color-surface-border);
--docs-search-text-color: var(--wa-color-text-normal);
--docs-search-text-color-muted: var(--wa-color-text-quiet);
--docs-search-font-weight-normal: var(--wa-font-weight-normal);
--docs-search-font-weight-semibold: var(--wa-font-weight-medium);
--docs-search-border-radius: calc(2 * var(--wa-corners-s));
--docs-search-accent-color: var(--wa-color-brand-text-on-surface);
--docs-search-icon-color: var(--wa-color-neutral-spot);
--docs-search-icon-color-active: color-mix(in lch, var(--wa-color-neutral-spot), 8% black);
--docs-search-shadow: var(--wa-shadow-level-3);
--docs-search-result-background-hover: var(--wa-color-neutral-fill-highlight);
--docs-search-result-color-hover: var(--wa-color-neutral-text-on-fill);
--docs-search-result-background-active: var(--wa-color-brand-spot);
--docs-search-result-color-active: var(--wa-color-brand-text-on-spot);
--docs-search-focus-ring: var(--wa-focus-ring);
--docs-search-overlay-background: rgb(0 0 0 / 0.33);
}
body.search-visible {
@@ -47,7 +44,7 @@ body.search-visible {
font: inherit;
color: var(--docs-search-box-color);
padding: 0.75rem 1rem;
margin: var(--sl-spacing-large) 0;
margin: var(--wa-space-l) 0;
cursor: pointer;
}
@@ -150,7 +147,7 @@ body.search-visible {
align-items: center;
}
.search__input-wrapper sl-icon {
.search__input-wrapper wa-icon {
width: 1.5rem;
height: 1.5rem;
flex: 0 0 auto;
@@ -172,7 +169,7 @@ body.search-visible {
display: none;
}
.search__clear-button:active sl-icon {
.search__clear-button:active wa-icon {
color: var(--docs-search-icon-color-active);
}
@@ -276,7 +273,7 @@ body.search-visible {
color: var(--docs-search-text-color-muted);
}
.search__result-icon sl-icon {
.search__result-icon wa-icon {
font-size: 1.5rem;
}

146
docs/astro.config.mjs Normal file
View File

@@ -0,0 +1,146 @@
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import * as url from 'node:url';
import * as path from 'node:path';
// const __filename = url.fileURLToPath(import.meta.url);
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
import FullReload from 'vite-plugin-full-reload';
import { customElementsManifest } from './src/js/cem.js';
import { RemarkPluginFindAndReplace } from 'remark-plugin-find-and-replace';
import rehypeExternalLinks from 'rehype-external-links';
import remarkCodeHighlighter from './src/plugins/prism';
import GithubAutolink from './src/plugins/github-autolink.ts';
const version = customElementsManifest().package.version;
const cdndir = 'cdn';
const npmdir = 'dist';
function remarkFrontmatterPlugin() {
// All remark and rehype plugins return a separate function
return function (tree, file) {
const frontmatter = file.data.astro.frontmatter;
frontmatter.npmdir = npmdir;
frontmatter.cdndir = cdndir;
frontmatter.version = version;
};
}
// https://astro.build/config
export default defineConfig({
server: {
open: true,
port: 4000,
host: true,
fs: {
strict: false
}
},
vite: {
plugins: [
FullReload([
path.relative(__dirname, '../dist/custom-elements.json'),
path.relative(__dirname, './public/**/*.*')
])
]
},
outDir: '../_site',
site: 'https://shoelace.style',
compressHTML: false,
markdown: {
syntaxHighlight: 'prism',
remarkPlugins: [
remarkFrontmatterPlugin,
RemarkPluginFindAndReplace({
replacements: [
{ pattern: '%VERSION%', replacement: version },
{ pattern: '%CDNDIR%', replacement: cdndir },
{ pattern: '%NPMDIR%', replacement: npmdir }
]
}),
GithubAutolink,
remarkCodeHighlighter
],
rehypePlugins: [
() =>
rehypeExternalLinks({
rel: ['nofollow', 'noopener', 'noreferrer'],
target: ['_blank'],
properties: {
class: 'external-link'
}
})
]
},
integrations: [
starlight({
expressiveCode: false,
title: 'Web Awesome',
social: {
github: 'https://github.com/shoelace-style/shoelace',
twitter: 'https://twitter.com/shoelace_style'
},
sidebar: [
{
label: 'Experimental',
autogenerate: { directory: 'experimental' }
},
{
label: 'Getting Started',
autogenerate: { directory: 'getting-started' }
},
{
label: 'Frameworks',
autogenerate: { directory: 'frameworks' }
},
{
label: 'Resources',
autogenerate: { directory: 'resources' },
items: [
{
label: 'Community',
link: '/resources/community'
},
{
label: 'Help & Support',
link: 'https://github.com/shoelace-style/shoelace/discussions'
},
{
label: 'Accessibility',
link: '/resources/accessibility'
},
{
label: 'Contributing',
link: '/resources/contributing'
},
{
label: 'Changelog',
link: '/resources/changelog'
}
]
},
{
label: 'Components',
autogenerate: { directory: 'components' }
},
{
label: 'Design Tokens',
autogenerate: { directory: 'tokens' }
},
{
label: 'Tutorials',
autogenerate: { directory: 'tutorials' }
}
],
// Component overrides
components: {
// Override the default `Head` component.
Head: './src/components/overrides/Head.astro',
TableOfContents: './src/components/overrides/TableOfContents.astro',
Search: './src/components/overrides/Search.astro'
}
})
]
});

View File

@@ -1,239 +0,0 @@
/* eslint-disable no-invalid-this */
const fs = require('fs');
const path = require('path');
const lunr = require('lunr');
const { capitalCase } = require('change-case');
const { JSDOM } = require('jsdom');
const { customElementsManifest, getAllComponents } = require('./_utilities/cem.cjs');
const shoelaceFlavoredMarkdown = require('./_utilities/markdown.cjs');
const activeLinks = require('./_utilities/active-links.cjs');
const anchorHeadings = require('./_utilities/anchor-headings.cjs');
const codePreviews = require('./_utilities/code-previews.cjs');
const copyCodeButtons = require('./_utilities/copy-code-buttons.cjs');
const externalLinks = require('./_utilities/external-links.cjs');
const highlightCodeBlocks = require('./_utilities/highlight-code.cjs');
const tableOfContents = require('./_utilities/table-of-contents.cjs');
const prettier = require('./_utilities/prettier.cjs');
const scrollingTables = require('./_utilities/scrolling-tables.cjs');
const typography = require('./_utilities/typography.cjs');
const replacer = require('./_utilities/replacer.cjs');
const assetsDir = 'assets';
const cdndir = 'cdn';
const npmdir = 'dist';
const allComponents = getAllComponents();
let hasBuiltSearchIndex = false;
module.exports = function (eleventyConfig) {
//
// Global data
//
eleventyConfig.addGlobalData('baseUrl', 'https://shoelace.style/'); // the production URL
eleventyConfig.addGlobalData('layout', 'default'); // make 'default' the default layout
eleventyConfig.addGlobalData('toc', true); // enable the table of contents
eleventyConfig.addGlobalData('meta', {
title: 'Shoelace',
description: 'A forward-thinking library of web components.',
image: 'images/og-image.png',
version: customElementsManifest.package.version,
components: allComponents,
cdndir,
npmdir
});
//
// Layout aliases
//
eleventyConfig.addLayoutAlias('default', 'default.njk');
//
// Copy assets
//
eleventyConfig.addPassthroughCopy(assetsDir);
eleventyConfig.setServerPassthroughCopyBehavior('passthrough'); // emulates passthrough copy during --serve
//
// Functions
//
// Generates a URL relative to the site's root
eleventyConfig.addNunjucksGlobal('rootUrl', (value = '', absolute = false) => {
value = path.join('/', value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Generates a URL relative to the site's asset directory
eleventyConfig.addNunjucksGlobal('assetUrl', (value = '', absolute = false) => {
value = path.join(`/${assetsDir}`, value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Fetches a specific component's metadata
eleventyConfig.addNunjucksGlobal('getComponent', tagName => {
const component = allComponents.find(c => c.tagName === tagName);
if (!component) {
throw new Error(
`Unable to find a component called "${tagName}". Make sure the file name is the same as the component's tag ` +
`name (minus the sl- prefix).`
);
}
return component;
});
//
// Custom markdown syntaxes
//
eleventyConfig.setLibrary('md', shoelaceFlavoredMarkdown);
//
// Filters
//
eleventyConfig.addFilter('markdown', content => {
return shoelaceFlavoredMarkdown.render(content);
});
eleventyConfig.addFilter('markdownInline', content => {
return shoelaceFlavoredMarkdown.renderInline(content);
});
eleventyConfig.addFilter('classNameToComponentName', className => {
let name = capitalCase(className.replace(/^Sl/, ''));
if (name === 'Qr Code') name = 'QR Code'; // manual override
return name;
});
eleventyConfig.addFilter('removeSlPrefix', tagName => {
return tagName.replace(/^sl-/, '');
});
//
// Transforms
//
eleventyConfig.addTransform('html-transform', function (content) {
// Parse the template and get a Document object
const doc = new JSDOM(content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
// DOM transforms
activeLinks(doc, { pathname: this.page.url });
anchorHeadings(doc, {
within: '#content .content__body',
levels: ['h2', 'h3', 'h4', 'h5']
});
tableOfContents(doc, {
levels: ['h2', 'h3'],
container: '#content .content__toc > ul',
within: '#content .content__body'
});
codePreviews(doc);
externalLinks(doc, { target: '_blank' });
highlightCodeBlocks(doc);
scrollingTables(doc);
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
typography(doc, '#content');
replacer(doc, [
{ pattern: '%VERSION%', replacement: customElementsManifest.package.version },
{ pattern: '%CDNDIR%', replacement: cdndir },
{ pattern: '%NPMDIR%', replacement: npmdir }
]);
// Serialize the Document object to an HTML string and prepend the doctype
content = `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
// String transforms
content = prettier(content);
return content;
});
//
// Build a search index
//
eleventyConfig.on('eleventy.after', ({ results }) => {
// We only want to build the search index on the first run so all pages get indexed.
if (hasBuiltSearchIndex) {
return;
}
const map = {};
const searchIndexFilename = path.join(eleventyConfig.dir.output, assetsDir, 'search.json');
const lunrInput = path.resolve('../node_modules/lunr/lunr.min.js');
const lunrOutput = path.join(eleventyConfig.dir.output, assetsDir, 'scripts/lunr.js');
const searchIndex = lunr(function () {
// The search index uses these field names extensively, so shortening them can save some serious bytes. The
// initial index file went from 468 KB => 401 KB by using single-character names!
this.ref('id'); // id
this.field('t', { boost: 50 }); // title
this.field('h', { boost: 25 }); // headings
this.field('c'); // content
results.forEach((result, index) => {
const url = path
.join('/', path.relative(eleventyConfig.dir.output, result.outputPath))
.replace(/\\/g, '/') // convert backslashes to forward slashes
.replace(/\/index.html$/, '/'); // convert trailing /index.html to /
const doc = new JSDOM(result.content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
const content = doc.querySelector('#content');
// Get title and headings
const title = (doc.querySelector('title')?.textContent || path.basename(result.outputPath)).trim();
const headings = [...content.querySelectorAll('h1, h2, h3, h4')]
.map(heading => heading.textContent)
.join(' ')
.replace(/\s+/g, ' ')
.trim();
// Remove code blocks and whitespace from content
[...content.querySelectorAll('code[class|=language]')].forEach(code => code.remove());
const textContent = content.textContent.replace(/\s+/g, ' ').trim();
// Update the index and map
this.add({ id: index, t: title, h: headings, c: textContent });
map[index] = { title, url };
});
});
// Copy the Lunr search client and write the index
fs.mkdirSync(path.dirname(lunrOutput), { recursive: true });
fs.copyFileSync(lunrInput, lunrOutput);
fs.writeFileSync(searchIndexFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
hasBuiltSearchIndex = true;
});
//
// Send a signal to stdout that let's the build know we've reached this point
//
eleventyConfig.on('eleventy.after', () => {
console.log('[eleventy.after]');
});
//
// Dev server options (see https://www.11ty.dev/docs/dev-server/#options)
//
eleventyConfig.setServerOptions({
domDiff: false, // disable dom diffing so custom elements don't break on reload,
port: 4000, // if port 4000 is taken, 11ty will use the next one available
watch: ['cdn/**/*'] // additional files to watch that will trigger server updates (array of paths or globs)
});
//
// 11ty config
//
return {
dir: {
input: 'pages',
output: '../_site',
includes: '../_includes' // resolved relative to the input dir
},
markdownTemplateEngine: 'njk', // use Nunjucks instead of Liquid for markdown files
templateEngineOverride: ['njk'] // just Nunjucks and then markdown
};
};

View File

@@ -1,19 +0,0 @@
---
meta:
title: Page Not Found
description: "The page you were looking for couldn't be found."
permalink: 404.html
toc: false
---
<div style="text-align: center;">
# Page Not Found
![A UFO takes one of the little worker monsters](/assets/images/undraw-taken.svg)
The page you were looking for couldn't be found.
Press [[/]] to search, or [head back to the homepage](/).
</div>

View File

@@ -1,235 +0,0 @@
---
meta:
title: Badge
description: Badges are used to draw attention and display statuses or counts.
layout: component
---
```html:preview
<sl-badge>Badge</sl-badge>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const App = () => <SlBadge>Badge</SlBadge>;
```
## Examples
### Variants
Set the `variant` attribute to change the badge's variant.
```html:preview
<sl-badge variant="primary">Primary</sl-badge>
<sl-badge variant="success">Success</sl-badge>
<sl-badge variant="neutral">Neutral</sl-badge>
<sl-badge variant="warning">Warning</sl-badge>
<sl-badge variant="danger">Danger</sl-badge>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const App = () => (
<>
<SlBadge variant="primary">Primary</SlBadge>
<SlBadge variant="success">Success</SlBadge>
<SlBadge variant="neutral">Neutral</SlBadge>
<SlBadge variant="warning">Warning</SlBadge>
<SlBadge variant="danger">Danger</SlBadge>
</>
);
```
### Pill Badges
Use the `pill` attribute to give badges rounded edges.
```html:preview
<sl-badge variant="primary" pill>Primary</sl-badge>
<sl-badge variant="success" pill>Success</sl-badge>
<sl-badge variant="neutral" pill>Neutral</sl-badge>
<sl-badge variant="warning" pill>Warning</sl-badge>
<sl-badge variant="danger" pill>Danger</sl-badge>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const App = () => (
<>
<SlBadge variant="primary" pill>
Primary
</SlBadge>
<SlBadge variant="success" pill>
Success
</SlBadge>
<SlBadge variant="neutral" pill>
Neutral
</SlBadge>
<SlBadge variant="warning" pill>
Warning
</SlBadge>
<SlBadge variant="danger" pill>
Danger
</SlBadge>
</>
);
```
### Pulsating Badges
Use the `pulse` attribute to draw attention to the badge with a subtle animation.
```html:preview
<div class="badge-pulse">
<sl-badge variant="primary" pill pulse>1</sl-badge>
<sl-badge variant="success" pill pulse>1</sl-badge>
<sl-badge variant="neutral" pill pulse>1</sl-badge>
<sl-badge variant="warning" pill pulse>1</sl-badge>
<sl-badge variant="danger" pill pulse>1</sl-badge>
</div>
<style>
.badge-pulse sl-badge:not(:last-of-type) {
margin-right: 1rem;
}
</style>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const css = `
.badge-pulse sl-badge:not(:last-of-type) {
margin-right: 1rem;
}
`;
const App = () => (
<>
<div className="badge-pulse">
<SlBadge variant="primary" pill pulse>
1
</SlBadge>
<SlBadge variant="success" pill pulse>
1
</SlBadge>
<SlBadge variant="neutral" pill pulse>
1
</SlBadge>
<SlBadge variant="warning" pill pulse>
1
</SlBadge>
<SlBadge variant="danger" pill pulse>
1
</SlBadge>
</div>
<style>{css}</style>
</>
);
```
### With Buttons
One of the most common use cases for badges is attaching them to buttons. To make this easier, badges will be automatically positioned at the top-right when they're a child of a button.
```html:preview
<sl-button>
Requests
<sl-badge pill>30</sl-badge>
</sl-button>
<sl-button style="margin-inline-start: 1rem;">
Warnings
<sl-badge variant="warning" pill>8</sl-badge>
</sl-button>
<sl-button style="margin-inline-start: 1rem;">
Errors
<sl-badge variant="danger" pill>6</sl-badge>
</sl-button>
```
{% raw %}
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton>
Requests
<SlBadge pill>30</SlBadge>
</SlButton>
<SlButton style={{ marginInlineStart: '1rem' }}>
Warnings
<SlBadge variant="warning" pill>
8
</SlBadge>
</SlButton>
<SlButton style={{ marginInlineStart: '1rem' }}>
Errors
<SlBadge variant="danger" pill>
6
</SlBadge>
</SlButton>
</>
);
```
{% endraw %}
### With Menu Items
When including badges in menu items, use the `suffix` slot to make sure they're aligned correctly.
```html:preview
<sl-menu style="max-width: 240px;">
<sl-menu-label>Messages</sl-menu-label>
<sl-menu-item>Comments <sl-badge slot="suffix" variant="neutral" pill>4</sl-badge></sl-menu-item>
<sl-menu-item>Replies <sl-badge slot="suffix" variant="neutral" pill>12</sl-badge></sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
import SlMenuLabel from '@shoelace-style/shoelace/dist/react/menu-label';
const App = () => (
<SlMenu
style={{
maxWidth: '240px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<SlMenuLabel>Messages</SlMenuLabel>
<SlMenuItem>
Comments
<SlBadge slot="suffix" variant="neutral" pill>
4
</SlBadge>
</SlMenuItem>
<SlMenuItem>
Replies
<SlBadge slot="suffix" variant="neutral" pill>
12
</SlBadge>
</SlMenuItem>
</SlMenu>
);
```
{% endraw %}

View File

@@ -1,38 +0,0 @@
---
meta:
title: Breadcrumb Item
description: Breadcrumb Items are used inside breadcrumbs to represent different links.
layout: component
---
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>
<sl-icon slot="prefix" name="house"></sl-icon>
Home
</sl-breadcrumb-item>
<sl-breadcrumb-item>Clothing</sl-breadcrumb-item>
<sl-breadcrumb-item>Shirts</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx:react
import SlBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
import SlBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem>
<SlIcon slot="prefix" name="house"></SlIcon>
Home
</SlBreadcrumbItem>
<SlBreadcrumbItem>Clothing</SlBreadcrumbItem>
<SlBreadcrumbItem>Shirts</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
:::tip
Additional demonstrations can be found in the [breadcrumb examples](/components/breadcrumb).
:::

View File

@@ -1,258 +0,0 @@
---
meta:
title: Breadcrumb
description: Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
layout: component
---
Breadcrumbs are usually placed before a page's main content with the current page shown last to indicate the user's position in the navigation.
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>Catalog</sl-breadcrumb-item>
<sl-breadcrumb-item>Clothing</sl-breadcrumb-item>
<sl-breadcrumb-item>Women's</sl-breadcrumb-item>
<sl-breadcrumb-item>Shirts &amp; Tops</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx:react
import SlBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
import SlBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem>Catalog</SlBreadcrumbItem>
<SlBreadcrumbItem>Clothing</SlBreadcrumbItem>
<SlBreadcrumbItem>Women's</SlBreadcrumbItem>
<SlBreadcrumbItem>Shirts &amp; Tops</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
## Examples
### Breadcrumb Links
By default, breadcrumb items are rendered as buttons so you can use them to navigate single-page applications. In this case, you'll need to add event listeners to handle clicks.
For websites, you'll probably want to use links instead. You can make any breadcrumb item a link by applying an `href` attribute to it. Now, when the user activates it, they'll be taken to the corresponding page — no event listeners required.
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item href="https://example.com/home">Homepage</sl-breadcrumb-item>
<sl-breadcrumb-item href="https://example.com/home/services">Our Services</sl-breadcrumb-item>
<sl-breadcrumb-item href="https://example.com/home/services/digital">Digital Media</sl-breadcrumb-item>
<sl-breadcrumb-item href="https://example.com/home/services/digital/web-design">Web Design</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx:react
import SlBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
import SlBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem href="https://example.com/home">Homepage</SlBreadcrumbItem>
<SlBreadcrumbItem href="https://example.com/home/services">Our Services</SlBreadcrumbItem>
<SlBreadcrumbItem href="https://example.com/home/services/digital">Digital Media</SlBreadcrumbItem>
<SlBreadcrumbItem href="https://example.com/home/services/digital/web-design">Web Design</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
### Custom Separators
Use the `separator` slot to change the separator that goes between breadcrumb items. Icons work well, but you can also use text or an image.
```html:preview
<sl-breadcrumb>
<sl-icon name="dot" slot="separator"></sl-icon>
<sl-breadcrumb-item>First</sl-breadcrumb-item>
<sl-breadcrumb-item>Second</sl-breadcrumb-item>
<sl-breadcrumb-item>Third</sl-breadcrumb-item>
</sl-breadcrumb>
<br />
<sl-breadcrumb>
<sl-icon name="arrow-right" slot="separator"></sl-icon>
<sl-breadcrumb-item>First</sl-breadcrumb-item>
<sl-breadcrumb-item>Second</sl-breadcrumb-item>
<sl-breadcrumb-item>Third</sl-breadcrumb-item>
</sl-breadcrumb>
<br />
<sl-breadcrumb>
<span slot="separator">/</span>
<sl-breadcrumb-item>First</sl-breadcrumb-item>
<sl-breadcrumb-item>Second</sl-breadcrumb-item>
<sl-breadcrumb-item>Third</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx:react
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import SlBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
import SlBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
const App = () => (
<>
<SlBreadcrumb>
<sl-icon name="dot" slot="separator" />
<SlBreadcrumbItem>First</SlBreadcrumbItem>
<SlBreadcrumbItem>Second</SlBreadcrumbItem>
<SlBreadcrumbItem>Third</SlBreadcrumbItem>
</SlBreadcrumb>
<br />
<SlBreadcrumb>
<sl-icon name="arrow-right" slot="separator" />
<SlBreadcrumbItem>First</SlBreadcrumbItem>
<SlBreadcrumbItem>Second</SlBreadcrumbItem>
<SlBreadcrumbItem>Third</SlBreadcrumbItem>
</SlBreadcrumb>
<br />
<SlBreadcrumb>
<span slot="separator">/</span>
<SlBreadcrumbItem>First</SlBreadcrumbItem>
<SlBreadcrumbItem>Second</SlBreadcrumbItem>
<SlBreadcrumbItem>Third</SlBreadcrumbItem>
</SlBreadcrumb>
</>
);
```
### Prefixes
Use the `prefix` slot to add content before any breadcrumb item.
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>
<sl-icon slot="prefix" name="house"></sl-icon>
Home
</sl-breadcrumb-item>
<sl-breadcrumb-item>Articles</sl-breadcrumb-item>
<sl-breadcrumb-item>Traveling</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx:react
import SlBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
import SlBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem>
<SlIcon slot="prefix" name="house" />
Home
</SlBreadcrumbItem>
<SlBreadcrumbItem>Articles</SlBreadcrumbItem>
<SlBreadcrumbItem>Traveling</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
### Suffixes
Use the `suffix` slot to add content after any breadcrumb item.
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>Documents</sl-breadcrumb-item>
<sl-breadcrumb-item>Policies</sl-breadcrumb-item>
<sl-breadcrumb-item>
Security
<sl-icon slot="suffix" name="shield-lock"></sl-icon>
</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx:react
import SlBreadcrumb from '@shoelace-style/shoelace/dist/react/breadcrumb';
import SlBreadcrumbItem from '@shoelace-style/shoelace/dist/react/breadcrumb-item';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem>Documents</SlBreadcrumbItem>
<SlBreadcrumbItem>Policies</SlBreadcrumbItem>
<SlBreadcrumbItem>
Security
<SlIcon slot="suffix" name="shield-lock"></SlIcon>
</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
### With Dropdowns
Dropdown menus can be placed in a prefix or suffix slot to provide additional options.
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>Homepage</sl-breadcrumb-item>
<sl-breadcrumb-item>Our Services</sl-breadcrumb-item>
<sl-breadcrumb-item>Digital Media</sl-breadcrumb-item>
<sl-breadcrumb-item>
Web Design
<sl-dropdown slot="suffix">
<sl-button slot="trigger" size="small" circle>
<sl-icon label="More options" name="three-dots"></sl-icon>
</sl-button>
<sl-menu>
<sl-menu-item type="checkbox" checked>Web Design</sl-menu-item>
<sl-menu-item type="checkbox">Web Development</sl-menu-item>
<sl-menu-item type="checkbox">Marketing</sl-menu-item>
</sl-menu>
</sl-dropdown>
</sl-breadcrumb-item>
</sl-breadcrumb>
```
```jsx:react
import {
SlBreadcrumb,
SlBreadcrumbItem,
SlButton,
SlDropdown,
SlIcon,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem>Homepage</SlBreadcrumbItem>
<SlBreadcrumbItem>Our Services</SlBreadcrumbItem>
<SlBreadcrumbItem>Digital Media</SlBreadcrumbItem>
<SlBreadcrumbItem>
Web Design
<SlDropdown slot="suffix">
<SlButton slot="trigger" size="small" circle>
<SlIcon label="More options" name="three-dots"></SlIcon>
</SlButton>
<SlMenu>
<SlMenuItem type="checkbox" checked>
Web Design
</SlMenuItem>
<SlMenuItem type="checkbox">Web Development</SlMenuItem>
<SlMenuItem type="checkbox">Marketing</SlMenuItem>
</SlMenu>
</SlDropdown>
</SlBreadcrumbItem>
</SlBreadcrumb>
);
```

View File

@@ -1,511 +0,0 @@
---
meta:
title: Button Group
description: Button groups can be used to group related buttons into sections.
layout: component
---
```html:preview
<sl-button-group label="Alignment">
<sl-button>Left</sl-button>
<sl-button>Center</sl-button>
<sl-button>Right</sl-button>
</sl-button-group>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
const App = () => (
<SlButtonGroup label="Alignment">
<SlButton>Left</SlButton>
<SlButton>Center</SlButton>
<SlButton>Right</SlButton>
</SlButtonGroup>
);
```
## Examples
### Button Sizes
All button sizes are supported, but avoid mixing sizes within the same button group.
```html:preview
<sl-button-group label="Alignment">
<sl-button size="small">Left</sl-button>
<sl-button size="small">Center</sl-button>
<sl-button size="small">Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button size="medium">Left</sl-button>
<sl-button size="medium">Center</sl-button>
<sl-button size="medium">Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button size="large">Left</sl-button>
<sl-button size="large">Center</sl-button>
<sl-button size="large">Right</sl-button>
</sl-button-group>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
const App = () => (
<>
<SlButtonGroup label="Alignment">
<SlButton size="small">Left</SlButton>
<SlButton size="small">Center</SlButton>
<SlButton size="small">Right</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton size="medium">Left</SlButton>
<SlButton size="medium">Center</SlButton>
<SlButton size="medium">Right</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton size="large">Left</SlButton>
<SlButton size="large">Center</SlButton>
<SlButton size="large">Right</SlButton>
</SlButtonGroup>
</>
);
```
### Theme Buttons
Theme buttons are supported through the button's `variant` attribute.
```html:preview
<sl-button-group label="Alignment">
<sl-button variant="primary">Left</sl-button>
<sl-button variant="primary">Center</sl-button>
<sl-button variant="primary">Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button variant="success">Left</sl-button>
<sl-button variant="success">Center</sl-button>
<sl-button variant="success">Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button variant="neutral">Left</sl-button>
<sl-button variant="neutral">Center</sl-button>
<sl-button variant="neutral">Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button variant="warning">Left</sl-button>
<sl-button variant="warning">Center</sl-button>
<sl-button variant="warning">Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button variant="danger">Left</sl-button>
<sl-button variant="danger">Center</sl-button>
<sl-button variant="danger">Right</sl-button>
</sl-button-group>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
const App = () => (
<>
<SlButtonGroup label="Alignment">
<SlButton variant="primary">Left</SlButton>
<SlButton variant="primary">Center</SlButton>
<SlButton variant="primary">Right</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton variant="success">Left</SlButton>
<SlButton variant="success">Center</SlButton>
<SlButton variant="success">Right</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton variant="neutral">Left</SlButton>
<SlButton variant="neutral">Center</SlButton>
<SlButton variant="neutral">Right</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton variant="warning">Left</SlButton>
<SlButton variant="warning">Center</SlButton>
<SlButton variant="warning">Right</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton variant="danger">Left</SlButton>
<SlButton variant="danger">Center</SlButton>
<SlButton variant="danger">Right</SlButton>
</SlButtonGroup>
</>
);
```
### Pill Buttons
Pill buttons are supported through the button's `pill` attribute.
```html:preview
<sl-button-group label="Alignment">
<sl-button size="small" pill>Left</sl-button>
<sl-button size="small" pill>Center</sl-button>
<sl-button size="small" pill>Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button size="medium" pill>Left</sl-button>
<sl-button size="medium" pill>Center</sl-button>
<sl-button size="medium" pill>Right</sl-button>
</sl-button-group>
<br /><br />
<sl-button-group label="Alignment">
<sl-button size="large" pill>Left</sl-button>
<sl-button size="large" pill>Center</sl-button>
<sl-button size="large" pill>Right</sl-button>
</sl-button-group>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
const App = () => (
<>
<SlButtonGroup label="Alignment">
<SlButton size="small" pill>
Left
</SlButton>
<SlButton size="small" pill>
Center
</SlButton>
<SlButton size="small" pill>
Right
</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton size="medium" pill>
Left
</SlButton>
<SlButton size="medium" pill>
Center
</SlButton>
<SlButton size="medium" pill>
Right
</SlButton>
</SlButtonGroup>
<br />
<br />
<SlButtonGroup label="Alignment">
<SlButton size="large" pill>
Left
</SlButton>
<SlButton size="large" pill>
Center
</SlButton>
<SlButton size="large" pill>
Right
</SlButton>
</SlButtonGroup>
</>
);
```
### Dropdowns in Button Groups
Dropdowns can be placed inside button groups as long as the trigger is an `<sl-button>` element.
```html:preview
<sl-button-group label="Example Button Group">
<sl-button>Button</sl-button>
<sl-button>Button</sl-button>
<sl-dropdown>
<sl-button slot="trigger" caret>Dropdown</sl-button>
<sl-menu>
<sl-menu-item>Item 1</sl-menu-item>
<sl-menu-item>Item 2</sl-menu-item>
<sl-menu-item>Item 3</sl-menu-item>
</sl-menu>
</sl-dropdown>
</sl-button-group>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlButtonGroup label="Example Button Group">
<SlButton>Button</SlButton>
<SlButton>Button</SlButton>
<SlDropdown>
<SlButton slot="trigger" caret>
Dropdown
</SlButton>
<SlMenu>
<SlMenuItem>Item 1</SlMenuItem>
<SlMenuItem>Item 2</SlMenuItem>
<SlMenuItem>Item 3</SlMenuItem>
</SlMenu>
</SlDropdown>
</SlButtonGroup>
);
```
### Split Buttons
Create a split button using a button and a dropdown. Use a [visually hidden](/components/visually-hidden) label to ensure the dropdown is accessible to users with assistive devices.
```html:preview
<sl-button-group label="Example Button Group">
<sl-button variant="primary">Save</sl-button>
<sl-dropdown placement="bottom-end">
<sl-button slot="trigger" variant="primary" caret>
<sl-visually-hidden>More options</sl-visually-hidden>
</sl-button>
<sl-menu>
<sl-menu-item>Save</sl-menu-item>
<sl-menu-item>Save as&hellip;</sl-menu-item>
<sl-menu-item>Save all</sl-menu-item>
</sl-menu>
</sl-dropdown>
</sl-button-group>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlButtonGroup label="Example Button Group">
<SlButton variant="primary">Save</SlButton>
<SlDropdown placement="bottom-end">
<SlButton slot="trigger" variant="primary" caret></SlButton>
<SlMenu>
<SlMenuItem>Save</SlMenuItem>
<SlMenuItem>Save as&hellip;</SlMenuItem>
<SlMenuItem>Save all</SlMenuItem>
</SlMenu>
</SlDropdown>
</SlButtonGroup>
);
```
### Tooltips in Button Groups
Buttons can be wrapped in tooltips to provide more detail when the user interacts with them.
```html:preview
<sl-button-group label="Alignment">
<sl-tooltip content="I'm on the left">
<sl-button>Left</sl-button>
</sl-tooltip>
<sl-tooltip content="I'm in the middle">
<sl-button>Center</sl-button>
</sl-tooltip>
<sl-tooltip content="I'm on the right">
<sl-button>Right</sl-button>
</sl-tooltip>
</sl-button-group>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => (
<>
<SlButtonGroup label="Alignment">
<SlTooltip content="I'm on the left">
<SlButton>Left</SlButton>
</SlTooltip>
<SlTooltip content="I'm in the middle">
<SlButton>Center</SlButton>
</SlTooltip>
<SlTooltip content="I'm on the right">
<SlButton>Right</SlButton>
</SlTooltip>
</SlButtonGroup>
</>
);
```
### Toolbar Example
Create interactive toolbars with button groups.
```html:preview
<div class="button-group-toolbar">
<sl-button-group label="History">
<sl-tooltip content="Undo">
<sl-button><sl-icon name="arrow-counterclockwise" label="Undo"></sl-icon></sl-button>
</sl-tooltip>
<sl-tooltip content="Redo">
<sl-button><sl-icon name="arrow-clockwise" label="Redo"></sl-icon></sl-button>
</sl-tooltip>
</sl-button-group>
<sl-button-group label="Formatting">
<sl-tooltip content="Bold">
<sl-button><sl-icon name="type-bold" label="Bold"></sl-icon></sl-button>
</sl-tooltip>
<sl-tooltip content="Italic">
<sl-button><sl-icon name="type-italic" label="Italic"></sl-icon></sl-button>
</sl-tooltip>
<sl-tooltip content="Underline">
<sl-button><sl-icon name="type-underline" label="Underline"></sl-icon></sl-button>
</sl-tooltip>
</sl-button-group>
<sl-button-group label="Alignment">
<sl-tooltip content="Align Left">
<sl-button><sl-icon name="justify-left" label="Align Left"></sl-icon></sl-button>
</sl-tooltip>
<sl-tooltip content="Align Center">
<sl-button><sl-icon name="justify" label="Align Center"></sl-icon></sl-button>
</sl-tooltip>
<sl-tooltip content="Align Right">
<sl-button><sl-icon name="justify-right" label="Align Right"></sl-icon></sl-button>
</sl-tooltip>
</sl-button-group>
</div>
<style>
.button-group-toolbar sl-button-group:not(:last-of-type) {
margin-right: var(--sl-spacing-x-small);
}
</style>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlButtonGroup from '@shoelace-style/shoelace/dist/react/button-group';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const css = `
.button-group-toolbar sl-button-group:not(:last-of-type) {
margin-right: var(--sl-spacing-x-small);
}
`;
const App = () => (
<>
<div className="button-group-toolbar">
<SlButtonGroup label="History">
<SlTooltip content="Undo">
<SlButton>
<SlIcon name="arrow-counterclockwise"></SlIcon>
</SlButton>
</SlTooltip>
<SlTooltip content="Redo">
<SlButton>
<SlIcon name="arrow-clockwise"></SlIcon>
</SlButton>
</SlTooltip>
</SlButtonGroup>
<SlButtonGroup label="Formatting">
<SlTooltip content="Bold">
<SlButton>
<SlIcon name="type-bold"></SlIcon>
</SlButton>
</SlTooltip>
<SlTooltip content="Italic">
<SlButton>
<SlIcon name="type-italic"></SlIcon>
</SlButton>
</SlTooltip>
<SlTooltip content="Underline">
<SlButton>
<SlIcon name="type-underline"></SlIcon>
</SlButton>
</SlTooltip>
</SlButtonGroup>
<SlButtonGroup label="Alignment">
<SlTooltip content="Align Left">
<SlButton>
<SlIcon name="justify-left"></SlIcon>
</SlButton>
</SlTooltip>
<SlTooltip content="Align Center">
<SlButton>
<SlIcon name="justify"></SlIcon>
</SlButton>
</SlTooltip>
<SlTooltip content="Align Right">
<SlButton>
<SlIcon name="justify-right"></SlIcon>
</SlButton>
</SlTooltip>
</SlButtonGroup>
</div>
<style>{css}</style>
</>
);
```

View File

@@ -1,545 +0,0 @@
---
meta:
title: Button
description: Buttons represent actions that are available to the user.
layout: component
---
```html:preview
<sl-button>Button</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => <SlButton>Button</SlButton>;
```
## Examples
### Variants
Use the `variant` attribute to set the button's variant.
```html:preview
<sl-button variant="default">Default</sl-button>
<sl-button variant="primary">Primary</sl-button>
<sl-button variant="success">Success</sl-button>
<sl-button variant="neutral">Neutral</sl-button>
<sl-button variant="warning">Warning</sl-button>
<sl-button variant="danger">Danger</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton variant="default">Default</SlButton>
<SlButton variant="primary">Primary</SlButton>
<SlButton variant="success">Success</SlButton>
<SlButton variant="neutral">Neutral</SlButton>
<SlButton variant="warning">Warning</SlButton>
<SlButton variant="danger">Danger</SlButton>
</>
);
```
### Sizes
Use the `size` attribute to change a button's size.
```html:preview
<sl-button size="small">Small</sl-button>
<sl-button size="medium">Medium</sl-button>
<sl-button size="large">Large</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton size="small">Small</SlButton>
<SlButton size="medium">Medium</SlButton>
<SlButton size="large">Large</SlButton>
</>
);
```
### Outline Buttons
Use the `outline` attribute to draw outlined buttons with transparent backgrounds.
```html:preview
<sl-button variant="default" outline>Default</sl-button>
<sl-button variant="primary" outline>Primary</sl-button>
<sl-button variant="success" outline>Success</sl-button>
<sl-button variant="neutral" outline>Neutral</sl-button>
<sl-button variant="warning" outline>Warning</sl-button>
<sl-button variant="danger" outline>Danger</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton variant="default" outline>
Default
</SlButton>
<SlButton variant="primary" outline>
Primary
</SlButton>
<SlButton variant="success" outline>
Success
</SlButton>
<SlButton variant="neutral" outline>
Neutral
</SlButton>
<SlButton variant="warning" outline>
Warning
</SlButton>
<SlButton variant="danger" outline>
Danger
</SlButton>
</>
);
```
### Pill Buttons
Use the `pill` attribute to give buttons rounded edges.
```html:preview
<sl-button size="small" pill>Small</sl-button>
<sl-button size="medium" pill>Medium</sl-button>
<sl-button size="large" pill>Large</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton size="small" pill>
Small
</SlButton>
<SlButton size="medium" pill>
Medium
</SlButton>
<SlButton size="large" pill>
Large
</SlButton>
</>
);
```
### Circle Buttons
Use the `circle` attribute to create circular icon buttons. When this attribute is set, the button expects a single `<sl-icon>` in the default slot.
```html:preview
<sl-button variant="default" size="small" circle>
<sl-icon name="gear" label="Settings"></sl-icon>
</sl-button>
<sl-button variant="default" size="medium" circle>
<sl-icon name="gear" label="Settings"></sl-icon>
</sl-button>
<sl-button variant="default" size="large" circle>
<sl-icon name="gear" label="Settings"></sl-icon>
</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<>
<SlButton variant="default" size="small" circle>
<SlIcon name="gear" />
</SlButton>
<SlButton variant="default" size="medium" circle>
<SlIcon name="gear" />
</SlButton>
<SlButton variant="default" size="large" circle>
<SlIcon name="gear" />
</SlButton>
</>
);
```
### Text Buttons
Use the `text` variant to create text buttons that share the same size as regular buttons but don't have backgrounds or borders.
```html:preview
<sl-button variant="text" size="small">Text</sl-button>
<sl-button variant="text" size="medium">Text</sl-button>
<sl-button variant="text" size="large">Text</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton variant="text" size="small">
Text
</SlButton>
<SlButton variant="text" size="medium">
Text
</SlButton>
<SlButton variant="text" size="large">
Text
</SlButton>
</>
);
```
### Link Buttons
It's often helpful to have a button that works like a link. This is possible by setting the `href` attribute, which will make the component render an `<a>` under the hood. This gives you all the default link behavior the browser provides (e.g. [[CMD/CTRL/SHIFT]] + [[CLICK]]) and exposes the `target` and `download` attributes.
```html:preview
<sl-button href="https://example.com/">Link</sl-button>
<sl-button href="https://example.com/" target="_blank">New Window</sl-button>
<sl-button href="/assets/images/wordmark.svg" download="shoelace.svg">Download</sl-button>
<sl-button href="https://example.com/" disabled>Disabled</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton href="https://example.com/">Link</SlButton>
<SlButton href="https://example.com/" target="_blank">
New Window
</SlButton>
<SlButton href="/assets/images/wordmark.svg" download="shoelace.svg">
Download
</SlButton>
<SlButton href="https://example.com/" disabled>
Disabled
</SlButton>
</>
);
```
:::tip
When a `target` is set, the link will receive `rel="noreferrer noopener"` for [security reasons](https://mathiasbynens.github.io/rel-noopener/).
:::
### Setting a Custom Width
As expected, buttons can be given a custom width by setting the `width` attribute. This is useful for making buttons span the full width of their container on smaller screens.
```html:preview
<sl-button variant="default" size="small" style="width: 100%; margin-bottom: 1rem;">Small</sl-button>
<sl-button variant="default" size="medium" style="width: 100%; margin-bottom: 1rem;">Medium</sl-button>
<sl-button variant="default" size="large" style="width: 100%;">Large</sl-button>
```
{% raw %}
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton variant="default" size="small" style={{ width: '100%', marginBottom: '1rem' }}>
Small
</SlButton>
<SlButton variant="default" size="medium" style={{ width: '100%', marginBottom: '1rem' }}>
Medium
</SlButton>
<SlButton variant="default" size="large" style={{ width: '100%' }}>
Large
</SlButton>
</>
);
```
{% endraw %}
### Prefix and Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
```html:preview
<sl-button variant="default" size="small">
<sl-icon slot="prefix" name="gear"></sl-icon>
Settings
</sl-button>
<sl-button variant="default" size="small">
<sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon>
Refresh
</sl-button>
<sl-button variant="default" size="small">
<sl-icon slot="prefix" name="link-45deg"></sl-icon>
<sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon>
Open
</sl-button>
<br /><br />
<sl-button variant="default">
<sl-icon slot="prefix" name="gear"></sl-icon>
Settings
</sl-button>
<sl-button variant="default">
<sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon>
Refresh
</sl-button>
<sl-button variant="default">
<sl-icon slot="prefix" name="link-45deg"></sl-icon>
<sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon>
Open
</sl-button>
<br /><br />
<sl-button variant="default" size="large">
<sl-icon slot="prefix" name="gear"></sl-icon>
Settings
</sl-button>
<sl-button variant="default" size="large">
<sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon>
Refresh
</sl-button>
<sl-button variant="default" size="large">
<sl-icon slot="prefix" name="link-45deg"></sl-icon>
<sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon>
Open
</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<>
<SlButton variant="default" size="small">
<SlIcon slot="prefix" name="gear"></SlIcon>
Settings
</SlButton>
<SlButton variant="default" size="small">
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
Refresh
</SlButton>
<SlButton variant="default" size="small">
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
Open
</SlButton>
<br />
<br />
<SlButton variant="default">
<SlIcon slot="prefix" name="gear"></SlIcon>
Settings
</SlButton>
<SlButton variant="default">
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
Refresh
</SlButton>
<SlButton variant="default">
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
Open
</SlButton>
<br />
<br />
<SlButton variant="default" size="large">
<SlIcon slot="prefix" name="gear"></SlIcon>
Settings
</SlButton>
<SlButton variant="default" size="large">
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
Refresh
</SlButton>
<SlButton variant="default" size="large">
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
Open
</SlButton>
</>
);
```
### Caret
Use the `caret` attribute to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
```html:preview
<sl-button size="small" caret>Small</sl-button>
<sl-button size="medium" caret>Medium</sl-button>
<sl-button size="large" caret>Large</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton size="small" caret>
Small
</SlButton>
<SlButton size="medium" caret>
Medium
</SlButton>
<SlButton size="large" caret>
Large
</SlButton>
</>
);
```
### Loading
Use the `loading` attribute to make a button busy. The width will remain the same as before, preventing adjacent elements from moving around. Clicks will be suppressed until the loading state is removed.
```html:preview
<sl-button variant="default" loading>Default</sl-button>
<sl-button variant="primary" loading>Primary</sl-button>
<sl-button variant="success" loading>Success</sl-button>
<sl-button variant="neutral" loading>Neutral</sl-button>
<sl-button variant="warning" loading>Warning</sl-button>
<sl-button variant="danger" loading>Danger</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton variant="default" loading>
Default
</SlButton>
<SlButton variant="primary" loading>
Primary
</SlButton>
<SlButton variant="success" loading>
Success
</SlButton>
<SlButton variant="neutral" loading>
Neutral
</SlButton>
<SlButton variant="warning" loading>
Warning
</SlButton>
<SlButton variant="danger" loading>
Danger
</SlButton>
</>
);
```
### Disabled
Use the `disabled` attribute to disable a button.
```html:preview
<sl-button variant="default" disabled>Default</sl-button>
<sl-button variant="primary" disabled>Primary</sl-button>
<sl-button variant="success" disabled>Success</sl-button>
<sl-button variant="neutral" disabled>Neutral</sl-button>
<sl-button variant="warning" disabled>Warning</sl-button>
<sl-button variant="danger" disabled>Danger</sl-button>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton variant="default" disabled>
Default
</SlButton>
<SlButton variant="primary" disabled>
Primary
</SlButton>
<SlButton variant="success" disabled>
Success
</SlButton>
<SlButton variant="neutral" disabled>
Neutral
</SlButton>
<SlButton variant="warning" disabled>
Warning
</SlButton>
<SlButton variant="danger" disabled>
Danger
</SlButton>
</>
);
```
### Styling Buttons
This example demonstrates how to style buttons using a custom class. This is the recommended approach if you need to add additional variations. To customize an existing variation, modify the selector to target the button's `variant` attribute instead of a class (e.g. `sl-button[variant="primary"]`).
```html:preview
<sl-button class="pink">Pink Button</sl-button>
<style>
sl-button.pink::part(base) {
/* Set design tokens for height and border width */
--sl-input-height-medium: 48px;
--sl-input-border-width: 4px;
border-radius: 0;
background-color: #ff1493;
border-top-color: #ff7ac1;
border-left-color: #ff7ac1;
border-bottom-color: #ad005c;
border-right-color: #ad005c;
color: white;
font-size: 1.125rem;
box-shadow: 0 2px 10px #0002;
transition: var(--sl-transition-medium) transform ease, var(--sl-transition-medium) border ease;
}
sl-button.pink::part(base):hover {
transform: scale(1.05) rotate(-1deg);
}
sl-button.pink::part(base):active {
border-top-color: #ad005c;
border-right-color: #ff7ac1;
border-bottom-color: #ff7ac1;
border-left-color: #ad005c;
transform: scale(1.05) rotate(-1deg) translateY(2px);
}
sl-button.pink::part(base):focus-visible {
outline: dashed 2px deeppink;
outline-offset: 4px;
}
</style>
```

View File

@@ -1,164 +0,0 @@
---
meta:
title: Checkbox
description: Checkboxes allow the user to toggle an option on or off.
layout: component
---
```html:preview
<sl-checkbox>Checkbox</sl-checkbox>
```
```jsx:react
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
const App = () => <SlCheckbox>Checkbox</SlCheckbox>;
```
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
### Checked
Use the `checked` attribute to activate the checkbox.
```html:preview
<sl-checkbox checked>Checked</sl-checkbox>
```
```jsx:react
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
const App = () => <SlCheckbox checked>Checked</SlCheckbox>;
```
### Indeterminate
Use the `indeterminate` attribute to make the checkbox indeterminate.
```html:preview
<sl-checkbox indeterminate>Indeterminate</sl-checkbox>
```
```jsx:react
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
const App = () => <SlCheckbox indeterminate>Indeterminate</SlCheckbox>;
```
### Disabled
Use the `disabled` attribute to disable the checkbox.
```html:preview
<sl-checkbox disabled>Disabled</sl-checkbox>
```
```jsx:react
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
const App = () => <SlCheckbox disabled>Disabled</SlCheckbox>;
```
### Sizes
Use the `size` attribute to change a checkbox's size.
```html:preview
<sl-checkbox size="small">Small</sl-checkbox>
<br />
<sl-checkbox size="medium">Medium</sl-checkbox>
<br />
<sl-checkbox size="large">Large</sl-checkbox>
```
```jsx:react
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
const App = () => (
<>
<SlCheckbox size="small">Small</SlCheckbox>
<br />
<SlCheckbox size="medium">Medium</SlCheckbox>
<br />
<SlCheckbox size="large">Large</SlCheckbox>
</>
);
```
### Custom Validity
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
```html:preview
<form class="custom-validity">
<sl-checkbox>Check me</sl-checkbox>
<br />
<sl-button type="submit" variant="primary" style="margin-top: 1rem;">Submit</sl-button>
</form>
<script>
const form = document.querySelector('.custom-validity');
const checkbox = form.querySelector('sl-checkbox');
const errorMessage = `Don't forget to check me!`;
// Set initial validity as soon as the element is defined
customElements.whenDefined('sl-checkbox').then(async () => {
await checkbox.updateComplete;
checkbox.setCustomValidity(errorMessage);
});
// Update validity on change
checkbox.addEventListener('sl-change', () => {
checkbox.setCustomValidity(checkbox.checked ? '' : errorMessage);
});
// Handle submit
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
```
{% raw %}
```jsx:react
import { useEffect, useRef } from 'react';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
const App = () => {
const checkbox = useRef(null);
const errorMessage = `Don't forget to check me!`;
function handleChange() {
checkbox.current.setCustomValidity(checkbox.current.checked ? '' : errorMessage);
}
function handleSubmit(event) {
event.preventDefault();
alert('All fields are valid!');
}
useEffect(() => {
checkbox.current.setCustomValidity(errorMessage);
}, []);
return (
<form class="custom-validity" onSubmit={handleSubmit}>
<SlCheckbox ref={checkbox} onSlChange={handleChange}>
Check me
</SlCheckbox>
<br />
<SlButton type="submit" variant="primary" style={{ marginTop: '1rem' }}>
Submit
</SlButton>
</form>
);
};
```
{% endraw %}

View File

@@ -1,158 +0,0 @@
---
meta:
title: Divider
description: Dividers are used to visually separate or group elements.
layout: component
---
```html:preview
<sl-divider></sl-divider>
```
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
const App = () => <SlDivider />;
```
## Examples
### Width
Use the `--width` custom property to change the width of the divider.
```html:preview
<sl-divider style="--width: 4px;"></sl-divider>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
const App = () => <SlDivider style={{ '--width': '4px' }} />;
```
{% endraw %}
### Color
Use the `--color` custom property to change the color of the divider.
```html:preview
<sl-divider style="--color: tomato;"></sl-divider>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
const App = () => <SlDivider style={{ '--color': 'tomato' }} />;
```
{% endraw %}
### Spacing
Use the `--spacing` custom property to change the amount of space between the divider and it's neighboring elements.
```html:preview
<div style="text-align: center;">
Above
<sl-divider style="--spacing: 2rem;"></sl-divider>
Below
</div>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
const App = () => (
<>
Above
<SlDivider style={{ '--spacing': '2rem' }} />
Below
</>
);
```
{% endraw %}
### Vertical
Add the `vertical` attribute to draw the divider in a vertical orientation. The divider will span the full height of its container. Vertical dividers work especially well inside of a flex container.
```html:preview
<div style="display: flex; align-items: center; height: 2rem;">
First
<sl-divider vertical></sl-divider>
Middle
<sl-divider vertical></sl-divider>
Last
</div>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
const App = () => (
<div
style={{
display: 'flex',
alignItems: 'center',
height: '2rem'
}}
>
First
<SlDivider vertical />
Middle
<SlDivider vertical />
Last
</div>
);
```
{% endraw %}
### Menu Dividers
Use dividers in [menus](/components/menu) to visually group menu items.
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item value="1">Option 1</sl-menu-item>
<sl-menu-item value="2">Option 2</sl-menu-item>
<sl-menu-item value="3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="4">Option 4</sl-menu-item>
<sl-menu-item value="5">Option 5</sl-menu-item>
<sl-menu-item value="6">Option 6</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem value="1">Option 1</SlMenuItem>
<SlMenuItem value="2">Option 2</SlMenuItem>
<SlMenuItem value="3">Option 3</SlMenuItem>
<sl-divider />
<SlMenuItem value="4">Option 4</SlMenuItem>
<SlMenuItem value="5">Option 5</SlMenuItem>
<SlMenuItem value="6">Option 6</SlMenuItem>
</SlMenu>
);
```
{% endraw %}

View File

@@ -1,482 +0,0 @@
---
meta:
title: Dropdown
description: 'Dropdowns expose additional content that "drops down" in a panel.'
layout: component
---
Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and interacting outside of the panel will close it.
Dropdowns are designed to work well with [menus](/components/menu) to provide a list of options the user can select from. However, dropdowns can also be used in lower-level applications (e.g. [color picker](/components/color-picker)). The API gives you complete control over showing, hiding, and positioning the panel.
```html:preview
<sl-dropdown>
<sl-button slot="trigger" caret>Dropdown</sl-button>
<sl-menu>
<sl-menu-item>Dropdown Item 1</sl-menu-item>
<sl-menu-item>Dropdown Item 2</sl-menu-item>
<sl-menu-item>Dropdown Item 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
<sl-menu-item disabled>Disabled</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
Prefix
<sl-icon slot="prefix" name="gift"></sl-icon>
</sl-menu-item>
<sl-menu-item>
Suffix Icon
<sl-icon slot="suffix" name="heart"></sl-icon>
</sl-menu-item>
</sl-menu>
</sl-dropdown>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlDropdown>
<SlButton slot="trigger" caret>
Dropdown
</SlButton>
<SlMenu>
<SlMenuItem>Dropdown Item 1</SlMenuItem>
<SlMenuItem>Dropdown Item 2</SlMenuItem>
<SlMenuItem>Dropdown Item 3</SlMenuItem>
<SlDivider />
<SlMenuItem type="checkbox" checked>
Checkbox
</SlMenuItem>
<SlMenuItem disabled>Disabled</SlMenuItem>
<SlDivider />
<SlMenuItem>
Prefix
<SlIcon slot="prefix" name="gift" />
</SlMenuItem>
<SlMenuItem>
Suffix Icon
<SlIcon slot="suffix" name="heart" />
</SlMenuItem>
</SlMenu>
</SlDropdown>
);
```
## Examples
### Getting the Selected Item
When dropdowns are used with [menus](/components/menu), you can listen for the [`sl-select`](/components/menu#events) event to determine which menu item was selected. The menu item element will be exposed in `event.detail.item`. You can set `value` props to make it easier to identify commands.
```html:preview
<div class="dropdown-selection">
<sl-dropdown>
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
</sl-menu>
</sl-dropdown>
</div>
<script>
const container = document.querySelector('.dropdown-selection');
const dropdown = container.querySelector('sl-dropdown');
dropdown.addEventListener('sl-select', event => {
const selectedItem = event.detail.item;
console.log(selectedItem.value);
});
</script>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => {
function handleSelect(event) {
const selectedItem = event.detail.item;
console.log(selectedItem.value);
}
return (
<SlDropdown>
<SlButton slot="trigger" caret>
Edit
</SlButton>
<SlMenu onSlSelect={handleSelect}>
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
</SlMenu>
</SlDropdown>
);
};
```
Alternatively, you can listen for the `click` event on individual menu items. Note that, using this approach, disabled menu items will still emit a `click` event.
```html:preview
<div class="dropdown-selection-alt">
<sl-dropdown>
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
</sl-menu>
</sl-dropdown>
</div>
<script>
const container = document.querySelector('.dropdown-selection-alt');
const cut = container.querySelector('sl-menu-item[value="cut"]');
const copy = container.querySelector('sl-menu-item[value="copy"]');
const paste = container.querySelector('sl-menu-item[value="paste"]');
cut.addEventListener('click', () => console.log('cut'));
copy.addEventListener('click', () => console.log('copy'));
paste.addEventListener('click', () => console.log('paste'));
</script>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => {
function handleCut() {
console.log('cut');
}
function handleCopy() {
console.log('copy');
}
function handlePaste() {
console.log('paste');
}
return (
<SlDropdown>
<SlButton slot="trigger" caret>
Edit
</SlButton>
<SlMenu>
<SlMenuItem onClick={handleCut}>Cut</SlMenuItem>
<SlMenuItem onClick={handleCopy}>Copy</SlMenuItem>
<SlMenuItem onClick={handlePaste}>Paste</SlMenuItem>
</SlMenu>
</SlDropdown>
);
};
```
### Placement
The preferred placement of the dropdown can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport.
```html:preview
<sl-dropdown placement="top-start">
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
<sl-menu-item>Cut</sl-menu-item>
<sl-menu-item>Copy</sl-menu-item>
<sl-menu-item>Paste</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>Find</sl-menu-item>
<sl-menu-item>Replace</sl-menu-item>
</sl-menu>
</sl-dropdown>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlDropdown placement="top-start">
<SlButton slot="trigger" caret>
Edit
</SlButton>
<SlMenu>
<SlMenuItem>Cut</SlMenuItem>
<SlMenuItem>Copy</SlMenuItem>
<SlMenuItem>Paste</SlMenuItem>
<SlDivider />
<SlMenuItem>Find</SlMenuItem>
<SlMenuItem>Replace</SlMenuItem>
</SlMenu>
</SlDropdown>
);
```
### Distance
The distance from the panel to the trigger can be customized using the `distance` attribute. This value is specified in pixels.
```html:preview
<sl-dropdown distance="30">
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
<sl-menu-item>Cut</sl-menu-item>
<sl-menu-item>Copy</sl-menu-item>
<sl-menu-item>Paste</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>Find</sl-menu-item>
<sl-menu-item>Replace</sl-menu-item>
</sl-menu>
</sl-dropdown>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlDropdown distance={30}>
<SlButton slot="trigger" caret>
Edit
</SlButton>
<SlMenu>
<SlMenuItem>Cut</SlMenuItem>
<SlMenuItem>Copy</SlMenuItem>
<SlMenuItem>Paste</SlMenuItem>
<SlDivider />
<SlMenuItem>Find</SlMenuItem>
<SlMenuItem>Replace</SlMenuItem>
</SlMenu>
</SlDropdown>
);
```
### Skidding
The offset of the panel along the trigger can be customized using the `skidding` attribute. This value is specified in pixels.
```html:preview
<sl-dropdown skidding="30">
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
<sl-menu-item>Cut</sl-menu-item>
<sl-menu-item>Copy</sl-menu-item>
<sl-menu-item>Paste</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>Find</sl-menu-item>
<sl-menu-item>Replace</sl-menu-item>
</sl-menu>
</sl-dropdown>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlDropdown skidding={30}>
<SlButton slot="trigger" caret>
Edit
</SlButton>
<SlMenu>
<SlMenuItem>Cut</SlMenuItem>
<SlMenuItem>Copy</SlMenuItem>
<SlMenuItem>Paste</SlMenuItem>
<SlDivider />
<SlMenuItem>Find</SlMenuItem>
<SlMenuItem>Replace</SlMenuItem>
</SlMenu>
</SlDropdown>
);
```
### Submenus
To create a submenu, nest an `<sl-menu slot="submenu">` element in a [menu item](/components/menu-item).
```html:preview
<sl-dropdown>
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu style="max-width: 200px;">
<sl-menu-item value="undo">Undo</sl-menu-item>
<sl-menu-item value="redo">Redo</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
Find
<sl-menu slot="submenu">
<sl-menu-item value="find">Find…</sl-menu-item>
<sl-menu-item value="find-previous">Find Next</sl-menu-item>
<sl-menu-item value="find-next">Find Previous</sl-menu-item>
</sl-menu>
</sl-menu-item>
<sl-menu-item>
Transformations
<sl-menu slot="submenu">
<sl-menu-item value="uppercase">Make uppercase</sl-menu-item>
<sl-menu-item value="lowercase">Make lowercase</sl-menu-item>
<sl-menu-item value="capitalize">Capitalize</sl-menu-item>
</sl-menu>
</sl-menu-item>
</sl-menu>
</sl-dropdown>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const css = `
.dropdown-hoist {
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
}
`;
const App = () => (
<>
<SlDropdown>
<SlButton slot="trigger" caret>Edit</SlButton>
<SlMenu style="max-width: 200px;">
<SlMenuItem value="undo">Undo</SlMenuItem>
<SlMenuItem value="redo">Redo</SlMenuItem>
<SlDivider />
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
<SlDivider />
<SlMenuItem>
Find
<SlMenu slot="submenu">
<SlMenuItem value="find">Find…</SlMenuItem>
<SlMenuItem value="find-previous">Find Next</SlMenuItem>
<SlMenuItem value="find-next">Find Previous</SlMenuItem>
</SlMenu>
</SlMenuItem>
<SlMenuItem>
Transformations
<SlMenu slot="submenu">
<SlMenuItem value="uppercase">Make uppercase</SlMenuItem>
<SlMenuItem value="lowercase">Make lowercase</SlMenuItem>
<SlMenuItem value="capitalize">Capitalize</SlMenuItem>
</SlMenu>
</SlMenuItem>
</SlMenu>
</SlDropdown>
</>
);
```
:::warning
As a UX best practice, avoid using more than one level of submenu when possible.
:::
### Hoisting
Dropdown panels will be clipped if they're inside a container that has `overflow: auto|hidden`. The `hoist` attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
```html:preview
<div class="dropdown-hoist">
<sl-dropdown>
<sl-button slot="trigger" caret>No Hoist</sl-button>
<sl-menu>
<sl-menu-item>Item 1</sl-menu-item>
<sl-menu-item>Item 2</sl-menu-item>
<sl-menu-item>Item 3</sl-menu-item>
</sl-menu>
</sl-dropdown>
<sl-dropdown hoist>
<sl-button slot="trigger" caret>Hoist</sl-button>
<sl-menu>
<sl-menu-item>Item 1</sl-menu-item>
<sl-menu-item>Item 2</sl-menu-item>
<sl-menu-item>Item 3</sl-menu-item>
</sl-menu>
</sl-dropdown>
</div>
<style>
.dropdown-hoist {
position: relative;
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
}
</style>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const css = `
.dropdown-hoist {
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
}
`;
const App = () => (
<>
<div className="dropdown-hoist">
<SlDropdown>
<SlButton slot="trigger" caret>
No Hoist
</SlButton>
<SlMenu>
<SlMenuItem>Item 1</SlMenuItem>
<SlMenuItem>Item 2</SlMenuItem>
<SlMenuItem>Item 3</SlMenuItem>
</SlMenu>
</SlDropdown>
<SlDropdown hoist>
<SlButton slot="trigger" caret>
Hoist
</SlButton>
<SlMenu>
<SlMenuItem>Item 1</SlMenuItem>
<SlMenuItem>Item 2</SlMenuItem>
<SlMenuItem>Item 3</SlMenuItem>
</SlMenu>
</SlDropdown>
</div>
<style>{css}</style>
</>
);
```

View File

@@ -1,134 +0,0 @@
---
meta:
title: Format Bytes
description: Formats a number as a human readable bytes value.
layout: component
---
```html:preview
<div class="format-bytes-overview">
The file is <sl-format-bytes value="1000"></sl-format-bytes> in size. <br /><br />
<sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input>
</div>
<script>
const container = document.querySelector('.format-bytes-overview');
const formatter = container.querySelector('sl-format-bytes');
const input = container.querySelector('sl-input');
input.addEventListener('sl-input', () => (formatter.value = input.value || 0));
</script>
```
{% raw %}
```jsx:react
import { useState } from 'react';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => {
const [value, setValue] = useState(1000);
return (
<>
The file is <SlFormatBytes value={value} /> in size.
<br />
<br />
<SlInput
type="number"
value={value}
label="Number to Format"
style={{ maxWidth: '180px' }}
onSlInput={event => setValue(event.target.value)}
/>
</>
);
};
```
{% endraw %}
## Examples
### Formatting Bytes
Set the `value` attribute to a number to get the value in bytes.
```html:preview
<sl-format-bytes value="12"></sl-format-bytes><br />
<sl-format-bytes value="1200"></sl-format-bytes><br />
<sl-format-bytes value="1200000"></sl-format-bytes><br />
<sl-format-bytes value="1200000000"></sl-format-bytes>
```
```jsx:react
import SlFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
const App = () => (
<>
<SlFormatBytes value="12" />
<br />
<SlFormatBytes value="1200" />
<br />
<SlFormatBytes value="1200000" />
<br />
<SlFormatBytes value="1200000000" />
</>
);
```
### Formatting Bits
To get the value in bits, set the `unit` attribute to `bit`.
```html:preview
<sl-format-bytes value="12" unit="bit"></sl-format-bytes><br />
<sl-format-bytes value="1200" unit="bit"></sl-format-bytes><br />
<sl-format-bytes value="1200000" unit="bit"></sl-format-bytes><br />
<sl-format-bytes value="1200000000" unit="bit"></sl-format-bytes>
```
```jsx:react
import SlFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
const App = () => (
<>
<SlFormatBytes value="12" unit="bit" />
<br />
<SlFormatBytes value="1200" unit="bit" />
<br />
<SlFormatBytes value="1200000" unit="bit" />
<br />
<SlFormatBytes value="1200000000" unit="bit" />
</>
);
```
### Localization
Use the `lang` attribute to set the number formatting locale.
```html:preview
<sl-format-bytes value="12" lang="de"></sl-format-bytes><br />
<sl-format-bytes value="1200" lang="de"></sl-format-bytes><br />
<sl-format-bytes value="1200000" lang="de"></sl-format-bytes><br />
<sl-format-bytes value="1200000000" lang="de"></sl-format-bytes>
```
```jsx:react
import SlFormatBytes from '@shoelace-style/shoelace/dist/react/format-bytes';
const App = () => (
<>
<SlFormatBytes value="12" lang="de" />
<br />
<SlFormatBytes value="1200" lang="de" />
<br />
<SlFormatBytes value="1200000" lang="de" />
<br />
<SlFormatBytes value="1200000000" lang="de" />
</>
);
```

View File

@@ -1,139 +0,0 @@
---
meta:
title: Format Number
description: Formats a number using the specified locale and options.
layout: component
---
Localization is handled by the browser's [`Intl.NumberFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). No language packs are required.
```html:preview
<div class="format-number-overview">
<sl-format-number value="1000"></sl-format-number>
<br /><br />
<sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input>
</div>
<script>
const container = document.querySelector('.format-number-overview');
const formatter = container.querySelector('sl-format-number');
const input = container.querySelector('sl-input');
input.addEventListener('sl-input', () => (formatter.value = input.value || 0));
</script>
```
{% raw %}
```jsx:react
import { useState } from 'react';
import SlFormatNumber from '@shoelace-style/shoelace/dist/react/format-number';
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => {
const [value, setValue] = useState(1000);
return (
<>
<SlFormatNumber value={value} />
<br />
<br />
<SlInput
type="number"
value={value}
label="Number to Format"
style={{ maxWidth: '180px' }}
onSlInput={event => setValue(event.target.value)}
/>
</>
);
};
```
{% endraw %}
## Examples
### Percentages
To get the value as a percent, set the `type` attribute to `percent`.
```html:preview
<sl-format-number type="percent" value="0"></sl-format-number><br />
<sl-format-number type="percent" value="0.25"></sl-format-number><br />
<sl-format-number type="percent" value="0.50"></sl-format-number><br />
<sl-format-number type="percent" value="0.75"></sl-format-number><br />
<sl-format-number type="percent" value="1"></sl-format-number>
```
```jsx:react
import SlFormatNumber from '@shoelace-style/shoelace/dist/react/format-number';
const App = () => (
<>
<SlFormatNumber type="percent" value={0} />
<br />
<SlFormatNumber type="percent" value={0.25} />
<br />
<SlFormatNumber type="percent" value={0.5} />
<br />
<SlFormatNumber type="percent" value={0.75} />
<br />
<SlFormatNumber type="percent" value={1} />
</>
);
```
### Localization
Use the `lang` attribute to set the number formatting locale.
```html:preview
English: <sl-format-number value="2000" lang="en" minimum-fraction-digits="2"></sl-format-number><br />
German: <sl-format-number value="2000" lang="de" minimum-fraction-digits="2"></sl-format-number><br />
Russian: <sl-format-number value="2000" lang="ru" minimum-fraction-digits="2"></sl-format-number>
```
```jsx:react
import SlFormatNumber from '@shoelace-style/shoelace/dist/react/format-number';
const App = () => (
<>
English: <SlFormatNumber value="2000" lang="en" minimum-fraction-digits="2" />
<br />
German: <SlFormatNumber value="2000" lang="de" minimum-fraction-digits="2" />
<br />
Russian: <SlFormatNumber value="2000" lang="ru" minimum-fraction-digits="2" />
</>
);
```
### Currency
To format a number as a monetary value, set the `type` attribute to `currency` and set the `currency` attribute to the desired ISO 4217 currency code. You should also specify `lang` to ensure the the number is formatted correctly for the target locale.
```html:preview
<sl-format-number type="currency" currency="USD" value="2000" lang="en-US"></sl-format-number><br />
<sl-format-number type="currency" currency="GBP" value="2000" lang="en-GB"></sl-format-number><br />
<sl-format-number type="currency" currency="EUR" value="2000" lang="de"></sl-format-number><br />
<sl-format-number type="currency" currency="RUB" value="2000" lang="ru"></sl-format-number><br />
<sl-format-number type="currency" currency="CNY" value="2000" lang="zh-cn"></sl-format-number>
```
```jsx:react
import SlFormatNumber from '@shoelace-style/shoelace/dist/react/format-number';
const App = () => (
<>
<SlFormatNumber type="currency" currency="USD" value="2000" lang="en-US" />
<br />
<SlFormatNumber type="currency" currency="GBP" value="2000" lang="en-GB" />
<br />
<SlFormatNumber type="currency" currency="EUR" value="2000" lang="de" />
<br />
<SlFormatNumber type="currency" currency="RUB" value="2000" lang="ru" />
<br />
<SlFormatNumber type="currency" currency="CNY" value="2000" lang="zh-cn" />
</>
);
```

View File

@@ -1,153 +0,0 @@
---
meta:
title: Icon Button
description: Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars.
layout: component
---
For a full list of icons that come bundled with Shoelace, refer to the [icon component](/components/icon).
```html:preview
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
```
```jsx:react
import SlIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
const App = () => <SlIconButton name="gear" label="Settings" />;
```
## Examples
### Sizes
Icon buttons inherit their parent element's `font-size`.
```html:preview
<sl-icon-button name="pencil" label="Edit" style="font-size: 1.5rem;"></sl-icon-button>
<sl-icon-button name="pencil" label="Edit" style="font-size: 2rem;"></sl-icon-button>
<sl-icon-button name="pencil" label="Edit" style="font-size: 2.5rem;"></sl-icon-button>
```
{% raw %}
```jsx:react
import SlIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
const App = () => (
<>
<SlIconButton name="pencil" label="Edit" style={{ fontSize: '1.5rem' }} />
<SlIconButton name="pencil" label="Edit" style={{ fontSize: '2rem' }} />
<SlIconButton name="pencil" label="Edit" style={{ fontSize: '2.5rem' }} />
</>
);
```
{% endraw %}
### Colors
Icon buttons are designed to have a uniform appearance, so their color is not inherited. However, you can still customize them by styling the `base` part.
```html:preview
<div class="icon-button-color">
<sl-icon-button name="type-bold" label="Bold"></sl-icon-button>
<sl-icon-button name="type-italic" label="Italic"></sl-icon-button>
<sl-icon-button name="type-underline" label="Underline"></sl-icon-button>
</div>
<style>
.icon-button-color sl-icon-button::part(base) {
color: #b00091;
}
.icon-button-color sl-icon-button::part(base):hover,
.icon-button-color sl-icon-button::part(base):focus {
color: #c913aa;
}
.icon-button-color sl-icon-button::part(base):active {
color: #960077;
}
</style>
```
```jsx:react
import SlIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
const css = `
.icon-button-color sl-icon-button::part(base) {
color: #b00091;
}
.icon-button-color sl-icon-button::part(base):hover,
.icon-button-color sl-icon-button::part(base):focus {
color: #c913aa;
}
.icon-button-color sl-icon-button::part(base):active {
color: #960077;
}
`;
const App = () => (
<>
<div className="icon-button-color">
<SlIconButton name="type-bold" label="Bold" />
<SlIconButton name="type-italic" label="Italic" />
<SlIconButton name="type-underline" label="Underline" />
</div>
<style>{css}</style>
</>
);
```
### Link Buttons
Use the `href` attribute to convert the button to a link.
```html:preview
<sl-icon-button name="gear" label="Settings" href="https://example.com" target="_blank"></sl-icon-button>
```
```jsx:react
import SlIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
const App = () => <SlIconButton name="gear" label="Settings" href="https://example.com" target="_blank" />;
```
### Icon Button with Tooltip
Wrap a tooltip around an icon button to provide contextual information to the user.
```html:preview
<sl-tooltip content="Settings">
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
</sl-tooltip>
```
```jsx:react
import SlIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => (
<SlTooltip content="Settings">
<SlIconButton name="gear" label="Settings" />
</SlTooltip>
);
```
### Disabled
Use the `disabled` attribute to disable the icon button.
```html:preview
<sl-icon-button name="gear" label="Settings" disabled></sl-icon-button>
```
```jsx:react
import SlIconButton from '@shoelace-style/shoelace/dist/react/icon-button';
const App = () => <SlIconButton name="gear" label="Settings" disabled />;
```

View File

@@ -1,869 +0,0 @@
---
meta:
title: Icon
description: Icons are symbols that can be used to represent various options within an application.
layout: component
---
Shoelace comes bundled with over 1,500 icons courtesy of the [Bootstrap Icons](https://icons.getbootstrap.com/) project. These icons are part of the `default` icon library. If you prefer, you can register [custom icon libraries](#icon-libraries) as well.
:::tip
Depending on how you're loading Shoelace, you may need to copy icon assets and/or [set the base path](/getting-started/installation/#setting-the-base-path) so Shoelace knows where to load them from. Otherwise, icons may not appear and you'll see 404 Not Found errors in the dev console.
:::
## Default Icons
All available icons in the `default` icon library are shown below. Click or tap on any icon to copy its name, then you can use it in your HTML like this.
```html
<sl-icon name="icon-name-here"></sl-icon>
```
<div class="icon-search">
<div class="icon-search-controls">
<sl-input placeholder="Search Icons" clearable>
<sl-icon slot="prefix" name="search"></sl-icon>
</sl-input>
<sl-select value="outline">
<sl-option value="outline">Outlined</sl-option>
<sl-option value="fill">Filled</sl-option>
<sl-option value="all">All icons</sl-option>
</sl-select>
</div>
<div class="icon-list"></div>
<input type="text" class="icon-copy-input" aria-hidden="true" tabindex="-1">
</div>
## Examples
### Colors
Icons inherit their color from the current text color. Thus, you can set the `color` property on the `<sl-icon>` element or an ancestor to change the color.
```html:preview
<div style="color: #4a90e2;">
<sl-icon name="exclamation-triangle"></sl-icon>
<sl-icon name="archive"></sl-icon>
<sl-icon name="battery-charging"></sl-icon>
<sl-icon name="bell"></sl-icon>
</div>
<div style="color: #9013fe;">
<sl-icon name="clock"></sl-icon>
<sl-icon name="cloud"></sl-icon>
<sl-icon name="download"></sl-icon>
<sl-icon name="file-earmark"></sl-icon>
</div>
<div style="color: #417505;">
<sl-icon name="flag"></sl-icon>
<sl-icon name="heart"></sl-icon>
<sl-icon name="image"></sl-icon>
<sl-icon name="lightning"></sl-icon>
</div>
<div style="color: #f5a623;">
<sl-icon name="mic"></sl-icon>
<sl-icon name="search"></sl-icon>
<sl-icon name="star"></sl-icon>
<sl-icon name="trash"></sl-icon>
</div>
```
{% raw %}
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<>
<div style={{ color: '#4a90e2' }}>
<SlIcon name="exclamation-triangle"></SlIcon>
<SlIcon name="archive"></SlIcon>
<SlIcon name="battery-charging"></SlIcon>
<SlIcon name="bell"></SlIcon>
</div>
<div style={{ color: '#9013fe' }}>
<SlIcon name="clock"></SlIcon>
<SlIcon name="cloud"></SlIcon>
<SlIcon name="download"></SlIcon>
<SlIcon name="file-earmark"></SlIcon>
</div>
<div style={{ color: '#417505' }}>
<SlIcon name="flag"></SlIcon>
<SlIcon name="heart"></SlIcon>
<SlIcon name="image"></SlIcon>
<SlIcon name="lightning"></SlIcon>
</div>
<div style={{ color: '#f5a623' }}>
<SlIcon name="mic"></SlIcon>
<SlIcon name="search"></SlIcon>
<SlIcon name="star"></SlIcon>
<SlIcon name="trash"></SlIcon>
</div>
</>
);
```
{% endraw %}
### Sizing
Icons are sized relative to the current font size. To change their size, set the `font-size` property on the icon itself or on a parent element as shown below.
```html:preview
<div style="font-size: 32px;">
<sl-icon name="exclamation-triangle"></sl-icon>
<sl-icon name="archive"></sl-icon>
<sl-icon name="battery-charging"></sl-icon>
<sl-icon name="bell"></sl-icon>
<sl-icon name="clock"></sl-icon>
<sl-icon name="cloud"></sl-icon>
<sl-icon name="download"></sl-icon>
<sl-icon name="file-earmark"></sl-icon>
<sl-icon name="flag"></sl-icon>
<sl-icon name="heart"></sl-icon>
<sl-icon name="image"></sl-icon>
<sl-icon name="lightning"></sl-icon>
<sl-icon name="mic"></sl-icon>
<sl-icon name="search"></sl-icon>
<sl-icon name="star"></sl-icon>
<sl-icon name="trash"></sl-icon>
</div>
```
{% raw %}
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<div style={{ fontSize: '32px' }}>
<SlIcon name="exclamation-triangle" />
<SlIcon name="archive" />
<SlIcon name="battery-charging" />
<SlIcon name="bell" />
<SlIcon name="clock" />
<SlIcon name="cloud" />
<SlIcon name="download" />
<SlIcon name="file-earmark" />
<SlIcon name="flag" />
<SlIcon name="heart" />
<SlIcon name="image" />
<SlIcon name="lightning" />
<SlIcon name="mic" />
<SlIcon name="search" />
<SlIcon name="star" />
<SlIcon name="trash" />
</div>
);
```
{% endraw %}
### Labels
For non-decorative icons, use the `label` attribute to announce it to assistive devices.
```html:preview
<sl-icon name="star-fill" label="Add to favorites"></sl-icon>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => <SlIcon name="star-fill" label="Add to favorites" />;
```
### Custom Icons
Custom icons can be loaded individually with the `src` attribute. Only SVGs on a local or CORS-enabled endpoint are supported. If you're using more than one custom icon, it might make sense to register a [custom icon library](#icon-libraries).
```html:preview
<sl-icon src="https://shoelace.style/assets/images/shoe.svg" style="font-size: 8rem;"></sl-icon>
```
{% raw %}
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => <SlIcon src="https://shoelace.style/assets/images/shoe.svg" style={{ fontSize: '8rem' }}></SlIcon>;
```
{% endraw %}
## Icon Libraries
You can register additional icons to use with the `<sl-icon>` component through icon libraries. Icon files can exist locally or on a CORS-enabled endpoint (e.g. a CDN). There is no limit to how many icon libraries you can register and there is no cost associated with registering them, as individual icons are only requested when they're used.
Shoelace ships with two built-in icon libraries, `default` and `system`. The [default icon library](#customizing-the-default-library) contains all of the icons in the Bootstrap Icons project. The [system icon library](#customizing-the-system-library) contains only a small subset of icons that are used internally by Shoelace components.
To register an additional icon library, use the `registerIconLibrary()` function that's exported from `utilities/icon-library.js`. At a minimum, you must provide a name and a resolver function. The resolver function translates an icon name to a URL where the corresponding SVG file exists. Refer to the examples below to better understand how it works.
If necessary, a mutator function can be used to mutate the SVG element before rendering. This is necessary for some libraries due to the many possible ways SVGs are crafted. For example, icons should ideally inherit the current text color via `currentColor`, so you may need to apply `fill="currentColor` or `stroke="currentColor"` to the SVG element using this function.
Here's an example that registers an icon library located in the `/assets/icons` directory.
```html
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('my-icons', {
resolver: name => `/assets/icons/${name}.svg`,
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
```
To display an icon, set the `library` and `name` attributes of an `<sl-icon>` element.
```html
<!-- This will show the icon located at /assets/icons/smile.svg -->
<sl-icon library="my-icons" name="smile"></sl-icon>
```
If an icon is used before registration occurs, it will be empty initially but shown when registered.
The following examples demonstrate how to register a number of popular, open source icon libraries via CDN. Feel free to adapt the code as you see fit to use your own origin or naming conventions.
### Boxicons
This will register the [Boxicons](https://boxicons.com/) library using the jsDelivr CDN. This library has three variations: regular (`bx-*`), solid (`bxs-*`), and logos (`bxl-*`). A mutator function is required to set the SVG's `fill` to `currentColor`.
Icons in this library are licensed under the [Creative Commons 4.0 License](https://github.com/atisawd/boxicons#license).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('boxicons', {
resolver: name => {
let folder = 'regular';
if (name.substring(0, 4) === 'bxs-') folder = 'solid';
if (name.substring(0, 4) === 'bxl-') folder = 'logos';
return `https://cdn.jsdelivr.net/npm/boxicons@2.0.5/svg/${folder}/${name}.svg`;
},
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
<div style="font-size: 24px;">
<sl-icon library="boxicons" name="bx-bot"></sl-icon>
<sl-icon library="boxicons" name="bx-cookie"></sl-icon>
<sl-icon library="boxicons" name="bx-joystick"></sl-icon>
<sl-icon library="boxicons" name="bx-save"></sl-icon>
<sl-icon library="boxicons" name="bx-server"></sl-icon>
<sl-icon library="boxicons" name="bx-wine"></sl-icon>
<br />
<sl-icon library="boxicons" name="bxs-bot"></sl-icon>
<sl-icon library="boxicons" name="bxs-cookie"></sl-icon>
<sl-icon library="boxicons" name="bxs-joystick"></sl-icon>
<sl-icon library="boxicons" name="bxs-save"></sl-icon>
<sl-icon library="boxicons" name="bxs-server"></sl-icon>
<sl-icon library="boxicons" name="bxs-wine"></sl-icon>
<br />
<sl-icon library="boxicons" name="bxl-apple"></sl-icon>
<sl-icon library="boxicons" name="bxl-chrome"></sl-icon>
<sl-icon library="boxicons" name="bxl-edge"></sl-icon>
<sl-icon library="boxicons" name="bxl-firefox"></sl-icon>
<sl-icon library="boxicons" name="bxl-opera"></sl-icon>
<sl-icon library="boxicons" name="bxl-microsoft"></sl-icon>
</div>
```
### Lucide
This will register the [Lucide](https://lucide.dev/) icon library using the jsDelivr CDN. This project is a community-maintained fork of the popular [Feather](https://feathericons.com/) icon library.
Icons in this library are licensed under the [MIT License](https://github.com/lucide-icons/lucide/blob/master/LICENSE).
```html:preview
<div style="font-size: 24px;">
<sl-icon library="lucide" name="feather"></sl-icon>
<sl-icon library="lucide" name="pie-chart"></sl-icon>
<sl-icon library="lucide" name="settings"></sl-icon>
<sl-icon library="lucide" name="map-pin"></sl-icon>
<sl-icon library="lucide" name="printer"></sl-icon>
<sl-icon library="lucide" name="shopping-cart"></sl-icon>
</div>
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('lucide', {
resolver: name => `https://cdn.jsdelivr.net/npm/lucide-static@0.16.29/icons/${name}.svg`
});
</script>
```
### Font Awesome
This will register the [Font Awesome Free](https://fontawesome.com/) library using the jsDelivr CDN. This library has three variations: regular (`far-*`), solid (`fas-*`), and brands (`fab-*`). A mutator function is required to set the SVG's `fill` to `currentColor`.
Icons in this library are licensed under the [Font Awesome Free License](https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt). Some of the icons that appear on the Font Awesome website require a license and are therefore not available in the CDN.
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('fa', {
resolver: name => {
const filename = name.replace(/^fa[rbs]-/, '');
let folder = 'regular';
if (name.substring(0, 4) === 'fas-') folder = 'solid';
if (name.substring(0, 4) === 'fab-') folder = 'brands';
return `https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/svgs/${folder}/${filename}.svg`;
},
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
<div style="font-size: 24px;">
<sl-icon library="fa" name="far-bell"></sl-icon>
<sl-icon library="fa" name="far-comment"></sl-icon>
<sl-icon library="fa" name="far-hand-point-right"></sl-icon>
<sl-icon library="fa" name="far-hdd"></sl-icon>
<sl-icon library="fa" name="far-heart"></sl-icon>
<sl-icon library="fa" name="far-star"></sl-icon>
<br />
<sl-icon library="fa" name="fas-archive"></sl-icon>
<sl-icon library="fa" name="fas-book"></sl-icon>
<sl-icon library="fa" name="fas-chess-knight"></sl-icon>
<sl-icon library="fa" name="fas-dice"></sl-icon>
<sl-icon library="fa" name="fas-pizza-slice"></sl-icon>
<sl-icon library="fa" name="fas-scroll"></sl-icon>
<br />
<sl-icon library="fa" name="fab-apple"></sl-icon>
<sl-icon library="fa" name="fab-chrome"></sl-icon>
<sl-icon library="fa" name="fab-edge"></sl-icon>
<sl-icon library="fa" name="fab-firefox"></sl-icon>
<sl-icon library="fa" name="fab-opera"></sl-icon>
<sl-icon library="fa" name="fab-microsoft"></sl-icon>
</div>
```
### Heroicons
This will register the [Heroicons](https://heroicons.com/) library using the jsDelivr CDN.
Icons in this library are licensed under the [MIT License](https://github.com/tailwindlabs/heroicons/blob/master/LICENSE).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('heroicons', {
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@2.0.1/24/outline/${name}.svg`
});
</script>
<div style="font-size: 24px;">
<sl-icon library="heroicons" name="chat-bubble-left"></sl-icon>
<sl-icon library="heroicons" name="cloud"></sl-icon>
<sl-icon library="heroicons" name="cog"></sl-icon>
<sl-icon library="heroicons" name="document-text"></sl-icon>
<sl-icon library="heroicons" name="gift"></sl-icon>
<sl-icon library="heroicons" name="speaker-wave"></sl-icon>
</div>
```
### Iconoir
This will register the [Iconoir](https://iconoir.com/) library using the jsDelivr CDN.
Icons in this library are licensed under the [MIT License](https://github.com/lucaburgio/iconoir/blob/master/LICENSE).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('iconoir', {
resolver: name => `https://cdn.jsdelivr.net/gh/lucaburgio/iconoir@latest/icons/${name}.svg`
});
</script>
<div style="font-size: 24px;">
<sl-icon library="iconoir" name="check-circled-outline"></sl-icon>
<sl-icon library="iconoir" name="drawer"></sl-icon>
<sl-icon library="iconoir" name="keyframes"></sl-icon>
<sl-icon library="iconoir" name="headset-help"></sl-icon>
<sl-icon library="iconoir" name="color-picker"></sl-icon>
<sl-icon library="iconoir" name="wifi"></sl-icon>
</div>
```
### Ionicons
This will register the [Ionicons](https://ionicons.com/) library using the jsDelivr CDN. This library has three variations: outline (default), filled (`*-filled`), and sharp (`*-sharp`). A mutator function is required to polyfill a handful of styles we're not including.
Icons in this library are licensed under the [MIT License](https://github.com/ionic-team/ionicons/blob/master/LICENSE).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('ionicons', {
resolver: name => `https://cdn.jsdelivr.net/npm/ionicons@5.1.2/dist/ionicons/svg/${name}.svg`,
mutator: svg => {
svg.setAttribute('fill', 'currentColor');
svg.setAttribute('stroke', 'currentColor');
[...svg.querySelectorAll('.ionicon-fill-none')].map(el => el.setAttribute('fill', 'none'));
[...svg.querySelectorAll('.ionicon-stroke-width')].map(el => el.setAttribute('stroke-width', '32px'));
}
});
</script>
<div style="font-size: 24px;">
<sl-icon library="ionicons" name="alarm"></sl-icon>
<sl-icon library="ionicons" name="american-football"></sl-icon>
<sl-icon library="ionicons" name="bug"></sl-icon>
<sl-icon library="ionicons" name="chatbubble"></sl-icon>
<sl-icon library="ionicons" name="settings"></sl-icon>
<sl-icon library="ionicons" name="warning"></sl-icon>
<br />
<sl-icon library="ionicons" name="alarm-outline"></sl-icon>
<sl-icon library="ionicons" name="american-football-outline"></sl-icon>
<sl-icon library="ionicons" name="bug-outline"></sl-icon>
<sl-icon library="ionicons" name="chatbubble-outline"></sl-icon>
<sl-icon library="ionicons" name="settings-outline"></sl-icon>
<sl-icon library="ionicons" name="warning-outline"></sl-icon>
<br />
<sl-icon library="ionicons" name="alarm-sharp"></sl-icon>
<sl-icon library="ionicons" name="american-football-sharp"></sl-icon>
<sl-icon library="ionicons" name="bug-sharp"></sl-icon>
<sl-icon library="ionicons" name="chatbubble-sharp"></sl-icon>
<sl-icon library="ionicons" name="settings-sharp"></sl-icon>
<sl-icon library="ionicons" name="warning-sharp"></sl-icon>
</div>
```
### Jam Icons
This will register the [Jam Icons](https://jam-icons.com/) library using the jsDelivr CDN. This library has two variations: regular (default) and filled (`*-f`). A mutator function is required to set the SVG's `fill` to `currentColor`.
Icons in this library are licensed under the [MIT License](https://github.com/michaelampr/jam/blob/master/LICENSE).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('jam', {
resolver: name => `https://cdn.jsdelivr.net/npm/jam-icons@2.0.0/svg/${name}.svg`,
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
<div style="font-size: 24px;">
<sl-icon library="jam" name="calendar"></sl-icon>
<sl-icon library="jam" name="camera"></sl-icon>
<sl-icon library="jam" name="filter"></sl-icon>
<sl-icon library="jam" name="leaf"></sl-icon>
<sl-icon library="jam" name="picture"></sl-icon>
<sl-icon library="jam" name="set-square"></sl-icon>
<br />
<sl-icon library="jam" name="calendar-f"></sl-icon>
<sl-icon library="jam" name="camera-f"></sl-icon>
<sl-icon library="jam" name="filter-f"></sl-icon>
<sl-icon library="jam" name="leaf-f"></sl-icon>
<sl-icon library="jam" name="picture-f"></sl-icon>
<sl-icon library="jam" name="set-square-f"></sl-icon>
</div>
```
### Material Icons
This will register the [Material Icons](https://material.io/resources/icons/?style=baseline) library using the jsDelivr CDN. This library has three variations: outline (default), round (`*_round`), and sharp (`*_sharp`). A mutator function is required to set the SVG's `fill` to `currentColor`.
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/google/material-design-icons/blob/master/LICENSE).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('material', {
resolver: name => {
const match = name.match(/^(.*?)(_(round|sharp))?$/);
return `https://cdn.jsdelivr.net/npm/@material-icons/svg@1.0.5/svg/${match[1]}/${match[3] || 'outline'}.svg`;
},
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
<div style="font-size: 24px;">
<sl-icon library="material" name="notifications"></sl-icon>
<sl-icon library="material" name="email"></sl-icon>
<sl-icon library="material" name="delete"></sl-icon>
<sl-icon library="material" name="volume_up"></sl-icon>
<sl-icon library="material" name="settings"></sl-icon>
<sl-icon library="material" name="shopping_basket"></sl-icon>
<br />
<sl-icon library="material" name="notifications_round"></sl-icon>
<sl-icon library="material" name="email_round"></sl-icon>
<sl-icon library="material" name="delete_round"></sl-icon>
<sl-icon library="material" name="volume_up_round"></sl-icon>
<sl-icon library="material" name="settings_round"></sl-icon>
<sl-icon library="material" name="shopping_basket_round"></sl-icon>
<br />
<sl-icon library="material" name="notifications_sharp"></sl-icon>
<sl-icon library="material" name="email_sharp"></sl-icon>
<sl-icon library="material" name="delete_sharp"></sl-icon>
<sl-icon library="material" name="volume_up_sharp"></sl-icon>
<sl-icon library="material" name="settings_sharp"></sl-icon>
<sl-icon library="material" name="shopping_basket_sharp"></sl-icon>
</div>
```
### Remix Icon
This will register the [Remix Icon](https://remixicon.com/) library using the jsDelivr CDN. This library groups icons by categories, so the name must include the category and icon separated by a slash, as well as the `-line` or `-fill` suffix as needed. A mutator function is required to set the SVG's `fill` to `currentColor`.
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/Remix-Design/RemixIcon/blob/master/License).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('remixicon', {
resolver: name => {
const match = name.match(/^(.*?)\/(.*?)?$/);
match[1] = match[1].charAt(0).toUpperCase() + match[1].slice(1);
return `https://cdn.jsdelivr.net/npm/remixicon@2.5.0/icons/${match[1]}/${match[2]}.svg`;
},
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
<div style="font-size: 24px;">
<sl-icon library="remixicon" name="business/cloud-line"></sl-icon>
<sl-icon library="remixicon" name="design/brush-line"></sl-icon>
<sl-icon library="remixicon" name="business/pie-chart-line"></sl-icon>
<sl-icon library="remixicon" name="development/bug-line"></sl-icon>
<sl-icon library="remixicon" name="media/image-line"></sl-icon>
<sl-icon library="remixicon" name="system/alert-line"></sl-icon>
<br />
<sl-icon library="remixicon" name="business/cloud-fill"></sl-icon>
<sl-icon library="remixicon" name="design/brush-fill"></sl-icon>
<sl-icon library="remixicon" name="business/pie-chart-fill"></sl-icon>
<sl-icon library="remixicon" name="development/bug-fill"></sl-icon>
<sl-icon library="remixicon" name="media/image-fill"></sl-icon>
<sl-icon library="remixicon" name="system/alert-fill"></sl-icon>
</div>
```
### Tabler Icons
This will register the [Tabler Icons](https://tabler-icons.io/) library using the jsDelivr CDN. This library features over 1,950 open source icons.
Icons in this library are licensed under the [MIT License](https://github.com/tabler/tabler-icons/blob/master/LICENSE).
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('tabler', {
resolver: name => `https://cdn.jsdelivr.net/npm/@tabler/icons@1.68.0/icons/${name}.svg`
});
</script>
<div style="font-size: 24px;">
<sl-icon library="tabler" name="alert-triangle"></sl-icon>
<sl-icon library="tabler" name="arrow-back"></sl-icon>
<sl-icon library="tabler" name="at"></sl-icon>
<sl-icon library="tabler" name="ball-baseball"></sl-icon>
<sl-icon library="tabler" name="cake"></sl-icon>
<sl-icon library="tabler" name="files"></sl-icon>
<br />
<sl-icon library="tabler" name="keyboard"></sl-icon>
<sl-icon library="tabler" name="moon"></sl-icon>
<sl-icon library="tabler" name="pig"></sl-icon>
<sl-icon library="tabler" name="printer"></sl-icon>
<sl-icon library="tabler" name="ship"></sl-icon>
<sl-icon library="tabler" name="toilet-paper"></sl-icon>
</div>
```
### Unicons
This will register the [Unicons](https://iconscout.com/unicons) library using the jsDelivr CDN. This library has two variations: line (default) and solid (`*-s`). A mutator function is required to set the SVG's `fill` to `currentColor`.
Icons in this library are licensed under the [Apache 2.0 License](https://github.com/Iconscout/unicons/blob/master/LICENSE). Some of the icons that appear on the Unicons website, particularly many of the solid variations, require a license and are therefore not available in the CDN.
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('unicons', {
resolver: name => {
const match = name.match(/^(.*?)(-s)?$/);
return `https://cdn.jsdelivr.net/npm/@iconscout/unicons@3.0.3/svg/${match[2] === '-s' ? 'solid' : 'line'}/${
match[1]
}.svg`;
},
mutator: svg => svg.setAttribute('fill', 'currentColor')
});
</script>
<div style="font-size: 24px;">
<sl-icon library="unicons" name="clock"></sl-icon>
<sl-icon library="unicons" name="graph-bar"></sl-icon>
<sl-icon library="unicons" name="padlock"></sl-icon>
<sl-icon library="unicons" name="polygon"></sl-icon>
<sl-icon library="unicons" name="rocket"></sl-icon>
<sl-icon library="unicons" name="star"></sl-icon>
<br />
<sl-icon library="unicons" name="clock-s"></sl-icon>
<sl-icon library="unicons" name="graph-bar-s"></sl-icon>
<sl-icon library="unicons" name="padlock-s"></sl-icon>
<sl-icon library="unicons" name="polygon-s"></sl-icon>
<sl-icon library="unicons" name="rocket-s"></sl-icon>
<sl-icon library="unicons" name="star-s"></sl-icon>
</div>
```
### Customizing the Default Library
The default icon library contains over 1,300 icons courtesy of the [Bootstrap Icons](https://icons.getbootstrap.com/) project. These are the icons that display when you use `<sl-icon>` without the `library` attribute. If you prefer to have these icons resolve elsewhere or to a different icon library, register an icon library using the `default` name and a custom resolver.
This example will load the same set of icons from the jsDelivr CDN instead of your local assets folder.
```html
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('default', {
resolver: name => `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.0.0/icons/${name}.svg`
});
</script>
```
#### Customize the default library to use SVG sprites
To improve performance you can use a SVG sprites to avoid multiple trips for each SVG. The browser will load the sprite sheet once and then you reference the particular SVG within the sprite sheet using hash selector.
As always, make sure to benchmark these changes. When using HTTP/2, it may in fact be more bandwidth-friendly to use multiple small requests instead of 1 large sprite sheet.
:::danger
When using sprite sheets, the `sl-load` and `sl-error` events will not fire.
:::
:::danger
For security reasons, browsers may apply the same-origin policy on `<use>` elements located in the `<sl-icon>` shadow DOM and may refuse to load a cross-origin URL. There is currently no defined way to set a cross-origin policy for `<use>` elements. For this reason, sprite sheets should only be used if you're self-hosting them.
:::
```html:preview
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('sprite', {
resolver: name => `/assets/images/sprite.svg#${name}`,
mutator: svg => svg.setAttribute('fill', 'currentColor'),
spriteSheet: true
});
</script>
<div style="font-size: 24px;">
<sl-icon library="sprite" name="clock"></sl-icon>
<sl-icon library="sprite" name="speedometer"></sl-icon>
</div>
```
### Customizing the System Library
The system library contains only the icons used internally by Shoelace components. Unlike the default icon library, the system library does not rely on physical assets. Instead, its icons are hard-coded as data URIs into the resolver to ensure their availability.
If you want to change the icons Shoelace uses internally, you can register an icon library using the `system` name and a custom resolver. If you choose to do this, it's your responsibility to provide all of the icons that are required by components. You can reference `src/components/library.system.ts` for a complete list of system icons used by Shoelace.
```html
<script type="module">
import { registerIconLibrary } from '/dist/utilities/icon-library.js';
registerIconLibrary('system', {
resolver: name => `/path/to/custom/icons/${name}.svg`
});
</script>
```
<!-- Supporting scripts and styles for the search utility -->
<script>
function wrapWithTooltip(item) {
const tooltip = document.createElement('sl-tooltip');
tooltip.content = item.getAttribute('data-name');
// Close open tooltips
document.querySelectorAll('.icon-list sl-tooltip[open]').forEach(tooltip => tooltip.hide());
// Wrap it with a tooltip and trick it into showing up
item.parentNode.insertBefore(tooltip, item);
tooltip.appendChild(item);
requestAnimationFrame(() => tooltip.dispatchEvent(new MouseEvent('mouseover')));
}
fetch('/dist/assets/icons/icons.json')
.then(res => res.json())
.then(icons => {
const container = document.querySelector('.icon-search');
const input = container.querySelector('sl-input');
const select = container.querySelector('sl-select');
const copyInput = container.querySelector('.icon-copy-input');
const loader = container.querySelector('.icon-loader');
const list = container.querySelector('.icon-list');
const queue = [];
let inputTimeout;
// Generate icons
icons.map(i => {
const item = document.createElement('div');
item.classList.add('icon-list-item');
item.setAttribute('data-name', i.name);
item.setAttribute('data-terms', [i.name, i.title, ...(i.tags || []), ...(i.categories || [])].join(' '));
item.innerHTML = `
<svg width="1em" height="1em" fill="currentColor">
<use href="/assets/images/sprite.svg#${i.name}"></use>
</svg>
`;
list.appendChild(item);
// Wrap it with a tooltip the first time the mouse lands on it. We do this instead of baking them into the DOM
// to improve this page's performance. See: https://github.com/shoelace-style/shoelace/issues/1122
item.addEventListener('mouseover', () => wrapWithTooltip(item), { once: true });
// Copy on click
item.addEventListener('click', () => {
const tooltip = item.closest('sl-tooltip');
copyInput.value = i.name;
copyInput.select();
document.execCommand('copy');
if (tooltip) {
tooltip.content = 'Copied!';
setTimeout(() => tooltip.content = i.name, 1000);
}
});
});
// Filter as the user types
input.addEventListener('sl-input', () => {
clearTimeout(inputTimeout);
inputTimeout = setTimeout(() => {
[...list.querySelectorAll('.icon-list-item')].map(item => {
const filter = input.value.toLowerCase();
if (filter === '') {
item.hidden = false;
} else {
const terms = item.getAttribute('data-terms').toLowerCase();
item.hidden = terms.indexOf(filter) < 0;
}
});
}, 250);
});
// Sort by type and remember preference
const iconType = sessionStorage.getItem('sl-icon:type') || 'outline';
select.value = iconType;
list.setAttribute('data-type', select.value);
select.addEventListener('sl-change', () => {
list.setAttribute('data-type', select.value);
sessionStorage.setItem('sl-icon:type', select.value);
});
});
</script>
<style>
.icon-search {
border: solid 1px var(--sl-panel-border-color);
border-radius: var(--sl-border-radius-medium);
padding: var(--sl-spacing-medium);
}
.icon-search [hidden] {
display: none;
}
.icon-search-controls {
display: flex;
}
.icon-search-controls sl-input {
flex: 1 1 auto;
}
.icon-search-controls sl-select {
width: 10rem;
flex: 0 0 auto;
margin-left: 1rem;
}
.icon-loader {
display: flex;
align-items: center;
justify-content: center;
min-height: 30vh;
}
.icon-list {
display: grid;
grid-template-columns: repeat(12, 1fr);
position: relative;
margin-top: 1rem;
}
.icon-loader[hidden],
.icon-list[hidden] {
display: none;
}
.icon-list-item {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: var(--sl-border-radius-medium);
font-size: 24px;
width: 2em;
height: 2em;
margin: 0 auto;
cursor: copy;
transition: var(--sl-transition-medium) all;
}
.icon-list-item:hover {
background-color: var(--sl-color-primary-50);
color: var(--sl-color-primary-600);
}
.icon-list[data-type="outline"] .icon-list-item[data-name$="-fill"] {
display: none;
}
.icon-list[data-type="fill"] .icon-list-item:not([data-name$="-fill"]) {
display: none;
}
.icon-copy-input {
position: absolute;
opacity: 0;
pointer-events: none;
}
@media screen and (max-width: 1000px) {
.icon-list {
grid-template-columns: repeat(8, 1fr);
}
.icon-list-item {
font-size: 20px;
}
.icon-search-controls {
display: block;
}
.icon-search-controls sl-select {
width: auto;
margin: 1rem 0 0 0;
}
}
@media screen and (max-width: 500px) {
.icon-list {
grid-template-columns: repeat(4, 1fr);
}
}
</style>

View File

@@ -1,281 +0,0 @@
---
meta:
title: Input
description: Inputs collect data from the user.
layout: component
---
```html:preview
<sl-input></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput />;
```
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
### Labels
Use the `label` attribute to give the input an accessible label. For labels that contain HTML, use the `label` slot instead.
```html:preview
<sl-input label="What is your name?"></sl-input>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput label="What is your name?" />;
```
### Help Text
Add descriptive help text to an input with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html:preview
<sl-input label="Nickname" help-text="What would you like people to call you?"></sl-input>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput label="Nickname" help-text="What would you like people to call you?" />;
```
### Placeholders
Use the `placeholder` attribute to add a placeholder.
```html:preview
<sl-input placeholder="Type something"></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput placeholder="Type something" />;
```
### Clearable
Add the `clearable` attribute to add a clear button when the input has content.
```html:preview
<sl-input placeholder="Clearable" clearable></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput placeholder="Clearable" clearable />;
```
### Toggle Password
Add the `password-toggle` attribute to add a toggle button that will show the password when activated.
```html:preview
<sl-input type="password" placeholder="Password Toggle" password-toggle></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput type="password" placeholder="Password Toggle" size="medium" password-toggle />;
```
### Filled Inputs
Add the `filled` attribute to draw a filled input.
```html:preview
<sl-input placeholder="Type something" filled></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput placeholder="Type something" filled />;
```
### Disabled
Use the `disabled` attribute to disable an input.
```html:preview
<sl-input placeholder="Disabled" disabled></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => <SlInput placeholder="Disabled" disabled />;
```
### Sizes
Use the `size` attribute to change an input's size.
```html:preview
<sl-input placeholder="Small" size="small"></sl-input>
<br />
<sl-input placeholder="Medium" size="medium"></sl-input>
<br />
<sl-input placeholder="Large" size="large"></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => (
<>
<SlInput placeholder="Small" size="small" />
<br />
<SlInput placeholder="Medium" size="medium" />
<br />
<SlInput placeholder="Large" size="large" />
</>
);
```
### Pill
Use the `pill` attribute to give inputs rounded edges.
```html:preview
<sl-input placeholder="Small" size="small" pill></sl-input>
<br />
<sl-input placeholder="Medium" size="medium" pill></sl-input>
<br />
<sl-input placeholder="Large" size="large" pill></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => (
<>
<SlInput placeholder="Small" size="small" pill />
<br />
<SlInput placeholder="Medium" size="medium" pill />
<br />
<SlInput placeholder="Large" size="large" pill />
</>
);
```
### Input Types
The `type` attribute controls the type of input the browser renders.
```html:preview
<sl-input type="email" placeholder="Email"></sl-input>
<br />
<sl-input type="number" placeholder="Number"></sl-input>
<br />
<sl-input type="date" placeholder="Date"></sl-input>
```
```jsx:react
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => (
<>
<SlInput type="email" placeholder="Email" />
<br />
<SlInput type="number" placeholder="Number" />
<br />
<SlInput type="date" placeholder="Date" />
</>
);
```
### Prefix & Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
```html:preview
<sl-input placeholder="Small" size="small">
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-icon name="chat" slot="suffix"></sl-icon>
</sl-input>
<br />
<sl-input placeholder="Medium" size="medium">
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-icon name="chat" slot="suffix"></sl-icon>
</sl-input>
<br />
<sl-input placeholder="Large" size="large">
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-icon name="chat" slot="suffix"></sl-icon>
</sl-input>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlInput from '@shoelace-style/shoelace/dist/react/input';
const App = () => (
<>
<SlInput placeholder="Small" size="small">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlIcon name="chat" slot="suffix"></SlIcon>
</SlInput>
<br />
<SlInput placeholder="Medium" size="medium">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlIcon name="chat" slot="suffix"></SlIcon>
</SlInput>
<br />
<SlInput placeholder="Large" size="large">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlIcon name="chat" slot="suffix"></SlIcon>
</SlInput>
</>
);
```
### Customizing Label Position
Use [CSS parts](#css-parts) to customize the way form controls are drawn. This example uses CSS grid to position the label to the left of the control, but the possible orientations are nearly endless. The same technique works for inputs, textareas, radio groups, and similar form controls.
```html:preview
<sl-input class="label-on-left" label="Name" help-text="Enter your name"></sl-input>
<sl-input class="label-on-left" label="Email" type="email" help-text="Enter your email"></sl-input>
<sl-textarea class="label-on-left" label="Bio" help-text="Tell us something about yourself"></sl-textarea>
<style>
.label-on-left {
--label-width: 3.75rem;
--gap-width: 1rem;
}
.label-on-left + .label-on-left {
margin-top: var(--sl-spacing-medium);
}
.label-on-left::part(form-control) {
display: grid;
grid: auto / var(--label-width) 1fr;
gap: var(--sl-spacing-3x-small) var(--gap-width);
align-items: center;
}
.label-on-left::part(form-control-label) {
text-align: right;
}
.label-on-left::part(form-control-help-text) {
grid-column-start: 2;
}
</style>
```

View File

@@ -1,245 +0,0 @@
---
meta:
title: Menu Item
description: Menu items provide options for the user to pick from in a menu.
layout: component
---
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item>Option 1</sl-menu-item>
<sl-menu-item>Option 2</sl-menu-item>
<sl-menu-item>Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
<sl-menu-item disabled>Disabled</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
Prefix Icon
<sl-icon slot="prefix" name="gift"></sl-icon>
</sl-menu-item>
<sl-menu-item>
Suffix Icon
<sl-icon slot="suffix" name="heart"></sl-icon>
</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem>Option 1</SlMenuItem>
<SlMenuItem>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
<SlDivider />
<SlMenuItem type="checkbox" checked>
Checkbox
</SlMenuItem>
<SlMenuItem disabled>Disabled</SlMenuItem>
<SlDivider />
<SlMenuItem>
Prefix Icon
<SlIcon slot="prefix" name="gift" />
</SlMenuItem>
<SlMenuItem>
Suffix Icon
<SlIcon slot="suffix" name="heart" />
</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
## Examples
### Disabled
Add the `disabled` attribute to disable the menu item so it cannot be selected.
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item>Option 1</sl-menu-item>
<sl-menu-item disabled>Option 2</sl-menu-item>
<sl-menu-item>Option 3</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem>Option 1</SlMenuItem>
<SlMenuItem disabled>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
### Prefix & Suffix
Add content to the start and end of menu items using the `prefix` and `suffix` slots.
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item>
<sl-icon slot="prefix" name="house"></sl-icon>
Home
</sl-menu-item>
<sl-menu-item>
<sl-icon slot="prefix" name="envelope"></sl-icon>
Messages
<sl-badge slot="suffix" variant="primary" pill>12</sl-badge>
</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
<sl-icon slot="prefix" name="gear"></sl-icon>
Settings
</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem>
<SlIcon slot="prefix" name="house" />
Home
</SlMenuItem>
<SlMenuItem>
<SlIcon slot="prefix" name="envelope" />
Messages
<SlBadge slot="suffix" variant="primary" pill>
12
</SlBadge>
</SlMenuItem>
<SlDivider />
<SlMenuItem>
<SlIcon slot="prefix" name="gear" />
Settings
</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
### Checkbox Menu Items
Set the `type` attribute to `checkbox` to create a menu item that will toggle on and off when selected. You can use the `checked` attribute to set the initial state.
Checkbox menu items are visually indistinguishable from regular menu items. Their ability to be toggled is primarily inferred from context, much like you'd find in the menu of a native app.
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item type="checkbox">Autosave</sl-menu-item>
<sl-menu-item type="checkbox" checked>Check Spelling</sl-menu-item>
<sl-menu-item type="checkbox">Word Wrap</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem type="checkbox">Autosave</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Check Spelling
</SlMenuItem>
<SlMenuItem type="checkbox">Word Wrap</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
### Value & Selection
The `value` attribute can be used to assign a hidden value, such as a unique identifier, to a menu item. When an item is selected, the `sl-select` event will be emitted and a reference to the item will be available at `event.detail.item`. You can use this reference to access the selected item's value, its checked state, and more.
```html:preview
<sl-menu class="menu-value" style="max-width: 200px;">
<sl-menu-item value="opt-1">Option 1</sl-menu-item>
<sl-menu-item value="opt-2">Option 2</sl-menu-item>
<sl-menu-item value="opt-3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item type="checkbox" value="opt-4">Checkbox 4</sl-menu-item>
<sl-menu-item type="checkbox" value="opt-5">Checkbox 5</sl-menu-item>
<sl-menu-item type="checkbox" value="opt-6">Checkbox 6</sl-menu-item>
</sl-menu>
<script>
const menu = document.querySelector('.menu-value');
menu.addEventListener('sl-select', event => {
const item = event.detail.item;
// Log value
if (item.type === 'checkbox') {
console.log(`Selected value: ${item.value} (${item.checked ? 'checked' : 'unchecked'})`);
} else {
console.log(`Selected value: ${item.value}`);
}
});
</script>
```
{% raw %}
```jsx:react
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => {
function handleSelect(event) {
const item = event.detail.item;
// Toggle checked state
item.checked = !item.checked;
// Log value
console.log(`Selected value: ${item.value}`);
}
return (
<SlMenu style={{ maxWidth: '200px' }} onSlSelect={handleSelect}>
<SlMenuItem value="opt-1">Option 1</SlMenuItem>
<SlMenuItem value="opt-2">Option 2</SlMenuItem>
<SlMenuItem value="opt-3">Option 3</SlMenuItem>
</SlMenu>
);
};
```
{% endraw %}

View File

@@ -1,45 +0,0 @@
---
meta:
title: Menu Label
description: Menu labels are used to describe a group of menu items.
layout: component
---
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-label>Fruits</sl-menu-label>
<sl-menu-item value="apple">Apple</sl-menu-item>
<sl-menu-item value="banana">Banana</sl-menu-item>
<sl-menu-item value="orange">Orange</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-label>Vegetables</sl-menu-label>
<sl-menu-item value="broccoli">Broccoli</sl-menu-item>
<sl-menu-item value="carrot">Carrot</sl-menu-item>
<sl-menu-item value="zucchini">Zucchini</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuLabel from '@shoelace-style/shoelace/dist/react/menu-label';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuLabel>Fruits</SlMenuLabel>
<SlMenuItem value="apple">Apple</SlMenuItem>
<SlMenuItem value="banana">Banana</SlMenuItem>
<SlMenuItem value="orange">Orange</SlMenuItem>
<SlDivider />
<SlMenuLabel>Vegetables</SlMenuLabel>
<SlMenuItem value="broccoli">Broccoli</SlMenuItem>
<SlMenuItem value="carrot">Carrot</SlMenuItem>
<SlMenuItem value="zucchini">Zucchini</SlMenuItem>
</SlMenu>
);
```
{% endraw %}

View File

@@ -1,155 +0,0 @@
---
meta:
title: Menu
description: Menus provide a list of options for the user to choose from.
layout: component
---
You can use [menu items](/components/menu-item), [menu labels](/components/menu-label), and [dividers](/components/divider) to compose a menu. Menus support keyboard interactions, including type-to-select an option.
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item value="undo">Undo</sl-menu-item>
<sl-menu-item value="redo">Redo</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
<sl-menu-item value="delete">Delete</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem value="undo">Undo</SlMenuItem>
<SlMenuItem value="redo">Redo</SlMenuItem>
<SlDivider />
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
<SlMenuItem value="delete">Delete</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
:::tip
Menus are intended for system menus (dropdown menus, select menus, context menus, etc.). They should not be mistaken for navigation menus which serve a different purpose and have a different semantic meaning. If you're building navigation, use `<nav>` and `<a>` elements instead.
:::
## Examples
### In Dropdowns
Menus work really well when used inside [dropdowns](/components/dropdown).
```html:preview
<sl-dropdown>
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
</sl-menu>
</sl-dropdown>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlDropdown>
<SlButton slot="trigger" caret>Edit</SlButton>
<SlMenu>
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
</SlMenu>
</SlDropdown>
);
```
### Submenus
To create a submenu, nest an `<sl-menu slot="submenu">` in any [menu item](/components/menu-item).
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item value="undo">Undo</sl-menu-item>
<sl-menu-item value="redo">Redo</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
Find
<sl-menu slot="submenu">
<sl-menu-item value="find">Find…</sl-menu-item>
<sl-menu-item value="find-previous">Find Next</sl-menu-item>
<sl-menu-item value="find-next">Find Previous</sl-menu-item>
</sl-menu>
</sl-menu-item>
<sl-menu-item>
Transformations
<sl-menu slot="submenu">
<sl-menu-item value="uppercase">Make uppercase</sl-menu-item>
<sl-menu-item value="lowercase">Make lowercase</sl-menu-item>
<sl-menu-item value="capitalize">Capitalize</sl-menu-item>
</sl-menu>
</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem value="undo">Undo</SlMenuItem>
<SlMenuItem value="redo">Redo</SlMenuItem>
<SlDivider />
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
<SlDivider />
<SlMenuItem>
Find
<SlMenu slot="submenu">
<SlMenuItem value="find">Find…</SlMenuItem>
<SlMenuItem value="find-previous">Find Next</SlMenuItem>
<SlMenuItem value="find-next">Find Previous</SlMenuItem>
</SlMenu>
</SlMenuItem>
<SlMenuItem>
Transformations
<SlMenu slot="submenu">
<SlMenuItem value="uppercase">Make uppercase</SlMenuItem>
<SlMenuItem value="lowercase">Make lowercase</SlMenuItem>
<SlMenuItem value="capitalize">Capitalize</SlMenuItem>
</SlMenu>
</SlMenuItem>
</SlMenu>
);
```
:::warning
As a UX best practice, avoid using more than one level of submenus when possible.
:::
{% endraw %}

View File

@@ -1,82 +0,0 @@
---
meta:
title: Option
description: Options define the selectable items within various form controls such as select.
layout: component
---
```html:preview
<sl-select label="Select one">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
## Examples
### Disabled
Use the `disabled` attribute to disable an option and prevent it from being selected.
```html:preview
<sl-select label="Select one">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2" disabled>Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2" disabled>
Option 2
</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Prefix & Suffix
Add icons to the start and end of menu items using the `prefix` and `suffix` slots.
```html:preview
<sl-select label="Select one">
<sl-option value="option-1">
<sl-icon slot="prefix" name="envelope"></sl-icon>
Email
<sl-icon slot="suffix" name="patch-check"></sl-icon>
</sl-option>
<sl-option value="option-2">
<sl-icon slot="prefix" name="telephone"></sl-icon>
Phone
<sl-icon slot="suffix" name="patch-check"></sl-icon>
</sl-option>
<sl-option value="option-3">
<sl-icon slot="prefix" name="chat-dots"></sl-icon>
Chat
<sl-icon slot="suffix" name="patch-check"></sl-icon>
</sl-option>
</sl-select>
```

View File

@@ -1,305 +0,0 @@
---
meta:
title: Radio Button
description: Radios buttons allow the user to select a single option from a group using a button-like control.
layout: component
---
Radio buttons are designed to be used with [radio groups](/components/radio-group). When a radio button has focus, the arrow keys can be used to change the selected option just like standard radio controls.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
## Examples
### Checked States
To set the initial value and checked state, use the `value` attribute on the containing radio group.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Disabled
Use the `disabled` attribute to disable a radio button.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2" disabled>Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2" disabled>
Option 2
</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Sizes
Use the `size` attribute to change a radio button's size.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="small" value="1">Option 1</sl-radio-button>
<sl-radio-button size="small" value="2">Option 2</sl-radio-button>
<sl-radio-button size="small" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="medium" value="1">Option 1</sl-radio-button>
<sl-radio-button size="medium" value="2">Option 2</sl-radio-button>
<sl-radio-button size="medium" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button size="large" value="1">Option 1</sl-radio-button>
<sl-radio-button size="large" value="2">Option 2</sl-radio-button>
<sl-radio-button size="large" value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="small" value="1">Option 1</SlRadioButton>
<SlRadioButton size="small" value="2">Option 2</SlRadioButton>
<SlRadioButton size="small" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="medium" value="1">Option 1</SlRadioButton>
<SlRadioButton size="medium" value="2">Option 2</SlRadioButton>
<SlRadioButton size="medium" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton size="large" value="1">Option 1</SlRadioButton>
<SlRadioButton size="large" value="2">Option 2</SlRadioButton>
<SlRadioButton size="large" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Pill Buttons
Use the `pill` attribute to give radio buttons rounded edges.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="small" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="small" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="small" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="medium" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="medium" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="medium" value="3">Option 3</sl-radio-button>
</sl-radio-group>
<br />
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button pill size="large" value="1">Option 1</sl-radio-button>
<sl-radio-button pill size="large" value="2">Option 2</sl-radio-button>
<sl-radio-button pill size="large" value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="small" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="small" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="small" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="medium" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="medium" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="medium" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
<br />
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton pill size="large" value="1">Option 1</SlRadioButton>
<SlRadioButton pill size="large" value="2">Option 2</SlRadioButton>
<SlRadioButton pill size="large" value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Prefix and Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio-button value="1">
<sl-icon slot="prefix" name="archive"></sl-icon>
Option 1
</sl-radio-button>
<sl-radio-button value="2">
<sl-icon slot="suffix" name="bag"></sl-icon>
Option 2
</sl-radio-button>
<sl-radio-button value="3">
<sl-icon slot="prefix" name="gift"></sl-icon>
<sl-icon slot="suffix" name="cart"></sl-icon>
Option 3
</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">
<SlIcon slot="prefix" name="archive" />
Option 1
</SlRadioButton>
<SlRadioButton value="2">
<SlIcon slot="suffix" name="bag" />
Option 2
</SlRadioButton>
<SlRadioButton value="3">
<SlIcon slot="prefix" name="gift" />
<SlIcon slot="suffix" name="cart" />
Option 3
</SlRadioButton>
</SlRadioGroup>
);
```
### Buttons with Icons
You can omit button labels and use icons instead. Make sure to set a `label` attribute on each icon so screen readers will announce each option correctly.
```html:preview
<sl-radio-group label="Select an option" name="a" value="neutral">
<sl-radio-button value="angry">
<sl-icon name="emoji-angry" label="Angry"></sl-icon>
</sl-radio-button>
<sl-radio-button value="sad">
<sl-icon name="emoji-frown" label="Sad"></sl-icon>
</sl-radio-button>
<sl-radio-button value="neutral">
<sl-icon name="emoji-neutral" label="Neutral"></sl-icon>
</sl-radio-button>
<sl-radio-button value="happy">
<sl-icon name="emoji-smile" label="Happy"></sl-icon>
</sl-radio-button>
<sl-radio-button value="laughing">
<sl-icon name="emoji-laughing" label="Laughing"></sl-icon>
</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="neutral">
<SlRadioButton value="angry">
<SlIcon name="emoji-angry" label="Angry" />
</SlRadioButton>
<SlRadioButton value="sad">
<SlIcon name="emoji-frown" label="Sad" />
</SlRadioButton>
<SlRadioButton value="neutral">
<SlIcon name="emoji-neutral" label="Neutral" />
</SlRadioButton>
<SlRadioButton value="happy">
<SlIcon name="emoji-smile" label="Happy" />
</SlRadioButton>
<SlRadioButton value="laughing">
<SlIcon name="emoji-laughing" label="Laughing" />
</SlRadioButton>
</SlRadioGroup>
);
```

View File

@@ -1,293 +0,0 @@
---
meta:
title: Radio Group
description: Radio groups are used to group multiple radios or radio buttons so they function as a single form control.
layout: component
---
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
## Examples
### Help Text
Add descriptive help text to a radio group with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html:preview
<sl-radio-group label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" help-text="Choose the most appropriate option." name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Radio Buttons
[Radio buttons](/components/radio-button) offer an alternate way to display radio controls. In this case, an internal [button group](/components/button-group) is used to group the buttons into a single, cohesive control.
```html:preview
<sl-radio-group label="Select an option" help-text="Select an option that makes you proud." name="a" value="1">
<sl-radio-button value="1">Option 1</sl-radio-button>
<sl-radio-button value="2">Option 2</sl-radio-button>
<sl-radio-button value="3">Option 3</sl-radio-button>
</sl-radio-group>
```
```jsx:react
import SlRadioButton from '@shoelace-style/shoelace/dist/react/radio-button';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadioButton value="1">Option 1</SlRadioButton>
<SlRadioButton value="2">Option 2</SlRadioButton>
<SlRadioButton value="3">Option 3</SlRadioButton>
</SlRadioGroup>
);
```
### Disabling Options
Radios and radio buttons can be disabled by adding the `disabled` attribute to the respective options inside the radio group.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2" disabled>Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2" disabled>
Option 2
</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Sizing Options
The size of [Radios](/components/radio) and [Radio Buttons](/components/radio-buttons) will be determined by the Radio Group's `size` attribute.
```html preview
<sl-radio-group label="Select an option" size="medium" value="medium" class="radio-group-size">
<sl-radio value="small">Small</sl-radio>
<sl-radio value="medium">Medium</sl-radio>
<sl-radio value="large">Large</sl-radio>
</sl-radio-group>
<script>
const radioGroup = document.querySelector('.radio-group-size');
radioGroup.addEventListener('sl-change', () => {
radioGroup.size = radioGroup.value;
});
</script>
```
```jsx react
import { useState } from 'react';
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => {
const [size, setSize] = useState('medium');
return (
<>
<SlRadioGroup
label="Select an option"
size={size}
value={size}
class="radio-group-size"
onSlChange={event => setSize(event.target.value)}
>
<SlRadio value="small">Small</SlRadio>
<SlRadio value="medium">Medium</SlRadio>
<SlRadio value="large">Large</SlRadio>
</SlRadioGroup>
</>
);
};
```
:::tip
[Radios](/components/radio) and [Radio Buttons](/components/radio-button) also have a `size` attribute. This can be useful in certain compositions, but it will be ignored when used inside of a Radio Group.
:::
### Validation
Setting the `required` attribute to make selecting an option mandatory. If a value has not been selected, it will prevent the form from submitting and display an error message.
```html:preview
<form class="validation">
<sl-radio-group label="Select an option" name="a" required>
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
<br />
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
<script>
const form = document.querySelector('.validation');
// Handle form submit
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => {
function handleSubmit(event) {
event.preventDefault();
alert('All fields are valid!');
}
return (
<form class="custom-validity" onSubmit={handleSubmit}>
<SlRadioGroup label="Select an option" name="a" required onSlChange={handleChange}>
<SlRadio value="1">
Option 1
</SlRadio>
<SlRadiovalue="2">
Option 2
</SlRadio>
<SlRadio value="3">
Option 3
</SlRadio>
</SlRadioGroup>
<br />
<SlButton type="submit" variant="primary">
Submit
</SlButton>
</form>
);
};
```
### Custom Validity
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.
```html:preview
<form class="custom-validity">
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Not me</sl-radio>
<sl-radio value="2">Me neither</sl-radio>
<sl-radio value="3">Choose me</sl-radio>
</sl-radio-group>
<br />
<sl-button type="submit" variant="primary">Submit</sl-button>
</form>
<script>
const form = document.querySelector('.custom-validity');
const radioGroup = form.querySelector('sl-radio-group');
const errorMessage = 'You must choose the last option';
// Set initial validity as soon as the element is defined
customElements.whenDefined('sl-radio').then(() => {
radioGroup.setCustomValidity(errorMessage);
});
// Update validity when a selection is made
form.addEventListener('sl-change', () => {
const isValid = radioGroup.value === '3';
radioGroup.setCustomValidity(isValid ? '' : errorMessage);
});
// Handle form submit
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
</script>
```
```jsx:react
import { useEffect, useRef } from 'react';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => {
const radioGroup = useRef(null);
const errorMessage = 'You must choose this option';
function handleChange() {
radioGroup.current.setCustomValidity(radioGroup.current.value === '3' ? '' : errorMessage);
}
function handleSubmit(event) {
event.preventDefault();
alert('All fields are valid!');
}
useEffect(() => {
radio.current.setCustomValidity(errorMessage);
}, []);
return (
<form class="custom-validity" onSubmit={handleSubmit}>
<SlRadioGroup ref={radioGroup} label="Select an option" name="a" value="1" onSlChange={handleChange}>
<SlRadio value="1">Not me</SlRadio>
<SlRadio value="2">Me neither</SlRadio>
<SlRadio value="3">Choose me</SlRadio>
</SlRadioGroup>
<br />
<SlButton type="submit" variant="primary">
Submit
</SlButton>
</form>
);
};
```

View File

@@ -1,145 +0,0 @@
---
meta:
title: Radio
description: Radios allow the user to select a single option from a group.
layout: component
---
Radios are designed to be used with [radio groups](/components/radio-group).
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
### Initial Value
To set the initial value and checked state, use the `value` attribute on the containing radio group.
```html:preview
<sl-radio-group label="Select an option" name="a" value="3">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="3">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
### Disabled
Use the `disabled` attribute to disable a radio.
```html:preview
<sl-radio-group label="Select an option" name="a" value="1">
<sl-radio value="1">Option 1</sl-radio>
<sl-radio value="2" disabled>Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
</sl-radio-group>
```
```jsx:react
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
import SlRadioGroup from '@shoelace-style/shoelace/dist/react/radio-group';
const App = () => (
<SlRadioGroup label="Select an option" name="a" value="1">
<SlRadio value="1">Option 1</SlRadio>
<SlRadio value="2" disabled>
Option 2
</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
## Sizes
Add the `size` attribute to the [Radio Group](/components/radio-group) to change the radios' size.
```html preview
<sl-radio-group size="small" value="1">
<sl-radio value="1">Small 1</sl-radio>
<sl-radio value="2">Small 2</sl-radio>
<sl-radio value="3">Small 3</sl-radio>
</sl-radio-group>
<br />
<sl-radio-group size="medium" value="1">
<sl-radio value="1">Medium 1</sl-radio>
<sl-radio value="2">Medium 2</sl-radio>
<sl-radio value="3">Medium 3</sl-radio>
</sl-radio-group>
<br />
<sl-radio-group size="large" value="1">
<sl-radio value="1">Large 1</sl-radio>
<sl-radio value="2">Large 2</sl-radio>
<sl-radio value="3">Large 3</sl-radio>
</sl-radio-group>
```
```jsx react
import SlRadio from '@shoelace-style/shoelace/dist/react/radio';
const App = () => (
<>
<SlRadioGroup size="small" value="1">
<SlRadio value="1">Small 1</SlRadio>
<SlRadio value="2">Small 2</SlRadio>
<SlRadio value="3">Small 3</SlRadio>
</SlRadioGroup>
<br />
<SlRadioGroup size="medium" value="1">
<SlRadio value="1">Medium 1</SlRadio>
<SlRadio value="2">Medium 2</SlRadio>
<SlRadio value="3">Medium 3</SlRadio>
</SlRadioGroup>
<br />
<SlRadioGroup size="large" value="1">
<SlRadio value="1">Large 1</SlRadio>
<SlRadio value="2">Large 2</SlRadio>
<SlRadio value="3">Large 3</SlRadio>
</SlRadioGroup>
</>
);
```

View File

@@ -1,504 +0,0 @@
---
meta:
title: Select
description: Selects allow you to choose items from a menu of predefined options.
layout: component
---
```html:preview
<sl-select>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-option value="option-4">Option 4</sl-option>
<sl-option value="option-5">Option 5</sl-option>
<sl-option value="option-6">Option 6</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
<SlOption value="option-4">Option 4</SlOption>
<SlOption value="option-5">Option 5</SlOption>
<SlOption value="option-6">Option 6</SlOption>
</SlSelect>
);
```
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
### Labels
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
```html:preview
<sl-select label="Select one">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect label="Select one">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Help Text
Add descriptive help text to a select with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html:preview
<sl-select label="Experience" help-text="Please tell us your skill level.">
<sl-option value="1">Novice</sl-option>
<sl-option value="2">Intermediate</sl-option>
<sl-option value="3">Advanced</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect label="Experience" help-text="Please tell us your skill level.">
<SlOption value="1">Novice</SlOption>
<SlOption value="2">Intermediate</SlOption>
<SlOption value="3">Advanced</SlOption>
</SlSelect>
);
```
### Placeholders
Use the `placeholder` attribute to add a placeholder.
```html:preview
<sl-select placeholder="Select one">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect placeholder="Select one">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Clearable
Use the `clearable` attribute to make the control clearable. The clear button only appears when an option is selected.
```html:preview
<sl-select clearable value="option-1">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect placeholder="Clearable" clearable>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Filled Selects
Add the `filled` attribute to draw a filled select.
```html:preview
<sl-select filled>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect filled>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Pill
Use the `pill` attribute to give selects rounded edges.
```html:preview
<sl-select pill>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect pill>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Disabled
Use the `disabled` attribute to disable a select.
```html:preview
<sl-select placeholder="Disabled" disabled>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect placeholder="Disabled" disabled>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Multiple
To allow multiple options to be selected, use the `multiple` attribute. It's a good practice to use `clearable` when this option is enabled. To set multiple values at once, set `value` to a space-delimited list of values.
```html:preview
<sl-select label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-option value="option-4">Option 4</sl-option>
<sl-option value="option-5">Option 5</sl-option>
<sl-option value="option-6">Option 6</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect label="Select a Few" value="option-1 option-2 option-3" multiple clearable>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
<SlOption value="option-4">Option 4</SlOption>
<SlOption value="option-5">Option 5</SlOption>
<SlOption value="option-6">Option 6</SlOption>
</SlSelect>
);
```
:::tip
Note that multi-select options may wrap, causing the control to expand vertically. You can use the `max-options-visible` attribute to control the maximum number of selected options to show at once.
:::
### Setting Initial Values
Use the `value` attribute to set the initial selection.
When using `multiple`, the `value` _attribute_ uses space-delimited values to select more than one option. Because of this, `<sl-option>` values cannot contain spaces. If you're accessing the `value` _property_ through Javascript, it will be an array.
```html:preview
<sl-select value="option-1 option-2" multiple clearable>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-option value="option-4">Option 4</sl-option>
</sl-select>
```
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect value="option-1 option-2" multiple clearable>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Grouping Options
Use `<sl-divider>` to group listbox items visually. You can also use `<small>` to provide labels, but they won't be announced by most assistive devices.
```html:preview
<sl-select>
<small>Section 1</small>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-divider></sl-divider>
<small>Section 2</small>
<sl-option value="option-4">Option 4</sl-option>
<sl-option value="option-5">Option 5</sl-option>
<sl-option value="option-6">Option 6</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
<SlOption value="option-4">Option 4</SlOption>
<SlOption value="option-5">Option 5</SlOption>
<SlOption value="option-6">Option 6</SlOption>
</SlSelect>
);
```
### Sizes
Use the `size` attribute to change a select's size. Note that size does not apply to listbox options.
```html:preview
<sl-select placeholder="Small" size="small">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Medium" size="medium">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Large" size="large">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<>
<SlSelect placeholder="Small" size="small">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
</>
);
```
### Placement
The preferred placement of the select's listbox can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport. Valid placements are `top` and `bottom`.
```html:preview
<sl-select placement="top">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<SlSelect placement="top">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlDropdown>
);
```
### Prefix Icons
Use the `prefix` slot to prepend an icon to the control.
```html:preview
<sl-select placeholder="Small" size="small" clearable>
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Medium" size="medium" clearable>
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Large" size="large" clearable>
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlOption from '@shoelace-style/shoelace/dist/react/option';
import SlSelect from '@shoelace-style/shoelace/dist/react/select';
const App = () => (
<>
<SlSelect placeholder="Small" size="small">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
</>
);
```
### Custom Tags
When multiple options can be selected, you can provide custom tags by passing a function to the `getTag` property. Your function can return a string of HTML, a <a href="https://lit.dev/docs/templates/overview/">Lit Template</a>, or an [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement). The `getTag()` function will be called for each option. The first argument is an `<sl-option>` element and the second argument is the tag's index (its position in the tag list).
Remember that custom tags are rendered in a shadow root. To style them, you can use the `style` attribute in your template or you can add your own [parts](/getting-started/customizing/#css-parts) and target them with the [`::part()`](https://developer.mozilla.org/en-US/docs/Web/CSS/::part) selector.
```html:preview
<sl-select
placeholder="Select one"
value="email phone"
multiple
clearable
class="custom-tag"
>
<sl-option value="email">
<sl-icon slot="prefix" name="envelope"></sl-icon>
Email
</sl-option>
<sl-option value="phone">
<sl-icon slot="prefix" name="telephone"></sl-icon>
Phone
</sl-option>
<sl-option value="chat">
<sl-icon slot="prefix" name="chat-dots"></sl-icon>
Chat
</sl-option>
</sl-select>
<script type="module">
const select = document.querySelector('.custom-tag');
select.getTag = (option, index) => {
// Use the same icon used in the <sl-option>
const name = option.querySelector('sl-icon[slot="prefix"]').name;
// You can return a string, a Lit Template, or an HTMLElement here
return `
<sl-tag removable>
<sl-icon name="${name}" style="padding-inline-end: .5rem;"></sl-icon>
${option.getTextLabel()}
</sl-tag>
`;
};
</script>
```
:::warning
Be sure you trust the content you are outputting! Passing unsanitized user input to `getTag()` can result in XSS vulnerabilities.
:::

View File

@@ -1,95 +0,0 @@
---
meta:
title: Spinner
description: Spinners are used to show the progress of an indeterminate operation.
layout: component
---
```html:preview
<sl-spinner></sl-spinner>
```
```jsx:react
import SlSpinner from '@shoelace-style/shoelace/dist/react/spinner';
const App = () => <SlSpinner />;
```
## Examples
### Size
Spinners are sized based on the current font size. To change their size, set the `font-size` property on the spinner itself or on a parent element as shown below.
```html:preview
<sl-spinner></sl-spinner>
<sl-spinner style="font-size: 2rem;"></sl-spinner>
<sl-spinner style="font-size: 3rem;"></sl-spinner>
```
{% raw %}
```jsx:react
import SlSpinner from '@shoelace-style/shoelace/dist/react/spinner';
const App = () => (
<>
<SlSpinner />
<SlSpinner style={{ fontSize: '2rem' }} />
<SlSpinner style={{ fontSize: '3rem' }} />
</>
);
```
{% endraw %}
### Track Width
The width of the spinner's track can be changed by setting the `--track-width` custom property.
```html:preview
<sl-spinner style="font-size: 50px; --track-width: 10px;"></sl-spinner>
```
{% raw %}
```jsx:react
import SlSpinner from '@shoelace-style/shoelace/dist/react/spinner';
const App = () => (
<SlSpinner
style={{
fontSize: '3rem',
'--track-width': '6px'
}}
/>
);
```
{% endraw %}
### Color
The spinner's colors can be changed by setting the `--indicator-color` and `--track-color` custom properties.
```html:preview
<sl-spinner style="font-size: 3rem; --indicator-color: deeppink; --track-color: pink;"></sl-spinner>
```
{% raw %}
```jsx:react
import SlSpinner from '@shoelace-style/shoelace/dist/react/spinner';
const App = () => (
<SlSpinner
style={{
fontSize: '3rem',
'--indicator-color': 'deeppink',
'--track-color': 'pink'
}}
/>
);
```
{% endraw %}

View File

@@ -1,102 +0,0 @@
---
meta:
title: Switch
description: Switches allow the user to toggle an option on or off.
layout: component
---
```html:preview
<sl-switch>Switch</sl-switch>
```
```jsx:react
import SlSwitch from '@shoelace-style/shoelace/dist/react/switch';
const App = () => <SlSwitch>Switch</SlSwitch>;
```
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
### Checked
Use the `checked` attribute to activate the switch.
```html:preview
<sl-switch checked>Checked</sl-switch>
```
```jsx:react
import SlSwitch from '@shoelace-style/shoelace/dist/react/switch';
const App = () => <SlSwitch checked>Checked</SlSwitch>;
```
### Disabled
Use the `disabled` attribute to disable the switch.
```html:preview
<sl-switch disabled>Disabled</sl-switch>
```
```jsx:react
import SlSwitch from '@shoelace-style/shoelace/dist/react/switch';
const App = () => <SlSwitch disabled>Disabled</SlSwitch>;
```
### Sizes
Use the `size` attribute to change a switch's size.
```html:preview
<sl-switch size="small">Small</sl-switch>
<br />
<sl-switch size="medium">Medium</sl-switch>
<br />
<sl-switch size="large">Large</sl-switch>
```
```jsx:react
import SlSwitch from '@shoelace-style/shoelace/dist/react/switch';
const App = () => (
<>
<SlSwitch size="small">Small</SlSwitch>
<br />
<SlSwitch size="medium">Medium</SlSwitch>
<br />
<SlSwitch size="large">Large</SlSwitch>
</>
);
```
### Custom Styles
Use the available custom properties to change how the switch is styled.
```html:preview
<sl-switch style="--width: 80px; --height: 40px; --thumb-size: 36px;">Really big</sl-switch>
```
{% raw %}
```jsx:react
import SlSwitch from '@shoelace-style/shoelace/dist/react/switch';
const App = () => (
<SlSwitch
style={{
'--width': '80px',
'--height': '32px',
'--thumb-size': '26px'
}}
/>
);
```
{% endraw %}

View File

@@ -1,458 +0,0 @@
---
meta:
title: Tab Group
description: Tab groups organize content into a container that shows one section at a time.
layout: component
---
Tab groups make use of [tabs](/components/tab) and [tab panels](/components/tab-panel). Each tab must be slotted into the `nav` slot and its `panel` must refer to a tab panel of the same name.
```html:preview
<sl-tab-group>
<sl-tab slot="nav" panel="general">General</sl-tab>
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
<sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab>
<sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel>
<sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel>
<sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel>
<sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel>
</sl-tab-group>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => (
<SlTabGroup>
<SlTab slot="nav" panel="general">
General
</SlTab>
<SlTab slot="nav" panel="custom">
Custom
</SlTab>
<SlTab slot="nav" panel="advanced">
Advanced
</SlTab>
<SlTab slot="nav" panel="disabled" disabled>
Disabled
</SlTab>
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
</SlTabGroup>
);
```
## Examples
### Tabs on Bottom
Tabs can be shown on the bottom by setting `placement` to `bottom`.
```html:preview
<sl-tab-group placement="bottom">
<sl-tab slot="nav" panel="general">General</sl-tab>
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
<sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab>
<sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel>
<sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel>
<sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel>
<sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel>
</sl-tab-group>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => (
<SlTabGroup placement="bottom">
<SlTab slot="nav" panel="general">
General
</SlTab>
<SlTab slot="nav" panel="custom">
Custom
</SlTab>
<SlTab slot="nav" panel="advanced">
Advanced
</SlTab>
<SlTab slot="nav" panel="disabled" disabled>
Disabled
</SlTab>
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
</SlTabGroup>
);
```
### Tabs on Start
Tabs can be shown on the starting side by setting `placement` to `start`.
```html:preview
<sl-tab-group placement="start">
<sl-tab slot="nav" panel="general">General</sl-tab>
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
<sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab>
<sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel>
<sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel>
<sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel>
<sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel>
</sl-tab-group>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => (
<SlTabGroup placement="start">
<SlTab slot="nav" panel="general">
General
</SlTab>
<SlTab slot="nav" panel="custom">
Custom
</SlTab>
<SlTab slot="nav" panel="advanced">
Advanced
</SlTab>
<SlTab slot="nav" panel="disabled" disabled>
Disabled
</SlTab>
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
</SlTabGroup>
);
```
### Tabs on End
Tabs can be shown on the ending side by setting `placement` to `end`.
```html:preview
<sl-tab-group placement="end">
<sl-tab slot="nav" panel="general">General</sl-tab>
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
<sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab>
<sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel>
<sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel>
<sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel>
<sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel>
</sl-tab-group>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => (
<SlTabGroup placement="end">
<SlTab slot="nav" panel="general">
General
</SlTab>
<SlTab slot="nav" panel="custom">
Custom
</SlTab>
<SlTab slot="nav" panel="advanced">
Advanced
</SlTab>
<SlTab slot="nav" panel="disabled" disabled>
Disabled
</SlTab>
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
</SlTabGroup>
);
```
### Closable Tabs
Add the `closable` attribute to a tab to show a close button. This example shows how you can dynamically remove tabs from the DOM when the close button is activated.
```html:preview
<sl-tab-group class="tabs-closable">
<sl-tab slot="nav" panel="general">General</sl-tab>
<sl-tab slot="nav" panel="closable-1" closable>Closable 1</sl-tab>
<sl-tab slot="nav" panel="closable-2" closable>Closable 2</sl-tab>
<sl-tab slot="nav" panel="closable-3" closable>Closable 3</sl-tab>
<sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel>
<sl-tab-panel name="closable-1">This is the first closable tab panel.</sl-tab-panel>
<sl-tab-panel name="closable-2">This is the second closable tab panel.</sl-tab-panel>
<sl-tab-panel name="closable-3">This is the third closable tab panel.</sl-tab-panel>
</sl-tab-group>
<script>
const tabGroup = document.querySelector('.tabs-closable');
tabGroup.addEventListener('sl-close', async event => {
const tab = event.target;
const panel = tabGroup.querySelector(`sl-tab-panel[name="${tab.panel}"]`);
// Show the previous tab if the tab is currently active
if (tab.active) {
tabGroup.show(tab.previousElementSibling.panel);
}
// Remove the tab + panel
tab.remove();
panel.remove();
});
</script>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => {
function handleClose(event) {
//
// This is a crude example that removes the tab and its panel from the DOM.
// There are better ways to manage tab creation/removal in React, but that
// would significantly complicate the example.
//
const tab = event.target;
const tabGroup = tab.closest('sl-tab-group');
const tabPanel = tabGroup.querySelector(`[aria-labelledby="${tab.id}"]`);
tab.remove();
tabPanel.remove();
}
return (
<SlTabGroup className="tabs-closable" onSlClose={handleClose}>
<SlTab slot="nav" panel="general">
General
</SlTab>
<SlTab slot="nav" panel="closable-1" closable onSlClose={handleClose}>
Closable 1
</SlTab>
<SlTab slot="nav" panel="closable-2" closable onSlClose={handleClose}>
Closable 2
</SlTab>
<SlTab slot="nav" panel="closable-3" closable onSlClose={handleClose}>
Closable 3
</SlTab>
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
<SlTabPanel name="closable-1">This is the first closable tab panel.</SlTabPanel>
<SlTabPanel name="closable-2">This is the second closable tab panel.</SlTabPanel>
<SlTabPanel name="closable-3">This is the third closable tab panel.</SlTabPanel>
</SlTabGroup>
);
};
```
### Scrolling Tabs
When there are more tabs than horizontal space allows, the nav will be scrollable.
```html:preview
<sl-tab-group>
<sl-tab slot="nav" panel="tab-1">Tab 1</sl-tab>
<sl-tab slot="nav" panel="tab-2">Tab 2</sl-tab>
<sl-tab slot="nav" panel="tab-3">Tab 3</sl-tab>
<sl-tab slot="nav" panel="tab-4">Tab 4</sl-tab>
<sl-tab slot="nav" panel="tab-5">Tab 5</sl-tab>
<sl-tab slot="nav" panel="tab-6">Tab 6</sl-tab>
<sl-tab slot="nav" panel="tab-7">Tab 7</sl-tab>
<sl-tab slot="nav" panel="tab-8">Tab 8</sl-tab>
<sl-tab slot="nav" panel="tab-9">Tab 9</sl-tab>
<sl-tab slot="nav" panel="tab-10">Tab 10</sl-tab>
<sl-tab slot="nav" panel="tab-11">Tab 11</sl-tab>
<sl-tab slot="nav" panel="tab-12">Tab 12</sl-tab>
<sl-tab slot="nav" panel="tab-13">Tab 13</sl-tab>
<sl-tab slot="nav" panel="tab-14">Tab 14</sl-tab>
<sl-tab slot="nav" panel="tab-15">Tab 15</sl-tab>
<sl-tab slot="nav" panel="tab-16">Tab 16</sl-tab>
<sl-tab slot="nav" panel="tab-17">Tab 17</sl-tab>
<sl-tab slot="nav" panel="tab-18">Tab 18</sl-tab>
<sl-tab slot="nav" panel="tab-19">Tab 19</sl-tab>
<sl-tab slot="nav" panel="tab-20">Tab 20</sl-tab>
<sl-tab-panel name="tab-1">Tab panel 1</sl-tab-panel>
<sl-tab-panel name="tab-2">Tab panel 2</sl-tab-panel>
<sl-tab-panel name="tab-3">Tab panel 3</sl-tab-panel>
<sl-tab-panel name="tab-4">Tab panel 4</sl-tab-panel>
<sl-tab-panel name="tab-5">Tab panel 5</sl-tab-panel>
<sl-tab-panel name="tab-6">Tab panel 6</sl-tab-panel>
<sl-tab-panel name="tab-7">Tab panel 7</sl-tab-panel>
<sl-tab-panel name="tab-8">Tab panel 8</sl-tab-panel>
<sl-tab-panel name="tab-9">Tab panel 9</sl-tab-panel>
<sl-tab-panel name="tab-10">Tab panel 10</sl-tab-panel>
<sl-tab-panel name="tab-11">Tab panel 11</sl-tab-panel>
<sl-tab-panel name="tab-12">Tab panel 12</sl-tab-panel>
<sl-tab-panel name="tab-13">Tab panel 13</sl-tab-panel>
<sl-tab-panel name="tab-14">Tab panel 14</sl-tab-panel>
<sl-tab-panel name="tab-15">Tab panel 15</sl-tab-panel>
<sl-tab-panel name="tab-16">Tab panel 16</sl-tab-panel>
<sl-tab-panel name="tab-17">Tab panel 17</sl-tab-panel>
<sl-tab-panel name="tab-18">Tab panel 18</sl-tab-panel>
<sl-tab-panel name="tab-19">Tab panel 19</sl-tab-panel>
<sl-tab-panel name="tab-20">Tab panel 20</sl-tab-panel>
</sl-tab-group>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => (
<SlTabGroup>
<SlTab slot="nav" panel="tab-1">
Tab 1
</SlTab>
<SlTab slot="nav" panel="tab-2">
Tab 2
</SlTab>
<SlTab slot="nav" panel="tab-3">
Tab 3
</SlTab>
<SlTab slot="nav" panel="tab-4">
Tab 4
</SlTab>
<SlTab slot="nav" panel="tab-5">
Tab 5
</SlTab>
<SlTab slot="nav" panel="tab-6">
Tab 6
</SlTab>
<SlTab slot="nav" panel="tab-7">
Tab 7
</SlTab>
<SlTab slot="nav" panel="tab-8">
Tab 8
</SlTab>
<SlTab slot="nav" panel="tab-9">
Tab 9
</SlTab>
<SlTab slot="nav" panel="tab-10">
Tab 10
</SlTab>
<SlTab slot="nav" panel="tab-11">
Tab 11
</SlTab>
<SlTab slot="nav" panel="tab-12">
Tab 12
</SlTab>
<SlTab slot="nav" panel="tab-13">
Tab 13
</SlTab>
<SlTab slot="nav" panel="tab-14">
Tab 14
</SlTab>
<SlTab slot="nav" panel="tab-15">
Tab 15
</SlTab>
<SlTab slot="nav" panel="tab-16">
Tab 16
</SlTab>
<SlTab slot="nav" panel="tab-17">
Tab 17
</SlTab>
<SlTab slot="nav" panel="tab-18">
Tab 18
</SlTab>
<SlTab slot="nav" panel="tab-19">
Tab 19
</SlTab>
<SlTab slot="nav" panel="tab-20">
Tab 20
</SlTab>
<SlTabPanel name="tab-1">Tab panel 1</SlTabPanel>
<SlTabPanel name="tab-2">Tab panel 2</SlTabPanel>
<SlTabPanel name="tab-3">Tab panel 3</SlTabPanel>
<SlTabPanel name="tab-4">Tab panel 4</SlTabPanel>
<SlTabPanel name="tab-5">Tab panel 5</SlTabPanel>
<SlTabPanel name="tab-6">Tab panel 6</SlTabPanel>
<SlTabPanel name="tab-7">Tab panel 7</SlTabPanel>
<SlTabPanel name="tab-8">Tab panel 8</SlTabPanel>
<SlTabPanel name="tab-9">Tab panel 9</SlTabPanel>
<SlTabPanel name="tab-10">Tab panel 10</SlTabPanel>
<SlTabPanel name="tab-11">Tab panel 11</SlTabPanel>
<SlTabPanel name="tab-12">Tab panel 12</SlTabPanel>
<SlTabPanel name="tab-13">Tab panel 13</SlTabPanel>
<SlTabPanel name="tab-14">Tab panel 14</SlTabPanel>
<SlTabPanel name="tab-15">Tab panel 15</SlTabPanel>
<SlTabPanel name="tab-16">Tab panel 16</SlTabPanel>
<SlTabPanel name="tab-17">Tab panel 17</SlTabPanel>
<SlTabPanel name="tab-18">Tab panel 18</SlTabPanel>
<SlTabPanel name="tab-19">Tab panel 19</SlTabPanel>
<SlTabPanel name="tab-20">Tab panel 20</SlTabPanel>
</SlTabGroup>
);
```
### Manual Activation
When focused, keyboard users can press [[Left]] or [[Right]] to select the desired tab. By default, the corresponding tab panel will be shown immediately (automatic activation). You can change this behavior by setting `activation="manual"` which will require the user to press [[Space]] or [[Enter]] before showing the tab panel (manual activation).
```html:preview
<sl-tab-group activation="manual">
<sl-tab slot="nav" panel="general">General</sl-tab>
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
<sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab>
<sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel>
<sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel>
<sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel>
<sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel>
</sl-tab-group>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => (
<SlTabGroup activation="manual">
<SlTab slot="nav" panel="general">
General
</SlTab>
<SlTab slot="nav" panel="custom">
Custom
</SlTab>
<SlTab slot="nav" panel="advanced">
Advanced
</SlTab>
<SlTab slot="nav" panel="disabled" disabled>
Disabled
</SlTab>
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
</SlTabGroup>
);
```

View File

@@ -1,52 +0,0 @@
---
meta:
title: Tab Panel
description: Tab panels are used inside tab groups to display tabbed content.
layout: component
---
```html:preview
<sl-tab-group>
<sl-tab slot="nav" panel="general">General</sl-tab>
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
<sl-tab slot="nav" panel="advanced">Advanced</sl-tab>
<sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab>
<sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel>
<sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel>
<sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel>
<sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel>
</sl-tab-group>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
import SlTabGroup from '@shoelace-style/shoelace/dist/react/tab-group';
import SlTabPanel from '@shoelace-style/shoelace/dist/react/tab-panel';
const App = () => (
<SlTabGroup>
<SlTab slot="nav" panel="general">
General
</SlTab>
<SlTab slot="nav" panel="custom">
Custom
</SlTab>
<SlTab slot="nav" panel="advanced">
Advanced
</SlTab>
<SlTab slot="nav" panel="disabled" disabled>
Disabled
</SlTab>
<SlTabPanel name="general">This is the general tab panel.</SlTabPanel>
<SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel>
<SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel>
<SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel>
</SlTabGroup>
);
```
:::tip
Additional demonstrations can be found in the [tab group examples](/components/tab-group).
:::

View File

@@ -1,30 +0,0 @@
---
meta:
title: Tab
description: Tabs are used inside tab groups to represent and activate tab panels.
layout: component
---
```html:preview
<sl-tab>Tab</sl-tab>
<sl-tab active>Active</sl-tab>
<sl-tab closable>Closable</sl-tab>
<sl-tab disabled>Disabled</sl-tab>
```
```jsx:react
import SlTab from '@shoelace-style/shoelace/dist/react/tab';
const App = () => (
<>
<SlTab>Tab</SlTab>
<SlTab active>Active</SlTab>
<SlTab closable>Closable</SlTab>
<SlTab disabled>Disabled</SlTab>
</>
);
```
:::tip
Additional demonstrations can be found in the [tab group examples](/components/tab-group).
:::

View File

@@ -1,146 +0,0 @@
---
meta:
title: Tag
description: Tags are used as labels to organize things or to indicate a selection.
layout: component
---
```html:preview
<sl-tag variant="primary">Primary</sl-tag>
<sl-tag variant="success">Success</sl-tag>
<sl-tag variant="neutral">Neutral</sl-tag>
<sl-tag variant="warning">Warning</sl-tag>
<sl-tag variant="danger">Danger</sl-tag>
```
```jsx:react
import SlTag from '@shoelace-style/shoelace/dist/react/tag';
const App = () => (
<>
<SlTag variant="primary">Primary</SlTag>
<SlTag variant="success">Success</SlTag>
<SlTag variant="neutral">Neutral</SlTag>
<SlTag variant="warning">Warning</SlTag>
<SlTag variant="danger">Danger</SlTag>
</>
);
```
## Examples
### Sizes
Use the `size` attribute to change a tab's size.
```html:preview
<sl-tag size="small">Small</sl-tag>
<sl-tag size="medium">Medium</sl-tag>
<sl-tag size="large">Large</sl-tag>
```
```jsx:react
import SlTag from '@shoelace-style/shoelace/dist/react/tag';
const App = () => (
<>
<SlTag size="small">Small</SlTag>
<SlTag size="medium">Medium</SlTag>
<SlTag size="large">Large</SlTag>
</>
);
```
### Pill
Use the `pill` attribute to give tabs rounded edges.
```html:preview
<sl-tag size="small" pill>Small</sl-tag>
<sl-tag size="medium" pill>Medium</sl-tag>
<sl-tag size="large" pill>Large</sl-tag>
```
```jsx:react
import SlTag from '@shoelace-style/shoelace/dist/react/tag';
const App = () => (
<>
<SlTag size="small" pill>
Small
</SlTag>
<SlTag size="medium" pill>
Medium
</SlTag>
<SlTag size="large" pill>
Large
</SlTag>
</>
);
```
### Removable
Use the `removable` attribute to add a remove button to the tag.
```html:preview
<div class="tags-removable">
<sl-tag size="small" removable>Small</sl-tag>
<sl-tag size="medium" removable>Medium</sl-tag>
<sl-tag size="large" removable>Large</sl-tag>
</div>
<script>
const div = document.querySelector('.tags-removable');
div.addEventListener('sl-remove', event => {
const tag = event.target;
tag.style.opacity = '0';
setTimeout(() => (tag.style.opacity = '1'), 2000);
});
</script>
<style>
.tags-removable sl-tag {
transition: var(--sl-transition-medium) opacity;
}
</style>
```
```jsx:react
import SlTag from '@shoelace-style/shoelace/dist/react/tag';
const css = `
.tags-removable sl-tag {
transition: var(--sl-transition-medium) opacity;
}
`;
const App = () => {
function handleRemove(event) {
const tag = event.target;
tag.style.opacity = '0';
setTimeout(() => (tag.style.opacity = '1'), 2000);
}
return (
<>
<div className="tags-removable">
<SlTag size="small" removable onSlRemove={handleRemove}>
Small
</SlTag>
<SlTag size="medium" removable onSlRemove={handleRemove}>
Medium
</SlTag>
<SlTag size="large" removable onSlRemove={handleRemove}>
Large
</SlTag>
</div>
<style>{css}</style>
</>
);
};
```

View File

@@ -1,160 +0,0 @@
---
meta:
title: Textarea
description: Textareas collect data from the user and allow multiple lines of text.
layout: component
---
```html:preview
<sl-textarea></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea />;
```
:::tip
This component works with standard `<form>` elements. Please refer to the section on [form controls](/getting-started/form-controls) to learn more about form submission and client-side validation.
:::
## Examples
### Labels
Use the `label` attribute to give the textarea an accessible label. For labels that contain HTML, use the `label` slot instead.
```html:preview
<sl-textarea label="Comments"></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea label="Comments" />;
```
### Help Text
Add descriptive help text to a textarea with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html:preview
<sl-textarea label="Feedback" help-text="Please tell us what you think."> </sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea label="Feedback" help-text="Please tell us what you think." />;
```
### Rows
Use the `rows` attribute to change the number of text rows that get shown.
```html:preview
<sl-textarea rows="2"></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea rows={2} />;
```
### Placeholders
Use the `placeholder` attribute to add a placeholder.
```html:preview
<sl-textarea placeholder="Type something"></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea placeholder="Type something" />;
```
### Filled Textareas
Add the `filled` attribute to draw a filled textarea.
```html:preview
<sl-textarea placeholder="Type something" filled></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea placeholder="Type something" filled />;
```
### Disabled
Use the `disabled` attribute to disable a textarea.
```html:preview
<sl-textarea placeholder="Textarea" disabled></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea placeholder="Textarea" disabled />;
```
### Sizes
Use the `size` attribute to change a textarea's size.
```html:preview
<sl-textarea placeholder="Small" size="small"></sl-textarea>
<br />
<sl-textarea placeholder="Medium" size="medium"></sl-textarea>
<br />
<sl-textarea placeholder="Large" size="large"></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => (
<>
<SlTextarea placeholder="Small" size="small"></SlTextarea>
<br />
<SlTextarea placeholder="Medium" size="medium"></SlTextarea>
<br />
<SlTextarea placeholder="Large" size="large"></SlTextarea>
</>
);
```
### Prevent Resizing
By default, textareas can be resized vertically by the user. To prevent resizing, set the `resize` attribute to `none`.
```html:preview
<sl-textarea resize="none"></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea resize="none" />;
```
### Expand with Content
Textareas will automatically resize to expand to fit their content when `resize` is set to `auto`.
```html:preview
<sl-textarea resize="auto"></sl-textarea>
```
```jsx:react
import SlTextarea from '@shoelace-style/shoelace/dist/react/textarea';
const App = () => <SlTextarea resize="auto" />;
```

View File

@@ -1,440 +0,0 @@
---
meta:
title: Tooltip
description: Tooltips display additional information based on a specific action.
layout: component
---
A tooltip's target is its _first child element_, so you should only wrap one element inside of the tooltip. If you need the tooltip to show up for multiple elements, nest them inside a container first.
Tooltips use `display: contents` so they won't interfere with how elements are positioned in a flex or grid layout.
```html:preview
<sl-tooltip content="This is a tooltip">
<sl-button>Hover Me</sl-button>
</sl-tooltip>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => (
<SlTooltip content="This is a tooltip">
<SlButton>Hover Me</SlButton>
</SlTooltip>
);
```
## Examples
### Placement
Use the `placement` attribute to set the preferred placement of the tooltip.
```html:preview
<div class="tooltip-placement-example">
<div class="tooltip-placement-example-row">
<sl-tooltip content="top-start" placement="top-start">
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="top" placement="top">
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="top-end" placement="top-end">
<sl-button></sl-button>
</sl-tooltip>
</div>
<div class="tooltip-placement-example-row">
<sl-tooltip content="left-start" placement="left-start">
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="right-start" placement="right-start">
<sl-button></sl-button>
</sl-tooltip>
</div>
<div class="tooltip-placement-example-row">
<sl-tooltip content="left" placement="left">
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="right" placement="right">
<sl-button></sl-button>
</sl-tooltip>
</div>
<div class="tooltip-placement-example-row">
<sl-tooltip content="left-end" placement="left-end">
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="right-end" placement="right-end">
<sl-button></sl-button>
</sl-tooltip>
</div>
<div class="tooltip-placement-example-row">
<sl-tooltip content="bottom-start" placement="bottom-start">
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="bottom" placement="bottom">
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="bottom-end" placement="bottom-end">
<sl-button></sl-button>
</sl-tooltip>
</div>
</div>
<style>
.tooltip-placement-example {
width: 250px;
margin: 1rem;
}
.tooltip-placement-example-row:after {
content: '';
display: table;
clear: both;
}
.tooltip-placement-example sl-button {
float: left;
width: 2.5rem;
margin-right: 0.25rem;
margin-bottom: 0.25rem;
}
.tooltip-placement-example-row:nth-child(1) sl-tooltip:first-child sl-button,
.tooltip-placement-example-row:nth-child(5) sl-tooltip:first-child sl-button {
margin-left: calc(40px + 0.25rem);
}
.tooltip-placement-example-row:nth-child(2) sl-tooltip:nth-child(2) sl-button,
.tooltip-placement-example-row:nth-child(3) sl-tooltip:nth-child(2) sl-button,
.tooltip-placement-example-row:nth-child(4) sl-tooltip:nth-child(2) sl-button {
margin-left: calc((40px * 3) + (0.25rem * 3));
}
</style>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const css = `
.tooltip-placement-example {
width: 250px;
}
.tooltip-placement-example-row:after {
content: '';
display: table;
clear: both;
}
.tooltip-placement-example sl-button {
float: left;
width: 2.5rem;
margin-right: 0.25rem;
margin-bottom: 0.25rem;
}
.tooltip-placement-example-row:nth-child(1) sl-tooltip:first-child sl-button,
.tooltip-placement-example-row:nth-child(5) sl-tooltip:first-child sl-button {
margin-left: calc(40px + 0.25rem);
}
.tooltip-placement-example-row:nth-child(2) sl-tooltip:nth-child(2) sl-button,
.tooltip-placement-example-row:nth-child(3) sl-tooltip:nth-child(2) sl-button,
.tooltip-placement-example-row:nth-child(4) sl-tooltip:nth-child(2) sl-button {
margin-left: calc((40px * 3) + (0.25rem * 3));
}
`;
const App = () => (
<>
<div className="tooltip-placement-example">
<div className="tooltip-placement-example-row">
<SlTooltip content="top-start" placement="top-start">
<SlButton />
</SlTooltip>
<SlTooltip content="top" placement="top">
<SlButton />
</SlTooltip>
<SlTooltip content="top-end" placement="top-end">
<SlButton />
</SlTooltip>
</div>
<div className="tooltip-placement-example-row">
<SlTooltip content="left-start" placement="left-start">
<SlButton />
</SlTooltip>
<SlTooltip content="right-start" placement="right-start">
<SlButton />
</SlTooltip>
</div>
<div className="tooltip-placement-example-row">
<SlTooltip content="left" placement="left">
<SlButton />
</SlTooltip>
<SlTooltip content="right" placement="right">
<SlButton />
</SlTooltip>
</div>
<div className="tooltip-placement-example-row">
<SlTooltip content="left-end" placement="left-end">
<SlButton />
</SlTooltip>
<SlTooltip content="right-end" placement="right-end">
<SlButton />
</SlTooltip>
</div>
<div className="tooltip-placement-example-row">
<SlTooltip content="bottom-start" placement="bottom-start">
<SlButton />
</SlTooltip>
<SlTooltip content="bottom" placement="bottom">
<SlButton />
</SlTooltip>
<SlTooltip content="bottom-end" placement="bottom-end">
<SlButton />
</SlTooltip>
</div>
</div>
<style>{css}</style>
</>
);
```
### Click Trigger
Set the `trigger` attribute to `click` to toggle the tooltip on click instead of hover.
```html:preview
<sl-tooltip content="Click again to dismiss" trigger="click">
<sl-button>Click to Toggle</sl-button>
</sl-tooltip>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => (
<SlTooltip content="Click again to dismiss" trigger="click">
<SlButton>Click to Toggle</SlButton>
</SlTooltip>
);
```
### Manual Trigger
Tooltips can be controller programmatically by setting the `trigger` attribute to `manual`. Use the `open` attribute to control when the tooltip is shown.
```html:preview
<sl-button style="margin-right: 4rem;">Toggle Manually</sl-button>
<sl-tooltip content="This is an avatar" trigger="manual" class="manual-tooltip">
<sl-avatar label="User"></sl-avatar>
</sl-tooltip>
<script>
const tooltip = document.querySelector('.manual-tooltip');
const toggle = tooltip.previousElementSibling;
toggle.addEventListener('click', () => (tooltip.open = !tooltip.open));
</script>
```
{% raw %}
```jsx:react
import { useState } from 'react';
import SlAvatar from '@shoelace-style/shoelace/dist/react/avatar';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlButton style={{ marginRight: '4rem' }} onClick={() => setOpen(!open)}>
Toggle Manually
</SlButton>
<SlTooltip open={open} content="This is an avatar" trigger="manual">
<SlAvatar />
</SlTooltip>
</>
);
};
```
{% endraw %}
### Removing Arrows
You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow-size` design token. To remove them, set the value to `0` as shown below.
```html:preview
<sl-tooltip content="This is a tooltip" style="--sl-tooltip-arrow-size: 0;">
<sl-button>No Arrow</sl-button>
</sl-tooltip>
```
{% raw %}
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => (
<div style={{ '--sl-tooltip-arrow-size': '0' }}>
<SlTooltip content="This is a tooltip">
<SlButton>Above</SlButton>
</SlTooltip>
<SlTooltip content="This is a tooltip" placement="bottom">
<SlButton>Below</SlButton>
</SlTooltip>
</div>
);
```
{% endraw %}
To override it globally, set it in a root block in your stylesheet after the Shoelace stylesheet is loaded.
```css
:root {
--sl-tooltip-arrow-size: 0;
}
```
### HTML in Tooltips
Use the `content` slot to create tooltips with HTML content. Tooltips are designed only for text and presentational elements. Avoid placing interactive content, such as buttons, links, and form controls, in a tooltip.
```html:preview
<sl-tooltip>
<div slot="content">I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!</div>
<sl-button>Hover me</sl-button>
</sl-tooltip>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => (
<SlTooltip>
<div slot="content">
I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!
</div>
<SlButton>Hover Me</SlButton>
</SlTooltip>
);
```
### Setting a Maximum Width
Use the `--max-width` custom property to change the width the tooltip can grow to before wrapping occurs.
```html:preview
<sl-tooltip style="--max-width: 80px;" content="This tooltip will wrap after only 80 pixels.">
<sl-button>Hover me</sl-button>
</sl-tooltip>
```
{% raw %}
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const App = () => (
<SlTooltip style={{ '--max-width': '80px' }} content="This tooltip will wrap after only 80 pixels.">
<SlButton>Hover Me</SlButton>
</SlTooltip>
);
```
{% endraw %}
### Hoisting
Tooltips will be clipped if they're inside a container that has `overflow: auto|hidden|scroll`. The `hoist` attribute forces the tooltip to use a fixed positioning strategy, allowing it to break out of the container. In this case, the tooltip will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details.
```html:preview
<div class="tooltip-hoist">
<sl-tooltip content="This is a tooltip">
<sl-button>No Hoist</sl-button>
</sl-tooltip>
<sl-tooltip content="This is a tooltip" hoist>
<sl-button>Hoist</sl-button>
</sl-tooltip>
</div>
<style>
.tooltip-hoist {
position: relative;
border: solid 2px var(--sl-panel-border-color);
overflow: hidden;
padding: var(--sl-spacing-medium);
}
</style>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlTooltip from '@shoelace-style/shoelace/dist/react/tooltip';
const css = `
.tooltip-hoist {
border: solid 2px var(--sl-panel-border-color);
overflow: hidden;
padding: var(--sl-spacing-medium);
position: relative;
}
`;
const App = () => (
<>
<div class="tooltip-hoist">
<SlTooltip content="This is a tooltip">
<SlButton>No Hoist</SlButton>
</SlTooltip>
<SlTooltip content="This is a tooltip" hoist>
<SlButton>Hoist</SlButton>
</SlTooltip>
</div>
<style>{css}</style>
</>
);
```

View File

@@ -1,168 +0,0 @@
---
meta:
title: Tree Item
description: A tree item serves as a hierarchical node that lives inside a tree.
layout: component
---
```html:preview
<sl-tree>
<sl-tree-item>
Item 1
<sl-tree-item>Item A</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => (
<SlTree>
<SlTreeItem>
Item 1
<SlTreeItem>Item A</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```
## Examples
### Nested tree items
A tree item can contain other tree items. This allows the node to be expanded or collapsed by the user.
```html:preview
<sl-tree>
<sl-tree-item>
Item 1
<sl-tree-item>
Item A
<sl-tree-item>Item Z</sl-tree-item>
<sl-tree-item>Item Y</sl-tree-item>
<sl-tree-item>Item X</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => (
<SlTree>
<SlTreeItem>
Item 1
<SlTreeItem>
Item A
<SlTreeItem>Item Z</SlTreeItem>
<SlTreeItem>Item Y</SlTreeItem>
<SlTreeItem>Item X</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```
### Selected
Use the `selected` attribute to select a tree item initially.
```html:preview
<sl-tree>
<sl-tree-item selected>
Item 1
<sl-tree-item>Item A</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => (
<SlTree>
<SlTreeItem selected>
Item 1
<SlTreeItem>Item A</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```
### Expanded
Use the `expanded` attribute to expand a tree item initially.
```html:preview
<sl-tree>
<sl-tree-item expanded>
Item 1
<sl-tree-item expanded>
Item A
<sl-tree-item>Item Z</sl-tree-item>
<sl-tree-item>Item Y</sl-tree-item>
<sl-tree-item>Item X</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => (
<SlTree>
<SlTreeItem expanded>
Item 1
<SlTreeItem expanded>
Item A
<SlTreeItem>Item Z</SlTreeItem>
<SlTreeItem>Item Y</SlTreeItem>
<SlTreeItem>Item X</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
);
```

View File

@@ -1,476 +0,0 @@
---
meta:
title: Tree
description: Trees allow you to display a hierarchical list of selectable tree items. Items with children can be expanded and collapsed as desired by the user.
layout: component
---
```html:preview
<sl-tree>
<sl-tree-item>
Deciduous
<sl-tree-item>Birch</sl-tree-item>
<sl-tree-item>
Maple
<sl-tree-item>Field maple</sl-tree-item>
<sl-tree-item>Red maple</sl-tree-item>
<sl-tree-item>Sugar maple</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Oak</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Coniferous
<sl-tree-item>Cedar</sl-tree-item>
<sl-tree-item>Pine</sl-tree-item>
<sl-tree-item>Spruce</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Non-trees
<sl-tree-item>Bamboo</sl-tree-item>
<sl-tree-item>Cactus</sl-tree-item>
<sl-tree-item>Fern</sl-tree-item>
</sl-tree-item>
</sl-tree>
```
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => (
<SlTree>
<SlTreeItem>
Deciduous
<SlTreeItem>Birch</SlTreeItem>
<SlTreeItem>
Maple
<SlTreeItem>Field maple</SlTreeItem>
<SlTreeItem>Red maple</SlTreeItem>
<SlTreeItem>Sugar maple</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Oak</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Coniferous
<SlTreeItem>Cedar</SlTreeItem>
<SlTreeItem>Pine</SlTreeItem>
<SlTreeItem>Spruce</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Non-trees
<SlTreeItem>Bamboo</SlTreeItem>
<SlTreeItem>Cactus</SlTreeItem>
<SlTreeItem>Fern</SlTreeItem>
</SlTreeItem>
</SlTree>
);
```
## Examples
### Selection Modes
The `selection` attribute lets you change the selection behavior of the tree.
- Use `single` to allow the selection of a single item (default).
- Use `multiple` to allow the selection of multiple items.
- Use `leaf` to only allow leaf nodes to be selected.
```html:preview
<sl-select id="selection-mode" value="single" label="Selection">
<sl-option value="single">Single</sl-option>
<sl-option value="multiple">Multiple</sl-option>
<sl-option value="leaf">Leaf</sl-option>
</sl-select>
<br />
<sl-tree class="tree-selectable">
<sl-tree-item>
Item 1
<sl-tree-item>
Item A
<sl-tree-item>Item Z</sl-tree-item>
<sl-tree-item>Item Y</sl-tree-item>
<sl-tree-item>Item X</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item B</sl-tree-item>
<sl-tree-item>Item C</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Item 2</sl-tree-item>
<sl-tree-item>Item 3</sl-tree-item>
</sl-tree>
<script>
const selectionMode = document.querySelector('#selection-mode');
const tree = document.querySelector('.tree-selectable');
selectionMode.addEventListener('sl-change', () => {
tree.querySelectorAll('sl-tree-item').forEach(item => (item.selected = false));
tree.selection = selectionMode.value;
});
</script>
```
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => {
const [selection, setSelection] = useState('single');
return (
<>
<SlSelect label="Selection" value={selection} onSlChange={event => setSelection(event.target.value)}>
<SlMenuItem value="single">single</SlMenuItem>
<SlMenuItem value="multiple">multiple</SlMenuItem>
<SlMenuItem value="leaf">leaf</SlMenuItem>
</SlSelect>
<br />
<SlTree selection={selection}>
<SlTreeItem>
Item 1
<SlTreeItem>
Item A
<SlTreeItem>Item Z</SlTreeItem>
<SlTreeItem>Item Y</SlTreeItem>
<SlTreeItem>Item X</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item B</SlTreeItem>
<SlTreeItem>Item C</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Item 2</SlTreeItem>
<SlTreeItem>Item 3</SlTreeItem>
</SlTree>
</>
);
};
```
### Showing Indent Guides
Indent guides can be drawn by setting `--indent-guide-width`. You can also change the color, offset, and style, using `--indent-guide-color`, `--indent-guide-style`, and `--indent-guide-offset`, respectively.
```html:preview
<sl-tree class="tree-with-lines">
<sl-tree-item expanded>
Deciduous
<sl-tree-item>Birch</sl-tree-item>
<sl-tree-item expanded>
Maple
<sl-tree-item>Field maple</sl-tree-item>
<sl-tree-item>Red maple</sl-tree-item>
<sl-tree-item>Sugar maple</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Oak</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Coniferous
<sl-tree-item>Cedar</sl-tree-item>
<sl-tree-item>Pine</sl-tree-item>
<sl-tree-item>Spruce</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Non-trees
<sl-tree-item>Bamboo</sl-tree-item>
<sl-tree-item>Cactus</sl-tree-item>
<sl-tree-item>Fern</sl-tree-item>
</sl-tree-item>
</sl-tree>
<style>
.tree-with-lines {
--indent-guide-width: 1px;
}
</style>
```
{% raw %}
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => (
<SlTree class="tree-with-lines" style={{ '--indent-guide-width': '1px' }}>
<SlTreeItem expanded>
Deciduous
<SlTreeItem>Birch</SlTreeItem>
<SlTreeItem expanded>
Maple
<SlTreeItem>Field maple</SlTreeItem>
<SlTreeItem>Red maple</SlTreeItem>
<SlTreeItem>Sugar maple</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Oak</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Coniferous
<SlTreeItem>Cedar</SlTreeItem>
<SlTreeItem>Pine</SlTreeItem>
<SlTreeItem>Spruce</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Non-trees
<SlTreeItem>Bamboo</SlTreeItem>
<SlTreeItem>Cactus</SlTreeItem>
<SlTreeItem>Fern</SlTreeItem>
</SlTreeItem>
</SlTree>
);
```
{% endraw %}
### Lazy Loading
Use the `lazy` attribute on a tree item to indicate that the content is not yet present and will be loaded later. When the user tries to expand the node, the `loading` state is set to `true` and the `sl-lazy-load` event will be emitted to allow you to load data asynchronously. The item will remain in a loading state until its content is changed.
If you want to disable this behavior after the first load, simply remove the `lazy` attribute and, on the next expand, the existing content will be shown instead.
```html:preview
<sl-tree>
<sl-tree-item lazy>Available Trees</sl-tree-item>
</sl-tree>
<script type="module">
const lazyItem = document.querySelector('sl-tree-item[lazy]');
lazyItem.addEventListener('sl-lazy-load', () => {
// Simulate asynchronous loading
setTimeout(() => {
const subItems = ['Birch', 'Cedar', 'Maple', 'Pine'];
for (const item of subItems) {
const treeItem = document.createElement('sl-tree-item');
treeItem.innerText = item;
lazyItem.append(treeItem);
}
// Disable lazy mode once the content has been loaded
lazyItem.lazy = false;
}, 1000);
});
</script>
```
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => {
const [childItems, setChildItems] = useState([]);
const [lazy, setLazy] = useState(true);
const handleLazyLoad = () => {
// Simulate asynchronous loading
setTimeout(() => {
setChildItems(['Birch', 'Cedar', 'Maple', 'Pine']);
// Disable lazy mode once the content has been loaded
setLazy(false);
}, 1000);
};
return (
<SlTree>
<SlTreeItem lazy={lazy} onSlLazyLoad={handleLazyLoad}>
Available Trees
{childItems.map(item => (
<SlTreeItem>{item}</SlTreeItem>
))}
</SlTreeItem>
</SlTree>
);
};
```
### Customizing the Expand and Collapse Icons
Use the `expand-icon` and `collapse-icon` slots to change the expand and collapse icons, respectively. To disable the animation, override the `rotate` property on the `expand-button` part as shown below.
```html:preview
<sl-tree class="custom-icons">
<sl-icon name="plus-square" slot="expand-icon"></sl-icon>
<sl-icon name="dash-square" slot="collapse-icon"></sl-icon>
<sl-tree-item>
Deciduous
<sl-tree-item>Birch</sl-tree-item>
<sl-tree-item>
Maple
<sl-tree-item>Field maple</sl-tree-item>
<sl-tree-item>Red maple</sl-tree-item>
<sl-tree-item>Sugar maple</sl-tree-item>
</sl-tree-item>
<sl-tree-item>Oak</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Coniferous
<sl-tree-item>Cedar</sl-tree-item>
<sl-tree-item>Pine</sl-tree-item>
<sl-tree-item>Spruce</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Non-trees
<sl-tree-item>Bamboo</sl-tree-item>
<sl-tree-item>Cactus</sl-tree-item>
<sl-tree-item>Fern</sl-tree-item>
</sl-tree-item>
</sl-tree>
<style>
.custom-icons sl-tree-item::part(expand-button) {
/* Disable the expand/collapse animation */
rotate: none;
}
</style>
```
<!-- prettier-ignore -->
```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => (
<SlTree>
<SlIcon name="plus-square" slot="expand-icon"></SlIcon>
<SlIcon name="dash-square" slot="collapse-icon"></SlIcon>
<SlTreeItem>
Deciduous
<SlTreeItem>Birch</SlTreeItem>
<SlTreeItem>
Maple
<SlTreeItem>Field maple</SlTreeItem>
<SlTreeItem>Red maple</SlTreeItem>
<SlTreeItem>Sugar maple</SlTreeItem>
</SlTreeItem>
<SlTreeItem>Oak</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Coniferous
<SlTreeItem>Cedar</SlTreeItem>
<SlTreeItem>Pine</SlTreeItem>
<SlTreeItem>Spruce</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Non-trees
<SlTreeItem>Bamboo</SlTreeItem>
<SlTreeItem>Cactus</SlTreeItem>
<SlTreeItem>Fern</SlTreeItem>
</SlTreeItem>
</SlTree>
);
```
### With Icons
Decorative icons can be used before labels to provide hints for each node.
```html:preview
<sl-tree class="tree-with-icons">
<sl-tree-item expanded>
<sl-icon name="folder"></sl-icon>
Documents
<sl-tree-item>
<sl-icon name="folder"> </sl-icon>
Photos
<sl-tree-item>
<sl-icon name="image"></sl-icon>
birds.jpg
</sl-tree-item>
<sl-tree-item>
<sl-icon name="image"></sl-icon>
kitten.jpg
</sl-tree-item>
<sl-tree-item>
<sl-icon name="image"></sl-icon>
puppy.jpg
</sl-tree-item>
</sl-tree-item>
<sl-tree-item>
<sl-icon name="folder"></sl-icon>
Writing
<sl-tree-item>
<sl-icon name="file"></sl-icon>
draft.txt
</sl-tree-item>
<sl-tree-item>
<sl-icon name="file-pdf"></sl-icon>
final.pdf
</sl-tree-item>
<sl-tree-item>
<sl-icon name="file-bar-graph"></sl-icon>
sales.xls
</sl-tree-item>
</sl-tree-item>
</sl-tree-item>
</sl-tree>
```
```jsx:react
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';
const App = () => {
return (
<SlTree class="tree-with-icons">
<SlTreeItem expanded>
<SlIcon name="folder" />
Root
<SlTreeItem>
<SlIcon name="folder" />
Folder 1<SlTreeItem>
<SlIcon name="files" />
File 1 - 1
</SlTreeItem>
<SlTreeItem disabled>
<SlIcon name="files" />
File 1 - 2
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
File 1 - 3
</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
Folder 2<SlTreeItem>
<SlIcon name="files" />
File 2 - 1
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
File 2 - 2
</SlTreeItem>
</SlTreeItem>
<SlTreeItem>
<SlIcon name="files" />
File 1
</SlTreeItem>
</SlTreeItem>
</SlTree>
);
};
```

View File

@@ -1,88 +0,0 @@
---
meta:
title: Angular
description: Tips for using Shoelace in your Angular app.
---
# Angular
Angular [plays nice](https://custom-elements-everywhere.com/#angular) with custom elements, so you can use Shoelace in your Angular apps with ease.
## Installation
To add Shoelace to your Angular app, install the package from npm.
```bash
npm install @shoelace-style/shoelace
```
Next, [include a theme](/getting-started/themes) and set the [base path](/getting-started/installation#setting-the-base-path) for icons and other assets. In this example, we'll import the light theme and use the CDN as a base path.
```jsx
import '@shoelace-style/shoelace/%NPMDIR%/themes/light.css';
import { setBasePath } from '@shoelace-style/shoelace/%NPMDIR%/utilities/base-path';
setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/%CDNDIR%/');
```
:::tip
If you'd rather not use the CDN for assets, you can create a build task that copies `node_modules/@shoelace-style/shoelace/%NPMDIR%/assets` into a public folder in your app. Then you can point the base path to that folder instead.
:::
## Configuration
Then make sure to apply the custom elements schema as shown below.
```js
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}
```
## Reference Shoelace components in your Angular component code
```js
import { SlDrawer } from '@shoelace-style/shoelace';
@Component({
selector: 'app-drawer-example',
template: '<div id="page"><button (click)="showDrawer()">Show drawer</button><sl-drawer #drawer label="Drawer" class="drawer-focus" style="--size: 50vw"><p>Drawer content</p></sl-drawer></div>'
})
export class DrawerExampleComponent implements OnInit {
// use @ViewChild to get a reference to the #drawer element within component template
@ViewChild('drawer')
drawer?: ElementRef<SlDrawer>;
...
constructor(...) {
}
ngOnInit() {
}
...
showDrawer() {
// use nativeElement to access Shoelace components
this.drawer?.nativeElement.show();
}
}
```
Now you can start using Shoelace components in your app!
:::tip
Are you using Shoelace with Angular? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/angular.md)
:::

View File

@@ -1,127 +0,0 @@
---
meta:
title: Vue
description: Tips for using Shoelace in your Vue 3 app.
---
# Vue
Vue [plays nice](https://custom-elements-everywhere.com/#vue) with custom elements, so you can use Shoelace in your Vue apps with ease.
:::tip
These instructions are for Vue 3 and above. If you're using Vue 2, please see the [Vue 2 instructions](/frameworks/vue-2).
:::
## Installation
To add Shoelace to your Vue app, install the package from npm.
```bash
npm install @shoelace-style/shoelace
```
Next, [include a theme](/getting-started/themes) and set the [base path](/getting-started/installation#setting-the-base-path) for icons and other assets. In this example, we'll import the light theme and use the CDN as a base path.
```jsx
import '@shoelace-style/shoelace/dist/themes/light.css';
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/%CDNDIR%/');
```
:::tip
If you'd rather not use the CDN for assets, you can create a build task that copies `node_modules/@shoelace-style/shoelace/dist/assets` into a public folder in your app. Then you can point the base path to that folder instead.
:::
## Configuration
You'll need to tell Vue to ignore Shoelace components. This is pretty easy because they all start with `sl-`.
```js
import { fileURLToPath, URL } from 'url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: tag => tag.startsWith('sl-')
}
}
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
});
```
Now you can start using Shoelace components in your app!
## Usage
### QR code generator example
```html
<template>
<div class="container">
<h1>QR code generator</h1>
<sl-input maxlength="255" clearable label="Value" v-model="qrCode"></sl-input>
<sl-qr-code :value="qrCode"></sl-qr-code>
</div>
</template>
<script setup>
import { ref } from 'vue';
import '@shoelace-style/shoelace/dist/components/qr-code/qr-code.js';
import '@shoelace-style/shoelace/dist/components/input/input.js';
const qrCode = ref();
</script>
<style>
.container {
max-width: 400px;
margin: 0 auto;
}
sl-input {
margin: var(--sl-spacing-large) 0;
}
</style>
```
### Binding Complex Data
When binding complex data such as objects and arrays, use the `.prop` modifier to make Vue bind them as a property instead of an attribute.
```html
<sl-color-picker :swatches.prop="mySwatches" />
```
:::tip
Are you using Shoelace with Vue? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue.md)
:::
### Slots
To use Shoelace components with slots, follow the Vue documentation on using [slots with custom elements](https://vuejs.org/guide/extras/web-components.html#building-custom-elements-with-vue).
Here is an example:
```html
<sl-drawer label="Drawer" placement="start" class="drawer-placement-start" :open="drawerIsOpen">
This drawer slides in from the start.
<div slot="footer">
<sl-button variant="primary" @click=" drawerIsOpen = false">Close</sl-button>
</div>
</sl-drawer>
```

View File

@@ -1,59 +0,0 @@
---
meta:
title: Community
description: Shoelace has a growing community of designers and developers that are building amazing things with web components.
---
# Community
Shoelace has a growing community of designers and developers that are building amazing things with web components. We'd love for you to become a part of it!
Please be respectful of other users and remember that Shoelace is an open source project. We'll try to help when we can, but there's no guarantee we'll be able solve your problem. Please manage your expectations and don't forget to contribute back to the conversation when you can!
## Discussion Forum
The [discussion forum](https://github.com/shoelace-style/shoelace/discussions) is open to anyone with a GitHub account. This is the best place to:
- Ask for help
- Share ideas and get feedback
- Show the community what you're working on
- Learn more about the project, its values, and its roadmap
<sl-button variant="primary" href="https://github.com/shoelace-style/shoelace/discussions" target="_blank">
<sl-icon name="github" slot="prefix"></sl-icon>
Join the Discussion
</sl-button>
## Community Chat
The [community chat](https://discord.gg/mg8f26C) is open to the public and powered by [Discord](https://discord.com/). This is a good place to:
- Ask for help
- Share ideas and get feedback
- Show the community what you're working on
- Chat live with other designers, developers, and Shoelace fans
<sl-button variant="primary" href="https://discord.gg/mg8f26C" target="_blank">
<sl-icon name="discord" slot="prefix"></sl-icon>
Join the Chat
</sl-button>
## Stack Overflow
You can post questions on Stack Overflow using [the "shoelace" tag](https://stackoverflow.com/questions/tagged/shoelace). This is a public forum where talented developers answer questions. It's a great way to get help, but it is not maintained by the Shoelace author.
<sl-button variant="primary" href="https://stackoverflow.com/questions/ask?tags=shoelace" target="_blank">
<sl-icon name="stack-overflow" slot="prefix"></sl-icon>
Ask for Help
</sl-button>
## Twitter
Follow [@shoelace_style](https://twitter.com/shoelace_style) on Twitter for general updates and announcements about Shoelace. This is a great place to say "hi" or to share something you're working on. You're also welcome to follow [@claviska](https://twitter.com/claviska), the creator, for tweets about web components, web development, and life.
**Please avoid using Twitter for support questions.** The [discussion forum](https://github.com/shoelace-style/shoelace/discussions) is a much better place to share code snippets, screenshots, and other troubleshooting info. You'll have much better luck there, as more users will have a chance to help you.
<sl-button variant="primary" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon name="twitter" slot="prefix"></sl-icon>
Follow on Twitter
</sl-button>

View File

@@ -1,18 +0,0 @@
---
meta:
title: Border Radius
description: Border radius tokens are used to give sharp edges a more subtle, rounded effect.
---
# Border Radius Tokens
Border radius tokens are used to give sharp edges a more subtle, rounded effect. They use rem units so they scale with the base font size. The pixel values displayed are based on a 16px font size.
| Token | Value | Example |
| ---------------------------- | --------------- | -------------------------------------------------------------------------------------------------------- |
| `--sl-border-radius-small` | 0.1875rem (3px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-small);"></div> |
| `--sl-border-radius-medium` | 0.25rem (4px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-medium);"></div> |
| `--sl-border-radius-large` | 0.5rem (8px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-large);"></div> |
| `--sl-border-radius-x-large` | 1rem (16px) | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-x-large);"></div> |
| `--sl-border-radius-circle` | 50% | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-circle);"></div> |
| `--sl-border-radius-pill` | 9999px | <div class="border-radius-demo" style="border-radius: var(--sl-border-radius-pill); width: 6rem;"></div> |

View File

@@ -1,446 +0,0 @@
---
meta:
title: Color Tokens
description: Color tokens help maintain consistent use of color throughout your app.
---
# Color Tokens
Color tokens help maintain consistent use of color throughout your app. Shoelace provides palettes for theme colors and primitives that you can use as a foundation for your design system.
Color tokens are referenced using the `--sl-color-{name}-{n}` CSS custom property, where `{name}` is the name of the palette and `{n}` is the numeric value of the desired swatch.
## Theme Tokens
Theme tokens give you a semantic way to reference colors in your app. The primary palette is typically based on a brand color, whereas success, neutral, warning, and danger are used to visualize actions that correspond to their respective meanings.
<div class="color-palette">
<div class="color-palette__name">
Primary<br>
<code>--sl-color-primary-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-primary-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Success<br>
<code>--sl-color-success-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-success-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Warning<br>
<code>--sl-color-warning-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-warning-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Danger<br>
<code>--sl-color-danger-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-danger-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Neutral<br>
<code>--sl-color-neutral-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-neutral-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Black & White<br>
<code>--sl-color-neutral-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch color-palette__swatch--border" style="background-color: var(--sl-color-neutral-0);"></div>0</div>
<div class="color-palette__example"><div class="color-palette__swatch " style="background-color: var(--sl-color-neutral-1000);"></div>1000</div>
</div>
:::tip
Looking for an easy way to customize your theme? [Try the color token generator!](https://codepen.io/claviska/full/QWveRgL)
:::
## Primitives
Additional palettes are provided in the form of color primitives. Use these when you need arbitrary colors that don't have semantic meaning. Color primitives are derived from the fantastic [Tailwind color palette](https://tailwindcss.com/docs/customizing-colors).
<div class="color-palette">
<div class="color-palette__name">
Gray<br>
<code>--sl-color-gray-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-gray-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Red<br>
<code>--sl-color-red-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-red-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Orange<br>
<code>--sl-color-orange-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-orange-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Amber<br>
<code>--sl-color-amber-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-amber-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Yellow<br>
<code>--sl-color-yellow-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-yellow-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Lime<br>
<code>--sl-color-lime-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-lime-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Green<br>
<code>--sl-color-green-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-green-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Emerald<br>
<code>--sl-color-emerald-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-emerald-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Teal<br>
<code>--sl-color-teal-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-teal-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Cyan<br>
<code>--sl-color-cyan-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-cyan-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Sky<br>
<code>--sl-color-sky-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-sky-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Blue<br>
<code>--sl-color-blue-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-blue-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Indigo<br>
<code>--sl-color-indigo-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-indigo-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Violet<br>
<code>--sl-color-violet-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-violet-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Purple<br>
<code>--sl-color-purple-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-purple-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Fuchsia<br>
<code>--sl-color-fuchsia-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-fuchsia-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Pink<br>
<code>--sl-color-pink-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-pink-950);"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Rose<br>
<code>--sl-color-rose-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-50);"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-100);"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-200);"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-300);"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-400);"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-500);"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-600);"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-700);"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-800);"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-900);"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: var(--sl-color-rose-950);"></div>950</div>
</div>

View File

@@ -1,17 +0,0 @@
---
meta:
title: Elevation
description: Elevation tokens are used to give elements the appearance of being raised off the page.
---
# Elevation Tokens
Elevation tokens are used to give elements the appearance of being raised off the page. Use them with the `box-shadow` property. These are especially useful for menus, popovers, and dialogs.
| Token | Example |
| --------------------- | -------------------------------------------------------------------------------- |
| `--sl-shadow-x-small` | <div class="elevation-demo" style="box-shadow: var(--sl-shadow-x-small);"></div> |
| `--sl-shadow-small` | <div class="elevation-demo" style="box-shadow: var(--sl-shadow-small);"></div> |
| `--sl-shadow-medium` | <div class="elevation-demo" style="box-shadow: var(--sl-shadow-medium);"></div> |
| `--sl-shadow-large` | <div class="elevation-demo" style="box-shadow: var(--sl-shadow-large);"></div> |
| `--sl-shadow-x-large` | <div class="elevation-demo" style="box-shadow: var(--sl-shadow-x-large);"></div> |

View File

@@ -1,163 +0,0 @@
---
meta:
title: More Design Tokens
description: Additional design tokens can be found here.
---
# More Design Tokens
All of the design tokens described herein are considered relatively stable. However, some changes might occur in future versions to address mission critical bugs or improvements. If such changes occur, they _will not_ be considered breaking changes and will be clearly documented in the [changelog](/resources/changelog).
Most design tokens are consistent across the light and dark theme. Those that vary will show both values.
:::tip
Currently, the source of design tokens is considered to be [`light.css`](https://github.com/shoelace-style/shoelace/blob/next/src/themes/light.css). The dark theme, [dark.css](https://github.com/shoelace-style/shoelace/blob/next/src/themes/dark.css), mirrors all of the same tokens with dark mode-specific values where appropriate. Work is planned to move all design tokens to a single file, perhaps JSON or YAML, in the near future.
:::
## Focus Rings
Focus ring tokens control the appearance of focus rings. Note that form inputs use `--sl-input-focus-ring-*` tokens instead.
| Token | Value |
| ------------------------ | ----------------------------------------------------------------------------------------- |
| `--sl-focus-ring-color` | `var(--sl-color-primary-600)` (light theme)<br>`var(--sl-color-primary-700)` (dark theme) |
| `--sl-focus-ring-style` | `solid` |
| `--sl-focus-ring-width` | `3px` |
| `--sl-focus-ring` | `var(--sl-focus-ring-style) var(--sl-focus-ring-width) var(--sl-focus-ring-color)` |
| `--sl-focus-ring-offset` | `1px` |
## Buttons
Button tokens control the appearance of buttons. In addition, buttons also currently use some form input tokens such as `--sl-input-height-*` and `--sl-input-border-*`. More button tokens may be added in the future to make it easier to style them more independently.
| Token | Value |
| ------------------------------ | ----------------------------- |
| `--sl-button-font-size-small` | `var(--sl-font-size-x-small)` |
| `--sl-button-font-size-medium` | `var(--sl-font-size-small)` |
| `--sl-button-font-size-large` | `var(--sl-font-size-medium)` |
## Form Inputs
Form input tokens control the appearance of form controls such as [input](/components/input), [select](/components/select), [textarea](/components/textarea), etc.
| Token | Value |
| --------------------------------------- | ---------------------------------- |
| `--sl-input-height-small` | `1.875rem` (30px @ 16px base) |
| `--sl-input-height-medium` | `2.5rem` (40px @ 16px base) |
| `--sl-input-height-large` | `3.125rem` (50px @ 16px base) |
| `--sl-input-background-color` | `var(--sl-color-neutral-0)` |
| `--sl-input-background-color-hover` | `var(--sl-input-background-color)` |
| `--sl-input-background-color-focus` | `var(--sl-input-background-color)` |
| `--sl-input-background-color-disabled` | `var(--sl-color-neutral-100)` |
| `--sl-input-border-color` | `var(--sl-color-neutral-300)` |
| `--sl-input-border-color-hover` | `var(--sl-color-neutral-400)` |
| `--sl-input-border-color-focus` | `var(--sl-color-primary-500)` |
| `--sl-input-border-color-disabled` | `var(--sl-color-neutral-300)` |
| `--sl-input-border-width` | `1px` |
| `--sl-input-required-content` | `*` |
| `--sl-input-required-content-offset` | `-2px` |
| `--sl-input-required-content-color` | `var(--sl-input-label-color)` |
| `--sl-input-border-radius-small` | `var(--sl-border-radius-medium)` |
| `--sl-input-border-radius-medium` | `var(--sl-border-radius-medium)` |
| `--sl-input-border-radius-large` | `var(--sl-border-radius-medium)` |
| `--sl-input-font-family` | `var(--sl-font-sans)` |
| `--sl-input-font-weight` | `var(--sl-font-weight-normal)` |
| `--sl-input-font-size-small` | `var(--sl-font-size-small)` |
| `--sl-input-font-size-medium` | `var(--sl-font-size-medium)` |
| `--sl-input-font-size-large` | `var(--sl-font-size-large)` |
| `--sl-input-letter-spacing` | `var(--sl-letter-spacing-normal)` |
| `--sl-input-color` | `var(--sl-color-neutral-700)` |
| `--sl-input-color-hover` | `var(--sl-color-neutral-700)` |
| `--sl-input-color-focus` | `var(--sl-color-neutral-700)` |
| `--sl-input-color-disabled` | `var(--sl-color-neutral-900)` |
| `--sl-input-icon-color` | `var(--sl-color-neutral-500)` |
| `--sl-input-icon-color-hover` | `var(--sl-color-neutral-600)` |
| `--sl-input-icon-color-focus` | `var(--sl-color-neutral-600)` |
| `--sl-input-placeholder-color` | `var(--sl-color-neutral-500)` |
| `--sl-input-placeholder-color-disabled` | `var(--sl-color-neutral-600)` |
| `--sl-input-spacing-small` | `var(--sl-spacing-small)` |
| `--sl-input-spacing-medium` | `var(--sl-spacing-medium)` |
| `--sl-input-spacing-large` | `var(--sl-spacing-large)` |
| `--sl-input-focus-ring-color` | `hsl(198.6 88.7% 48.4% / 40%)` |
| `--sl-input-focus-ring-offset` | `0` |
## Filled Form Inputs
Filled form input tokens control the appearance of form controls using the `filled` variant.
| Token | Value |
| --------------------------------------------- | ----------------------------- |
| `--sl-input-filled-background-color` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-background-color-hover` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-background-color-focus` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-background-color-disabled` | `var(--sl-color-neutral-100)` |
| `--sl-input-filled-color` | `var(--sl-color-neutral-800)` |
| `--sl-input-filled-color-hover` | `var(--sl-color-neutral-800)` |
| `--sl-input-filled-color-focus` | `var(--sl-color-neutral-700)` |
| `--sl-input-filled-color-disabled` | `var(--sl-color-neutral-800)` |
## Form Labels
Form label tokens control the appearance of labels in form controls.
| Token | Value |
| ----------------------------------- | ---------------------------- |
| `--sl-input-label-font-size-small` | `var(--sl-font-size-small)` |
| `--sl-input-label-font-size-medium` | `var(--sl-font-size-medium`) |
| `--sl-input-label-font-size-large` | `var(--sl-font-size-large)` |
| `--sl-input-label-color` | `inherit` |
## Help Text
Help text tokens control the appearance of help text in form controls.
| Token | Value |
| --------------------------------------- | ----------------------------- |
| `--sl-input-help-text-font-size-small` | `var(--sl-font-size-x-small)` |
| `--sl-input-help-text-font-size-medium` | `var(--sl-font-size-small)` |
| `--sl-input-help-text-font-size-large` | `var(--sl-font-size-medium)` |
| `--sl-input-help-text-color` | `var(--sl-color-neutral-500)` |
## Toggles
Toggle tokens control the appearance of toggles such as [checkbox](/components/checkbox), [radio](/components/radio), [switch](/components/switch), etc.
| Token | Value |
| ------------------------- | ----------------------------- |
| `--sl-toggle-size-small` | `0.875rem` (14px @ 16px base) |
| `--sl-toggle-size-medium` | `1.125rem` (18px @ 16px base) |
| `--sl-toggle-size-large` | `1.375rem` (22px @ 16px base) |
## Overlays
Overlay tokens control the appearance of overlays as used in [dialog](/components/dialog), [drawer](/components/drawer), etc.
| Token | Value |
| ------------------------------- | --------------------------- |
| `--sl-overlay-background-color` | `hsl(240 3.8% 46.1% / 33%)` |
## Panels
Panel tokens control the appearance of panels such as those used in [dialog](/components/dialog), [drawer](/components/drawer), [menu](/components/menu), etc.
| Token | Value |
| ----------------------------- | ----------------------------- |
| `--sl-panel-background-color` | `var(--sl-color-neutral-0)` |
| `--sl-panel-border-color` | `var(--sl-color-neutral-200)` |
| `--sl-panel-border-width` | `1px` |
## Tooltips
Tooltip tokens control the appearance of tooltips. This includes the [tooltip](/components/tooltip) component as well as other implementations, such [range tooltips](/components/range).
| Token | Value |
| ------------------------------- | ------------------------------------------------------ |
| `--sl-tooltip-border-radius` | `var(--sl-border-radius-medium)` |
| `--sl-tooltip-background-color` | `var(--sl-color-neutral-800)` |
| `--sl-tooltip-color` | `var(--sl-color-neutral-0)` |
| `--sl-tooltip-font-family` | `var(--sl-font-sans)` |
| `--sl-tooltip-font-weight` | `var(--sl-font-weight-normal)` |
| `--sl-tooltip-font-size` | `var(--sl-font-size-small)` |
| `--sl-tooltip-line-height` | `var(--sl-line-height-dense)` |
| `--sl-tooltip-padding` | `var(--sl-spacing-2x-small) var(--sl-spacing-x-small)` |
| `--sl-tooltip-arrow-size` | `6px` |

View File

@@ -1,22 +0,0 @@
---
meta:
title: Spacing Tokens
description: Spacing tokens are used to provide consistent spacing between content in your app.
---
# Spacing Tokens
Spacing tokens are used to provide consistent spacing between content in your app.
| Token | Value | Example |
| ----------------------- | -------------- | --------------------------------------------------------------------------------------------------------------- |
| `--sl-spacing-3x-small` | 0.125rem (2px) | <div class="spacing-demo" style="width: var(--sl-spacing-3x-small); height: var(--sl-spacing-3x-small);"></div> |
| `--sl-spacing-2x-small` | 0.25rem (4px) | <div class="spacing-demo" style="width: var(--sl-spacing-2x-small); height: var(--sl-spacing-2x-small);"></div> |
| `--sl-spacing-x-small` | 0.5rem (8px) | <div class="spacing-demo" style="width: var(--sl-spacing-x-small); height: var(--sl-spacing-x-small);"></div> |
| `--sl-spacing-small` | 0.75rem (12px) | <div class="spacing-demo" style="width: var(--sl-spacing-small); height: var(--sl-spacing-small);"></div> |
| `--sl-spacing-medium` | 1rem (16px) | <div class="spacing-demo" style="width: var(--sl-spacing-medium); height: var(--sl-spacing-medium);"></div> |
| `--sl-spacing-large` | 1.25rem (20px) | <div class="spacing-demo" style="width: var(--sl-spacing-large); height: var(--sl-spacing-large);"></div> |
| `--sl-spacing-x-large` | 1.75rem (28px) | <div class="spacing-demo" style="width: var(--sl-spacing-x-large); height: var(--sl-spacing-x-large);"></div> |
| `--sl-spacing-2x-large` | 2.25rem (36px) | <div class="spacing-demo" style="width: var(--sl-spacing-2x-large); height: var(--sl-spacing-2x-large);"></div> |
| `--sl-spacing-3x-large` | 3rem (48px) | <div class="spacing-demo" style="width: var(--sl-spacing-3x-large); height: var(--sl-spacing-3x-large);"></div> |
| `--sl-spacing-4x-large` | 4.5rem (72px) | <div class="spacing-demo" style="width: var(--sl-spacing-4x-large); height: var(--sl-spacing-4x-large);"></div> |

View File

@@ -1,17 +0,0 @@
---
meta:
title: Transition Tokens
description: Transition tokens are used to provide consistent transitions throughout your app.
---
# Transition Tokens
Transition tokens are used to provide consistent transitions throughout your app.
| Token | Value | Example |
| ------------------------ | ------ | --------------------------------------------------------------------------------------------- |
| `--sl-transition-x-slow` | 1000ms | <div class="transition-demo" style="transition-duration: var(--sl-transition-x-slow);"></div> |
| `--sl-transition-slow` | 500ms | <div class="transition-demo" style="transition-duration: var(--sl-transition-slow);"></div> |
| `--sl-transition-medium` | 250ms | <div class="transition-demo" style="transition-duration: var(--sl-transition-medium);"></div> |
| `--sl-transition-fast` | 150ms | <div class="transition-demo" style="transition-duration: var(--sl-transition-fast);"></div> |
| `--sl-transition-x-fast` | 50ms | <div class="transition-demo" style="transition-duration: var(--sl-transition-x-fast);"></div> |

View File

@@ -1,64 +0,0 @@
---
meta:
title: Typography
description: Typography tokens are used to maintain a consistent set of font styles throughout your app.
---
# Typography Tokens
Typography tokens are used to maintain a consistent set of font styles throughout your app.
## Font Family
The default font stack is designed to be simple and highly available on as many devices as possible.
| Token | Value | Example |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `--sl-font-sans` | -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol' | <span style="font-family: var(--sl-font-sans)">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-serif` | Georgia, 'Times New Roman', serif | <span style="font-family: var(--sl-font-serif)">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-mono` | SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; | <span style="font-family: var(--sl-font-mono)">The quick brown fox jumped over the lazy dog.</span> |
## Font Size
Font sizes use `rem` units so they scale with the base font size. The pixel values displayed are based on a 16px font size.
| Token | Value | Example |
| ------------------------- | --------------- | --------------------------------------------------------------- |
| `--sl-font-size-2x-small` | 0.625rem (10px) | <span style="font-size: var(--sl-font-size-2x-small)">Aa</span> |
| `--sl-font-size-x-small` | 0.75rem (12px) | <span style="font-size: var(--sl-font-size-x-small)">Aa</span> |
| `--sl-font-size-small` | 0.875rem (14px) | <span style="font-size: var(--sl-font-size-small)">Aa</span> |
| `--sl-font-size-medium` | 1rem (16px) | <span style="font-size: var(--sl-font-size-medium)">Aa</span> |
| `--sl-font-size-large` | 1.25rem (20px) | <span style="font-size: var(--sl-font-size-large)">Aa</span> |
| `--sl-font-size-x-large` | 1.5rem (24px) | <span style="font-size: var(--sl-font-size-x-large)">Aa</span> |
| `--sl-font-size-2x-large` | 2.25rem (36px) | <span style="font-size: var(--sl-font-size-2x-large)">Aa</span> |
| `--sl-font-size-3x-large` | 3rem (48px) | <span style="font-size: var(--sl-font-size-3x-large)">Aa</span> |
| `--sl-font-size-4x-large` | 4.5rem (72px) | <span style="font-size: var(--sl-font-size-4x-large)">Aa</span> |
## Font Weight
| Token | Value | Example |
| --------------------------- | ----- | --------------------------------------------------------------------------------------------------------------- |
| `--sl-font-weight-light` | 300 | <span style="font-weight: var(--sl-font-weight-light);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-weight-normal` | 400 | <span style="font-weight: var(--sl-font-weight-normal);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-weight-semibold` | 500 | <span style="font-weight: var(--sl-font-weight-semibold);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-font-weight-bold` | 700 | <span style="font-weight: var(--sl-font-weight-bold);">The quick brown fox jumped over the lazy dog.</span> |
## Letter Spacing
| Token | Value | Example |
| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| `--sl-letter-spacing-denser` | -0.03em | <span style="letter-spacing: var(--sl-letter-spacing-denser);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-dense` | -0.015em | <span style="letter-spacing: var(--sl-letter-spacing-dense);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-normal` | normal | <span style="letter-spacing: var(--sl-letter-spacing-normal);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-loose` | 0.075em | <span style="letter-spacing: var(--sl-letter-spacing-loose);">The quick brown fox jumped over the lazy dog.</span> |
| `--sl-letter-spacing-looser` | 0.15em | <span style="letter-spacing: var(--sl-letter-spacing-looser);">The quick brown fox jumped over the lazy dog.</span> |
## Line Height
| Token | Value | Example |
| ------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--sl-line-height-denser` | 1 | <div style="line-height: var(--sl-line-height-denser);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-dense` | 1.4 | <div style="line-height: var(--sl-line-height-dense);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-normal` | 1.8 | <div style="line-height: var(--sl-line-height-normal);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-loose` | 2.2 | <div style="line-height: var(--sl-line-height-loose);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |
| `--sl-line-height-looser` | 2.6 | <div style="line-height: var(--sl-line-height-looser);">The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.<br>The quick brown fox jumped over the lazy dog.</div> |

View File

@@ -1,17 +0,0 @@
---
meta:
title: Z-Index Tokens
description: Z-indexes are used to stack components in a logical manner.
---
# Z-Index Tokens
Z-indexes are used to stack components in a logical manner.
| Token | Value |
| -------------------------- | ----- |
| `--sl-z-index-drawer` | 700 |
| `--sl-z-index-dialog` | 800 |
| `--sl-z-index-dropdown` | 900 |
| `--sl-z-index-alert-group` | 950 |
| `--sl-z-index-tooltip` | 1000 |

View File

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Some files were not shown because too many files have changed in this diff Show More