Compare commits

..

58 Commits

Author SHA1 Message Date
Cory LaViska
87286593cb 2.0.0-beta.61 2021-11-19 10:49:59 -05:00
Cory LaViska
2e1a74f79a update version 2021-11-19 10:48:58 -05:00
Cory LaViska
341ea70dbc add accessibility commitment 2021-11-19 10:42:59 -05:00
Cory LaViska
54e4c57ae5 fixes #591 2021-11-19 08:36:59 -05:00
Cory LaViska
9432ffebad reduce base lightness slightly 2021-11-19 08:26:03 -05:00
Cory LaViska
c748792f16 improve text contrast in doc 2021-11-19 08:19:23 -05:00
Cory LaViska
f1b304304c improve string colors 2021-11-18 18:00:19 -05:00
Cory LaViska
8532f7df26 make it stable 2021-11-18 17:46:37 -05:00
Cory LaViska
9cb3314494 improve dark theme and remove rgb() from tokens 2021-11-18 17:41:03 -05:00
Cory LaViska
cfc7b2ac93 fix button slots 2021-11-18 08:01:00 -05:00
Cory LaViska
2f2709abc6 remove unused function 2021-11-18 08:00:28 -05:00
Cory LaViska
8fd8087370 fix docs 2021-11-17 09:26:22 -05:00
Cory LaViska
d140dc2c71 prettier 2021-11-17 09:22:41 -05:00
Cory LaViska
1a4f330bd5 update bs icons 2021-11-17 09:12:34 -05:00
ErikOnBike
65a0125035 Remove duplicate values for autocapitalize (#590) 2021-11-17 08:24:36 -05:00
Cory LaViska
bfe506dbf3 add expanded support 2021-11-16 10:49:26 -05:00
Cory LaViska
da7a177599 add react examples 2021-11-16 10:09:31 -05:00
Cory LaViska
fe677f133b add visually hidden component 2021-11-16 09:31:38 -05:00
Cory LaViska
d6e4d2f24b add border to control button 2021-11-16 09:30:23 -05:00
Cory LaViska
a14642b62a don't guess labels 2021-11-16 09:19:49 -05:00
Cory LaViska
9d223067ae add contribution links 2021-11-16 09:19:34 -05:00
Cory LaViska
39009a6ee6 add script tag import 2021-11-15 17:43:13 -05:00
Cory LaViska
0ea16b1d97 update docs 2021-11-15 17:42:53 -05:00
Cory LaViska
977e9e0019 update form examples 2021-11-15 09:36:30 -05:00
Cory LaViska
e186db3b8e npm audit 2021-11-12 17:50:48 -05:00
Cory LaViska
0135a37af8 a11y improvements; fixes #579 2021-11-12 17:40:26 -05:00
Cory LaViska
a923d1effc remove unused styles 2021-11-12 12:03:19 -05:00
Cory LaViska
11f7bf2bb1 fix example 2021-11-12 12:01:04 -05:00
Cory LaViska
b336cdffe5 add testing info 2021-11-12 11:20:04 -05:00
Cory LaViska
f85b9e1b2b update framework pages 2021-11-12 10:31:13 -05:00
Cory LaViska
1422e3ffb7 update intro 2021-11-12 10:09:04 -05:00
Cory LaViska
b1a080cb91 fix incorrect aria-controls 2021-11-12 09:59:29 -05:00
Cory LaViska
59ad01c560 fix lit dep 2021-11-12 09:55:53 -05:00
Cory LaViska
a24eaa6693 remove redundant role 2021-11-12 09:50:50 -05:00
Cory LaViska
b98b10c580 faster animations 2021-11-12 09:43:02 -05:00
Marcel Kuhmann
2fca01401b allow query strings (#582) 2021-11-11 10:55:26 -05:00
Thomas Klemm
f20296cf7c Docs: Fix typo in React setup code sample (#580) 2021-11-07 07:11:33 -05:00
Cory LaViska
fe041c5e10 fix typo 2021-11-06 08:28:47 -04:00
Cory LaViska
ca44d23031 2.0.0-beta.60 2021-11-05 11:57:49 -04:00
Cory LaViska
18452f692c update version 2021-11-05 11:57:33 -04:00
Cory LaViska
6aca68824a fixes #578 2021-11-05 11:53:53 -04:00
Cory LaViska
7177b9bf82 update intro 2021-11-05 11:51:24 -04:00
Cory LaViska
5b6e24bd13 fix example 2021-11-05 11:51:12 -04:00
Cory LaViska
cc93108df0 less verbose 2021-11-05 11:50:59 -04:00
Cory LaViska
95041b75b0 add react examples 2021-11-05 11:50:46 -04:00
Cory LaViska
0929799daf fix style attrs 2021-11-05 11:26:09 -04:00
Cory LaViska
c771534c43 fix className 2021-11-05 10:06:06 -04:00
Cory LaViska
3ffbc09630 improve flavor logic 2021-11-05 09:47:43 -04:00
Cory LaViska
de4207940c add react event names to manifest 2021-11-05 09:23:30 -04:00
Cory LaViska
3eb7d6337a fix mutation observer in react 2021-11-05 09:22:59 -04:00
Cory LaViska
1dd556d6c8 react examples and fixes 2021-11-04 18:12:47 -04:00
Cory LaViska
a250d9b184 support react imports in examples; set base path 2021-11-04 08:12:01 -04:00
Cory LaViska
bb6cedfce4 2.0.0-beta.59 2021-11-04 07:27:30 -04:00
Cory LaViska
a4c9b9c8cf add react support into lib 2021-11-04 07:27:18 -04:00
Cory LaViska
c88ea6666b update react imports 2021-11-02 17:13:12 -04:00
Cory LaViska
5ecd73c599 update example 2021-11-02 16:54:17 -04:00
Cory LaViska
84739ba695 update clear icon 2021-11-02 16:52:31 -04:00
Cory LaViska
3bb1e9ff91 update bootstrap icons 2021-11-02 16:46:17 -04:00
141 changed files with 7988 additions and 2883 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ docs/search.json
dist
examples
node_modules
src/react

View File

@@ -1,5 +1,6 @@
import fs from 'fs';
import commentParser from 'comment-parser';
import pascalCase from 'pascal-case';
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const { name, description, version, author, homepage, license } = packageData;
@@ -81,6 +82,23 @@ export default {
});
}
}
},
{
name: 'shoelace-react-event-names',
analyzePhase({ ts, node, moduleDoc, context }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration:
const className = node.name.getText();
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
if (classDoc?.events) {
classDoc.events.map(event => {
event.reactName = `on${pascalCase(event.name)}`;
});
}
}
}
}
]
};

View File

@@ -5,8 +5,14 @@
- [Themes](/getting-started/themes)
- [Customizing](/getting-started/customizing)
- Frameworks
- [React](/frameworks/react)
- [Vue](/frameworks/vue)
- [Angular](/frameworks/angular)
- Resources
- [Community](/resources/community)
- [Accessibility](/resources/accessibility)
- [Contributing](/resources/contributing)
- [Changelog](/resources/changelog)
@@ -21,7 +27,6 @@
- [Card](/components/card)
- [Checkbox](/components/checkbox)
- [Color Picker](/components/color-picker)
- [Context Menu](/components/context-menu)
- [Details](/components/details)
- [Dialog](/components/dialog)
- [Divider](/components/divider)
@@ -65,6 +70,7 @@
- [Relative Time](/components/relative-time)
- [Resize Observer](/components/resize-observer)
- [Responsive Media](/components/responsive-media)
- [Visually Hidden](/components/visually-hidden)
- Design Tokens
- [Typography](/tokens/typography)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 688 KiB

After

Width:  |  Height:  |  Size: 750 KiB

View File

@@ -1,17 +1,17 @@
.code-block {
position: relative;
border-radius: 3px;
background-color: rgb(var(--sl-color-neutral-50));
background-color: var(--sl-color-neutral-50);
margin-bottom: 1.5rem;
}
.code-block__preview {
position: relative;
border: solid 1px rgb(var(--sl-color-neutral-200));
border: solid 1px var(--sl-color-neutral-200);
border-bottom: none;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background-color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-neutral-0);
min-width: 20rem;
max-width: 100%;
padding: 1.5rem 3.25rem 1.5rem 1.5rem;
@@ -39,9 +39,9 @@
bottom: 0;
width: 1.75rem;
font-size: 20px;
color: rgb(var(--sl-color-neutral-600));
background-color: rgb(var(--sl-color-neutral-0));
border-left: solid 1px rgb(var(--sl-color-neutral-200));
color: var(--sl-color-neutral-600);
background-color: var(--sl-color-neutral-0);
border-left: solid 1px var(--sl-color-neutral-200);
border-top-right-radius: 3px;
cursor: ew-resize;
transition: 250ms background-color;
@@ -58,10 +58,9 @@
}
.code-block__source {
border: solid 1px rgb(var(--sl-color-neutral-200));
border: solid 1px var(--sl-color-neutral-200);
border-bottom: none;
border-radius: 0 !important;
margin: 0;
display: none;
}
@@ -69,9 +68,13 @@
display: block;
}
.code-block__source pre {
margin: 0;
}
.code-block__buttons {
position: relative;
border: solid 1px rgb(var(--sl-color-neutral-200));
border: solid 1px var(--sl-color-neutral-200);
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
display: flex;
@@ -83,18 +86,31 @@
min-width: 2.5rem;
border: none;
border-radius: 0;
background: rgb(var(--sl-color-neutral-0));
background: var(--sl-color-neutral-0);
font: inherit;
font-size: 0.7rem;
font-weight: 500;
text-transform: uppercase;
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
padding: 0 1rem;
cursor: pointer;
}
.code-block__button:not(:last-of-type) {
border-right: solid 1px rgb(var(--sl-color-neutral-200));
border-right: solid 1px var(--sl-color-neutral-200);
}
.code-block__button--html,
.code-block__button--react {
width: 70px;
display: flex;
place-items: center;
justify-content: center;
}
.code-block__button--selected {
font-weight: 700;
color: var(--sl-color-primary-600);
}
.code-block__button--codepen {
@@ -113,20 +129,20 @@
.code-block__button:hover,
.code-block__button:active {
box-shadow: 0 0 0 1px rgb(var(--sl-color-primary-400));
box-shadow: 0 0 0 1px var(--sl-color-primary-400);
border-right-color: transparent;
background-color: rgb(var(--sl-color-primary-50));
color: rgb(var(--sl-color-primary-700));
background-color: var(--sl-color-primary-50);
color: var(--sl-color-primary-700);
z-index: 1;
}
.code-block__button:focus-visible {
outline: none;
color: rgb(var(--sl-color-primary-600));
border-color: rgb(var(--sl-color-primary-400));
color: var(--sl-color-primary-600);
border-color: var(--sl-color-primary-400);
border-right-color: transparent;
background-color: rgb(var(--sl-color-primary-50));
box-shadow: 0 0 0 1px rgb(var(--sl-color-primary-400)), var(--sl-focus-ring);
background-color: var(--sl-color-primary-50);
box-shadow: 0 0 0 1px var(--sl-color-primary-400), var(--sl-focus-ring);
z-index: 2;
}
@@ -137,7 +153,7 @@
align-items: center;
justify-content: center;
width: 100%;
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
cursor: pointer;
-webkit-appearance: none;
}
@@ -156,7 +172,7 @@
.markdown-section .docsify-copy-code-button {
top: 4px;
right: 4px;
background-color: rgb(var(--sl-color-neutral-600));
background-color: var(--sl-color-neutral-500);
border-radius: var(--sl-border-radius-medium);
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-x-small);
@@ -169,7 +185,7 @@
.markdown-section .docsify-copy-code-button.copied {
animation: pulse 0.75s;
--pulse-color: rgb(var(--sl-color-neutral-600));
--pulse-color: var(--sl-color-neutral-600);
}
@keyframes pulse {
@@ -194,10 +210,19 @@
}
.markdown-section .docsify-copy-code-button:focus-visible {
box-shadow: 0 0 0 3px rgb(var(--sl-color-neutral-500) / 50%);
box-shadow: var(--sl-focus-ring);
}
.markdown-section .docsify-copy-code-button:active {
background-color: rgb(var(--sl-color-neutral-600));
background-color: var(--sl-color-neutral-600);
transform: scale(0.92);
}
/* We can apply data-flavor="html|react" to any element on the page to toggle it when the user's flavor changes */
body.flavor-html [data-flavor]:not([data-flavor='html']) {
display: none;
}
body.flavor-react [data-flavor]:not([data-flavor='react']) {
display: none;
}

View File

@@ -1,10 +1,40 @@
(() => {
const reactVersion = '17.0.2';
let flavor = getFlavor();
let count = 1;
// Sync flavor UI on page load
setFlavor(getFlavor());
if (!window.$docsify) {
throw new Error('Docsify must be loaded before installing this plugin.');
}
function convertModuleLinks(html) {
const version = sessionStorage.getItem('sl-version');
html = html
.replace(/@shoelace-style\/shoelace/g, `https://cdn.skypack.dev/@shoelace-style/shoelace@${version}`)
.replace(/from 'react'/g, `from 'https://cdn.skypack.dev/react@${reactVersion}'`)
.replace(/from "react"/g, `from "https://cdn.skypack.dev/react@${reactVersion}"`);
return html;
}
function getAdjacentExample(name, pre) {
let currentPre = pre.nextElementSibling;
while (currentPre?.tagName.toLowerCase() === 'pre') {
if (currentPre?.getAttribute('data-lang').split(' ').includes(name)) {
return currentPre;
}
currentPre = currentPre.nextElementSibling;
}
return null;
}
function runScript(script) {
const newScript = document.createElement('script');
@@ -18,6 +48,19 @@
script.parentNode.replaceChild(newScript, script);
}
function getFlavor() {
return localStorage.getItem('flavor') || 'html';
}
function setFlavor(newFlavor) {
flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html';
localStorage.setItem('flavor', flavor);
// Set the flavor class on the body
document.body.classList.toggle('flavor-html', flavor === 'html');
document.body.classList.toggle('flavor-react', flavor === 'react');
}
function wrap(el, wrapper) {
el.parentNode.insertBefore(wrapper, el);
wrapper.appendChild(el);
@@ -28,36 +71,66 @@
hook.afterEach(function (html, next) {
const domParser = new DOMParser();
const doc = domParser.parseFromString(html, 'text/html');
const codePenButton = `
<button type="button" class="code-block__button code-block__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"
const htmlButton = `
<button
type="button"
title="Show HTML code"
class="code-block__button code-block__button--html ${flavor === 'html' ? 'code-block__button--selected' : ''}"
>
<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>
`;
HTML
</button>
`;
const reactButton = `
<button
type="button"
title="Show React code"
class="code-block__button code-block__button--react ${
flavor === 'react' ? 'code-block__button--selected' : ''
}"
>
React
</button>
`;
const codePenButton = `
<button type="button" class="code-block__button code-block__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>
`;
[...doc.querySelectorAll('code[class^="lang-"]')].map(code => {
if (code.classList.contains('preview')) {
const isExpanded = code.classList.contains('expanded');
const pre = code.closest('pre');
const preId = `code-block-preview-${count}`;
const sourceGroupId = `code-block-source-group-${count}`;
const toggleId = `code-block-toggle-${count}`;
const reactPre = getAdjacentExample('react', pre);
const hasReact = reactPre !== null;
const examples = [
{
name: 'HTML',
codeBlock: pre
}
];
pre.id = preId;
pre.classList.add('code-block__source');
pre.setAttribute('data-lang', pre.getAttribute('data-lang').replace(/ preview$/, ''));
pre.setAttribute('aria-labelledby', toggleId);
const codeBlock = `
<div class="code-block">
<div class="code-block ${isExpanded ? 'code-block--expanded' : ''}">
<div class="code-block__preview">
${code.textContent}
<div class="code-block__resizer">
@@ -65,11 +138,32 @@
</div>
</div>
${pre.outerHTML}
<div class="code-block__source-group" id="${sourceGroupId}">
<div class="code-block__source code-block__source--html" ${hasReact ? 'data-flavor="html"' : ''}>
${pre.outerHTML}
</div>
${
hasReact
? `
<div class="code-block__source code-block__source--react" data-flavor="react">
${reactPre.outerHTML}
</div>
`
: ''
}
</div>
<div class="code-block__buttons">
<button type="button" class="code-block__button code-block__toggle" aria-expanded="false" aria-controls="${preId}">
View Source
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''}
<button
type="button"
class="code-block__button code-block__toggle"
aria-expanded="${isExpanded ? 'true' : 'false'}"
aria-controls="${sourceGroupId}"
>
Source
<svg
viewBox="0 0 24 24"
fill="none"
@@ -88,11 +182,15 @@
`;
pre.replaceWith(domParser.parseFromString(codeBlock, 'text/html').body);
if (reactPre) reactPre.remove();
count++;
}
});
// Force the highlighter to run again so JSX fields get highlighted properly
requestAnimationFrame(() => Prism.highlightAll());
next(doc.body.innerHTML);
});
@@ -139,49 +237,28 @@
});
});
// Open in CodePen
// Toggle source mode
document.addEventListener('click', event => {
const button = event.target.closest('button');
const codeBlock = button?.closest('.code-block');
if (button?.classList.contains('code-block__button--codepen')) {
const codeBlock = button.closest('.code-block');
const html = codeBlock.querySelector('.code-block__source > code').textContent;
const version = sessionStorage.getItem('sl-version');
const form = document.createElement('form');
form.action = 'https://codepen.io/pen/define';
form.method = 'POST';
form.target = '_blank';
// Docs: https://blog.codepen.io/documentation/prefill/
const data = {
title: '',
description: '',
tags: ['shoelace', 'web components'],
editors: '100',
head: `<meta name="viewport" content="width=device-width">`,
css_external: ``,
js_external: ``,
js_module: true,
html:
`<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/themes/light.css">\n` +
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/shoelace.js"></script>\n` +
`\n` +
html,
css: `body {\n font: 16px sans-serif;\n}`,
js: ``
};
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'data';
input.value = JSON.stringify(data);
form.append(input);
document.body.append(form);
form.submit();
form.remove();
if (button?.classList.contains('code-block__button--html')) {
setFlavor('html');
} else if (button?.classList.contains('code-block__button--react')) {
setFlavor('react');
} else {
return;
}
// Update flavor buttons
[...document.querySelectorAll('.code-block')].map(codeBlock => {
codeBlock
.querySelector('.code-block__button--html')
?.classList.toggle('code-block__button--selected', flavor === 'html');
codeBlock
.querySelector('.code-block__button--react')
?.classList.toggle('code-block__button--selected', flavor === 'react');
});
});
// Expand and collapse code blocks
@@ -205,4 +282,87 @@
});
}
});
// Open in CodePen
document.addEventListener('click', event => {
const button = event.target.closest('button');
const version = sessionStorage.getItem('sl-version');
if (button?.classList.contains('code-block__button--codepen')) {
const codeBlock = button.closest('.code-block');
const htmlExample = codeBlock.querySelector('.code-block__source--html > pre > code')?.textContent;
const reactExample = codeBlock.querySelector('.code-block__source--react > pre > code')?.textContent;
const isReact = flavor === 'react' && typeof reactExample === 'string';
const editors = isReact ? '0010' : '1000';
let htmlTemplate = '';
let jsTemplate = '';
let cssTemplate = '';
const form = document.createElement('form');
form.action = 'https://codepen.io/pen/define';
form.method = 'POST';
form.target = '_blank';
// HTML templates
if (!isReact) {
htmlTemplate =
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/shoelace.js"></script>\n` +
'\n' +
htmlExample;
jsTemplate = '';
}
// React templates
if (isReact) {
htmlTemplate = '<div id="root"></div>';
jsTemplate =
`import React from 'https://cdn.skypack.dev/react@${reactVersion}';\n` +
`import ReactDOM from 'https://cdn.skypack.dev/react-dom@${reactVersion}';\n` +
`import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/utilities/base-path';\n` +
'\n' +
`// Set the base path for Shoelace assets\n` +
`setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/')\n` +
'\n' +
convertModuleLinks(reactExample) +
'\n' +
'\n' +
`ReactDOM.render(<App />, document.getElementById('root'));`;
}
// CSS templates
cssTemplate =
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/themes/light.css';\n` +
'\n' +
'body {\n' +
' font: 16px sans-serif;\n' +
' padding: 1rem;\n' +
'}';
// Docs: https://blog.codepen.io/documentation/prefill/
const data = {
title: '',
description: '',
tags: ['shoelace', 'web components'],
editors,
head: `<meta name="viewport" content="width=device-width">`,
css_external: ``,
js_external: ``,
js_module: true,
js_pre_processor: isReact ? 'babel' : 'none',
html: htmlTemplate,
css: cssTemplate,
js: jsTemplate
};
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'data';
input.value = JSON.stringify(data);
form.append(input);
document.body.append(form);
form.submit();
form.remove();
}
});
})();

View File

@@ -67,7 +67,8 @@
table.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th data-flavor="html">Name</th>
<th data-flavor="react">React Event</th>
<th>Description</th>
<th>Event Detail</th>
</tr>
@@ -77,7 +78,8 @@
.map(
event => `
<tr>
<td><code class="nowrap">${escapeHtml(event.name)}</code></td>
<td data-flavor="html"><code class="nowrap">${escapeHtml(event.name)}</code></td>
<td data-flavor="react"><code class="nowrap">${escapeHtml(event.reactName)}</code></td>
<td>${escapeHtml(event.description)}</td>
<td>${event.type?.text ? `<code>${escapeHtml(event.type?.text)}` : '-'}</td>
</tr>
@@ -314,13 +316,13 @@
buttons.classList.add('sidebar-buttons');
buttons.innerHTML = `
<sl-button size="small" class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
<sl-icon name="heart"></sl-icon> Sponsor
<sl-icon slot="prefix" name="heart"></sl-icon> Sponsor
</sl-button>
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<sl-icon name="github"></sl-icon> <span class="github-star-count">Star</span>
<sl-icon slot="prefix" name="github"></sl-icon> <span class="github-star-count">Star</span>
</sl-button>
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon name="twitter"></sl-icon> Follow
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
</sl-button>
`;
target.appendChild(buttons);
@@ -392,6 +394,50 @@
return prop.kind === 'field' && prop.privacy !== 'private';
});
if (component.path) {
/* prettier-ignore */
result += `
## Importing
<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">\n
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a script tag:
\`\`\`html
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/${component.path}"></script>
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="import">\n
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a JavaScript import:
\`\`\`js
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/${component.path}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="bundler">\n
To import this component using [a bundler](/getting-started/installation#bundling):
\`\`\`js
import '@shoelace-style/shoelace/${component.path}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="react">\n
To import this component as a [React component](/frameworks/react):
\`\`\`js
import { ${component.name} } from '@shoelace-style/shoelace/dist/react';
\`\`\`
</sl-tab-panel>
</sl-tab-group>
`;
}
if (props?.length) {
result += `
## Properties
@@ -408,8 +454,20 @@
if (methods?.length) {
result += `
## Methods
${createMethodsTable(methods)}
## Methods
<p data-flavor="html">
Methods can be called by obtaining a reference to the element and calling
<code>el.methodName()</code>.
</p>
<p data-flavor="react">
Methods can be called by obtaining a <code>ref</code> to the element and calling
<code>ref.current.methodName()</code>.
</p>
${createMethodsTable(methods)}
`;
}
@@ -453,41 +511,6 @@
`;
}
if (component.path) {
/* prettier-ignore */
result += `
## Importing
<sl-tab-group>
<sl-tab slot="nav" panel="cdn" active>CDN</sl-tab>
<sl-tab slot="nav" panel="bundler">Bundler</sl-tab>
<sl-tab slot="nav" panel="react">React</sl-tab>
<sl-tab-panel name="cdn">\n
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace):
\`\`\`js
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/${component.path}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="bundler">\n
To import this component using [a bundler](/getting-started/installation#bundling):
\`\`\`js
import '@shoelace-style/shoelace/${component.path}';
\`\`\`
</sl-tab-panel>
<sl-tab-panel name="react">\n
To import this component using [\`@shoelace-style/react\`](https://www.npmjs.com/package/@shoelace-style/react):
\`\`\`js
import '@shoelace-style/react/dist/${component.tagName.replace(/^sl-/, '')}';
\`\`\`
</sl-tab-panel>
</sl-tab-group>
`;
}
// Strip whitespace so markdown doesn't process things as code blocks
return result.replace(/^ +| +$/gm, '');
});

View File

@@ -30,7 +30,7 @@ body.site-search-visible {
left: 0;
right: 0;
bottom: 0;
background: rgb(var(--sl-overlay-background-color) / var(--sl-overlay-opacity));
background: var(--sl-overlay-background-color);
z-index: -1;
}
@@ -39,7 +39,7 @@ body.site-search-visible {
flex-direction: column;
max-width: 460px;
max-height: calc(100vh - 20rem);
background-color: rgb(var(--sl-surface-base-alt));
background-color: var(--sl-color-neutral-50);
border-radius: var(--sl-border-radius-large);
box-shadow: var(--sl-shadow-x-large);
margin: 10rem auto;
@@ -74,7 +74,7 @@ body.site-search-visible {
}
.site-search--has-results .site-search__body {
border-top: solid 1px rgb(var(--sl-color-neutral-200));
border-top: solid 1px var(--sl-color-neutral-200);
}
.site-search__results {
@@ -97,15 +97,15 @@ body.site-search-visible {
.site-search__results li a:hover,
.site-search__results li a:hover small {
background-color: rgb(var(--sl-color-neutral-100));
background-color: var(--sl-color-neutral-100);
}
.site-search__results li[aria-selected='true'] a,
.site-search__results li[aria-selected='true'] a small,
.site-search__results li[aria-selected='true'] a sl-icon {
outline: none;
color: rgb(var(--sl-color-neutral-0));
background-color: rgb(var(--sl-color-primary-600));
color: var(--sl-color-neutral-0);
background-color: var(--sl-color-primary-600);
}
.site-search__results h3 {
@@ -115,7 +115,7 @@ body.site-search-visible {
.site-search__results small {
display: block;
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
}
.site-search__result {
@@ -132,7 +132,7 @@ body.site-search-visible {
.site-search__result-icon {
flex: 0 0 auto;
display: flex;
color: rgb(var(--sl-color-neutral-400));
color: var(--sl-color-neutral-400);
font-size: var(--sl-font-size-x-large);
}
@@ -142,7 +142,7 @@ body.site-search-visible {
.site-search__empty {
display: none;
border-top: solid 1px rgb(var(--sl-color-neutral-200));
border-top: solid 1px var(--sl-color-neutral-200);
text-align: center;
padding: var(--sl-spacing-x-large);
}
@@ -155,14 +155,14 @@ body.site-search-visible {
display: flex;
justify-content: center;
gap: var(--sl-spacing-large);
border-top: solid 1px rgb(var(--sl-color-neutral-200));
border-top: solid 1px var(--sl-color-neutral-200);
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
padding: var(--sl-spacing-medium);
}
.site-search__footer small {
color: rgb(var(--sl-color-neutral-700));
color: var(--sl-color-neutral-700);
}
@media screen and (max-width: 900px) {

View File

@@ -73,7 +73,6 @@
event.preventDefault();
setTheme(isDark() ? 'light' : 'dark');
show();
}
});

View File

@@ -13,17 +13,17 @@ body {
font-size: var(--sl-font-size-medium);
font-weight: var(--sl-font-weight-normal);
letter-spacing: var(--sl-letter-spacing-normal);
background-color: rgb(var(--sl-surface-base));
color: rgb(var(--sl-color-neutral-800));
background-color: var(--sl-color-neutral-0);
color: var(--sl-color-neutral-900);
line-height: var(--sl-line-height-normal);
}
a {
color: rgb(var(--sl-color-primary-600));
color: var(--sl-color-primary-600);
}
a:hover {
color: rgb(var(--sl-color-primary-700));
color: var(--sl-color-primary-700);
}
strong {
@@ -32,8 +32,8 @@ strong {
/* Sidebar */
.sidebar {
background-color: rgb(var(--sl-surface-base));
border-right: solid 1px rgb(var(--sl-color-neutral-200));
background-color: var(--sl-color-neutral-0);
border-right: solid 1px var(--sl-color-neutral-200);
}
.sidebar .app-name {
@@ -43,7 +43,7 @@ strong {
.sidebar-version {
font-size: var(--sl-font-size-x-small);
font-weight: var(--sl-font-weight-normal);
color: rgb(var(--sl-color-neutral-500));
color: var(--sl-color-neutral-500);
text-align: right;
padding: 0 var(--sl-spacing-small);
margin: -1.25rem 0 0.6rem 0;
@@ -61,7 +61,7 @@ strong {
width: 2rem;
height: 2rem;
border-radius: var(--sl-border-radius-medium);
background-color: rgb(var(--sl-surface-base));
background-color: var(--sl-color-neutral-0);
padding: 0.5rem;
}
@@ -70,7 +70,7 @@ strong {
}
.sidebar-toggle:active .sidebar-toggle-button span {
background-color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-600);
}
.sidebar-toggle:focus {
@@ -110,12 +110,12 @@ strong {
.sidebar-nav li.collapse > a,
.sidebar-nav li.active > a {
color: rgb(var(--sl-color-primary-600));
color: var(--sl-color-primary-600);
}
.sidebar li > p {
font-weight: var(--sl-font-weight-bold);
border-bottom: solid 1px rgb(var(--sl-color-neutral-200));
border-bottom: solid 1px var(--sl-color-neutral-200);
margin: 0 0.75rem 0.5rem 0;
}
@@ -197,16 +197,15 @@ strong {
.markdown-section {
max-width: 860px;
overflow-anchor: none;
}
.anchor span {
color: rgb(var(--sl-color-neutral-1000));
color: var(--sl-color-neutral-1000);
}
.markdown-section blockquote {
position: relative;
border-left: solid 4px rgb(var(--sl-color-neutral-200));
border-left: solid 4px var(--sl-color-neutral-200);
font-style: italic;
padding: 1rem 1.5rem;
margin: 0 0 1rem 0;
@@ -230,7 +229,7 @@ strong {
}
.docsify-pagination-container {
border-top-color: rgb(var(--sl-color-neutral-200)) !important;
border-top-color: var(--sl-color-neutral-200) !important;
}
.pagination-item-label,
@@ -255,7 +254,7 @@ strong {
.markdown-section h2 {
font-size: var(--sl-font-size-x-large);
border-bottom: solid 1px rgb(var(--sl-color-neutral-200));
border-bottom: solid 1px var(--sl-color-neutral-200);
margin-top: 2rem;
}
@@ -286,30 +285,30 @@ strong {
.markdown-section code {
font-family: var(--sl-font-mono);
font-size: 87.5%;
background-color: rgb(var(--sl-color-neutral-50));
background-color: var(--sl-color-neutral-50);
border-radius: var(--sl-border-radius-small);
padding: 2px 4px;
}
.markdown-section tr:nth-child(2n) code {
background-color: rgb(var(--sl-color-neutral-100));
background-color: var(--sl-color-neutral-100);
}
kbd,
.markdown-section kbd {
font-family: var(--sl-font-mono);
font-size: 87.5%;
background-color: rgb(var(--sl-color-neutral-50));
background-color: var(--sl-color-neutral-50);
border-radius: var(--sl-border-radius-small);
border: solid 1px rgb(var(--sl-color-neutral-200));
box-shadow: inset 0 1px 0 rgb(var(--sl-color-neutral-0));
border: solid 1px var(--sl-color-neutral-200);
box-shadow: inset 0 1px 0 var(--sl-color-neutral-0);
padding: 2px 5px;
}
/* Code blocks */
.markdown-section pre {
position: relative;
background-color: rgb(var(--sl-color-neutral-50));
background-color: var(--sl-color-neutral-50);
border-radius: var(--sl-border-radius-medium);
}
@@ -317,7 +316,7 @@ kbd,
display: block;
background: none;
border-radius: 0;
color: rgb(var(--sl-color-neutral-800));
color: var(--sl-color-neutral-800);
padding: var(--sl-spacing-medium);
overflow: auto;
hyphens: none;
@@ -325,18 +324,18 @@ kbd,
}
.markdown-section pre .token.comment {
color: rgb(var(--sl-color-neutral-500));
color: var(--sl-color-neutral-500);
}
.markdown-section pre .token.prolog,
.markdown-section pre .token.doctype,
.markdown-section pre .token.cdata,
.markdown-section pre .token.operator {
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
}
.markdown-section pre .token.punctuation {
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
}
.namespace {
@@ -347,12 +346,12 @@ kbd,
.markdown-section pre .token.keyword,
.markdown-section pre .token.tag,
.markdown-section pre .token.url {
color: rgb(var(--sl-color-sky-600));
color: var(--sl-color-sky-600);
}
.markdown-section pre .token.symbol,
.markdown-section pre .token.deleted {
color: rgb(var(--sl-color-pink-600));
color: var(--sl-color-pink-600);
}
.markdown-section pre .token.boolean,
@@ -363,24 +362,24 @@ kbd,
.markdown-section pre .token.char,
.markdown-section pre .token.builtin,
.markdown-section pre .token.inserted {
color: rgb(var(--sl-color-emerald-600));
color: var(--sl-color-emerald-600);
}
.markdown-section pre .token.atrule,
.markdown-section pre .token.attr-value,
.markdown-section pre .token.number,
.markdown-section pre .token.variable {
color: rgb(var(--sl-color-violet-600));
color: var(--sl-color-violet-700);
}
.markdown-section pre .token.function,
.markdown-section pre .token.class-name,
.markdown-section pre .token.regex {
color: rgb(var(--sl-color-orange-600));
color: var(--sl-color-orange-600);
}
.markdown-section pre .token.important {
color: rgb(var(--sl-color-red-600));
color: var(--sl-color-red-600);
}
.markdown-section pre .token.important,
@@ -413,7 +412,7 @@ kbd,
}
.markdown-section tr:nth-child(2n) {
background: rgb(var(--sl-color-neutral-50));
background: var(--sl-color-neutral-50);
}
.markdown-section th {
@@ -423,8 +422,8 @@ kbd,
}
.markdown-section td {
border-top: solid 1px rgb(var(--sl-color-neutral-200));
border-bottom: solid 1px rgb(var(--sl-color-neutral-200));
border-top: solid 1px var(--sl-color-neutral-200);
border-bottom: solid 1px var(--sl-color-neutral-200);
border-left: none;
border-right: none;
}
@@ -434,7 +433,7 @@ kbd,
}
.markdown-section table sl-tooltip code {
border-bottom: dashed 1px rgb(var(--sl-color-neutral-300));
border-bottom: dashed 1px var(--sl-color-neutral-300);
cursor: help;
}
@@ -447,7 +446,7 @@ kbd,
.markdown-section p.tip,
.markdown-section p.warn {
position: relative;
background-color: rgb(var(--sl-color-neutral-50));
background-color: var(--sl-color-neutral-50);
border-left: solid 4px transparent;
border-radius: var(--sl-border-radius-medium);
padding-left: 1.5rem;
@@ -457,7 +456,7 @@ kbd,
.markdown-section p.warn:before {
content: '!';
border-radius: 100%;
color: rgb(var(--sl-color-neutral-0));
color: var(--sl-color-neutral-0);
font-size: 14px;
font-weight: bold;
left: -12px;
@@ -470,29 +469,29 @@ kbd,
}
.markdown-section p.warn {
border-left-color: rgb(var(--sl-color-primary-600));
border-left-color: var(--sl-color-primary-600);
}
.markdown-section p.warn:before {
background-color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-600);
}
.markdown-section p.tip {
border-left-color: rgb(var(--sl-color-danger-600));
border-left-color: var(--sl-color-danger-600);
}
.markdown-section p.tip:before {
background-color: rgb(var(--sl-color-danger-600));
background-color: var(--sl-color-danger-600);
}
.markdown-section p.tip code,
.markdown-section p.warn code {
background-color: rgb(var(--sl-color-neutral-100));
background-color: var(--sl-color-neutral-100);
}
/* Component headers */
.component-header {
border-bottom: solid 1px rgb(var(--sl-color-neutral-200));
border-bottom: solid 1px var(--sl-color-neutral-200);
padding-bottom: 2rem;
margin-top: -1rem;
margin-bottom: 2rem;
@@ -506,7 +505,7 @@ kbd,
.markdown-section .component-header__tag code {
background: none;
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
font-size: var(--sl-font-size-large);
padding: 0;
margin: 0;
@@ -524,15 +523,15 @@ kbd,
/* Repo buttons */
.repo-button--sponsor sl-icon {
color: rgb(var(--sl-color-pink-600));
color: var(--sl-color-pink-600);
}
.repo-button--github sl-icon {
color: rgb(var(--sl-color-neutral-700));
color: var(--sl-color-neutral-700);
}
.repo-button--twitter sl-icon {
color: rgb(var(--sl-color-sky-500));
color: var(--sl-color-sky-500);
}
@media screen and (max-width: 400px) {
@@ -551,13 +550,13 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
.border-radius-demo {
width: 3rem;
height: 3rem;
background: rgb(var(--sl-color-primary-600));
background: var(--sl-color-primary-600);
}
/* Transition demo */
.transition-demo {
position: relative;
background: rgb(var(--sl-color-neutral-200));
background: var(--sl-color-neutral-200);
width: 8rem;
height: 2rem;
}
@@ -565,7 +564,7 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
.transition-demo:after {
content: '';
position: absolute;
background-color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-600);
top: 0;
left: 0;
width: 0;
@@ -581,7 +580,7 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
/* Spacing demo */
.spacing-demo {
width: 100px;
background: rgb(var(--sl-color-primary-600));
background: var(--sl-color-primary-600);
}
/* Elevation dmeo */
@@ -623,7 +622,7 @@ body[data-page^='/tokens/'] .table-wrapper td:first-child code {
}
.color-palette__swatch--border {
box-shadow: inset 0 0 0 1px rgb(var(--sl-color-neutral-1000) / 10%);
box-shadow: inset 0 0 0 1px var(--sl-color-neutral-300);
}
@media screen and (max-width: 1200px) {

View File

@@ -11,6 +11,17 @@ Alerts are used to display important messages either inline or as toast notifica
</sl-alert>
```
```jsx react
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAlert open>
<SlIcon slot="icon" name="info-circle" />
This is a standard alert. You can customize its content and even the icon.
</SlAlert>
);
```
?> Alerts will not be visible if the `open` attribute is not present.
## Examples
@@ -59,6 +70,52 @@ Set the `type` attribute to change the alert's type.
</sl-alert>
```
```jsx react
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlAlert type="primary" open>
<SlIcon slot="icon" name="info-circle" />
<strong>This is super informative</strong><br />
You can tell by how pretty the alert is.
</SlAlert>
<br />
<SlAlert type="success" open>
<SlIcon slot="icon" name="check2-circle" />
<strong>Your changes have been saved</strong><br />
You can safely exit the app now.
</SlAlert>
<br />
<SlAlert type="neutral" open>
<SlIcon slot="icon" name="gear" />
<strong>Your settings have been updated</strong><br />
Settings will take affect on next login.
</SlAlert>
<br />
<SlAlert type="warning" open>
<SlIcon slot="icon" name="exclamation-triangle" />
<strong>Your session has ended</strong><br />
Please login again to continue.
</SlAlert>
<br />
<SlAlert type="danger" open>
<SlIcon slot="icon" name="exclamation-octagon" />
<strong>Your account has been deleted</strong><br />
We're very sorry to see you go!
</SlAlert>
</>
);
```
### Closable
Add the `closable` attribute to show a close button that will hide the alert.
@@ -77,6 +134,31 @@ Add the `closable` attribute to show a close button that will hide the alert.
</script>
```
```jsx react
import { useState } from 'react';
import { SlAlert, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(true);
function handleHide() {
setOpen(false);
setTimeout(() => setOpen(true), 2000);
}
return (
<SlAlert
open={open}
closable
onSlAfterHide={handleHide}
>
<SlIcon slot="icon" name="info-circle" />
You can close this alert any time!
</SlAlert>
);
};
```
### Without Icons
Icons are optional. Simply omit the `icon` slot if you don't want them.
@@ -87,6 +169,16 @@ Icons are optional. Simply omit the `icon` slot if you don't want them.
</sl-alert>
```
```jsx react
import { SlAlert } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAlert type="primary" open>
Nothing fancy here, just a simple alert.
</SlAlert>
);
```
### Duration
Set the `duration` attribute to automatically hide an alert after a period of time. This is useful for alerts that don't require acknowledgement.
@@ -116,6 +208,46 @@ Set the `duration` attribute to automatically hide an alert after a period of ti
</style>
```
```jsx react
import { useState } from 'react';
import {
SlAlert,
SlButton,
SlIcon
} from '@shoelace-style/shoelace/dist/react';
const css = `
.alert-duration sl-alert {
margin-top: var(--sl-spacing-medium);
}
`;
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<div className="alert-duration">
<SlButton type="primary" onClick={() => setOpen(true)}>Show Alert</SlButton>
<SlAlert
type="primary"
duration="3000"
open={open}
closable
onSlAfterHide={() => setOpen(false)}
>
<SlIcon slot="icon" name="info-circle" />
This alert will automatically hide itself after three seconds, unless you interact with it.
</SlAlert>
</div>
<style>{css}</style>
</>
);
};
```
### Toast Notifications
To display an alert as a toast notification, or "toast", create the alert and call its `toast()` method. This will move the alert out of its position in the DOM and into [the toast stack](#the-toast-stack) where it will be shown. Once dismissed, it will be removed from the DOM completely. To reuse a toast, store a reference to it and call `toast()` again later on.
@@ -173,6 +305,81 @@ You should always use the `closable` attribute so users can dismiss the notifica
</script>
```
```jsx react
import { useRef } from 'react';
import {
SlAlert,
SlButton,
SlIcon
} from '@shoelace-style/shoelace/dist/react';
function showToast(alert) {
alert.toast();
}
const App = () => {
const primary = useRef(null);
const success = useRef(null);
const neutral = useRef(null);
const warning = useRef(null);
const danger = useRef(null);
return (
<>
<SlButton type="primary" onClick={() => primary.current.toast()}>
Primary
</SlButton>
<SlButton type="success" onClick={() => success.current.toast()}>
Success
</SlButton>
<SlButton type="neutral" onClick={() => neutral.current.toast()}>
Neutral
</SlButton>
<SlButton type="warning" onClick={() => warning.current.toast()}>
Warning
</SlButton>
<SlButton type="danger" onClick={() => danger.current.toast()}>
Danger
</SlButton>
<SlAlert ref={primary} type="primary" duration="3000" closable>
<SlIcon slot="icon" name="info-circle" />
<strong>This is super informative</strong><br />
You can tell by how pretty the alert is.
</SlAlert>
<SlAlert ref={success} type="success" duration="3000" closable>
<SlIcon slot="icon" name="check2-circle" />
<strong>Your changes have been saved</strong><br />
You can safely exit the app now.
</SlAlert>
<SlAlert ref={neutral} type="neutral" duration="3000" closable>
<SlIcon slot="icon" name="gear" />
<strong>Your settings have been updated</strong><br />
Settings will take affect on next login.
</SlAlert>
<SlAlert ref={warning} type="warning" duration="3000" closable>
<SlIcon slot="icon" name="exclamation-triangle" />
<strong>Your session has ended</strong><br />
Please login again to continue.
</SlAlert>
<SlAlert ref={danger} type="danger" duration="3000" closable>
<SlIcon slot="icon" name="exclamation-octagon" />
<strong>Your account has been deleted</strong><br />
We're very sorry to see you go!
</SlAlert>
</>
);
};
```
### Creating Toasts Imperatively
For convenience, you can create a utility that emits toast notifications with a function call rather than composing them in your HTML. To do this, generate the alert with JavaScript, append it to the body, and call the `toast()` method as shown in the example below.

View File

@@ -6,11 +6,24 @@ A component for displaying animated GIFs and WEBPs that play and pause on intera
```html preview
<sl-animated-image
src="/assets/images/walk.gif"
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
></sl-animated-image>
```
```jsx react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAnimatedImage
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
/>
);
```
?> This component uses `<canvas>` to draw freeze frames, so images are subject to [cross-origin restrictions](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image).
## Examples
### WEBP Images
@@ -19,31 +32,54 @@ Both GIF and WEBP images are supported.
```html preview
<sl-animated-image
src="/assets/images/tie.webp"
src="https://shoelace.style/assets/images/tie.webp"
alt="Animation of a shoe being tied"
></sl-animated-image>
```
```jsx react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAnimatedImage
src="https://shoelace.style/assets/images/tie.webp"
alt="Animation of a shoe being tied"
/>
);
```
### Setting a Width and Height
To set a custom size, apply a width and/or height to the host element.
```html preview
<sl-animated-image
src="/assets/images/walk.gif"
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
style="width: 150px; height: 200px;"
>
</sl-animated-image>
```
```jsx react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAnimatedImage
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
style={{ width: '150px', height: '200px' }}
/>
);
```
### Customizing the Control Box
You can change the appearance and location of the control box by targeting the `control-box` part in your styles.
```html preview
<sl-animated-image
src="/assets/images/walk.gif"
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
class="animated-image-custom-control-box"
></sl-animated-image>
@@ -55,9 +91,38 @@ You can change the appearance and location of the control box by targeting the `
bottom: 1rem;
left: 1rem;
background-color: deeppink;
color: white;
border: none;
color: pink;
}
</style>
```
```jsx react
import { SlAnimatedImage } from '@shoelace-style/shoelace/dist/react';
const css = `
.animated-image-custom-control-box::part(control-box) {
top: auto;
right: auto;
bottom: 1rem;
left: 1rem;
background-color: deeppink;
border: none;
color: pink;
}
`;
const App = () => (
<>
<SlAnimatedImage
className="animated-image-custom-control-box"
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
/>
<style>{css}</style>
</>
);
```
[component-metadata:sl-animated-image]

View File

@@ -19,12 +19,39 @@ To animate an element, wrap it in `<sl-animation>` and set an animation `name`.
display: inline-block;
width: 100px;
height: 100px;
background-color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-600);
margin: 1.5rem;
}
</style>
```
```jsx react
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
const css = `
.animation-overview .box {
display: inline-block;
width: 100px;
height: 100px;
background-color: var(--sl-color-primary-600);
margin: 1.5rem;
}
`;
const App = () => (
<>
<div class="animation-overview">
<SlAnimation name="bounce" duration={2000} play><div class="box" /></SlAnimation>
<SlAnimation name="jello" duration={2000} play><div class="box" /></SlAnimation>
<SlAnimation name="heartBeat" duration={2000} play><div class="box" /></SlAnimation>
<SlAnimation name="flip" duration={2000} play><div class="box" /></SlAnimation>
</div>
<style>{css}</style>
</>
);
```
?> The animation will only be applied to the first child element found in `<sl-animation>`.
## Examples
@@ -42,7 +69,7 @@ This example demonstrates all of the baked-in animations and easings. Animations
<div class="controls">
<sl-select label="Animation" value="bounce"></sl-select>
<sl-select label="Easing" value="linear"></sl-select>
<sl-range min="0" max="2" step=".5" value="1"></sl-range>
<sl-input label="Playback Rate" type="number" min="0" max="2" step=".25" value="1"></sl-input>
</div>
</div>
@@ -53,7 +80,7 @@ This example demonstrates all of the baked-in animations and easings. Animations
const animation = container.querySelector('sl-animation');
const animationName = container.querySelector('.controls sl-select:nth-child(1)');
const easingName = container.querySelector('.controls sl-select:nth-child(2)');
const playbackRate = container.querySelector('sl-range');
const playbackRate = container.querySelector('sl-input[type="number"]');
const animations = getAnimationNames();
const easings = getEasingNames();
@@ -75,15 +102,14 @@ This example demonstrates all of the baked-in animations and easings. Animations
animationName.addEventListener('sl-change', () => animation.name = animationName.value);
easingName.addEventListener('sl-change', () => animation.easing = easingName.value);
playbackRate.addEventListener('sl-change', () => animation.playbackRate = playbackRate.value);
playbackRate.tooltipFormatter = val => `Playback Rate = ${val}`;
playbackRate.addEventListener('sl-input', () => animation.playbackRate = playbackRate.value);
</script>
<style>
.animation-sandbox .box {
width: 100px;
height: 100px;
background-color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-600);
}
.animation-sandbox .controls {
@@ -129,11 +155,68 @@ Use an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/
display: inline-block;
width: 100px;
height: 100px;
background-color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-600);
}
</style>
```
```jsx react
import { useEffect, useRef, useState } from 'react';
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
const css = `
.animation-scroll {
height: calc(100vh + 100px);
}
.animation-scroll .box {
display: inline-block;
width: 100px;
height: 100px;
background-color: var(--sl-color-primary-600);
}
`;
const App = () => {
const animation = useRef(null);
const box = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
animation.current.play = true;
} else {
animation.current.play = false;
animation.current.currentTime = 0;
}
}
);
if (box.current) {
observer.observe(box.current);
}
}, [box]);
return (
<>
<div class="animation-scroll">
<SlAnimation
ref={animation}
name="jackInTheBox"
duration={2000}
iterations={1}
>
<div ref={box} class="box" />
</SlAnimation>
</div>
<style>{css}</style>
</>
);
};
```
### Custom Keyframe Formats
Supply your own [keyframe formats](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats) to build custom animations.
@@ -169,11 +252,55 @@ Supply your own [keyframe formats](https://developer.mozilla.org/en-US/docs/Web/
.animation-keyframes .box {
width: 100px;
height: 100px;
background-color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-600);
}
</style>
```
```jsx react
import { SlAnimation } from '@shoelace-style/shoelace/dist/react';
const css = `
.animation-keyframes .box {
width: 100px;
height: 100px;
background-color: var(--sl-color-primary-600);
}
`;
const App = () => (
<>
<div class="animation-keyframes">
<SlAnimation
easing="ease-in-out"
duration={2000}
play
keyframes={[
{
offset: 0,
easing: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',
fillMode: 'both',
transformOrigin: 'center center',
transform: 'rotate(0)'
},
{
offset: 1,
easing: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',
fillMode: 'both',
transformOrigin: 'center center',
transform: 'rotate(90deg)'
}
]}
>
<div class="box" />
</SlAnimation>
</div>
<style>{css}</style>
</>
);
```
### Playing Animations on Demand
Animations won't play until you apply the `play` attribute. You can omit it initially, then apply it on demand such as after a user interaction. In this example, the button will animate once every time the button is clicked.
@@ -196,4 +323,29 @@ Animations won't play until you apply the `play` attribute. You can omit it init
</script>
```
```jsx react
import { useState } from 'react';
import { SlAnimation, SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [play, setPlay] = useState(false);
return (
<div class="animation-form">
<SlAnimation
name="rubberBand"
duration={1000}
iterations={1}
play={play}
onSlFinish={() => setPlay(false)}
>
<SlButton type="primary" onClick={() => setPlay(true)}>
Click me
</SlButton>
</SlAnimation>
</div>
);
};
```
[component-metadata:sl-animation]

View File

@@ -4,8 +4,18 @@
Avatars are used to represent a person or object.
Like [images](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img), you should always provide `alt` text for avatars as alternate text for assistive devices.
```html preview
<sl-avatar></sl-avatar>
<sl-avatar alt="User avatar"></sl-avatar>
```
```jsx react
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAvatar alt="User avatar" />
);
```
## Examples
@@ -17,16 +27,35 @@ To use an image for the avatar, set the `image` and `alt` attributes. This will
```html preview
<sl-avatar
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
alt="Gray tabby kitten looking down"
alt="Avatar of a gray tabby kitten looking down"
></sl-avatar>
```
```jsx react
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAvatar
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
alt="Avatar of a gray tabby kitten looking down"
/>
);
```
### Initials
When you don't have an image to use, you can set the `initials` attribute to show something more personalized than an icon.
```html preview
<sl-avatar initials="SL"></sl-avatar>
<sl-avatar initials="SL" alt="Avatar with initials: SL"></sl-avatar>
```
```jsx react
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlAvatar initials="SL" alt="Avatar with initials: SL" />
);
```
### Custom Icons
@@ -34,31 +63,60 @@ When you don't have an image to use, you can set the `initials` attribute to sho
When no image or initials are set, an icon will be shown. The default avatar shows a generic "user" icon, but you can customize this with the `icon` slot.
```html preview
<sl-avatar>
<sl-avatar alt="Avatar with an image icon">
<sl-icon slot="icon" name="image"></sl-icon>
</sl-avatar>
<sl-avatar>
<sl-avatar alt="Avatar with an archive icon">
<sl-icon slot="icon" name="archive"></sl-icon>
</sl-avatar>
<sl-avatar>
<sl-avatar alt="Avatar with a briefcase icon">
<sl-icon slot="icon" name="briefcase"></sl-icon>
</sl-avatar>
```
```jsx react
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlAvatar alt="Avatar with an image icon">
<SlIcon slot="icon" name="image" />
</SlAvatar>
<SlAvatar alt="Avatar with an archive icon">
<SlIcon slot="icon" name="archive" />
</SlAvatar>
<SlAvatar alt="Avatar with a briefcase icon">
<SlIcon slot="icon" name="briefcase" />
</SlAvatar>
</>
);
```
### Shapes
Avatars can be shaped using the `shape` attribute.
```html preview
<sl-avatar shape="square"></sl-avatar>
<sl-avatar shape="rounded"></sl-avatar>
<sl-avatar shape="circle"></sl-avatar>
<sl-avatar shape="square" alt="Square avatar"></sl-avatar>
<sl-avatar shape="rounded" alt="Rounded avatar"></sl-avatar>
<sl-avatar shape="circle" alt="Circle avatar"></sl-avatar>
```
```jsx react
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlAvatar shape="square" alt="Square avatar" />
<SlAvatar shape="rounded" alt="Rounded avatar" />
<SlAvatar shape="circle" alt="Circle avatar" />
</>
);
```
### Avatar Groups
@@ -66,10 +124,25 @@ You can group avatars with a few lines of CSS.
```html preview
<div class="avatar-group">
<sl-avatar image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"></sl-avatar>
<sl-avatar image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"></sl-avatar>
<sl-avatar image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"></sl-avatar>
<sl-avatar image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"></sl-avatar>
<sl-avatar
image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"
alt="Avatar 1 of 4"
></sl-avatar>
<sl-avatar
image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
alt="Avatar 2 of 4"
></sl-avatar>
<sl-avatar
image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
alt="Avatar 3 of 4"
></sl-avatar>
<sl-avatar
image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"
alt="Avatar 4 of 4"
></sl-avatar>
</div>
<style>
@@ -78,9 +151,51 @@ You can group avatars with a few lines of CSS.
}
.avatar-group sl-avatar::part(base) {
border: solid 2px rgb(var(--sl-color-neutral-0));
border: solid 2px var(--sl-color-neutral-0);
}
</style>
```
```jsx react
import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
const css = `
.avatar-group sl-avatar:not(:first-of-type) {
margin-left: -1rem;
}
.avatar-group sl-avatar::part(base) {
border: solid 2px var(--sl-color-neutral-0);
}
`;
const App = () => (
<>
<div className="avatar-group">
<SlAvatar
image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"
alt="Avatar 1 of 4"
/>
<SlAvatar
image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
alt="Avatar 2 of 4"
/>
<SlAvatar
image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
alt="Avatar 3 of 4"
/>
<SlAvatar
image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"
alt="Avatar 4 of 4"
/>
</div>
<style>{css}</style>
</>
);
```
[component-metadata:sl-avatar]

View File

@@ -8,6 +8,14 @@ Badges are used to draw attention and display statuses or counts.
<sl-badge>Badge</sl-badge>
```
```jsx react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlBadge>Badge</SlBadge>
);
```
## Examples
### Types
@@ -22,6 +30,20 @@ Set the `type` attribute to change the badge's type.
<sl-badge type="danger">Danger</sl-badge>
```
```jsx react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlBadge type="primary">Primary</SlBadge>
<SlBadge type="success">Success</SlBadge>
<SlBadge type="neutral">Neutral</SlBadge>
<SlBadge type="warning">Warning</SlBadge>
<SlBadge type="danger">Danger</SlBadge>
</>
);
```
### Pill Badges
Use the `pill` attribute to give badges rounded edges.
@@ -34,6 +56,20 @@ Use the `pill` attribute to give badges rounded edges.
<sl-badge type="danger" pill>Danger</sl-badge>
```
```jsx react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlBadge type="primary" pill>Primary</SlBadge>
<SlBadge type="success" pill>Success</SlBadge>
<SlBadge type="neutral" pill>Neutral</SlBadge>
<SlBadge type="warning" pill>Warning</SlBadge>
<SlBadge type="danger" pill>Danger</SlBadge>
</>
);
```
### Pulsating Badges
Use the `pulse` attribute to draw attention to the badge with a subtle animation.
@@ -54,6 +90,30 @@ Use the `pulse` attribute to draw attention to the badge with a subtle animation
</style>
```
```jsx react
import { SlBadge } from '@shoelace-style/shoelace/dist/react';
const css = `
.badge-pulse sl-badge:not(:last-of-type) {
margin-right: 1rem;
}
`;
const App = () => (
<>
<div className="badge-pulse">
<SlBadge type="primary" pill pulse>1</SlBadge>
<SlBadge type="success" pill pulse>1</SlBadge>
<SlBadge type="neutral" pill pulse>1</SlBadge>
<SlBadge type="warning" pill pulse>1</SlBadge>
<SlBadge type="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.
@@ -75,16 +135,57 @@ One of the most common use cases for badges is attaching them to buttons. To mak
</sl-button>
```
```jsx react
import { SlBadge, SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton>
Requests
<SlBadge pill>30</SlBadge>
</SlButton>
<SlButton style={{ marginLeft: '1rem' }}>
Warnings
<SlBadge type="warning" pill>8</SlBadge>
</SlButton>
<SlButton style={{ marginLeft: '1rem' }}>
Errors
<SlBadge type="danger" pill>6</SlBadge>
</SlButton>
</>
);
```
### 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; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu style="max-width: 240px; border: solid 1px var(--sl-panel-border-color); border-radius: var(--sl-border-radius-medium);">
<sl-menu-label>Messages</sl-menu-label>
<sl-menu-item>Comments <sl-badge slot="suffix" type="neutral" pill>4</sl-badge></sl-menu-item>
<sl-menu-item>Replies <sl-badge slot="suffix" type="neutral" pill>12</sl-badge></sl-menu-item>
</sl-menu>
```
```jsx react
import { SlBadge, SlButton, SlMenu, SlMenuItem, SlMenuLabel } from '@shoelace-style/shoelace/dist/react';
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" type="neutral" pill>4</SlBadge></SlMenuItem>
<SlMenuItem>Replies <SlBadge slot="suffix" type="neutral" pill>12</SlBadge></SlMenuItem>
</SlMenu>
);
```
[component-metadata:sl-badge]

View File

@@ -15,6 +15,21 @@ Breadcrumb Items are used inside [breadcrumbs](/components/breadcrumb) to repres
</sl-breadcrumb>
```
```jsx react
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem>
<SlIcon slot="prefix" name="house"></SlIcon>
Home
</SlBreadcrumbItem>
<SlBreadcrumbItem>Clothing</SlBreadcrumbItem>
<SlBreadcrumbItem>Shirts</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
?> Additional demonstrations can be found in the [breadcrumb examples](/components/breadcrumb).
[component-metadata:sl-breadcrumb-item]

View File

@@ -15,6 +15,19 @@ Breadcrumbs are usually placed before a page's main content with the current pag
</sl-breadcrumb>
```
```jsx react
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlBreadcrumb>
<SlBreadcrumbItem>Catalog</SlBreadcrumbItem>
<SlBreadcrumbItem>Clothing</SlBreadcrumbItem>
<SlBreadcrumbItem>Women's</SlBreadcrumbItem>
<SlBreadcrumbItem>Shirts &amp; Tops</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
## Examples
### Breadcrumb Links
@@ -43,6 +56,30 @@ For websites, you'll probably want to use links instead. You can make any breadc
</sl-breadcrumb>
```
```jsx react
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
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.
@@ -74,6 +111,40 @@ Use the `separator` slot to change the separator that goes between breadcrumb it
</sl-breadcrumb>
```
```jsx react
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import { SlBreadcrumb, SlBreadcrumbItem } from '@shoelace-style/shoelace/dist/react';
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.
@@ -89,6 +160,21 @@ Use the `prefix` slot to add content before any breadcrumb item.
</sl-breadcrumb>
```
```jsx react
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
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.
@@ -104,6 +190,21 @@ Use the `suffix` slot to add content after any breadcrumb item.
</sl-breadcrumb>
```
```jsx react
import { SlBreadcrumb, SlBreadcrumbItem, SlIcon } from '@shoelace-style/shoelace/dist/react';
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.
@@ -129,4 +230,37 @@ Dropdown menus can be placed in a prefix or suffix slot to provide additional op
</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 checked>Web Design</SlMenuItem>
<SlMenuItem>Web Development</SlMenuItem>
<SlMenuItem>Marketing</SlMenuItem>
</SlMenu>
</SlDropdown>
</SlBreadcrumbItem>
</SlBreadcrumb>
);
```
[component-metadata:sl-breadcrumb]

View File

@@ -12,6 +12,18 @@ Button groups can be used to group related buttons into sections.
</sl-button-group>
```
```jsx react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButtonGroup>
<SlButton>Left</SlButton>
<SlButton>Center</SlButton>
<SlButton>Right</SlButton>
</SlButtonGroup>
);
```
## Examples
### Button Sizes
@@ -42,6 +54,36 @@ All button sizes are supported, but avoid mixing sizes within the same button gr
</sl-button-group>
```
```jsx react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButton size="small">Left</SlButton>
<SlButton size="small">Center</SlButton>
<SlButton size="small">Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<SlButton size="medium">Left</SlButton>
<SlButton size="medium">Center</SlButton>
<SlButton size="medium">Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<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 `type` attribute.
@@ -86,6 +128,52 @@ Theme buttons are supported through the button's `type` attribute.
</sl-button-group>
```
```jsx react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButton type="primary">Left</SlButton>
<SlButton type="primary">Center</SlButton>
<SlButton type="primary">Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<SlButton type="success">Left</SlButton>
<SlButton type="success">Center</SlButton>
<SlButton type="success">Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<SlButton type="neutral">Left</SlButton>
<SlButton type="neutral">Center</SlButton>
<SlButton type="neutral">Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<SlButton type="warning">Left</SlButton>
<SlButton type="warning">Center</SlButton>
<SlButton type="warning">Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<SlButton type="danger">Left</SlButton>
<SlButton type="danger">Center</SlButton>
<SlButton type="danger">Right</SlButton>
</SlButtonGroup>
</>
);
```
### Pill Buttons
Pill buttons are supported through the button's `pill` attribute.
@@ -114,6 +202,36 @@ Pill buttons are supported through the button's `pill` attribute.
</sl-button-group>
```
```jsx react
import { SlButton, SlButtonGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<SlButton size="small" pill>Left</SlButton>
<SlButton size="small" pill>Center</SlButton>
<SlButton size="small" pill>Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<SlButton size="medium" pill>Left</SlButton>
<SlButton size="medium" pill>Center</SlButton>
<SlButton size="medium" pill>Right</SlButton>
</SlButtonGroup>
<br /><br />
<SlButtonGroup>
<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.
@@ -133,6 +251,31 @@ Dropdowns can be placed inside button groups as long as the trigger is an `<sl-b
</sl-button-group>
```
```jsx react
import {
SlButton,
SlButtonGroup,
SlDropdown,
SlMenu,
SlMenuItem,
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButtonGroup>
<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.
@@ -151,6 +294,30 @@ Create a split button using a button and a dropdown.
</sl-button-group>
```
```jsx react
import {
SlButton,
SlButtonGroup,
SlDropdown,
SlMenu,
SlMenuItem,
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButtonGroup>
<SlButton type="primary">Save</SlButton>
<SlDropdown placement="bottom-end">
<SlButton slot="trigger" type="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.
@@ -171,6 +338,32 @@ Buttons can be wrapped in tooltips to provide more detail when the user interact
</sl-button-group>
```
```jsx react
import {
SlButton,
SlButtonGroup,
SlTooltip
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButtonGroup>
<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.
@@ -218,4 +411,60 @@ Create interactive toolbars with button groups.
</style>
```
```jsx react
import {
SlButton,
SlButtonGroup,
SlIcon,
SlTooltip
} from '@shoelace-style/shoelace/dist/react';
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>
</>
);
```
[component-metadata:sl-button-group]

View File

@@ -8,6 +8,14 @@ Buttons represent actions that are available to the user.
<sl-button>Button</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlButton>Button</SlButton>
);
```
## Examples
### Types
@@ -23,6 +31,21 @@ Use the `type` attribute to set the button's type.
<sl-button type="danger">Danger</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="default">Default</SlButton>
<SlButton type="primary">Primary</SlButton>
<SlButton type="success">Success</SlButton>
<SlButton type="neutral">Neutral</SlButton>
<SlButton type="warning">Warning</SlButton>
<SlButton type="danger">Danger</SlButton>
</>
);
```
### Sizes
Use the `size` attribute to change a button's size.
@@ -33,6 +56,18 @@ Use the `size` attribute to change a button's size.
<sl-button size="large">Large</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
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.
@@ -46,6 +81,21 @@ Use the `outline` attribute to draw outlined buttons with transparent background
<sl-button type="danger" outline>Danger</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="default" outline>Default</SlButton>
<SlButton type="primary" outline>Primary</SlButton>
<SlButton type="success" outline>Success</SlButton>
<SlButton type="neutral" outline>Neutral</SlButton>
<SlButton type="warning" outline>Warning</SlButton>
<SlButton type="danger" outline>Danger</SlButton>
</>
);
```
### Pill Buttons
Use the `pill` attribute to give buttons rounded edges.
@@ -56,6 +106,18 @@ Use the `pill` attribute to give buttons rounded edges.
<sl-button size="large" pill>Large</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
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.
@@ -66,6 +128,18 @@ Use the `circle` attribute to create circular icon buttons.
<sl-button type="default" size="large" circle><sl-icon name="gear"></sl-icon></sl-button>
```
```jsx react
import { SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="default" size="small" circle><SlIcon name="gear" /></SlButton>
<SlButton type="default" size="medium" circle><SlIcon name="gear" /></SlButton>
<SlButton type="default" size="large" circle><SlIcon name="gear" /></SlButton>
</>
);
```
### Text Buttons
Use the `text` type to create text buttons that share the same size as regular buttons but don't have backgrounds or borders.
@@ -76,6 +150,18 @@ Use the `text` type to create text buttons that share the same size as regular b
<sl-button type="text" size="large">Text</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="text" size="small">Text</SlButton>
<SlButton type="text" size="medium">Text</SlButton>
<SlButton type="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. <kbd>CMD/CTRL/SHIFT + CLICK</kbd>) and exposes the `target` and `download` attributes.
@@ -87,6 +173,19 @@ It's often helpful to have a button that works like a link. This is possible by
<sl-button href="https://example.com/" disabled>Disabled</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
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>
</>
);
```
?> 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
@@ -99,6 +198,18 @@ As expected, buttons can be given a custom width by setting its `width`. This is
<sl-button type="default" size="large" style="width: 100%;">Large</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="default" size="small" style={{ width: '100%', marginBottom: '1rem' }}>Small</SlButton>
<SlButton type="default" size="medium" style={{ width: '100%', marginBottom: '1rem' }}>Medium</SlButton>
<SlButton type="default" size="large" style={{ width: '100%' }}>Large</SlButton>
</>
);
```
### Prefix and Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
@@ -157,6 +268,66 @@ Use the `prefix` and `suffix` slots to add icons.
</sl-button>
```
```jsx react
import { SlButton, SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="default" size="small">
<SlIcon slot="prefix" name="gear"></SlIcon>
Settings
</SlButton>
<SlButton type="default" size="small">
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
Refresh
</SlButton>
<SlButton type="default" size="small">
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
Open
</SlButton>
<br /><br/ >
<SlButton type="default">
<SlIcon slot="prefix" name="gear"></SlIcon>
Settings
</SlButton>
<SlButton type="default">
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
Refresh
</SlButton>
<SlButton type="default">
<SlIcon slot="prefix" name="link-45deg"></SlIcon>
<SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon>
Open
</SlButton>
<br /><br />
<SlButton type="default" size="large">
<SlIcon slot="prefix" name="gear"></SlIcon>
Settings
</SlButton>
<SlButton type="default" size="large">
<SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon>
Refresh
</SlButton>
<SlButton type="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.
@@ -167,6 +338,18 @@ Use the `caret` attribute to add a dropdown indicator when a button will trigger
<sl-button size="large" caret>Large</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
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.
@@ -180,6 +363,21 @@ Use the `loading` attribute to make a button busy. The width will remain the sam
<sl-button type="danger" loading>Danger</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="default" loading>Default</SlButton>
<SlButton type="primary" loading>Primary</SlButton>
<SlButton type="success" loading>Success</SlButton>
<SlButton type="neutral" loading>Neutral</SlButton>
<SlButton type="warning" loading>Warning</SlButton>
<SlButton type="danger" loading>Danger</SlButton>
</>
);
```
### Disabled
Use the `disabled` attribute to disable a button. Clicks will be suppressed until the disabled state is removed.
@@ -193,6 +391,21 @@ Use the `disabled` attribute to disable a button. Clicks will be suppressed unti
<sl-button type="danger" disabled>Danger</sl-button>
```
```jsx react
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlButton type="default" disabled>Default</SlButton>
<SlButton type="primary" disabled>Primary</SlButton>
<SlButton type="success" disabled>Success</SlButton>
<SlButton type="neutral" disabled>Neutral</SlButton>
<SlButton type="warning" disabled>Warning</SlButton>
<SlButton type="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 type attribute instead of a class (e.g. `sl-button[type="primary"]`).

View File

@@ -28,7 +28,7 @@ Cards can be used to group related subjects in a container.
}
.card-overview small {
color: rgb(var(--sl-color-neutral-500));
color: var(--sl-color-neutral-500);
}
.card-overview [slot="footer"] {
@@ -39,6 +39,53 @@ Cards can be used to group related subjects in a container.
</style>
```
```jsx react
import {
SlButton,
SlCard,
SlRating
} from '@shoelace-style/shoelace/dist/react';
const css = `
.card-overview {
max-width: 300px;
}
.card-overview small {
color: var(--sl-color-neutral-500);
}
.card-overview [slot="footer"] {
display: flex;
justify-content: space-between;
align-items: center;
}
`;
const App = () => (
<>
<SlCard className="card-overview">
<img
slot="image"
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
alt="A kitten sits patiently between a terracotta pot and decorative grasses."
/>
<strong>Mittens</strong><br />
This kitten is as cute as he is playful. Bring him home today!<br />
<small>6 weeks old</small>
<div slot="footer">
<SlButton type="primary" pill>More Info</SlButton>
<SlRating></SlRating>
</div>
</SlCard>
<style>{css}</style>
</>
);
```
## Examples
## Basic Card
@@ -57,6 +104,26 @@ Basic cards aren't very exciting, but they can display any content you want them
</style>
```
```jsx react
import { SlCard } from '@shoelace-style/shoelace/dist/react';
const css = `
.card-basic {
max-width: 300px;
}
`;
const App = () => (
<>
<SlCard className="card-basic">
This is just a basic card. No image, no header, and no footer. Just your content.
</SlCard>
<style>{css}</style>
</>
);
```
## Card with Header
Headers can be used to display titles and more.
@@ -73,26 +140,66 @@ Headers can be used to display titles and more.
</sl-card>
<style>
.card-header {
max-width: 300px;
}
.card-header {
max-width: 300px;
}
.card-header [slot="header"] {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-header [slot="header"] {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-header h3 {
margin: 0;
}
.card-header h3 {
margin: 0;
}
.card-header sl-icon-button {
font-size: var(--sl-font-size-medium);
}
.card-header sl-icon-button {
font-size: var(--sl-font-size-medium);
}
</style>
```
```jsx react
import { SlCard, SlIconButton } from '@shoelace-style/shoelace/dist/react';
const css = `
.card-header {
max-width: 300px;
}
.card-header [slot="header"] {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-header h3 {
margin: 0;
}
.card-header sl-icon-button {
font-size: var(--sl-font-size-medium);
}
`;
const App = () => (
<>
<SlCard className="card-header">
<div slot="header">
Header Title
<SlIconButton name="gear"></SlIconButton>
</div>
This card has a header. You can put all sorts of things in it!
</SlCard>
<style>{css}</style>
</>
);
```
## Card with Footer
Footers can be used to display actions, summaries, or other relevant content.
@@ -120,6 +227,41 @@ Footers can be used to display actions, summaries, or other relevant content.
</style>
```
```jsx react
import {
SlButton,
SlCard,
SlRating
} from '@shoelace-style/shoelace/dist/react';
const css = `
.card-footer {
max-width: 300px;
}
.card-footer [slot="footer"] {
display: flex;
justify-content: space-between;
align-items: center;
}
`;
const App = () => (
<>
<SlCard className="card-footer">
This card has a footer. You can put all sorts of things in it!
<div slot="footer">
<SlRating></SlRating>
<SlButton slot="footer" type="primary">Preview</SlButton>
</div>
</SlCard>
<style>{css}</style>
</>
);
```
## Images
Cards accept an `image` slot. The image is displayed atop the card and stretches to fit.
@@ -141,4 +283,29 @@ Cards accept an `image` slot. The image is displayed atop the card and stretches
</style>
```
```jsx react
import { SlCard } from '@shoelace-style/shoelace/dist/react';
const css = `
.card-image {
max-width: 300px;
}
`;
const App = () => (
<>
<SlCard className="card-image">
<img
slot="image"
src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80"
alt="A kitten walks towards camera on top of pallet."
/>
This is a kitten, but not just any kitten. This kitten likes walking along pallets.
</SlCard>
<style>{css}</style>
</>
);
```
[component-metadata:sl-card]

View File

@@ -8,6 +8,14 @@ Checkboxes allow the user to toggle an option on or off.
<sl-checkbox>Checkbox</sl-checkbox>
```
```jsx react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlCheckbox>Checkbox</SlCheckbox>
);
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
## Examples
@@ -20,6 +28,14 @@ Use the `checked` attribute to activate the checkbox.
<sl-checkbox checked>Checked</sl-checkbox>
```
```jsx react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlCheckbox checked>Checked</SlCheckbox>
);
```
### Indeterminate
Use the `indeterminate` attribute to make the checkbox indeterminate.
@@ -28,6 +44,14 @@ Use the `indeterminate` attribute to make the checkbox indeterminate.
<sl-checkbox indeterminate>Indeterminate</sl-checkbox>
```
```jsx react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlCheckbox indeterminate>Indeterminate</SlCheckbox>
);
```
### Disabled
Use the `disabled` attribute to disable the checkbox.
@@ -36,4 +60,12 @@ Use the `disabled` attribute to disable the checkbox.
<sl-checkbox disabled>Disabled</sl-checkbox>
```
```jsx react
import { SlCheckbox } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlCheckbox disabled>Disabled</SlCheckbox>
);
```
[component-metadata:sl-checkbox]

View File

@@ -8,6 +8,14 @@ Color pickers allow the user to select a color.
<sl-color-picker></sl-color-picker>
```
```jsx react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlColorPicker />
);
```
## Examples
### Opacity
@@ -18,6 +26,14 @@ Use the `opacity` attribute to enable the opacity slider. When this is enabled,
<sl-color-picker opacity></sl-color-picker>
```
```jsx react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlColorPicker opacity />
);
```
### Formats
Set the color picker's format with the `format` attribute. Valid options include `hex`, `rgb`, and `hsl`. Note that the color picker's input will accept any parsable format (including CSS color names) regardless of this option.
@@ -30,6 +46,18 @@ To prevent users from toggling the format themselves, add the `no-format-toggle`
<sl-color-picker format="hsl" value="hsl(290, 87%, 47%)"></sl-color-picker>
```
```jsx react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlColorPicker format="hex" value="#4a90e2" />
<SlColorPicker format="rgb" value="rgb(80, 227, 194)" />
<SlColorPicker format="hsl" value="hsl(290, 87%, 47%)" />
</>
);
```
### Sizes
Use the `size` attribute to change the color picker's trigger size.
@@ -40,6 +68,17 @@ Use the `size` attribute to change the color picker's trigger size.
<sl-color-picker size="large"></sl-color-picker>
```
```jsx react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlColorPicker size="small" />
<SlColorPicker size="medium" />
<SlColorPicker size="large" />
</>
);
```
### Inline
@@ -49,4 +88,12 @@ The color picker can be rendered inline instead of in a dropdown using the `inli
<sl-color-picker inline></sl-color-picker>
```
```jsx react
import { SlColorPicker } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlColorPicker inline />
);
```
[component-metadata:sl-color-picker]

View File

@@ -1,140 +0,0 @@
# Context Menu
[component-header:sl-context-menu]
Context menus offer additional options through a menu that opens at the pointer's location, usually activated by a right-click.
Context menus are designed to work with [menus](/components/menu) and [menu items](/components/menu-item). The menu must include `slot="menu"`. Other content you provide will be part of the context menu's target area.
```html preview
<sl-context-menu>
<div style="height: 200px; background: rgb(var(--sl-color-neutral-100)); display: flex; align-items: center; justify-content: center; padding: 1rem;">
Right-click to activate the context menu
</div>
<sl-menu slot="menu">
<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>
</sl-context-menu>
```
## Examples
### Handling Selections
The [menu component](/components/menu) emits an `sl-select` event when a menu item is selected. You can use this to handle selections. The selected item will be available in `event.detail.item`.
```html preview
<div class="context-menu-selections">
<sl-context-menu>
<div style="height: 200px; background: rgb(var(--sl-color-neutral-100)); display: flex; align-items: center; justify-content: center; padding: 1rem;">
Right-click to activate the context menu
</div>
<sl-menu slot="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-context-menu>
</div>
<script>
const container = document.querySelector('.context-menu-selections');
const menu = container.querySelector('sl-menu');
const result = container.querySelector('.result');
menu.addEventListener('sl-select', event => {
console.log(`You selected: ${event.detail.item.value}`);
});
</script>
```
### Inline
The context menu uses `display: contents`, so it will assume the shape of the content you slot in.
```html preview
<sl-context-menu>
<span style="background: rgb(var(--sl-color-neutral-100)); padding: .5rem 1rem;">
Right-click here
</span>
<sl-menu slot="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-context-menu>
```
### Placement
The preferred placement of the context menu can be set with the `placement` attribute. Note that the actual position may vary to ensure the menu remains in the viewport.
```html preview
<sl-context-menu placement="top-end">
<div style="height: 200px; background: rgb(var(--sl-color-neutral-100)); display: flex; align-items: center; justify-content: center; padding: 1rem;">
Right-click to activate the context menu
</div>
<sl-menu slot="menu">
<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>
</sl-context-menu>
```
### Detecting the Target Item
A single context menu can wrap a number of items. To detect the item that activated the context menu...
TODO
```html preview
<div class="context-menu-detecting">
<sl-context-menu>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
<sl-menu slot="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-context-menu>
</div>
<style>
.context-menu-detecting ul {
max-width: 300px;
list-style: none;
padding: 0;
margin: 0;
}
.context-menu-detecting li {
background: rgb(var(--sl-color-neutral-100));
padding: .5rem 1rem;
margin: 0 0 2px 0;
}
</style>
```
[component-metadata:sl-context-menu]

View File

@@ -11,6 +11,17 @@ Details show a brief summary and expand to show additional content.
</sl-details>
```
```jsx react
import { SlDetails } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlDetails summary="Toggle Me">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</SlDetails>
);
```
## Examples
### Disabled
@@ -24,6 +35,17 @@ Use the `disable` attribute to prevent the details from expanding.
</sl-details>
```
```jsx react
import { SlDetails } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlDetails summary="Disabled" disabled>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</SlDetails>
);
```
### Grouping Details
Details are designed to function independently, but you can simulate a group or "accordion" where only one is shown at a time by listening for the `sl-show` event.

View File

@@ -22,6 +22,28 @@ Dialogs, sometimes called "modals", appear above the page and require the user's
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDialog>
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
</>
);
};
```
## UX Tips
- Use a dialog when you immediately require the user's attention, e.g. confirming a destructive action.
@@ -52,13 +74,40 @@ Use the `--width` custom property to set the dialog's width.
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDialog
label="Dialog"
open={open}
style={{ '--width': '50vw' }}
onSlAfterHide={() => setOpen(false)}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDialog>
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
</>
);
};
```
### Scrolling
By design, a dialog's height will never exceed that of the viewport. As such, dialogs will not scroll with the page ensuring the header and footer are always accessible to the user.
```html preview
<sl-dialog label="Dialog" class="dialog-scrolling">
<div style="height: 150vh; border: dashed 2px rgb(var(--sl-color-neutral-200)); padding: 0 1rem;">
<div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;">
<p>Scroll down and give it a try! 👇</p>
</div>
<sl-button slot="footer" type="primary">Close</sl-button>
@@ -76,6 +125,35 @@ By design, a dialog's height will never exceed that of the viewport. As such, di
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}>
<div style={{
height: '150vh',
border: 'dashed 2px var(--sl-color-neutral-200)',
padding: '0 1rem'
}}>
<p>Scroll down and give it a try! 👇</p>
</div>
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDialog>
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
</>
);
};
```
### Preventing the Dialog from Closing
By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
@@ -102,9 +180,36 @@ To keep the dialog open in such cases, you can cancel the `sl-request-close` eve
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDialog } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDialog
label="Dialog"
open={open}
onSlRequestClose={event => event.preventDefault()}
onSlAfterHide={() => setOpen(false)}
>
This dialog will not close unless you use the button below.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Save &amp; Close
</SlButton>
</SlDialog>
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
</>
);
};
```
### Customizing Initial Focus
By default, the dialog's panel will gain focus when opened. This allows the first tab press to focus on the first tabbable element within the dialog. To set focus on a different element, listen for and cancel the `sl-initial-focus` event.
By default, the dialog's panel will gain focus when opened. This allows a subsequent tab press to focus on the first tabbable element within the dialog. To set focus on a different element, listen for and cancel the `sl-initial-focus` event.
```html preview
<sl-dialog label="Dialog" class="dialog-focus">
@@ -130,4 +235,41 @@ By default, the dialog's panel will gain focus when opened. This allows the firs
</script>
```
```jsx react
import { useRef, useState } from 'react';
import {
SlButton,
SlDialog,
SlInput
} from '@shoelace-style/shoelace/dist/react';
const App = () => {
const input = useRef(null);
const [open, setOpen] = useState(false);
function handleInitialFocus(event) {
event.preventDefault();
input.current.focus();
}
return (
<>
<SlDialog
label="Dialog"
open={open}
onSlInitialFocus={handleInitialFocus}
onSlAfterHide={() => setOpen(false)}
>
<SlInput ref={input} placeholder="I will have focus when the dialog is opened" />
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDialog>
<SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton>
</>
);
};
```
[component-metadata:sl-dialog]

View File

@@ -8,6 +8,13 @@ Dividers are used to visually separate or group elements.
<sl-divider></sl-divider>
```
```jsx react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlDivider />
);
```
## Examples
### Width
@@ -18,6 +25,14 @@ Use the `--width` custom property to change the width of the divider.
<sl-divider style="--width: 4px;"></sl-divider>
```
```jsx react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlDivider style={{ '--width': '4px' }} />
);
```
### Color
Use the `--color` custom property to change the color of the divider.
@@ -26,6 +41,14 @@ Use the `--color` custom property to change the color of the divider.
<sl-divider style="--color: tomato;"></sl-divider>
```
```jsx react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlDivider style={{ '--color': 'tomato' }} />
);
```
### Spacing
Use the `--spacing` custom property to change the amount of space between the divider and it's neighboring elements.
@@ -38,6 +61,18 @@ Use the `--spacing` custom property to change the amount of space between the di
</div>
```
```jsx react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
Above
<SlDivider style={{ '--spacing': '2rem' }} />
Below
</>
);
```
### 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.
@@ -52,12 +87,32 @@ Add the `vertical` attribute to draw the divider in a vertical orientation. The
</div>
```
```jsx react
import { SlDivider } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<div
style={{
display: 'flex',
alignItems: 'center',
height: '2rem'
}}
>
First
<SlDivider vertical />
Middle
<SlDivider vertical />
Last
</div>
);
```
### Menu Dividers
Use dividers in [menus](/components/menu) to visually group menu items.
```html preview
<sl-menu style="max-width: 200px; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);">
<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>
@@ -68,4 +123,30 @@ Use dividers in [menus](/components/menu) to visually group menu items.
</sl-menu>
```
```jsx react
import {
SlDivider,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu
style={{
maxWidth: '200px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<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>
);
```
[component-metadata:sl-divider]

View File

@@ -22,6 +22,28 @@ Drawers slide in from a container to expose additional options and information.
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
## Examples
### Slide in From Start
@@ -46,6 +68,33 @@ By default, drawers slide in from the end. To make the drawer slide in from the
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer
label="Drawer"
placement="start"
open={open}
onSlAfterHide={() => setOpen(false)}
>
This drawer slides in from the start.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Slide in From Top
To make the drawer slide in from the top, set the `placement` attribute to `top`.
@@ -68,6 +117,33 @@ To make the drawer slide in from the top, set the `placement` attribute to `top`
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer
label="Drawer"
placement="top"
open={open}
onSlAfterHide={() => setOpen(false)}
>
This drawer slides in from the top.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Slide in From Bottom
To make the drawer slide in from the bottom, set the `placement` attribute to `bottom`.
@@ -90,13 +166,40 @@ To make the drawer slide in from the bottom, set the `placement` attribute to `b
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer
label="Drawer"
placement="bottom"
open={open}
onSlAfterHide={() => setOpen(false)}
>
This drawer slides in from the bottom.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Contained to an Element
By default, the drawer slides out of its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport. To make the drawer slide out of its parent element, add the `contained` attribute and `position: relative` to the parent.
```html preview
<div
style="position: relative; border: solid 2px rgb(var(--sl-panel-border-color)); height: 300px; padding: 1rem; margin-bottom: 1rem;"
style="position: relative; border: solid 2px var(--sl-panel-border-color); height: 300px; padding: 1rem; margin-bottom: 1rem;"
>
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer opens.
@@ -118,6 +221,46 @@ By default, the drawer slides out of its [containing block](https://developer.mo
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<div
style={{
position: 'relative',
border: 'solid 2px var(--sl-panel-border-color)',
height: '300px',
padding: '1rem',
marginBottom: '1rem'
}}
>
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer opens.
<SlDrawer
label="Drawer"
contained
open={open}
onSlAfterHide={() => setOpen(false)}
style={{ '--size': '50%' }}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
</div>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Custom Size
Use the `--size` custom property to set the drawer's size. This will be applied to the drawer's width or height depending on its `placement`.
@@ -140,13 +283,39 @@ Use the `--size` custom property to set the drawer's size. This will be applied
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer
label="Drawer"
open={open} onSlAfterHide={() => setOpen(false)}
style={{ '--size': '50vw' }}
>
This drawer is always 50% of the viewport.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Scrolling
By design, a drawer's height will never exceed 100% of its container. As such, drawers will not scroll with the page to ensure the header and footer are always accessible to the user.
```html preview
<sl-drawer label="Drawer" class="drawer-scrolling">
<div style="height: 150vh; border: dashed 2px rgb(var(--sl-color-neutral-200)); padding: 0 1rem;">
<div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;">
<p>Scroll down and give it a try! 👇</p>
</div>
<sl-button slot="footer" type="primary">Close</sl-button>
@@ -164,6 +333,36 @@ By design, a drawer's height will never exceed 100% of its container. As such, d
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}>
<div
style={{
height: '150vh',
border: 'dashed 2px var(--sl-color-neutral-200)',
padding: '0 1rem'
}}
>
<p>Scroll down and give it a try! 👇</p>
</div>
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Preventing the Drawer from Closing
By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.
@@ -173,7 +372,7 @@ To keep the drawer open in such cases, you can cancel the `sl-request-close` eve
```html preview
<sl-drawer label="Drawer" class="drawer-deny-close">
This dialog will not close unless you use the button below.
This drawer will not close unless you use the button below.
<sl-button slot="footer" type="primary">Save &amp; Close</sl-button>
</sl-drawer>
@@ -191,6 +390,33 @@ To keep the drawer open in such cases, you can cancel the `sl-request-close` eve
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlDrawer } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<SlDrawer
label="Drawer"
open={open}
onSlRequestClose={event => event.preventDefault()}
onSlAfterHide={() => setOpen(false)}
>
This drawer will not close unless you use the button below.
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Save &amp; Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
### Customizing Initial Focus
By default, the drawer's panel will gain focus when opened. This allows the first tab press to focus on the first tabbable element within the drawer. To set focus on a different element, listen for and cancel the `sl-initial-focus` event.
@@ -219,4 +445,41 @@ By default, the drawer's panel will gain focus when opened. This allows the firs
</script>
```
```jsx react
import { useRef, useState } from 'react';
import {
SlButton,
SlDrawer,
SlInput
} from '@shoelace-style/shoelace/dist/react';
const App = () => {
const input = useRef(null);
const [open, setOpen] = useState(false);
function handleInitialFocus(event) {
event.preventDefault();
input.current.focus();
}
return (
<>
<SlDrawer
label="Drawer"
open={open}
onSlInitialFocus={handleInitialFocus}
onSlAfterHide={() => setOpen(false)}
>
<SlInput ref={input} placeholder="I will have focus when the drawer is opened" />
<SlButton slot="footer" type="primary" onClick={() => setOpen(false)}>
Close
</SlButton>
</SlDrawer>
<SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton>
</>
);
};
```
[component-metadata:sl-drawer]

View File

@@ -31,6 +31,40 @@ Dropdowns are designed to work well with [menus](/components/menu) to provide a
</sl-dropdown>
```
```jsx react
import {
SlButton,
SlDivider,
SlDropdown,
SlIcon,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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 checked>Checked</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
@@ -60,6 +94,93 @@ When dropdowns are used with [menus](/components/menu), you can listen for the `
</script>
```
```jsx react
import {
SlButton,
SlDropdown,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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,
SlDropdown,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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.
@@ -78,6 +199,30 @@ The preferred placement of the dropdown can be set with the `placement` attribut
</sl-dropdown>
```
```jsx react
import {
SlButton,
SlDivider,
SlDropdown,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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.
@@ -96,6 +241,30 @@ The distance from the panel to the trigger can be customized using the `distance
</sl-dropdown>
```
```jsx react
import {
SlButton,
SlDivider,
SlDropdown,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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.
@@ -114,6 +283,30 @@ The offset of the panel along the trigger can be customized using the `skidding`
</sl-dropdown>
```
```jsx react
import {
SlButton,
SlDivider,
SlDropdown,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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>
);
```
### 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, 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.
@@ -141,11 +334,56 @@ Dropdown panels will be clipped if they're inside a container that has `overflow
<style>
.dropdown-hoist {
border: solid 2px rgb(var(--sl-panel-border-color));
border: solid 2px var(--sl-panel-border-color);
padding: var(--sl-spacing-medium);
overflow: hidden;
}
</style>
```
```jsx react
import {
SlButton,
SlDivider,
SlDropdown,
SlIcon,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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>
</>
);
```
[component-metadata:sl-dropdown]

View File

@@ -30,14 +30,11 @@ Shoelace forms don't make use of `action` and `method` attributes and they don't
<script>
const form = document.querySelector('.form-overview');
// Watch for the slSubmit event
form.addEventListener('sl-submit', event => {
const formData = event.detail.formData;
let output = '';
//
// Example 1: Post data to a server and wait for a JSON response
//
// Post data to a server and wait for a JSON response
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: formData
@@ -49,24 +46,129 @@ Shoelace forms don't make use of `action` and `method` attributes and they don't
.catch(error => {
console.error('Error:', error);
});
//
// Example 2: Output all form control names + values
//
for (const entry of formData.entries()) {
output += `${entry[0]}: ${entry[1]}\n`;
}
alert(output);
//
// Example 3: Get all form controls that were serialized as
// an array of HTML elements
//
console.log(event.detail.formControls);
});
</script>
```
```jsx react
import {
SlButton,
SlCheckbox,
SlForm,
SlInput,
SlMenuItem,
SlSelect,
} from '@shoelace-style/shoelace/dist/react';
function handleSubmit(event) {
let output = '';
// Post data to a server and wait for a JSON response
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: event.detail.formData
})
.then(response => response.json())
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
});
}
const App = () => (
<SlForm onSlSubmit={handleSubmit}>
<SlInput name="name" type="text" label="Name" />
<br />
<SlSelect name="favorite" label="Select your favorite">
<SlMenuItem value="birds">Birds</SlMenuItem>
<SlMenuItem value="cats">Cats</SlMenuItem>
<SlMenuItem value="dogs">Dogs</SlMenuItem>
</SlSelect>
<br />
<SlCheckbox name="agree" value="yes">
I totally agree
</SlCheckbox>
<br /><br />
<SlButton submit>Submit</SlButton>
</SlForm>
);
```
## Handling Submissions
### Using Form Data
On submit, a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object will be attached to `event.detail.formData`. You can use this along with [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) to pass data to the server.
```html preview
<sl-form class="form-formdata">
<sl-input name="name" type="text" label="Name" required></sl-input>
<sl-input name="age" type="number" label="Age" required></sl-input>
<br>
<sl-button submit>Submit</sl-button>
</sl-form>
<script>
const form = document.querySelector('.form-formdata');
form.addEventListener('sl-submit', event => {
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: event.detail.formData
}).then(res => {
console.log(res);
}).catch(err => {
console.error(err);
});
});
</script>
```
```jsx react
import {
SlButton,
SlForm,
SlInput
} from '@shoelace-style/shoelace/dist/react';
const App = () => {
function handleSubmit(event) {
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: event.detail.formData
}).then(res => {
console.log(res);
}).catch(err => {
console.error(err);
});
}
return (
<SlForm class="form-formdata" onSlSubmit={handleSubmit}>
<SlInput name="name" type="text" label="Name" required />
<SlInput name="age" type="number" label="Age" required />
<br />
<SlButton submit>Submit</SlButton>
</SlForm>
);
};
```
### Converting Form Data to JSON
It's sometimes useful to have form values in a plain object or a JSON string. You can convert the submitted `FormData` object to JSON by iterating and placing the name/value pairs in an object.
```js
form.addEventListener('sl-submit', event => {
const json = {};
event.detail.formData.forEach((value, key) => (json[key] = value));
console.log(JSON.stringify(json));
});
```
## Form Control Validation
Client-side validation can be enabled through the browser's [Constraint Validation API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) for many form controls. You can enable it using props such as `required`, `pattern`, `minlength`, and `maxlength`. As the user interacts with the form control, the `invalid` attribute will reflect its validity based on its current value and the constraints that have been defined.
@@ -105,6 +207,37 @@ To make a field required, use the `required` prop. The form will not be submitte
</script>
```
```jsx react
import {
SlButton,
SlCheckbox,
SlForm,
SlInput,
SlMenuItem,
SlSelect,
SlTextarea
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlForm onSlSubmit={() => alert('All fields are valid!')}>
<SlInput name="name" label="Name" required />
<br />
<SlSelect label="Favorite Animal" clearable required>
<SlMenuItem value="birds">Birds</SlMenuItem>
<SlMenuItem value="cats">Cats</SlMenuItem>
<SlMenuItem value="dogs">Dogs</SlMenuItem>
<SlMenuItem value="other">Other</SlMenuItem>
</SlSelect>
<br />
<SlTextarea name="comment" label="Comment" required></SlTextarea>
<br />
<SlCheckbox required>Check me before submitting</SlCheckbox>
<br /><br />
<SlButton type="primary" submit>Submit</SlButton>
</SlForm>
);
```
### Input Patterns
To restrict a value to a specific [pattern](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern), use the `pattern` attribute. This example only allows the letters A-Z, so the form will not submit if a number or symbol is entered. This only works with `<sl-input>` elements.
@@ -122,6 +255,22 @@ To restrict a value to a specific [pattern](https://developer.mozilla.org/en-US/
</script>
```
```jsx react
import {
SlButton,
SlForm,
SlInput
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlForm onSlSubmit={() => alert('All fields are valid!')}>
<SlInput name="letters" required label="Letters" pattern="[A-Za-z]+" />
<br />
<SlButton type="primary" submit>Submit</SlButton>
</SlForm>
);
```
### Input Types
Some input types will automatically trigger constraints, such as `email` and `url`.
@@ -141,6 +290,24 @@ Some input types will automatically trigger constraints, such as `email` and `ur
</script>
```
```jsx react
import {
SlButton,
SlForm,
SlInput
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlForm onSlSubmit={() => alert('All fields are valid!')}>
<SlInput type="email" label="Email" placeholder="you@example.com" required />
<br />
<SlInput type="url" label="URL" placeholder="https://example.com/" required />
<br />
<SlButton type="primary" submit>Submit</SlButton>
</SlForm>
);
```
### Custom Validation
To create a custom validation error, use the `setCustomValidity` method. The form will not be submitted when this method is called with anything other than an empty string, and its message will be shown by the browser as the validation error. To make the input valid again, call the method a second time with an empty string as the argument.
@@ -167,6 +334,44 @@ To create a custom validation error, use the `setCustomValidity` method. The for
</script>
```
```jsx react
import { useRef, useState } from 'react';
import {
SlButton,
SlForm,
SlInput
} from '@shoelace-style/shoelace/dist/react';
const App = () => {
const input = useRef(null);
const [value, setValue] = useState('');
function handleInput(event) {
setValue(event.target.value);
if (event.target.value === 'shoelace') {
input.current.setCustomValidity('');
} else {
input.current.setCustomValidity('Hey, you\'re supposed to type \'shoelace\' before submitting this!');
}
}
return (
<SlForm onSlSubmit={() => alert('All fields are valid!')}>
<SlInput
ref={input}
label="Type 'shoelace'"
required
value={value}
onSlInput={handleInput}
/>
<br />
<SlButton type="primary" submit>Submit</SlButton>
</SlForm>
);
};
```
### Custom Validation Styles
The `invalid` attribute reflects the form control's validity, so you can style invalid fields using the `[invalid]` selector. The example below demonstrates how you can give erroneous fields a different appearance. Type something other than "shoelace" to demonstrate this.
@@ -179,19 +384,48 @@ The `invalid` attribute reflects the form control's validity, so you can style i
<style>
.custom-input[invalid]:not([disabled])::part(label),
.custom-input[invalid]:not([disabled])::part(help-text) {
color: rgb(var(--sl-color-danger-600));
color: var(--sl-color-danger-600);
}
.custom-input[invalid]:not([disabled])::part(base) {
border-color: rgb(var(--sl-color-danger-500));
border-color: var(--sl-color-danger-500);
}
.custom-input[invalid]:focus-within::part(base) {
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-danger-500) / var(--sl-focus-ring-alpha));
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-500);
}
</style>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const css = `
.custom-input[invalid]:not([disabled])::part(label),
.custom-input[invalid]:not([disabled])::part(help-text) {
color: var(--sl-color-danger-600);
}
.custom-input[invalid]:not([disabled])::part(base) {
border-color: var(--sl-color-danger-500);
}
.custom-input[invalid]:focus-within::part(base) {
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-500);
}
`;
const App = () => (
<>
<SlInput className="custom-input" required pattern="shoelace">
<small slot="help-text">Please enter "shoelace" to continue</small>
</SlInput>
<style>{css}</style>
</>
);
```
### Third-party Validation
To opt out of the browser's built-in validation and use your own, add the `novalidate` attribute to the form. This will ignore all constraints and prevent the browser from showing its own warnings when form controls are invalid.

View File

@@ -20,6 +20,34 @@ Formats a number as a human readable bytes value.
</script>
```
```jsx react
import { useState } from 'react';
import {
SlButton,
SlFormatBytes,
SlInput
} from '@shoelace-style/shoelace/dist/react';
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)}
/>
</>
);
};
```
## Examples
### Formatting Bytes
@@ -33,6 +61,20 @@ Set the `value` attribute to a number to get the value in bytes.
<sl-format-bytes value="1200000000"></sl-format-bytes>
```
```jsx react
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
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 `bits`.
@@ -44,6 +86,19 @@ To get the value in bits, set the `unit` attribute to `bits`.
<sl-format-bytes value="1200000000" unit="bits"></sl-format-bytes>
```
```jsx react
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlFormatBytes value="12" unit="bits" /><br />
<SlFormatBytes value="1200" unit="bits" /><br />
<SlFormatBytes value="1200000" unit="bits" /><br />
<SlFormatBytes value="1200000000" unit="bits" />
</>
);
```
### Localization
Use the `locale` attribute to set the number formatting locale.
@@ -55,4 +110,17 @@ Use the `locale` attribute to set the number formatting locale.
<sl-format-bytes value="1200000000" locale="de"></sl-format-bytes>
```
```jsx react
import { SlFormatBytes } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlFormatBytes value="12" locale="de" /><br />
<SlFormatBytes value="1200" locale="de" /><br />
<SlFormatBytes value="1200000" locale="de" /><br />
<SlFormatBytes value="1200000000" locale="de" />
</>
);
```
[component-metadata:sl-format-bytes]

View File

@@ -11,11 +11,18 @@ Localization is handled by the browser's [`Intl.DateTimeFormat` API](https://dev
<sl-format-date date="2020-07-15T09:17:00-04:00"></sl-format-date>
```
```jsx react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlFormatDate date="2020-07-15T09:17:00-04:00" />
);
```
The `date` attribute determines the date/time to use when formatting. It must be a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret or a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object set via JavaScript. If omitted, the current date/time will be assumed.
?> When using strings, avoid ambiguous dates such as `03/04/2020` which can be interpreted as March 4 or April 3 depending on the user's browser and locale. Instead, always use a valid [ISO 8601 date time string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Date_Time_String_Format) to ensure the date will be parsed properly by all clients.
## Examples
### Date & Time Formatting
@@ -42,6 +49,32 @@ Formatting options are based on those found in the [`Intl.DateTimeFormat` API](h
<sl-format-date></sl-format-date>
```
```jsx react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
{/* Human-readable date */}
<SlFormatDate month="long" day="numeric" year="numeric" /><br />
{/* Time */}
<SlFormatDate hour="numeric" minute="numeric" /><br />
{/* Weekday */}
<SlFormatDate weekday="long" /><br />
{/* Month */}
<SlFormatDate month="long" /><br />
{/* Year */}
<SlFormatDate year="numeric" /><br />
{/* No formatting options */}
<SlFormatDate />
</>
);
```
### Hour Formatting
By default, the browser will determine whether to use 12-hour or 24-hour time. To force one or the other, set the `hour-format` attribute to `12` or `24`.
@@ -51,6 +84,17 @@ By default, the browser will determine whether to use 12-hour or 24-hour time. T
<sl-format-date hour="numeric" minute="numeric" hour-format="24"></sl-format-date>
```
```jsx react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlFormatDate hour="numeric" minute="numeric" hour-format="12" /><br />
<SlFormatDate hour="numeric" minute="numeric" hour-format="24" />
</>
);
```
### Localization
Use the `locale` attribute to set the date/time formatting locale.
@@ -58,7 +102,19 @@ Use the `locale` attribute to set the date/time formatting locale.
```html preview
English: <sl-format-date locale="en"></sl-format-date><br>
French: <sl-format-date locale="fr"></sl-format-date><br>
Russian: <sl-format-date locale="ru"></sl-format-date><br>
Russian: <sl-format-date locale="ru"></sl-format-date>
```
```jsx react
import { SlFormatDate } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
English: <SlFormatDate locale="en" /><br />
French: <SlFormatDate locale="fr" /><br />
Russian: <SlFormatDate locale="ru" />
</>
);
```
[component-metadata:sl-format-date]

View File

@@ -22,6 +22,29 @@ Localization is handled by the browser's [`Intl.NumberFormat` API](https://devel
</script>
```
```jsx react
import { useState } from 'react';
import { SlFormatNumber, SlInput } from '@shoelace-style/shoelace/dist/react';
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)}
/>
</>
);
};
```
## Examples
### Percentages
@@ -30,12 +53,26 @@ 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=".25"></sl-format-number><br>
<sl-format-number type="percent" value=".50"></sl-format-number><br>
<sl-format-number type="percent" value=".75"></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';
const App = () => (
<>
<SlFormatNumber type="percent" value={0} /><br />
<SlFormatNumber type="percent" value={0.25} /><br />
<SlFormatNumber type="percent" value={0.50} /><br />
<SlFormatNumber type="percent" value={0.75} /><br />
<SlFormatNumber type="percent" value={1} />
</>
);
```
### Localization
Use the `locale` attribute to set the number formatting locale.
@@ -46,6 +83,18 @@ German: <sl-format-number value="2000" locale="de" minimum-fraction-digits="2"><
Russian: <sl-format-number value="2000" locale="ru" minimum-fraction-digits="2"></sl-format-number>
```
```jsx react
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
English: <SlFormatNumber value="2000" locale="en" minimum-fraction-digits="2" /><br />
German: <SlFormatNumber value="2000" locale="de" minimum-fraction-digits="2" /><br />
Russian: <SlFormatNumber value="2000" locale="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 `locale` to ensure the the number is formatted correctly for the target locale.
@@ -58,4 +107,18 @@ To format a number as a monetary value, set the `type` attribute to `currency` a
<sl-format-number type="currency" currency="CNY" value="2000" locale="zh-cn"></sl-format-number>
```
```jsx react
import { SlFormatNumber } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlFormatNumber type="currency" currency="USD" value="2000" locale="en-US" /><br />
<SlFormatNumber type="currency" currency="GBP" value="2000" locale="en-GB" /><br />
<SlFormatNumber type="currency" currency="EUR" value="2000" locale="de" /><br />
<SlFormatNumber type="currency" currency="RUB" value="2000" locale="ru" /><br />
<SlFormatNumber type="currency" currency="CNY" value="2000" locale="zh-cn" />
</>
);
```
[component-metadata:sl-format-number]

View File

@@ -10,6 +10,14 @@ For a full list of icons that come bundled with Shoelace, refer to the [icon com
<sl-icon-button name="gear" label="Settings"></sl-icon-button>
```
```jsx react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlIconButton name="gear" label="Settings" />
);
```
## Examples
### Sizes
@@ -22,6 +30,18 @@ Icon buttons inherit their parent element's `font-size`.
<sl-icon-button name="pencil" label="Edit" style="font-size: 2.5rem;"></sl-icon-button>
```
```jsx react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
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' }} />
</>
);
```
### 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.
@@ -49,6 +69,37 @@ Icon buttons are designed to have a uniform appearance, so their color is not in
</style>
```
```jsx react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
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.
@@ -57,6 +108,19 @@ Use the `href` attribute to convert the button to a link.
<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';
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.
@@ -67,6 +131,16 @@ Wrap a tooltip around an icon button to provide contextual information to the us
</sl-tooltip>
```
```jsx react
import { SlIconButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTooltip content="Settings">
<SlIconButton name="gear" label="Settings" />
</SlTooltip>
);
```
### Disabled
Use the `disabled` attribute to disable the icon button.
@@ -75,4 +149,12 @@ Use the `disabled` attribute to disable the icon button.
<sl-icon-button name="gear" label="Settings" disabled></sl-icon-button>
```
```jsx react
import { SlIconButton } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlIconButton name="gear" label="Settings" disabled />
);
```
[component-metadata:sl-icon-button]

View File

@@ -40,6 +40,7 @@ Icons are sized relative to the current font size. To change their size, set the
<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>
@@ -50,16 +51,65 @@ Icons are sized relative to the current font size. To change their size, set the
<sl-icon name="search"></sl-icon>
<sl-icon name="star"></sl-icon>
<sl-icon name="trash"></sl-icon>
<sl-icon name="x-circle"></sl-icon>
</div>
```
```jsx react
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
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>
);
```
### 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';
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="/assets/images/shoe.svg" style="font-size: 8rem;"></sl-icon>
<sl-icon src="https://shoelace.style/assets/images/shoe.svg" style="font-size: 8rem;"></sl-icon>
```
```jsx react
import { SlIcon } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlIcon src="https://shoelace.style/assets/images/shoe.svg" style={{ fontSize: '8rem' }}></SlIcon>
);
```
## Icon Libraries
@@ -509,7 +559,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
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">
<svg width="1em" height="1em" fill="currentColor">
<use xlink:href="/assets/icons/sprite.svg#${i.name}"></use>
</svg>
`;
@@ -558,7 +608,7 @@ If you want to change the icons Shoelace uses internally, you can register an ic
<style>
.icon-search {
border: solid 1px rgb(var(--sl-panel-border-color));
border: solid 1px var(--sl-panel-border-color);
border-radius: var(--sl-border-radius-medium);
padding: var(--sl-spacing-medium);
}
@@ -614,8 +664,8 @@ If you want to change the icons Shoelace uses internally, you can register an ic
}
.icon-list-item:hover {
background-color: rgb(var(--sl-color-primary-50));
color: rgb(var(--sl-color-primary-600));
background-color: var(--sl-color-primary-50);
color: var(--sl-color-primary-600);
}
.icon-list[data-type="outline"] .icon-list-item[data-name$="-fill"] {

View File

@@ -13,6 +13,17 @@ For best results, use images that share the same dimensions. The slider can be c
</sl-image-comparer>
```
```jsx react
import { SlImageComparer } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlImageComparer>
<img slot="before" src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&sat=-100&bri=-5" alt="Grayscale version of kittens in a basket looking around." />
<img slot="after" src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80" alt="Color version of kittens in a basket looking around." />
</SlImageComparer>
);
```
## Examples
### Initial Position
@@ -26,4 +37,15 @@ Use the `position` attribute to set the initial position of the slider. This is
</sl-image-comparer>
```
```jsx react
import { SlImageComparer } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlImageComparer position={25}>
<img slot="before" src="https://images.unsplash.com/photo-1520903074185-8eca362b3dce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80" alt="A person sitting on bricks wearing untied boots." />
<img slot="after" src="https://images.unsplash.com/photo-1520640023173-50a135e35804?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80" alt="A person sitting on a yellow curb tying shoelaces on a boot." />
</SlImageComparer>
);
```
[component-metadata:sl-image-comparer]

View File

@@ -8,8 +8,16 @@ Included files are asynchronously requested using `window.fetch()`. Requests are
The included content will be inserted into the `<sl-include>` element's default slot so it can be easily accessed and styled through the light DOM.
```html preview no-codepen
<sl-include src="/assets/examples/include.html"></sl-include>
```html preview
<sl-include src="https://shoelace.style/assets/examples/include.html"></sl-include>
```
```jsx react
import { SlInclude } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlInclude src="https://shoelace.style/assets/examples/include.html" />
);
```
## Examples
@@ -21,7 +29,7 @@ When an include file loads successfully, the `sl-load` event will be emitted. Yo
If the request fails, the `sl-error` event will be emitted. In this case, `event.detail.status` will contain the resulting HTTP status code of the request, e.g. 404 (not found).
```html
<sl-include src="/assets/examples/include.html"></sl-include>
<sl-include src="https://shoelace.style/assets/examples/include.html"></sl-include>
<script>
const include = document.querySelector('sl-include');

View File

@@ -8,6 +8,14 @@ Inputs collect data from the user.
<sl-input></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlInput />
);
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
?> Please refer to the section on [form control validation](/components/form?id=form-control-validation) to learn how to do client-side validation.
@@ -22,6 +30,14 @@ Use the `placeholder` attribute to add a placeholder.
<sl-input placeholder="Type something"></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlInput placeholder="Type something" />
);
```
### Clearable
Add the `clearable` attribute to add a clear button when the input has content.
@@ -30,6 +46,14 @@ Add the `clearable` attribute to add a clear button when the input has content.
<sl-input placeholder="Clearable" clearable></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlInput placeholder="Clearable" clearable />
);
```
### Toggle Password
Add the `toggle-password` attribute to add a toggle button that will show the password when activated.
@@ -42,6 +66,20 @@ Add the `toggle-password` attribute to add a toggle button that will show the pa
<sl-input type="password" placeholder="Password Toggle" size="large" toggle-password></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput type="password" placeholder="Password Toggle" size="small" toggle-password />
<br />
<SlInput type="password" placeholder="Password Toggle" size="medium" toggle-password />
<br />
<SlInput type="password" placeholder="Password Toggle" size="large" toggle-password />
</>
);
```
### Filled Inputs
Add the `filled` attribute to draw a filled input.
@@ -50,6 +88,14 @@ Add the `filled` attribute to draw a filled input.
<sl-input placeholder="Type something" filled></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlInput placeholder="Type something" filled />
);
```
### Pill
Use the `pill` attribute to give inputs rounded edges.
@@ -62,6 +108,20 @@ Use the `pill` attribute to give inputs rounded edges.
<sl-input placeholder="Large" size="large" pill></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
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.
@@ -74,6 +134,20 @@ The `type` attribute controls the type of input the browser renders.
<sl-input type="date" Placeholder="Date"></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput type="email" Placeholder="Email" />
<br />
<SlInput type="number" Placeholder="Number" />
<br />
<SlInput type="date" Placeholder="Date" />
</>
);
```
### Disabled
Use the `disabled` attribute to disable an input.
@@ -86,6 +160,20 @@ Use the `disabled` attribute to disable an input.
<sl-input placeholder="Disabled" size="large" disabled></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput placeholder="Disabled" size="small" disabled />
<br />
<SlInput placeholder="Disabled" size="medium" disabled />
<br />
<SlInput placeholder="Disabled" size="large" disabled />
</>
);
```
### Sizes
Use the `size` attribute to change an input's size.
@@ -98,6 +186,20 @@ Use the `size` attribute to change an input's size.
<sl-input placeholder="Large" size="large"></sl-input>
```
```jsx react
import { SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlInput placeholder="Small" size="small" />
<br />
<SlInput placeholder="Medium" size="medium" />
<br />
<SlInput placeholder="Large" size="large" />
</>
);
```
### Prefix & Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
@@ -119,6 +221,29 @@ Use the `prefix` and `suffix` slots to add icons.
</sl-input>
```
```jsx react
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
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>
</>
);
```
### Labels
Use the `label` attribute to give the input an accessible label. For labels that contain HTML, use the `label` slot instead.
@@ -127,6 +252,14 @@ Use the `label` attribute to give the input an accessible label. For labels that
<sl-input label="What is your name?"></sl-input>
```
```jsx react
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
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.
@@ -138,4 +271,15 @@ Add descriptive help text to an input with the `help-text` attribute. For help t
></sl-input>
```
```jsx react
import { SlIcon, SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlInput
label="Nickname"
help-text="What would you like people to call you?"
/>
);
```
[component-metadata:sl-input]

View File

@@ -5,7 +5,7 @@
Menu items provide options for the user to pick from in a menu.
```html preview
<sl-menu style="max-width: 200px; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);">
<sl-menu-item>Option 1</sl-menu-item>
<sl-menu-item>Option 2</sl-menu-item>
<sl-menu-item>Option 3</sl-menu-item>
@@ -24,6 +24,41 @@ Menu items provide options for the user to pick from in a menu.
</sl-menu>
```
```jsx react
import {
SlDivider,
SlIcon,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu
style={{
maxWidth: '200px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<SlMenuItem>Option 1</SlMenuItem>
<SlMenuItem>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
<SlDivider />
<SlMenuItem checked>Checked</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>
);
```
## Examples
### Checked
@@ -31,31 +66,73 @@ Menu items provide options for the user to pick from in a menu.
Use the `checked` attribute to draw menu items in a checked state.
```html preview
<sl-menu style="max-width: 200px; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);">
<sl-menu-item>Option 1</sl-menu-item>
<sl-menu-item checked>Option 2</sl-menu-item>
<sl-menu-item>Option 3</sl-menu-item>
</sl-menu>
```
```jsx react
import {
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu
style={{
maxWidth: '200px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<SlMenuItem>Option 1</SlMenuItem>
<SlMenuItem checked>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
</SlMenu>
);
```
### Disabled
Add the `disabled` attribute to disable the menu item so it cannot be selected.
```html preview
<sl-menu style="max-width: 200px; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);">
<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>
```
```jsx react
import {
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu
style={{
maxWidth: '200px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<SlMenuItem>Option 1</SlMenuItem>
<SlMenuItem disabled>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
</SlMenu>
);
```
### 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; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);">
<sl-menu-item>
<sl-icon slot="prefix" name="house"></sl-icon>
Home
@@ -76,12 +153,50 @@ Add content to the start and end of menu items using the `prefix` and `suffix` s
</sl-menu>
```
```jsx react
import {
SlBadge,
SlDivider,
SlIcon,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu
style={{
maxWidth: '200px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<SlMenuItem>
<SlIcon slot="prefix" name="house" />
Home
</SlMenuItem>
<SlMenuItem>
<SlIcon slot="prefix" name="envelope" />
Messages
<SlBadge slot="suffix" type="primary" pill>12</SlBadge>
</SlMenuItem>
<SlDivider />
<SlMenuItem>
<SlIcon slot="prefix" name="gear" />
Settings
</SlMenuItem>
</SlMenu>
);
```
### 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; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu class="menu-value" style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);">
<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>
@@ -102,4 +217,39 @@ The `value` attribute can be used to assign a hidden value, such as a unique ide
</script>
```
```jsx react
import {
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
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',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
onSlSelect={handleSelect}
>
<SlMenuItem value="opt-1">Option 1</SlMenuItem>
<SlMenuItem value="opt-2">Option 2</SlMenuItem>
<SlMenuItem value="opt-3">Option 3</SlMenuItem>
</SlMenu>
);
};
```
[component-metadata:sl-menu-item]

View File

@@ -6,7 +6,7 @@ Menu labels are used to describe a group of menu items.
```html preview
<sl-menu
style="max-width: 200px; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);"
style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);"
>
<sl-menu-label>Fruits</sl-menu-label>
<sl-menu-item value="apple">Apple</sl-menu-item>
@@ -20,4 +20,33 @@ Menu labels are used to describe a group of menu items.
</sl-menu>
```
```jsx react
import {
SlDivider,
SlMenu,
SlMenuLabel,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu
style={{
maxWidth: '200px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<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>
);
```
[component-metadata:sl-menu-label]

View File

@@ -7,7 +7,7 @@ Menus provide a list of options for the user to choose from.
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; border: solid 1px rgb(var(--sl-panel-border-color)); border-radius: var(--sl-border-radius-medium);">
<sl-menu style="max-width: 200px; border: solid 1px var(--sl-panel-border-color); background: var(--sl-panel-background-color); border-radius: var(--sl-border-radius-medium);">
<sl-menu-item value="undo">Undo</sl-menu-item>
<sl-menu-item value="redo">Redo</sl-menu-item>
<sl-divider></sl-divider>
@@ -18,6 +18,32 @@ You can use [menu items](/components/menu-item), [menu labels](/components/menu-
</sl-menu>
```
```jsx react
import {
SlDivider,
SlMenu,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu
style={{
maxWidth: '200px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<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>
);
```
?> 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.
[component-metadata:sl-menu]

View File

@@ -8,7 +8,7 @@ The mutation observer will report changes to the content it wraps through the `s
```html preview
<div class="mutation-overview">
<sl-mutation-observer attr>
<sl-mutation-observer attr="type">
<sl-button type="primary">Click to mutate</sl-button>
</sl-mutation-observer>
@@ -42,6 +42,47 @@ The mutation observer will report changes to the content it wraps through the `s
</div>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlMutationObserver } from '@shoelace-style/shoelace/dist/react';
const css = `
.resize-observer-overview div {
display: flex;
border: solid 2px var(--sl-input-border-color);
align-items: center;
justify-content: center;
text-align: center;
padding: 4rem 2rem;
}
`;
const types = ['primary', 'success', 'neutral', 'warning', 'danger'];
let clicks = 0;
const App = () => {
const [type, setType] = useState('primary');
function handleClick() {
clicks++;
setType(types[clicks % types.length]);
}
return (
<>
<SlMutationObserver
attr="*"
onSlMutation={event => console.log(event.detail)}
>
<SlButton type={type} onClick={handleClick}>Click to mutate</SlButton>
</SlMutationObserver>
<style>{css}</style>
</>
);
};
```
?> When you create a mutation observer, you must indicate what changes it should respond to by including at least one of `attr`, `child-list`, or `char-data`. If you don't specify at least one of these attributes, no mutation events will be emitted.
## Examples
@@ -101,4 +142,55 @@ Use the `child-list` attribute to watch for new child elements that are added or
</div>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlMutationObserver } from '@shoelace-style/shoelace/dist/react';
const css = `
.mutation-child-list .buttons {
display: flex;
gap: .25rem;
flex-wrap: wrap;
margin-bottom: 1rem;
}
`;
let buttonCount = 0;
const App = () => {
const [buttonIds, setButtonIds] = useState([]);
function addButton() {
setButtonIds([...buttonIds, ++buttonCount]);
}
function removeButton(id) {
setButtonIds(buttonIds.filter(i => i !== id));
}
return (
<>
<div className="mutation-child-list">
<SlMutationObserver
child-list
onSlMutation={event => console.log(event.detail)}
>
<div className="buttons">
<SlButton type="primary" onClick={addButton}>Add button</SlButton>
{buttonIds.map(id => (
<SlButton key={id} type="default" onClick={() => removeButton(id)}>
{id}
</SlButton>
))}
</div>
</SlMutationObserver>
</div>
👆 Add and remove buttons and watch the console
<style>{css}</style>
</>
);
};
```
[component-metadata:sl-mutation-observer]

View File

@@ -8,6 +8,14 @@ Progress bars are used to show the status of an ongoing operation.
<sl-progress-bar value="50"></sl-progress-bar>
```
```jsx react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressBar value={50} />
);
```
## Examples
### Custom Height
@@ -18,6 +26,17 @@ Use the `--height` custom property to set the progress bar's height.
<sl-progress-bar value="50" style="--height: 6px;"></sl-progress-bar>
```
```jsx react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressBar
value={50}
style={{ '--height': '6px' }}
/>
);
```
### Labels
Use the `label` attribute to label the progress bar and tell assistive devices how to announce it.
@@ -26,6 +45,17 @@ Use the `label` attribute to label the progress bar and tell assistive devices h
<sl-progress-bar value="50" label="Upload progress"></sl-progress-bar>
```
```jsx react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressBar
value="50"
label="Upload progress"
/>
);
```
### Showing Values
Use the default slot to show a value.
@@ -57,6 +87,44 @@ Use the default slot to show a value.
</script>
```
```jsx react
import { useState } from 'react';
import {
SlButton,
SlIcon,
SlProgressBar
} from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [value, setValue] = useState(50);
function adjustValue(amount) {
let newValue = value + amount;
if (newValue < 0) newValue = 0;
if (newValue > 100) newValue = 100;
setValue(newValue);
}
return (
<>
<SlProgressBar value={value}>
{value}%
</SlProgressBar>
<br />
<SlButton circle onClick={() => adjustValue(-10)}>
<SlIcon name="dash" />
</SlButton>
<SlButton circle onClick={() => adjustValue(10)}>
<SlIcon name="plus" />
</SlButton>
</>
);
};
```
### Indeterminate
The `indeterminate` attribute can be used to inform the user that the operation is pending, but its status cannot currently be determined. In this state, `value` is ignored and the label, if present, will not be shown.
@@ -65,4 +133,12 @@ The `indeterminate` attribute can be used to inform the user that the operation
<sl-progress-bar indeterminate></sl-progress-bar>
```
```jsx react
import { SlProgressBar } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressBar indeterminate />
);
```
[component-metadata:sl-progress-bar]

View File

@@ -8,6 +8,14 @@ Progress rings are used to show the progress of a determinate operation in a cir
<sl-progress-ring value="25"></sl-progress-ring>
```
```jsx react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressRing value="25" />
);
```
## Examples
### Size
@@ -18,6 +26,17 @@ Use the `--size` custom property to set the diameter of the progress ring.
<sl-progress-ring value="50" style="--size: 200px;"></sl-progress-ring>
```
```jsx react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressRing
value="50"
style={{ '--size': '200px' }}
/>
);
```
### Track Width
Use the `--track-width` custom property to set the width of the progress ring's track.
@@ -26,6 +45,17 @@ Use the `--track-width` custom property to set the width of the progress ring's
<sl-progress-ring value="50" style="--track-width: 10px;"></sl-progress-ring>
```
```jsx react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressRing
value="50"
style={{ '--track-width': '10px' }}
/>
);
```
### Colors
To change the color, use the `--track-color` and `--indicator-color` custom properties.
@@ -40,6 +70,20 @@ To change the color, use the `--track-color` and `--indicator-color` custom prop
></sl-progress-ring>
```
```jsx react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressRing
value="50"
style={{
'--track-color': 'pink',
'--indicator-color': 'deeppink'
}}
/>
);
```
### Labels
Use the `label` attribute to label the progress ring and tell assistive devices how to announce it.
@@ -48,9 +92,20 @@ Use the `label` attribute to label the progress ring and tell assistive devices
<sl-progress-ring value="50" label="Upload progress"></sl-progress-ring>
```
```jsx react
import { SlProgressRing } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlProgressRing
value="50"
label="Upload progress"
/>
);
```
### Showing Values
Use the default slot to show a label.
Use the default slot to show a label inside the progress ring.
```html preview
<sl-progress-ring value="50" class="progress-ring-values" style="margin-bottom: .5rem;">50%</sl-progress-ring>
@@ -79,4 +134,45 @@ Use the default slot to show a label.
</script>
```
```jsx react
import { useState } from 'react';
import {
SlButton,
SlIcon,
SlProgressRing
} from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [value, setValue] = useState(50);
function adjustValue(amount) {
let newValue = value + amount;
if (newValue < 0) newValue = 0;
if (newValue > 100) newValue = 100;
setValue(newValue);
}
return (
<>
<SlProgressRing
value={value}
style={{ marginBottom: '.5rem' }}
>
{value}%
</SlProgressRing>
<br />
<SlButton circle onClick={() => adjustValue(-10)}>
<SlIcon name="dash" />
</SlButton>
<SlButton circle onClick={() => adjustValue(10)}>
<SlIcon name="plus" />
</SlButton>
</>
);
};
```
[component-metadata:sl-progress-ring]

View File

@@ -34,6 +34,38 @@ QR codes are useful for providing small pieces of information to users who can q
</style>
```
```jsx react
import { useState } from 'react';
import { SlQrCode, SlInput } from '@shoelace-style/shoelace/dist/react';
const css = `
.qr-overview {
max-width: 256px;
}
.qr-overview sl-input {
margin-top: 1rem;
}
`;
const App = () => {
const [value, setValue] = useState('https://shoelace.style/');
return (
<>
<div className="qr-overview">
<SlQrCode value={value} label="Scan this code to visit Shoelace on the web!" />
<br />
<SlInput maxlength="255" clearable onInput={event => setValue(event.target.value)} />
</div>
<style>{css}</style>
</>
);
};
```
## Examples
### Colors
@@ -44,6 +76,14 @@ Use the `fill` and `background` attributes to modify the QR code's colors. You s
<sl-qr-code value="https://shoelace.style/" fill="deeppink" background="white"></sl-qr-code>
```
```jsx react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlQrCode value="https://shoelace.style/" fill="deeppink" background="white" />
);
```
### Size
Use the `size` attribute to change the size of the QR code.
@@ -52,6 +92,14 @@ Use the `size` attribute to change the size of the QR code.
<sl-qr-code value="https://shoelace.style/" size="64"></sl-qr-code>
```
```jsx react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlQrCode value="https://shoelace.style/" size="64" />
);
```
### Radius
Create a rounded effect with the `radius` attribute.
@@ -60,6 +108,14 @@ Create a rounded effect with the `radius` attribute.
<sl-qr-code value="https://shoelace.style/" radius="0.5"></sl-qr-code>
```
```jsx react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlQrCode value="https://shoelace.style/" radius="0.5" />
);
```
### Error Correction
QR codes can be rendered with various levels of [error correction](https://www.qrcode.com/en/about/error_correction.html) that can be set using the `error-correction` attribute. This example generates four codes with the same value using different error correction levels.
@@ -81,4 +137,31 @@ QR codes can be rendered with various levels of [error correction](https://www.q
</style>
```
```jsx react
import { SlQrCode } from '@shoelace-style/shoelace/dist/react';
const css = `
.qr-error-correction {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
`;
const App = () => {
return (
<>
<div className="qr-error-correction">
<SlQrCode value="https://shoelace.style/" error-correction="L" />
<SlQrCode value="https://shoelace.style/" error-correction="M" />
<SlQrCode value="https://shoelace.style/" error-correction="Q" />
<SlQrCode value="https://shoelace.style/" error-correction="H" />
</div>
<style>{css}</style>
</>
);
};
```
[component-metadata:sl-qr-code]

View File

@@ -5,13 +5,25 @@
Radio Groups are used to group multiple radios so they function as a single control.
```html preview
<sl-radio-group label="Select an item">
<sl-radio value="1" checked>Item 1</sl-radio>
<sl-radio value="2">Item 2</sl-radio>
<sl-radio value="3">Item 3</sl-radio>
<sl-radio-group label="Select an option">
<sl-radio value="1" checked>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, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option">
<SlRadio value="1" checked>Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
## Examples
### Showing the Fieldset
@@ -19,11 +31,22 @@ Radio Groups are used to group multiple radios so they function as a single cont
You can show a fieldset and legend that wraps the radio group using the `fieldset` attribute.
```html preview
<sl-radio-group label="Select an item" fieldset>
<sl-radio value="1" checked>Item 1</sl-radio>
<sl-radio value="2">Item 2</sl-radio>
<sl-radio value="3">Item 3</sl-radio>
<sl-radio-group label="Select an option" fieldset>
<sl-radio value="1" checked>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, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option" fieldset>
<SlRadio value="1" checked>Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
[component-metadata:sl-radio-group]

View File

@@ -7,13 +7,25 @@ Radios allow the user to select one option from a group of many.
Radios are designed to be used with [radio groups](/components/radio-group). As such, all of the examples on this page utilize them to demonstrate their correct usage.
```html preview
<sl-radio-group label="Select an option" no-fieldset>
<sl-radio-group label="Select an option">
<sl-radio value="1" checked>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, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option">
<SlRadio value="1" checked>Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
</SlRadioGroup>
);
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
## Examples
@@ -23,7 +35,7 @@ Radios are designed to be used with [radio groups](/components/radio-group). As
Use the `disabled` attribute to disable a radio.
```html preview
<sl-radio-group label="Select an option" no-fieldset>
<sl-radio-group label="Select an option">
<sl-radio value="1" checked>Option 1</sl-radio>
<sl-radio value="2">Option 2</sl-radio>
<sl-radio value="3">Option 3</sl-radio>
@@ -31,4 +43,17 @@ Use the `disabled` attribute to disable a radio.
</sl-radio-group>
```
```jsx react
import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRadioGroup label="Select an option">
<SlRadio value="1" checked>Option 1</SlRadio>
<SlRadio value="2">Option 2</SlRadio>
<SlRadio value="3">Option 3</SlRadio>
<SlRadio value="4" disabled>Disabled</SlRadio>
</SlRadioGroup>
);
```
[component-metadata:sl-radio]

View File

@@ -8,16 +8,49 @@ Ranges allow the user to select a single value within a given range using a slid
<sl-range></sl-range>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange />
);
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
## Examples
### Min, Max, and Step
Use the `min` and `max` attributes to set the range's minimum and maximum values, respectively. The `step` attribute determines the value's interval when increasing and decreasing.
```html preview
<sl-range min="0" max="10" step="1"></sl-range>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange min={0} max={10} step={1} />
);
```
### Disabled
Use the `disabled` attribute to disable a slider.
```html preview
<sl-range min="0" max="100" step="1" disabled></sl-range>
<sl-range disabled></sl-range>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange disabled />
);
```
### Tooltip Placement
@@ -25,7 +58,15 @@ Use the `disabled` attribute to disable a slider.
By default, the tooltip is shown on top. Set `tooltip` to `bottom` to show it below the slider.
```html preview
<sl-range min="0" max="100" step="1" tooltip="bottom"></sl-range>
<sl-range tooltip="bottom"></sl-range>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange tooltip="bottom" />
);
```
### Disable the Tooltip
@@ -33,7 +74,15 @@ By default, the tooltip is shown on top. Set `tooltip` to `bottom` to show it be
To disable the tooltip, set `tooltip` to `none`.
```html preview
<sl-range min="0" max="100" step="1" tooltip="none"></sl-range>
<sl-range tooltip="none"></sl-range>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange tooltip="none" />
);
```
### Custom Track Colors
@@ -42,11 +91,24 @@ You can customize the active and inactive portions of the track using the `--tra
```html preview
<sl-range style="
--track-color-active: rgb(var(--sl-color-primary-600));
--track-color-inactive: rgb(var(--sl-color-primary-200));
--track-color-active: var(--sl-color-primary-600);
--track-color-inactive: var(--sl-color-primary-100);
"></sl-range>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange
style={{
'--track-color-active': 'var(--sl-color-primary-600)',
'--track-color-inactive': 'var(--sl-color-primary-200)'
}}
/>
);
```
### Custom Tooltip Formatter
You can change the tooltip's content by setting the `tooltipFormatter` property to a function that accepts the range's value as an argument.
@@ -60,6 +122,19 @@ You can change the tooltip's content by setting the `tooltipFormatter` property
</script>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange
min={0}
max={100}
step={1}
tooltipFormatter={value => `Total - ${value}%`}
/>
);
```
### Labels
Use the `label` attribute to give the range an accessible label. For labels that contain HTML, use the `label` slot instead.
@@ -68,6 +143,14 @@ Use the `label` attribute to give the range an accessible label. For labels that
<sl-range label="Volume" min="0" max="100"></sl-input>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange label="Volume" min={0} max={100} />
);
```
### Help Text
Add descriptive help text to a range with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
@@ -81,4 +164,17 @@ Add descriptive help text to a range with the `help-text` attribute. For help te
></sl-input>
```
```jsx react
import { SlRange } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRange
label="Volume"
help-text="Controls the volume of the current song."
min={0}
max={100}
/>
);
```
[component-metadata:sl-range]

View File

@@ -8,6 +8,14 @@ Ratings give users a way to quickly view and provide feedback.
<sl-rating></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRating />
);
```
## Examples
### Maximum Value
@@ -18,12 +26,28 @@ Ratings are 0-5 by default. To change the maximum possible value, use the `max`
<sl-rating max="3"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRating max={3} />
);
```
### Precision
Use the `precision` attribute to let users select fractional ratings.
```html preview
<sl-rating precision=".5" value="2.5"></sl-rating>
<sl-rating precision="0.5" value="2.5"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRating precision={0.5} value={2.5} />
);
```
## Symbol Sizes
@@ -34,6 +58,14 @@ Set the `--symbol-size` custom property to adjust the size.
<sl-rating style="--symbol-size: 2rem;"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRating style={{ '--symbol-size': '2rem' }} />
);
```
### Readonly
Use the `readonly` attribute to display a rating that users can't change.
@@ -42,6 +74,14 @@ Use the `readonly` attribute to display a rating that users can't change.
<sl-rating readonly value="3"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRating readonly value={3} />
);
```
### Disabled
Use the `disable` attribute to disable the rating.
@@ -50,6 +90,14 @@ Use the `disable` attribute to disable the rating.
<sl-rating disabled value="3"></sl-rating>
```
```jsx react
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRating disabled value={3} />
);
```
### Custom Icons
```html preview
@@ -61,6 +109,18 @@ Use the `disable` attribute to disable the rating.
</script>
```
```jsx react
import '@shoelace-style/shoelace/dist/components/icon/icon';
import { SlRating } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRating
getSymbol={() => '<sl-icon name="heart-fill"></sl-icon>'}
style={{ '--symbol-color-active': '#ff4136' }}
/>
);
```
### Value-based Icons
```html preview
@@ -76,4 +136,18 @@ Use the `disable` attribute to disable the rating.
</script>
```
```jsx react
import '@shoelace-style/shoelace/dist/components/icon/icon';
import { SlRating } from '@shoelace-style/shoelace/dist/react';
function getSymbol(value) {
const icons = ['emoji-angry', 'emoji-frown', 'emoji-expressionless', 'emoji-smile', 'emoji-laughing'];
return `<sl-icon name="${icons[value - 1]}"></sl-icon>`;
}
const App = () => (
<SlRating getSymbol={getSymbol} />
);
```
[component-metadata:sl-rating]

View File

@@ -11,6 +11,14 @@ Localization is handled by the browser's [`Intl.RelativeTimeFormat` API](https:/
<sl-relative-time date="2020-07-15T09:17:00-04:00"></sl-relative-time>
```
```jsx react
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlRelativeTime date="2020-07-15T09:17:00-04:00" />
);
```
The `date` attribute determines when the date/time is calculated from. It must be a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret or a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object set via JavaScript.
?> When using strings, avoid ambiguous dates such as `03/04/2020` which can be interpreted as March 4 or April 3 depending on the user's browser and locale. Instead, always use a valid [ISO 8601 date time string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Date_Time_String_Format) to ensure the date will be parsed properly by all clients.
@@ -36,6 +44,16 @@ Use the `sync` attribute to update the displayed value automatically as time pas
</script>
```
```jsx react
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
const date = new Date(new Date().getTime() - 60000);
const App = () => (
<SlRelativeTime date={date} sync />
);
```
### Formatting Styles
You can change how the time is displayed using the `format` attribute. Note that some locales may display the same values for `narrow` and `short` formats.
@@ -46,6 +64,18 @@ You can change how the time is displayed using the `format` attribute. Note that
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="long"></sl-relative-time>
```
```jsx react
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlRelativeTime date="2020-07-15T09:17:00-04:00" format="narrow" /><br />
<SlRelativeTime date="2020-07-15T09:17:00-04:00" format="short" /><br />
<SlRelativeTime date="2020-07-15T09:17:00-04:00" format="long" />
</>
);
```
### Localization
Use the `locale` attribute to set the desired locale.
@@ -58,4 +88,18 @@ Greek: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="el"></sl-relat
Russian: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="ru"></sl-relative-time>
```
```jsx react
import { SlRelativeTime } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
English: <SlRelativeTime date="2020-07-15T09:17:00-04:00" locale="en-US" /><br />
Chinese: <SlRelativeTime date="2020-07-15T09:17:00-04:00" locale="zh-CN" /><br />
German: <SlRelativeTime date="2020-07-15T09:17:00-04:00" locale="de" /><br />
Greek: <SlRelativeTime date="2020-07-15T09:17:00-04:00" locale="el" /><br />
Russian: <SlRelativeTime date="2020-07-15T09:17:00-04:00" locale="ru" />
</>
);
```
[component-metadata:sl-relative-time]

View File

@@ -27,7 +27,7 @@ The resize observer will report changes to the dimensions of the elements it wra
<style>
.resize-observer-overview div {
display: flex;
border: solid 2px rgb(var(--sl-input-border-color));
border: solid 2px var(--sl-input-border-color);
align-items: center;
justify-content: center;
text-align: center;
@@ -36,4 +36,33 @@ The resize observer will report changes to the dimensions of the elements it wra
</style>
```
```jsx react
import { SlResizeObserver } from '@shoelace-style/shoelace/dist/react';
const css = `
.resize-observer-overview div {
display: flex;
border: solid 2px var(--sl-input-border-color);
align-items: center;
justify-content: center;
text-align: center;
padding: 4rem 2rem;
}
`;
const App = () => (
<>
<div className="resize-observer-overview">
<SlResizeObserver onSlResize={event => console.log(event.detail)}>
<div>
Resize this box and watch the console 👉
</div>
</SlResizeObserver>
</div>
<style>{css}</style>
</>
);
```
[component-metadata:sl-resize-observer]

View File

@@ -12,6 +12,19 @@ You can slot in any [replaced element](https://developer.mozilla.org/en-US/docs/
</sl-responsive-media>
```
```jsx react
import { SlResponsiveMedia } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlResponsiveMedia>
<img
src="https://images.unsplash.com/photo-1541427468627-a89a96e5ca1d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1800&q=80"
alt="A train riding through autumn foliage with mountains in the distance."
/>
</SlResponsiveMedia>
);
```
## Examples
### Responsive Images
@@ -24,6 +37,19 @@ The following image maintains a `4:3` aspect ratio as its container is resized.
</sl-responsive-media>
```
```jsx react
import { SlResponsiveMedia } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlResponsiveMedia aspect-ratio="4:3">
<img
src="https://images.unsplash.com/photo-1473186578172-c141e6798cf4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1800&q=80"
alt="Two blue chairs on a sandy beach."
/>
</SlResponsiveMedia>
);
```
### Responsive Videos
The following video is embedded using an `iframe` and maintains a `16:9` aspect ratio as its container is resized.
@@ -34,4 +60,14 @@ The following video is embedded using an `iframe` and maintains a `16:9` aspect
</sl-responsive-media>
```
```jsx react
import { SlResponsiveMedia } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlResponsiveMedia aspect-ratio="16:9">
<iframe src="https://player.vimeo.com/video/1053647?title=0&byline=0&portrait=0" frameborder="0" allow="autoplay; fullscreen" allowfullscreen />
</SlResponsiveMedia>
);
```
[component-metadata:sl-responsive-media]

View File

@@ -16,6 +16,22 @@ Selects allow you to choose one or more items from a dropdown menu.
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlDivider />
<SlMenuItem value="option-4">Option 4</SlMenuItem>
<SlMenuItem value="option-5">Option 5</SlMenuItem>
<SlMenuItem value="option-6">Option 6</SlMenuItem>
</SlSelect>
);
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
## Examples
@@ -32,6 +48,18 @@ Use the `placeholder` attribute to add a placeholder.
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Select one">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
);
```
### Clearable
Use the `clearable` attribute to make the control clearable.
@@ -44,6 +72,18 @@ Use the `clearable` attribute to make the control clearable.
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Clearable" clearable>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
);
```
### Filled Selects
Add the `filled` attribute to draw a filled select.
@@ -56,6 +96,18 @@ Add the `filled` attribute to draw a filled select.
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect filled>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
);
```
### Pill
Use the `pill` attribute to give selects rounded edges.
@@ -68,6 +120,18 @@ Use the `pill` attribute to give selects rounded edges.
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect pill>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
);
```
### Disabled
Use the `disabled` attribute to disable a select.
@@ -80,6 +144,18 @@ Use the `disabled` attribute to disable a select.
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Disabled" disabled>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</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. When using this option, `value` will be an array instead of a string.
@@ -96,6 +172,22 @@ To allow multiple options to be selected, use the `multiple` attribute. It's a g
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Select a few" multiple clearable>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlDivider />
<SlMenuItem value="option-4">Option 4</SlMenuItem>
<SlMenuItem value="option-5">Option 5</SlMenuItem>
<SlMenuItem value="option-6">Option 6</SlMenuItem>
</SlSelect>
);
```
### Grouping Options
Options can be grouped visually using menu labels and dividers.
@@ -114,6 +206,24 @@ Options can be grouped visually using menu labels and dividers.
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlMenuLabel, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Select one">
<SlMenuLabel>Group 1</SlMenuLabel>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlDivider></SlDivider>
<SlMenuLabel>Group 2</SlMenuLabel>
<SlMenuItem value="option-4">Option 4</SlMenuItem>
<SlMenuItem value="option-5">Option 5</SlMenuItem>
<SlMenuItem value="option-6">Option 6</SlMenuItem>
</SlSelect>
);
```
### Sizes
Use the `size` attribute to change a select's size.
@@ -142,13 +252,43 @@ Use the `size` attribute to change a select's size.
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlSelect placeholder="Small" size="small" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
</>
);
```
### Selecting Options Programmatically
The `value` property is bound to the current selection. As the selection changes, so will the value. To programmatically manage the selection, update the `value` property.
```html preview
<div class="selecting-example">
<sl-select placeholder="">
<sl-select>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
@@ -173,6 +313,31 @@ The `value` property is bound to the current selection. As the selection changes
</script>
```
```jsx react
import { useState } from 'react';
import { SlButton, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [value, setValue] = useState('option-1');
return (
<>
<SlSelect value={value} onSlChange={event => setValue(event.target.value)}>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
<br />
<SlButton onClick={() => setValue('option-1')}>Set 1</SlButton>
<SlButton onClick={() => setValue('option-2')}>Set 2</SlButton>
<SlButton onClick={() => setValue('option-3')}>Set 3</SlButton>
</>
);
};
```
### Labels
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
@@ -185,6 +350,18 @@ Use the `label` attribute to give the select an accessible label. For labels tha
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect label="Select one">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</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.
@@ -194,12 +371,27 @@ Add descriptive help text to a select with the `help-text` attribute. For help t
label="Experience"
help-text="Please tell us your skill level."
>
<sl-menu-item value="option-1">Novice</sl-menu-item>
<sl-menu-item value="option-2">Intermediate</sl-menu-item>
<sl-menu-item value="option-3">Advanced</sl-menu-item>
<sl-menu-item value="1">Novice</sl-menu-item>
<sl-menu-item value="2">Intermediate</sl-menu-item>
<sl-menu-item value="3">Advanced</sl-menu-item>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect
label="Experience"
help-text="Please tell us your skill level."
>
<SlMenuItem value="1">Novice</SlMenuItem>
<SlMenuItem value="2">Intermediate</SlMenuItem>
<SlMenuItem value="3">Advanced</SlMenuItem>
</SlSelect>
);
```
### Prefix & Suffix Icons
Use the `prefix` and `suffix` slots to add icons.
@@ -230,4 +422,36 @@ Use the `prefix` and `suffix` slots to add icons.
</sl-select>
```
```jsx react
import { SlIcon, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlSelect placeholder="Small" size="small">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlIcon name="chat" slot="suffix"></SlIcon>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlIcon name="chat" slot="suffix"></SlIcon>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlIcon name="chat" slot="suffix"></SlIcon>
</SlSelect>
</>
);
```
[component-metadata:sl-select]

View File

@@ -54,6 +54,60 @@ Skeletons try not to be opinionated, as there are endless possibilities for desi
</style>
```
```jsx react
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
const css = `
.skeleton-overview header {
display: flex;
align-items: center;
margin-bottom: 1rem;
}
.skeleton-overview header sl-skeleton:last-child {
flex: 0 0 auto;
width: 30%;
}
.skeleton-overview sl-skeleton {
margin-bottom: 1rem;
}
.skeleton-overview sl-skeleton:nth-child(1) {
float: left;
width: 3rem;
height: 3rem;
margin-right: 1rem;
vertical-align: middle;
}
.skeleton-overview sl-skeleton:nth-child(3) {
width: 95%;
}
.skeleton-overview sl-skeleton:nth-child(4) {
width: 80%;
}
`;
const App = () => (
<>
<div className="skeleton-overview">
<header>
<SlSkeleton />
<SlSkeleton />
</header>
<SlSkeleton />
<SlSkeleton />
<SlSkeleton />
</div>
<style>{css}</style>
</>
);
```
## Examples
### Effects
@@ -83,6 +137,37 @@ There are two built-in effects, `sheen` and `pulse`. Effects are intentionally s
</style>
```
```jsx react
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
const css = `
.skeleton-effects {
font-size: var(--sl-font-size-small);
}
.skeleton-effects sl-skeleton:not(:first-child) {
margin-top: 1rem;
}
`;
const App = () => (
<>
<div className="skeleton-effects">
<SlSkeleton effect="none" />
None
<SlSkeleton effect="sheen" />
Sheen
<SlSkeleton effect="pulse" />
Pulse
</div>
<style>{css}</style>
</>
);
```
### Paragraphs
Use multiple skeletons and some clever styles to simulate paragraphs.
@@ -115,6 +200,42 @@ Use multiple skeletons and some clever styles to simulate paragraphs.
</style>
```
```jsx react
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
const css = `
.skeleton-paragraphs sl-skeleton {
margin-bottom: 1rem;
}
.skeleton-paragraphs sl-skeleton:nth-child(2) {
width: 95%;
}
.skeleton-paragraphs sl-skeleton:nth-child(4) {
width: 90%;
}
.skeleton-paragraphs sl-skeleton:last-child {
width: 50%;
}
`;
const App = () => (
<>
<div className="skeleton-paragraphs">
<SlSkeleton />
<SlSkeleton />
<SlSkeleton />
<SlSkeleton />
<SlSkeleton />
</div>
<style>{css}</style>
</>
);
```
### Avatars
Set a matching width and height to make a circle, square, or rounded avatar skeleton.
@@ -144,6 +265,39 @@ Set a matching width and height to make a circle, square, or rounded avatar skel
</style>
```
```jsx react
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
const css = `
.skeleton-avatars sl-skeleton {
display: inline-block;
width: 3rem;
height: 3rem;
margin-right: .5rem;
}
.skeleton-avatars sl-skeleton:nth-child(1) {
--border-radius: 0;
}
.skeleton-avatars sl-skeleton:nth-child(2) {
--border-radius: var(--sl-border-radius-medium);
}
`;
const App = () => (
<>
<div className="skeleton-avatars">
<SlSkeleton />
<SlSkeleton />
<SlSkeleton />
</div>
<style>{css}</style>
</>
);
```
### Custom Shapes
Use the `--border-radius` custom property to make circles, squares, and rectangles. For more complex shapes, you can apply `clip-path` to the `indicator` part. [Try Clippy](https://bennettfeely.com/clippy/) if you need help generating custom shapes.
@@ -193,6 +347,59 @@ Use the `--border-radius` custom property to make circles, squares, and rectangl
</style>
```
```jsx react
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
const css = `
.skeleton-shapes sl-skeleton {
display: inline-flex;
width: 50px;
height: 50px;
}
.skeleton-shapes .square::part(indicator) {
--border-radius: var(--sl-border-radius-medium);
}
.skeleton-shapes .circle::part(indicator) {
--border-radius: var(--sl-border-radius-circle);
}
.skeleton-shapes .triangle::part(indicator) {
--border-radius: 0;
clip-path: polygon(50% 0, 0 100%, 100% 100%);
}
.skeleton-shapes .cross::part(indicator) {
--border-radius: 0;
clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
}
.skeleton-shapes .comment::part(indicator) {
--border-radius: 0;
clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%);
}
.skeleton-shapes sl-skeleton:not(:last-child) {
margin-right: .5rem;
}
`;
const App = () => (
<>
<div className="skeleton-shapes">
<SlSkeleton className="square" />
<SlSkeleton className="circle" />
<SlSkeleton className="triangle" />
<SlSkeleton className="cross" />
<SlSkeleton className="comment" />
</div>
<style>{css}</style>
</>
);
```
### Custom Colors
Set the `--color` and `--sheen-color` custom properties to adjust the skeleton's color.
@@ -201,4 +408,32 @@ Set the `--color` and `--sheen-color` custom properties to adjust the skeleton's
<sl-skeleton effect="sheen" style="--color: tomato; --sheen-color: #ffb094;"></sl-skeleton>
```
```jsx react
import { SlSkeleton } from '@shoelace-style/shoelace/dist/react';
const css = `
.skeleton-avatars sl-skeleton {
display: inline-block;
width: 3rem;
height: 3rem;
margin-right: .5rem;
}
.skeleton-avatars sl-skeleton:nth-child(1) {
--border-radius: 0;
}
.skeleton-avatars sl-skeleton:nth-child(2) {
--border-radius: var(--sl-border-radius-medium);
}
`;
const App = () => (
<SlSkeleton
effect="sheen"
style={{ '--color': 'tomato', '--sheen-color': '#ffb094' }}
/>
);
```
[component-metadata:sl-skeleton]

View File

@@ -8,6 +8,14 @@ Spinners are used to show the progress of an indeterminate operation.
<sl-spinner></sl-spinner>
```
```jsx react
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSpinner />
);
```
## Examples
### Size
@@ -20,6 +28,18 @@ Spinners are sized based on the current font size. To change their size, set the
<sl-spinner style="font-size: 3rem;"></sl-spinner>
```
```jsx react
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlSpinner />
<SlSpinner style={{ fontSize: '2rem' }} />
<SlSpinner style={{ fontSize: '3rem' }} />
</>
);
```
### Track Width
The width of the spinner's track can be changed by setting the `--track-width` custom property.
@@ -28,6 +48,19 @@ The width of the spinner's track can be changed by setting the `--track-width` c
<sl-spinner style="font-size: 3rem; --track-width: 6px;"></sl-spinner>
```
```jsx react
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSpinner
style={{
fontSize: '3rem',
'--track-width': '6px'
}}
/>
);
```
### Color
The spinner's colors can be changed by setting the `--indicator-color` and `--track-color` custom properties.
@@ -36,4 +69,18 @@ The spinner's colors can be changed by setting the `--indicator-color` and `--tr
<sl-spinner style="font-size: 3rem; --indicator-color: deeppink; --track-color: pink;"></sl-spinner>
```
```jsx react
import { SlSpinner } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSpinner
style={{
fontSize: '3rem',
'--indicator-color': 'deeppink',
'--track-color': 'pink'
}}
/>
);
```
[component-metadata:sl-spinner]

View File

@@ -8,6 +8,14 @@ Switches allow the user to toggle an option on or off.
<sl-switch>Switch</sl-switch>
```
```jsx react
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSwitch>Switch</SlSwitch>
);
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
## Examples
@@ -20,6 +28,14 @@ Use the `checked` attribute to activate the switch.
<sl-switch checked>Checked</sl-switch>
```
```jsx react
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSwitch checked>Checked</SlSwitch>
);
```
### Disabled
Use the `disabled` attribute to disable the switch.
@@ -28,6 +44,14 @@ Use the `disabled` attribute to disable the switch.
<sl-switch disabled>Disabled</sl-switch>
```
```jsx react
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSwitch disabled>Disabled</SlSwitch>
);
```
### Custom Size
Use the available custom properties to make the switch a different size.
@@ -36,4 +60,18 @@ Use the available custom properties to make the switch a different size.
<sl-switch style="--width: 80px; --height: 32px; --thumb-size: 26px;"></sl-switch>
```
```jsx react
import { SlSwitch } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSwitch
style={{
'--width': '80px',
'--height': '32px',
'--thumb-size': '26px'
}}
/>
);
```
[component-metadata:sl-switch]

View File

@@ -20,6 +20,24 @@ Tab groups make use of [tabs](/components/tab) and [tab panels](/components/tab-
</sl-tab-group>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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
@@ -40,6 +58,24 @@ Tabs can be shown on the bottom by setting `placement` to `bottom`.
</sl-tab-group>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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`.
@@ -58,6 +94,25 @@ Tabs can be shown on the starting side by setting `placement` to `start`.
</sl-tab-group>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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`.
@@ -76,6 +131,25 @@ Tabs can be shown on the ending side by setting `placement` to `end`.
</sl-tab-group>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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.
@@ -112,6 +186,40 @@ Add the `closable` attribute to a tab to show a close button. This example shows
</script>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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.
@@ -162,6 +270,56 @@ When there are more tabs than horizontal space allows, the nav will be scrollabl
</sl-tab-group>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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 <kbd>Left</kbd> or <kbd>Right</kbd> 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 <kbd>Space</kbd> or <kbd>Enter</kbd> before showing the tab panel (manual activation).
@@ -180,4 +338,22 @@ When focused, keyboard users can press <kbd>Left</kbd> or <kbd>Right</kbd> to se
</sl-tab-group>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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>
);
```
[component-metadata:sl-tab-group]

View File

@@ -18,6 +18,24 @@ Tab panels are used inside [tab groups](/components/tab-group) to display tabbed
</sl-tab-group>
```
```jsx react
import { SlTab, SlTabGroup, SlTabPanel } from '@shoelace-style/shoelace/dist/react';
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>
);
```
?> Additional demonstrations can be found in the [tab group examples](/components/tab-group).
[component-metadata:sl-tab-panel]

View File

@@ -11,6 +11,19 @@ Tabs are used inside [tab groups](/components/tab-group) to represent and activa
<sl-tab disabled>Disabled</sl-tab>
```
```jsx react
import { SlTab } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlTab>Tab</SlTab>
<SlTab active>Active</SlTab>
<SlTab closable>Closable</SlTab>
<SlTab disabled>Disabled</SlTab>
</>
);
```
?> Additional demonstrations can be found in the [tab group examples](/components/tab-group).
[component-metadata:sl-tab]

View File

@@ -12,6 +12,21 @@ Tags are used as labels to organize things or to indicate a selection.
<sl-tag type="danger">Danger</sl-tag>
```
```jsx react
import { SlTag } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlTag type="primary">Primary</SlTag>
<SlTag type="success">Success</SlTag>
<SlTag type="neutral">Neutral</SlTag>
<SlTag type="warning">Warning</SlTag>
<SlTag type="danger">Danger</SlTag>
</>
);
```
## Examples
### Sizes
@@ -24,6 +39,18 @@ Use the `size` attribute to change a tab's size.
<sl-tag size="large">Large</sl-tag>
```
```jsx react
import { SlTag } from '@shoelace-style/shoelace/dist/react';
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.
@@ -34,6 +61,18 @@ Use the `pill` attribute to give tabs rounded edges.
<sl-tag size="large" pill>Large</sl-tag>
```
```jsx react
import { SlTag } from '@shoelace-style/shoelace/dist/react';
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.
@@ -62,4 +101,42 @@ Use the `removable` attribute to add a remove button to the tag.
</style>
```
```jsx react
import { SlTag } from '@shoelace-style/shoelace/dist/react';
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>
</>
)
};
```
[component-metadata:sl-tag]

View File

@@ -8,6 +8,14 @@ Textareas collect data from the user and allow multiple lines of text.
<sl-textarea></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea />
);
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
?> Please refer to the section on [form control validation](/components/form?id=form-control-validation) to learn how to do client-side validation.
@@ -22,6 +30,14 @@ Use the `rows` attribute to change the number of text rows that get shown.
<sl-textarea rows="2"></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea rows={2} />
);
```
### Placeholders
Use the `placeholder` attribute to add a placeholder.
@@ -30,6 +46,14 @@ Use the `placeholder` attribute to add a placeholder.
<sl-textarea placeholder="Type something"></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea placeholder="Type something" />
);
```
### Filled Textareas
Add the `filled` attribute to draw a filled textarea.
@@ -38,6 +62,14 @@ Add the `filled` attribute to draw a filled textarea.
<sl-textarea placeholder="Type something" filled></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea placeholder="Type something" filled />
);
```
### Disabled
Use the `disabled` attribute to disable a textarea.
@@ -46,6 +78,14 @@ Use the `disabled` attribute to disable a textarea.
<sl-textarea placeholder="Textarea" disabled></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea placeholder="Textarea" disabled />
);
```
### Sizes
Use the `size` attribute to change a textarea's size.
@@ -58,6 +98,20 @@ Use the `size` attribute to change a textarea's size.
<sl-textarea placeholder="Large" size="large"></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlTextarea placeholder="Small" size="small"></SlTextarea>
<br />
<SlTextarea placeholder="Medium" size="medium"></SlTextarea>
<br />
<SlTextarea placeholder="Large" size="large"></SlTextarea>
</>
);
```
### Labels
Use the `label` attribute to give the textarea an accessible label. For labels that contain HTML, use the `label` slot instead.
@@ -66,6 +120,14 @@ Use the `label` attribute to give the textarea an accessible label. For labels t
<sl-textarea label="Comments"></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
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.
@@ -78,6 +140,17 @@ Add descriptive help text to a textarea with the `help-text` attribute. For help
</sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea
label="Feedback"
help-text="Please tell us what you think."
/>
);
```
### Prevent Resizing
By default, textareas can be resized vertically by the user. To prevent resizing, set the `resize` attribute to `none`.
@@ -86,6 +159,14 @@ By default, textareas can be resized vertically by the user. To prevent resizing
<sl-textarea resize="none"></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea resize="none" />
);
```
### Expand with Content
Textareas will automatically resize to expand to fit their content when `resize` is set to `auto`.
@@ -94,4 +175,12 @@ Textareas will automatically resize to expand to fit their content when `resize`
<sl-textarea resize="auto"></sl-textarea>
```
```jsx react
import { SlTextarea } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTextarea resize="auto" />
);
```
[component-metadata:sl-textarea]

View File

@@ -14,6 +14,16 @@ Tooltips use `display: contents` so they won't interfere with how elements are p
</sl-tooltip>
```
```jsx react
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTooltip content="This is a tooltip">
<SlButton>Hover Me</SlButton>
</SlTooltip>
);
```
## Examples
### Placement
@@ -41,7 +51,7 @@ Use the `placement` attribute to set the preferred placement of the tooltip.
<sl-button></sl-button>
</sl-tooltip>
<sl-tooltip content="right-start" placement="right-start" style="margin-left: 400px;">
<sl-tooltip content="right-start" placement="right-start">
<sl-button></sl-button>
</sl-tooltip>
</div>
@@ -99,17 +109,119 @@ Use the `placement` attribute to set the preferred placement of the tooltip.
margin-bottom: 0.25rem;
}
.tooltip-placement-example [placement='top-start'] sl-button,
.tooltip-placement-example [placement='bottom-start'] sl-button {
.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 [placement^='right'] sl-button {
.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, SlTooltip } from '@shoelace-style/shoelace/dist/react';
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.
@@ -120,6 +232,16 @@ Set the `trigger` attribute to `click` to toggle the tooltip on click instead of
</sl-tooltip>
```
```jsx react
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
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.
@@ -139,6 +261,30 @@ Tooltips can be controller programmatically by setting the `trigger` attribute t
</script>
```
```jsx react
import { useState } from 'react';
import { SlAvatar, SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
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>
</>
);
};
```
### Remove Arrows
You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow-size` design token.
@@ -155,6 +301,22 @@ You can control the size of tooltip arrows by overriding the `--sl-tooltip-arrow
</div>
```
```jsx react
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
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>
);
```
To override it globally, set it in a root block in your stylesheet after the Shoelace stylesheet is loaded.
```css
@@ -169,11 +331,28 @@ Use the `content` slot to create tooltips with HTML content. Tooltips are design
```html preview
<sl-tooltip>
<div slot="content">I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!</div>
<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, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTooltip content="This is a tooltip">
<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>
);
```
### 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, 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.
@@ -191,7 +370,7 @@ Tooltips will be clipped if they're inside a container that has `overflow: auto|
<style>
.tooltip-hoist {
border: solid 2px rgb(var(--sl-panel-border-color));
border: solid 2px var(--sl-panel-border-color);
overflow: hidden;
padding: var(--sl-spacing-medium);
position: relative;
@@ -199,4 +378,33 @@ Tooltips will be clipped if they're inside a container that has `overflow: auto|
</style>
```
```jsx react
import { SlButton, SlTooltip } from '@shoelace-style/shoelace/dist/react';
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>
</>
);
```
[component-metadata:sl-tooltip]

View File

@@ -0,0 +1,47 @@
# Visually Hidden
[component-header:sl-visually-hidden]
The visually hidden utility makes content accessible to assistive devices without displaying it on the screen.
According to [The A11Y Project](https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/), "there are real world situations where visually hiding content may be appropriate, while the content should remain available to assistive technologies, such as screen readers. For instance, hiding a search field's label as a common magnifying glass icon is used in its stead."
Since visually hidden content can receive focus when tabbing, the element will become visible when something inside receives focus. This behavior is intentional, as sighted keyboards user won't be able to determine where the focus indicator is without it.
```html preview
<div style="min-height: 100px;">
<sl-visually-hidden>
<a href="#">Skip to main content</a>
</sl-visually-hidden>
</div>
```
## Examples
### Links That Open in New Windows
In this example, the link will open a new window. Screen readers will announce "opens in a new window" even though the text content isn't visible to sighted users.
```html preview
<a href="https://example.com/" target="_blank">
Visit External Page
<sl-icon name="box-arrow-up-right"></sl-icon>
<sl-visually-hidden>opens in a new window</sl-visually-hidden>
</a>
```
### Content Conveyed By Context
Adding a title or label may seem redundant at times, but they're very helpful for unsighted users. Rather than omit them, you can provide context to unsighted users with visually hidden content.
```html preview
<sl-card style="width: 100%; max-width: 360px;">
<header>
<sl-visually-hidden>Personal Info</sl-visually-hidden>
</header>
<sl-input label="Name" style="margin-bottom: .5rem;"></sl-input>
<sl-input label="Email" type="email"></sl-input>
</sl-card>
```
[component-metadata:sl-visually-hidden]

View File

@@ -0,0 +1,46 @@
# 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/dist/themes/light.css';
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
setBasePath('https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/');
```
?> 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
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 {}
```
Now you can start using Shoelace components in your app!
?> Are you using Shoelace with Angular? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/angular.md)

156
docs/frameworks/react.md Normal file
View File

@@ -0,0 +1,156 @@
# React
Shoelace offers a React version of every component to provide an idiomatic experience for React users. You can easily toggle between HTML and React examples throughout the documentation.
## Installation
To add Shoelace to your React 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
// App.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%/dist/');
```
?> If you'd rather not use the CDN for assets, you can create a [build task](https://webpack.js.org/plugins/copy-webpack-plugin/) that copies `node_modules/@shoelace-style/shoelace/dist/assets` into your app's `public` directory. Then you can point the base path to that folder instead.
Now you can start using components!
## Usage
### Importing Components
Every Shoelace component is available to import as a React component. Note that we're importing the `<SlButton>` _React component_ instead of the `<sl-button>` _custom element_ in the example below.
```jsx
import { SlButton } from '@shoelace-style/shoelace/dist/react';
const MyComponent = () => (
<SlButton type="primary">
Click me
</SlButton>
);
export default MyComponent;
```
You can find a copy + paste import for each component in the "importing" section of its documentation.
### Event Handling
Many Shoelace components emit [custom events](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). For example, the [input component](/components/input) emits the `sl-input` event when it receives input. In React, you can listen for the event using `onSlInput`.
Here's how you can bind the input's value to a state variable.
```jsx
import { useState } from 'react';
import { SlInput } from '@shoelace-style/shoelace/dist/react';
function MyComponent() {
const [value, setValue] = useState('');
return (
<SlInput
value={value}
onSlInput={event => setValue(event.target.value)}
/>
)
};
export default MyComponent;
```
If you're using TypeScript, it's important to note that `event.target` will be a reference to the underlying custom element. You can use `(event.target as any).value` as a quick fix, or you can strongly type the event target as shown below.
```tsx
import { useState } from 'react';
import { SlInput } from '@shoelace-style/shoelace/dist/react';
import type SlInputElement from '@shoelace-style/shoelace/dist/components/input/input';
function MyComponent() {
const [value, setValue] = useState('');
return (
<SlInput
value={value}
onSlInput={event => setValue((event.target as SlInputElement).value)}
/>
)
};
export default MyComponent;
```
## Testing with Jest
Testing with web components can be challenging if your test environment runs in a Node environment (i.e. it doesn't run in a real browser). Fortunately, [Jest](https://jestjs.io/) has made a number of strides to support web components and provide additional browser APIs. However, it's still not a complete replication of a browser environment.
Here are some tips that will help smooth things over if you're having trouble with Jest + Shoelace.
?> If you're looking for a fast, modern testing alternative, consider [Web Test Runner](https://modern-web.dev/docs/test-runner/overview/).
### Upgrade Jest
Jest underwent a major revamp and received support for web components in [version 26.5.0](https://github.com/facebook/jest/blob/main/CHANGELOG.md#2650) when it introduced [JSDOM 16.2.0](https://github.com/jsdom/jsdom/blob/master/Changelog.md#1620). This release also included a number of mocks for built-in browser functions such as `MutationObserver`, `document.createRange`, and others.
If you're using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app), you can update `react-scripts` which will also update Jest.
```
npm install react-scripts@latest
```
### Mock Missing APIs
Some components use `window.matchMedia`, but this function isn't supported by JSDOM so you'll need to mock it yourself.
In `src/setupTests.js`, add the following.
```js
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
```
For more details, refer to Jest's [manual mocking](https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom) documentation.
### Transform ES Modules
ES Modules are a [well-supported browser standard](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/). This is how Shoelace is distributed, but most React apps expect CommonJS. As a result, you'll probably run into the following error.
```
Error: Unable to import outside of a module
```
To fix this, add the following to your `package.json` which tells the transpiler to process Shoelace modules.
```js
{
"jest": {
"transformIgnorePatterns": ["node_modules/?!(@shoelace)"]
}
}
```
These instructions are for apps created via Create React App. If you're using Jest directly, you can add `transformIgnorePatterns` directly into `jest.config.js`.
For more details, refer to Jest's [`transformIgnorePatterns` customization](https://jestjs.io/docs/tutorial-react-native#transformignorepatterns-customization) documentation.
?> Are you using Shoelace with React? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/react.md)

92
docs/frameworks/vue.md Normal file
View File

@@ -0,0 +1,92 @@
# Vue
Vue [plays nice](https://custom-elements-everywhere.com/#vue) with custom elements, so you can use Shoelace in your Vue apps with ease.
?> These instructions are for Vue 2. If you're using Vue 3, [please help us update this page](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue.md).
## 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%/dist/');
```
?> 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 { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
app.mount('#app');
```
Now you can start using Shoelace components in your app!
## Usage
### 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" />
```
### Two-way Binding
One caveat is there's currently [no support for v-model on custom elements](https://github.com/vuejs/vue/issues/7830), but you can still achieve two-way binding manually.
```html
<!-- This doesn't work -->
<sl-input v-model="name">
<!-- This works, but it's a bit longer -->
<sl-input :value="name" @input="name = $event.target.value">
```
If that's too verbose for your liking, you can use a custom directive instead. [This utility](https://www.npmjs.com/package/@shoelace-style/vue-sl-model) adds a custom directive that will work just like `v-model` but for Shoelace components. To install it, use this command.
```bash
npm install @shoelace-style/vue-sl-model
```
Next, import the directive and enable it like this.
```js
import ShoelaceModelDirective from '@shoelace-style/vue-sl-model';
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.use(ShoelaceModelDirective);
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
app.mount('#app');
```
Now you can use the `v-sl-model` directive to keep your data in sync!
```html
<sl-input v-sl-model="name">
```
?> Are you using Shoelace with Vue? [Help us improve this page!](https://github.com/shoelace-style/shoelace/blob/next/docs/frameworks/vue.md)

View File

@@ -46,7 +46,7 @@ Here's an example that modifies buttons with the `tomato-button` class.
<style>
.tomato-button::part(base) {
background: rgb(var(--sl-color-neutral-0));
background: var(--sl-color-neutral-0);
border: solid 1px tomato;
}

View File

@@ -2,7 +2,9 @@
You can use Shoelace via CDN or by installing it locally. You can also [cherry pick](#cherry-picking) individual components for faster load times.
## CDN Installation (Recommended)
If you're using a framework, make sure to check out the pages for [React](/frameworks/react), [Vue](/frameworks/vue), and [Angular](/frameworks/angular).
## CDN Installation (Easiest)
The easiest way to install Shoelace is with the CDN. Just add the following tags to your page to get all components and the default light theme.
@@ -11,6 +13,8 @@ The easiest way to install Shoelace is with the CDN. Just add the following tags
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/dist/shoelace.js"></script>
```
?> If you're only using a handful of components, it will be more efficient to [cherry pick](#cherry-picking) the ones you need.
### Dark Theme
If you prefer to use the dark theme instead, use this. Note the `sl-theme-dark` class on the `<html>` element. [Learn more about the Dark Theme.](/getting-started/themes#dark-theme)
@@ -77,7 +81,7 @@ However, if you're [cherry picking](#cherry-picking) or [bundling](#bundling) Sh
## Cherry Picking
The previous approach is the _easiest_ way to load Shoelace, but easy isn't always efficient. You'll incur the full size of the library even if you only use a handful of components. This is convenient for prototyping, but may result in longer load times in production. To improve this, you can cherry pick the components you need.
The previous approach is the _easiest_ way to load Shoelace, but easy isn't always efficient. You'll incur the full size of the library even if you only use a handful of components. This is convenient for prototyping or if you're using most of the components, but it may result in longer load times in production. To improve this, you can cherry pick the components you need.
Cherry picking can be done from your local install or [directly from the CDN](https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/). This will limit the number of files the browser has to download and reduce the amount of bytes being transferred. The disadvantage is that you need to load component manually.

View File

@@ -9,7 +9,7 @@
- Fully customizable with CSS 🎨
- Includes a dark theme 🌛
- Built with accessibility in mind ♿️
- First-party [React wrappers](/getting-started/usage#react)
- First-class [React support](/frameworks/react)
- Open source 😸
Designed in New Hampshire by [Cory LaViska](https://twitter.com/claviska).
@@ -37,11 +37,12 @@ Add the following code to your page.
Now you have access to all of Shoelace's components! Try adding a button:
```html
```html preview expanded
<sl-button>Click me</sl-button>
```
See the [installation instructions](getting-started/installation) for more details and other ways to install Shoelace.
?> This will load all of Shoelace's components, but you should probably only load the ones you're actually using. To learn how, or for other ways to install Shoelace, refer to the [installation instructions](getting-started/installation).
## New to Web Components?
@@ -61,13 +62,15 @@ This is the technology that Shoelace is built on.
## What Problem Does This Solve?
Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology. Why spend hundreds of hours (or more) building a design system from scratch? Why make a component library that only works with one framework?
Shoelace provides a collection of professionally designed, highly customizable UI components built on a framework agnostic technology. Why spend hundreds of hours (or more) building a design system from scratch? Why make a component library that only works with one framework?
With Shoelace, you can:
- Start building things faster (no need to roll your own buttons)
- Build multiple apps with different frameworks that all share the same UI
- Skip having to learn a new component library every time you switch frameworks
- Build multiple apps with different frameworks that all share the same UI components
- Fully customize components to match your existing designs
- Incrementally adopt components as needed (no need to ditch your framework)
- Upgrade or switch frameworks without rebuilding foundational components
If your organization is looking to build a design system, [Shoelace will save you thousands of dollars](https://medium.com/eightshapes-llc/and-you-thought-buttons-were-easy-26eb5b5c1871).* All the foundational components you need are right here, ready to be customized for your brand. And since it's built on web standards, browsers will continue to support it for many years to come.
@@ -98,15 +101,15 @@ Designing, developing, and supporting this library requires a lot of time, effor
👇 Your support is very much appreciated! 👇
<sl-button class="repo-button repo-button--sponsor" href="https://github.com/sponsors/claviska" target="_blank">
<sl-icon name="heart"></sl-icon> Become a sponsor
<sl-icon slot="prefix" name="heart"></sl-icon> Become a sponsor
</sl-button>
<sl-button class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<sl-icon name="github"></sl-icon> <span class="github-star-count">Star</span>
<sl-icon slot="prefix" name="github"></sl-icon> <span class="github-star-count">Star</span>
</sl-button>
<sl-button class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon name="twitter"></sl-icon> Follow
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
</sl-button>
## Attribution
@@ -117,7 +120,7 @@ Special thanks to the following projects and individuals that help make Shoelace
- Component metadata is generated by the [Custom Elements Manifest Analyzer](https://github.com/open-wc/custom-elements-manifest)
- Documentation is powered by [Docsify](https://docsify.js.org/)
- CDN services are provided by [jsDelivr](https://www.jsdelivr.com/)
- Color primitives are derived from [Tailwind](https://tailwindcss.com/)
- Color primitives are inspired by [Tailwind](https://tailwindcss.com/)
- Icons are courtesy of [Bootstrap Icons](https://icons.getbootstrap.com/)
- The homepage illustration is courtesy of [unDraw](https://undraw.co/)
- Positioning of menus, tooltips, et al is handled by [Popper.js](https://popper.js.org/)

View File

@@ -108,7 +108,7 @@ You will, however, need to maintain your theme more carefully, as new versions o
## Dark Theme
The built-in dark theme uses an inverted + shifted color scale so, if you're using design tokens as intended, you'll get a decent dark mode for free. While this isn't the same as a professionally curated dark theme, it provides an excellent baseline for one and you're encouraged to customize it depending on your needs.
The built-in dark theme uses an inverted + shifted color scale so, if you're using design tokens as intended, you'll get dark mode for free. While this isn't the same as a professionally curated dark theme, it provides an excellent baseline for one and you're encouraged to customize it depending on your needs.
This was achieved by taking the light theme's [color tokens](/tokens/color) and "flipping" the scale so 100 becomes 900, 200 becomes 800, 300 becomes 700, etc. Next, the luminance of each primitive was increased slightly to avoid true black, a color that is typically undesirable in dark themes. The result is a custom palette that complements the light theme well and makes it easy to offer light and dark variations with minimal effort.

View File

@@ -1,10 +1,10 @@
# Usage
Shoelace components are just regular HTML elements, or "custom elements" to be precise. You can use them like any other element. Each component has detailed documentation that describes its full API, including properties, events, methods, and more.
Shoelace components are just regular HTML elements, or [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) to be precise. You can use them like any other element. Each component has detailed documentation that describes its full API, including properties, events, methods, and more.
## Web Component Basics
If you're new to custom elements, often referred to as "web components," this section will familiarize you with how to use them.
### Properties
## Properties
Many components have properties that can be set using attributes. For example, buttons accept a `size` attribute that maps to the `size` property which dictates the button's size.
@@ -31,7 +31,7 @@ In rare cases, a property may require an array, an object, or a function. For ex
Refer to a component's documentation for a complete list of its properties.
### Events
## Events
You can listen for standard events such as `click`, `mouseover`, etc. as you normally would. In addition, some components emit custom events. These work the same way as standard events, but are prefixed with `sl-` to prevent collisions with standard events and other libraries.
@@ -48,7 +48,7 @@ You can listen for standard events such as `click`, `mouseover`, etc. as you nor
Refer to a component's documentation for a complete list of its custom events.
### Methods
## Methods
Some components have methods you can call to trigger various behaviors. For example, you can set focus on a Shoelace input using the `focus()` method.
@@ -63,7 +63,7 @@ Some components have methods you can call to trigger various behaviors. For exam
Refer to a component's documentation for a complete list of its methods and their arguments.
### Slots
## Slots
Many components use slots to accept content inside of them. The most common slot is the _default_ slot, which includes any content inside the component that doesn't have a `slot` attribute.
@@ -86,7 +86,7 @@ The location of a named slot doesn't matter. You can put it anywhere inside the
Refer to a component's documentation for a complete list of available slots.
### Don't Use Self-closing Tags
## Don't Use Self-closing Tags
Custom elements cannot have self-closing tags. Similar to `<script>` and `<textarea>`, you must always include the full closing tag.
@@ -98,7 +98,7 @@ Custom elements cannot have self-closing tags. Similar to `<script>` and `<texta
<sl-input></sl-input>
```
### Differences from Native Elements
## Differences from Native Elements
You might expect similarly named elements to share the same API as native HTML elements. This is not always the case. Shoelace components **are not** designed to be one-to-one replacements for their HTML counterparts.
@@ -128,123 +128,3 @@ If `settings.json` already exists, simply add the above line to the root of the
### Other Editors
Most popular editors support custom code completion with a bit of configuration. Please [submit a feature request](https://github.com/shoelace-style/shoelace/issues/new/choose) for your editor of choice. PRs are also welcome!
## React
React [doesn't play nice](https://custom-elements-everywhere.com/#react) with custom elements — it's a bit finicky about props.
> React passes all data to Custom Elements in the form of HTML attributes. For primitive data this is fine, but the system breaks down when passing rich data, like objects or arrays. In these instances you end up with stringified values like `some-attr="[object Object]"` which can't actually be used.
Event handling can also be cumbersome.
> Because React implements its own synthetic event system, it cannot listen for DOM events coming from Custom Elements without the use of a workaround. Developers will need to reference their Custom Elements using a ref and manually attach event listeners with addEventListener. This makes working with Custom Elements cumbersome.
Fortunately, there's a package called [@shoelace-style/react](https://www.npmjs.com/package/@shoelace-style/react) that will let you use Shoelace components as if they were React components. You can install it using this command.
```bash
npm install @shoelace-style/react
```
Include the default theme and any components you want to use in your app.
```jsx
import '@shoelace-style/shoelace/dist/themes/light.css';
import SlButton from '@shoelace-style/react/dist/button';
// ...
const MyComponent = (props) => {
return (
<SlButton type="primary">
Click me
</SlButton>
)
};
```
## Vue
Vue [plays nice](https://custom-elements-everywhere.com/#vue) with custom elements. You just have to tell it to ignore Shoelace components. This is pretty easy because they all start with `sl-`.
```js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
app.mount('#app');
```
### 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" />
```
### Two-way Binding
One caveat is there's currently [no support for v-model on custom elements](https://github.com/vuejs/vue/issues/7830), but you can still achieve two-way binding manually.
```html
<!-- This doesn't work -->
<sl-input v-model="name">
<!-- This works, but it's a bit longer -->
<sl-input :value="name" @input="name = $event.target.value">
```
If that's too verbose, you can use a custom directive instead.
### Using a Custom Directive
You can use [this utility](https://www.npmjs.com/package/@shoelace-style/vue-sl-model) to add a custom directive that will work just like `v-model` but for Shoelace components. To install it, use this command.
```bash
npm install @shoelace-style/vue-sl-model
```
Next, import the directive and enable it like this.
```js
import ShoelaceModelDirective from '@shoelace-style/vue-sl-model';
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.use(ShoelaceModelDirective);
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('sl-');
app.mount('#app');
```
Now you can use the `v-sl-model` directive to keep your data in sync!
```html
<sl-input v-sl-model="name">
```
## Angular
Angular [plays nice](https://custom-elements-everywhere.com/#angular) with custom elements. Just 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 {}
```

View File

@@ -8,10 +8,7 @@
name="description"
content="Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology."
/>
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="custom-elements-manifest" content="dist/custom-elements.json" />
<meta property="og:title" content="Shoelace" />
<meta
@@ -44,7 +41,7 @@
<link rel="stylesheet" href="/dist/themes/dark.css" />
<script type="module" src="/dist/shoelace.js"></script>
</head>
<body>
<body data-shoelace="/dist">
<div id="app"></div>
<script>
// Set the initial theme to prevent flashing
@@ -81,15 +78,16 @@
crossChapterText: false
},
routerMode: 'history',
themeColor: 'rgb(var(--sl-color-primary-500))'
themeColor: 'var(--sl-color-primary-500)'
};
</script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/ga.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-pagination@2/dist/docsify-pagination.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.19.0/components/prism-bash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.19.0/components/prism-jsx.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.25.0/components/prism-bash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.25.0/components/prism-jsx.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.25.0/components/prism-tsx.min.js"></script>
<script src="/assets/plugins/code-block/code-block.js"></script>
<script src="/assets/plugins/metadata/metadata.js"></script>
<script src="/assets/plugins/scroll-position/scroll-position.js"></script>

View File

@@ -0,0 +1,18 @@
# Accessibility Commitment
Shoelace recognizes the need for all users, regardless of ability and device, to have undeterred access to the websites and applications that are created with it. This is an important goal of the project.
Oftentimes, people will ask “is Shoelace accessible?” Im reluctant to answer because accessibility isnt binary — theres no simple “yes” or “no” response to provide. What seems accessible to a sighted user might be completely inaccessible to a non-sighted user. And even if you optimize for various screen readers, you still have to account for low-level vision, color blindness, hearing impairments, mobility impairments, and more.
Accessibility is something you have to continuously strive for. No individual contributor — or perhaps even an entire team — can claim their software is 100% accessible because of the sheer diversity of abilities, devices, assistive technologies, and individual use cases.
Furthermore, accessibility doesnt stop at the component level. Using accessible building blocks doesnt magically make the rest of your webpage or application compliant. There is no library or overlay that will make your software “fully accessible” without putting in the effort. Its also worth noting that web components are still somewhat bleeding edge, so browsers, assistive devices, and [even specifications](https://wicg.github.io/aom/spec/) are still evolving to help improve accessibility on the web platform.
My commitment to Shoelace users is this: Everything I develop will be built with accessibility in mind. I will test and improve every component to the best of my ability and knowledge. I will work around upstream issues, such as browser bugs and limitations, to the best of my ability and within reason.
Im fully aware that I may not get it right every time for every user, so I invite the community to participate in this ongoing effort by submitting [issues](https://github.com/shoelace-style/shoelace/issues?q=is%3Aissue+is%3Aopen+label%3Aa11y), [pull requests](https://github.com/shoelace-style/shoelace/pulls?q=is%3Aopen+is%3Apr+label%3Aa11y), and [discussions](https://github.com/shoelace-style/shoelace/discussions). Many accessibility improvements have already been made as a result of users submitting feedback and suggesting more appropriate alternatives.
This is the path forward. Together, we will make Shoelace accessible to as many users as possible.
— Cory LaViska<br>
_Creator of Shoelace_

View File

@@ -6,13 +6,63 @@ Components with the <sl-badge type="warning" pill>Experimental</sl-badge> badge
_During the beta period, these restrictions may be relaxed in the event of a mission-critical bug._ 🐛
## Next
## 2.0.0-beta.61
- Added experimental `<sl-context-menu>` component
This release improves the dark theme by shifting luminance in both directions, slightly condensing the spectrum. This results in richer colors in dark mode. It also reduces theme stylesheet sizes by eliminating superfluous gray palette variations.
In [beta.48](#_200-beta48), I introduced a change to color tokens that allowed you to access alpha values at the expense of a verbose, non-standard syntax. After considering feedback from the community, I've decided to revert this change so the `rgb()` function is no longer required. Many users reported never using it for alpha, and even more reported having trouble remembering to use `rgb()` and that it was causing more harm than good.
Furthermore, both Safari and Firefox have implemented [`color-mix()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix()) behind a flag, so access to alpha channels and other capabilities are coming to the browser soon.
If you're using color tokens in your own stylesheet, simply remove the `rgb()` to update to this version.
```css
.your-styles {
/* change this */
color: rgb(var(--sl-color-primary-500));
/* to this */
color: var(--sl-color-primary-500);
}
```
Thank you for your help and patience with testing Shoelace. I promise, we're not far from a stable release!
- 🚨 BREAKING: removed blue gray, cool gray, true gray, and warm gray color palettes
- 🚨 BREAKING: removed `--sl-focus-ring-color`, and `--sl-focus-ring-alpha` (use `--sl-focus-ring` instead)
- 🚨 BREAKING: removed `--sl-surface-base` and `--sl-surface-base-alt` tokens (use the neutral palette instead)
- Added experimental `<sl-visually-hidden>` component
- Added `clear-icon` slot to `<sl-select>` [#591](https://github.com/shoelace-style/shoelace/issues/591)
- Fixed a bug in `<sl-progress-bar>` where the label would show in the default slot
- Improved the dark theme palette so colors are bolder and don't appear washed out
- Improved a11y of `<sl-avatar>` by representing it as an image with an `alt` [#579](https://github.com/shoelace-style/shoelace/issues/579)
- Improved a11y of the scroll buttons in `<sl-tab-group>`
- Improved a11y of the close button in `<sl-tab>`
- Improved a11y of `<sl-tab-panel>` by removing an invalid `aria-selected` attribute [#579](https://github.com/shoelace-style/shoelace/issues/579)
- Improved a11y of `<sl-icon>` by not using a variation of the `name` attribute for labels (use the `label` prop instead)
- Moved `role` from the shadow root to the host element in `<sl-menu>`
- Removed redundant `role="menu"` in `<sl-dropdown>`
- Slightly faster animations for showing and hiding `<sl-dropdown>`
- Updated to Bootstrap Icons to 1.7.1
- Upgraded the status of `<sl-mutation-observer>` from experimental to stable
## 2.0.0-beta.60
- Added React examples and CodePen links to all components
- Changed the `attr` in experimental `<sl-mutation-observer>` to require `"*"` instead of `""` to target all attributes
- Fixed a bug in `<sl-progress-bar>` where the `label` attribute didn't set the label
- Fixed a bug in `<sl-rating>` that caused disabled and readonly controls to transition on hover
- The `panel` property of `<sl-tab>` is now reflected
- The `name` property of `<sl-tab-panel>` is now reflected
## 2.0.0-beta.59
- Added React wrappers as first-class citizens
- Added eye dropper to `<sl-color-picker>` when the browser supports the [EyeDropper API](https://wicg.github.io/eyedropper-api/)
- Fixed a bug in `<sl-button-group>` where buttons groups with only one button would have an incorrect border radius
- Improved the `<sl-color-picker>` trigger's border in dark mode
- Refactored positioning logic in `<sl-dropdown>` so Popper is only active when the menu is open
- Switched clearable icon from `x-circle` to `x-circle-fill` to make it easier to recognize
- Updated to Bootstrap Icons to 1.7.0
- Updated to Lit 2.0.2
## 2.0.0-beta.58
@@ -58,11 +108,11 @@ Shoelace doesn't have a lot of dependencies, but this release unbundles most of
## 2.0.0-beta.53
- 🚨 BREAKING: removed `<sl-menu-divider>` (use `<sl-divider>` instead)
- 🚨 BREAKING: removed `percentage` attribute from `<sl-progress-bar>` and `<sl-progress-ring>` (use `value`) instead
- 🚨 BREAKING: removed `percentage` attribute from `<sl-progress-bar>` and `<sl-progress-ring>` (use `value` instead)
- 🚨 BREAKING: switched the default `type` of `<sl-tag>` from `primary` to `neutral`
- Added the experimental `<sl-mutation-observer>` component
- Added the `<sl-divider>` component
- Added `--sl-surface-base` and `--sl-surface-base-alt` as early surface tokens to improve the appearance of alert, card, and panels in dark mode
- Added `--sl-color-neutral-0` and `--sl-color-neutral-50` as early surface tokens to improve the appearance of alert, card, and panels in dark mode
- Added the `--sl-panel-border-width` design token
- Added missing background color to `<sl-details>`
- Added the `--padding` custom property to `<sl-tab-panel>`
@@ -145,7 +195,7 @@ Previously, color tokens were in hexidecimal format. Now, Shoelace now uses an `
```css
.example {
/* rgb() is required now */
color: rgb(var(--sl-color-neutral-500));
color: var(--sl-color-neutral-500);
}
```

View File

@@ -215,8 +215,8 @@ To expose custom properties as part of a component's API, scope them to the `:ho
```css
:host {
--color: rgb(var(--sl-color-primary-500));
--background-color: rgb(var(--sl-color-neutral-100));
--color: var(--sl-color-primary-500);
--background-color: var(--sl-color-neutral-100);
}
```
@@ -249,25 +249,6 @@ Parts let you target a specific element inside the component's shadow DOM but, b
This convention can be relaxed when the developer experience is greatly improved by not following these suggestions.
### Design Token Color Values
All design tokens that implement a color value must do so in the following `R G B` format for consistency.
```css
:root {
--sl-color-sky-500: 14 165 233;
}
```
This format requires the consumer to use the `rgb()` function in their stylesheets, but it also lets them control each token's alpha channel.
```css
.example {
color: rgb(var(--sl-color-sky-500));
background-color: rgb(var(--sl-color-sky-500) / 5%); /* 5% opacity */
}
```
### Form Controls
Form controls should support validation through the following conventions:

View File

@@ -2,34 +2,8 @@
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.
## Usage
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.
All color tokens are defined as a set of RGB integers, eg. `113 113 122`. CSS doesn't understand this format, however, so you need wrap them in the `rgb()` function.
```css
.example {
color: rgb(var(--sl-color-neutral-500));
}
```
This may seem a bit verbose, but it gives us a super power — we can adjust the alpha channel of any color token!
## Adjusting Alpha Channels
By default, color tokens produce an opaque color. With this syntax, you can easily adjust alpha channels.
```css
.example-with-alpha {
color: rgb(var(--sl-color-neutral-500) / 50%);
}
```
The browser evaluates this to `rgb(113 113 122 / 50%)`, where 50% is the alpha value. Note the required `/` delimiter after the color token. Alternatively, you can use a decimal such as 0.5 in lieu of a percentage.
This syntax may not look familiar, but it's perfectly valid per the [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#typedef-color) and [well-supported](https://caniuse.com/mdn-css_types_color_space_separated_functional_notation) by modern browsers.
## 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.
@@ -39,17 +13,17 @@ Theme tokens give you a semantic way to reference colors in your app. The primar
Primary<br>
<code>--sl-color-primary-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-primary-950));"></div>950</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">
@@ -57,17 +31,17 @@ Theme tokens give you a semantic way to reference colors in your app. The primar
Success<br>
<code>--sl-color-success-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-success-950));"></div>950</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">
@@ -75,17 +49,17 @@ Theme tokens give you a semantic way to reference colors in your app. The primar
Warning<br>
<code>--sl-color-warning-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warning-950));"></div>950</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">
@@ -93,17 +67,17 @@ Theme tokens give you a semantic way to reference colors in your app. The primar
Danger<br>
<code>--sl-color-danger-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-danger-950));"></div>950</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">
@@ -111,17 +85,17 @@ Theme tokens give you a semantic way to reference colors in your app. The primar
Neutral<br>
<code>--sl-color-neutral-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-neutral-950));"></div>950</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">
@@ -129,8 +103,8 @@ Theme tokens give you a semantic way to reference colors in your app. The primar
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: rgb(var(--sl-color-neutral-0));"></div>0</div>
<div class="color-palette__example"><div class="color-palette__swatch " style="background-color: rgb(var(--sl-color-neutral-1000));"></div>1000</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>
?> Looking for an easy way to customize your theme? [Try the color token generator!](https://codepen.io/claviska/full/QWveRgL)
@@ -140,94 +114,22 @@ Theme tokens give you a semantic way to reference colors in your app. The primar
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">
Blue Gray<br>
<code>--sl-color-blue-gray-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-gray-950));"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Cool Gray<br>
<code>--sl-color-cool-gray-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cool-gray-950));"></div>950</div>
</div>
<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: rgb(var(--sl-color-gray-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-gray-950));"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
True Gray<br>
<code>--sl-color-true-gray-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-true-gray-950));"></div>950</div>
</div>
<div class="color-palette">
<div class="color-palette__name">
Warm Gray<br>
<code>--sl-color-warm-gray-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-warm-gray-950));"></div>950</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">
@@ -235,17 +137,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Red<br>
<code>--sl-color-red-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-red-950));"></div>950</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">
@@ -253,17 +155,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Orange<br>
<code>--sl-color-orange-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-orange-950));"></div>950</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">
@@ -271,17 +173,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Amber<br>
<code>--sl-color-amber-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-amber-950));"></div>950</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">
@@ -289,17 +191,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Yellow<br>
<code>--sl-color-yellow-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-yellow-950));"></div>950</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">
@@ -307,17 +209,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Lime<br>
<code>--sl-color-lime-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-lime-950));"></div>950</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">
@@ -325,17 +227,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Green<br>
<code>--sl-color-green-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-green-950));"></div>950</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">
@@ -343,17 +245,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Emerald<br>
<code>--sl-color-emerald-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-emerald-950));"></div>950</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">
@@ -361,17 +263,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Teal<br>
<code>--sl-color-teal-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-teal-950));"></div>950</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">
@@ -379,17 +281,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Cyan<br>
<code>--sl-color-cyan-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-cyan-950));"></div>950</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">
@@ -397,17 +299,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Sky<br>
<code>--sl-color-sky-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-sky-950));"></div>950</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">
@@ -415,17 +317,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Blue<br>
<code>--sl-color-blue-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-blue-950));"></div>950</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">
@@ -433,17 +335,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Indigo<br>
<code>--sl-color-indigo-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-indigo-950));"></div>950</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">
@@ -451,17 +353,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Violet<br>
<code>--sl-color-violet-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-violet-950));"></div>950</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">
@@ -469,17 +371,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Purple<br>
<code>--sl-color-purple-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-purple-950));"></div>950</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">
@@ -487,17 +389,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Fuchsia<br>
<code>--sl-color-fuchsia-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-fuchsia-950));"></div>950</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">
@@ -505,17 +407,17 @@ Additional palettes are provided in the form of color primitives. Use these when
Pink<br>
<code>--sl-color-pink-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-pink-950));"></div>950</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">
@@ -523,15 +425,15 @@ Additional palettes are provided in the form of color primitives. Use these when
Rose<br>
<code>--sl-color-rose-<em>{n}</em></code>
</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-50));"></div>50</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-100));"></div>100</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-200));"></div>200</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-300));"></div>300</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-400));"></div>400</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-500));"></div>500</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-600));"></div>600</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-700));"></div>700</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-800));"></div>800</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-900));"></div>900</div>
<div class="color-palette__example"><div class="color-palette__swatch" style="background-color: rgb(var(--sl-color-rose-950));"></div>950</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,6 +1,8 @@
# Integrating with Laravel
This page explains how to integrate Shoelace with a [Laravel](https://laravel.com) app using a local Webpack bundle. This is a community-maintained document. For questions about this integration, please [ask the community](/resources/community).
This page explains how to integrate Shoelace with a [Laravel](https://laravel.com) app using a local Webpack bundle.
?> This is a community-maintained document. Please [ask the community](/resources/community) if you have questions about this integration. You can also [suggest improvements](https://github.com/shoelace-style/shoelace/blob/next/docs/tutorials/integrating-with-laravel.md) to make it better.
## Requirements

View File

@@ -1,6 +1,8 @@
# Integrating with NextJS
This page explains how to integrate Shoelace with a NextJS app. This is a community-maintained document. For questions about this integration, please [ask the community](/resources/community).
This page explains how to integrate Shoelace with a NextJS app.
?> This is a community-maintained document. Please [ask the community](/resources/community) if you have questions about this integration. You can also [suggest improvements](https://github.com/shoelace-style/shoelace/blob/next/docs/tutorials/integrating-with-nextjs.md) to make it better.
## Requirements
@@ -14,7 +16,7 @@ This integration has been tested with the following:
To get started using Shoelace with NextJS, the following packages must be installed.
```bash
yarn add @shoelace-style/shoelace @shoelace-style/react-wrapper copy-webpack-plugin next-compose-plugins next-transpile-modules
yarn add @shoelace-style/shoelace @shoelace-style/shoelace copy-webpack-plugin next-compose-plugins next-transpile-modules
```
### Importing the Default Theme

View File

@@ -1,6 +1,8 @@
# Integrating with Rails
This page explains how to integrate Shoelace with a Rails app. This is a community-maintained document. For questions about this integration, please [ask the community](/resources/community).
This page explains how to integrate Shoelace with a Rails app.
?> This is a community-maintained document. Please [ask the community](/resources/community) if you have questions about this integration. You can also [suggest improvements](https://github.com/shoelace-style/shoelace/blob/next/docs/tutorials/integrating-with-rails.md) to make it better.
## Requirements

1179
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "@shoelace-style/shoelace",
"description": "A forward-thinking library of web components.",
"version": "2.0.0-beta.58",
"version": "2.0.0-beta.61",
"homepage": "https://github.com/shoelace-style/shoelace",
"author": "Cory LaViska",
"license": "MIT",
@@ -39,20 +39,23 @@
"test:watch": "web-test-runner \"src/**/*.test.ts\" --node-resolve --puppeteer --watch"
},
"dependencies": {
"@lit-labs/react": "^1.0.1",
"@popperjs/core": "^2.7.0",
"@shoelace-style/animations": "^1.1.0",
"color": "^3.1.3",
"lit": "^2.0.2",
"qr-creator": "^1.0.0"
},
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.4.12",
"@open-wc/testing": "^2.5.33",
"@types/color": "^3.0.1",
"@types/react": "^17.0.33",
"@web/dev-server-esbuild": "^0.2.12",
"@web/test-runner": "^0.13.5",
"@web/test-runner-puppeteer": "^0.10.0",
"bluebird": "^3.7.2",
"bootstrap-icons": "^1.6.1",
"bootstrap-icons": "^1.7.1",
"browser-sync": "^2.26.14",
"chalk": "^4.1.0",
"command-line-args": "^5.2.0",
@@ -65,11 +68,11 @@
"get-port": "^5.1.1",
"globby": "^11.0.4",
"husky": "^4.3.8",
"lit": "^2.0.2",
"lunr": "^2.3.9",
"mkdirp": "^0.5.5",
"plop": "^2.7.4",
"prettier": "^2.2.1",
"react": "^17.0.2",
"recursive-copy": "^2.0.11",
"sinon": "^11.1.1",
"strip-css-comments": "^4.1.0",

View File

@@ -30,17 +30,19 @@ mkdirp.sync(outdir);
(async () => {
try {
if (types) execSync(`tsc --project . --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-search.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-react.js`, { stdio: 'inherit' });
execSync(`node scripts/make-vscode-data.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-css.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-icons.js --outdir "${outdir}"`, { stdio: 'inherit' });
if (types) execSync(`tsc --project . --outdir "${outdir}"`, { stdio: 'inherit' });
} catch (err) {
console.error(chalk.red(err));
process.exit(1);
}
const alwaysExternal = ['@lit-labs/react', 'react'];
const buildResult = await esbuild
.build({
format: 'esm',
@@ -53,13 +55,15 @@ mkdirp.sync(outdir);
// Public utilities
...(await glob('./src/utilities/**/!(*.(style|test)).ts')),
// Theme stylesheets
...(await glob('./src/themes/**/!(*.test).ts'))
...(await glob('./src/themes/**/!(*.test).ts')),
// React wrappers
...(await glob('./src/react/**/*.ts'))
],
outdir,
chunkNames: 'chunks/[name].[hash]',
incremental: serve,
define: {
// Popper.js expects this to be set
// Popper.js requires this to be set
'process.env.NODE_ENV': '"production"'
},
bundle: true,
@@ -67,7 +71,11 @@ mkdirp.sync(outdir);
// We don't bundle certain dependencies in the unbundled build. This ensures we ship bare module specifiers,
// allowing end users to better optimize when using a bundler. (Only packages that ship ESM can be external.)
//
external: bundle ? undefined : ['@popperjs/core', '@shoelace-style/animations', 'lit', 'qr-creator'],
// We never bundle React or @lit-labs/react though!
//
external: bundle
? alwaysExternal
: [...alwaysExternal, '@popperjs/core', '@shoelace-style/animations', 'lit', 'qr-creator'],
splitting: true,
plugins: []
})

66
scripts/make-react.js Normal file
View File

@@ -0,0 +1,66 @@
import chalk from 'chalk';
import commandLineArgs from 'command-line-args';
import fs from 'fs';
import del from 'del';
import mkdirp from 'mkdirp';
import path from 'path';
import pascalCase from 'pascal-case';
import prettier from 'prettier';
import prettierConfig from '../prettier.config.cjs';
import { execSync } from 'child_process';
import { getAllComponents } from './shared.js';
const outdir = path.join('./src/react');
// Clear build directory
del.sync(outdir);
mkdirp.sync(outdir);
// Fetch component metadata
const metadata = JSON.parse(fs.readFileSync('./dist/custom-elements.json', 'utf8'));
// Wrap components
console.log('Wrapping components for React...');
const components = getAllComponents(metadata);
const index = [];
components.map(component => {
const tagWithoutPrefix = component.tagName.replace(/^sl-/, '');
const componentDir = path.join(outdir, tagWithoutPrefix);
const componentFile = path.join(componentDir, 'index.ts');
const importPath = component.modulePath.replace(/^src\//, '').replace(/\.ts$/, '');
mkdirp.sync(componentDir);
const events = (component.events || []).map(event => `${`on${pascalCase(event.name)}`}: '${event.name}'`).join(',\n');
const source = prettier.format(
`
import * as React from 'react';
import { createComponent } from '@lit-labs/react';
import Component from '../../${importPath}';
export default createComponent(
React,
'${component.tagName}',
Component,
{
${events}
}
);
`,
Object.assign(prettierConfig, {
parser: 'babel-ts'
})
);
index.push(`export { default as ${component.name} } from './${tagWithoutPrefix}';`);
fs.writeFileSync(componentFile, source, 'utf8');
});
// Generate the index file
fs.writeFileSync(path.join(outdir, 'index.ts'), index.join('\n'), 'utf8');
console.log(chalk.cyan(`\nComponents have been wrapped for React! 📦\n`));

View File

@@ -5,9 +5,10 @@ export function getAllComponents(metadata) {
module.declarations?.map(declaration => {
if (declaration.customElement) {
const component = declaration;
const modulePath = module.path;
if (component) {
allComponents.push(component);
allComponents.push(Object.assign(component, { modulePath }));
}
}
});

View File

@@ -15,8 +15,8 @@ export default css`
position: relative;
display: flex;
align-items: stretch;
background-color: rgb(var(--sl-surface-base-alt));
border: solid var(--sl-panel-border-width) rgb(var(--sl-panel-border-color));
background-color: var(--sl-panel-background-color);
border: solid var(--sl-panel-border-width) var(--sl-panel-border-color);
border-top-width: calc(var(--sl-panel-border-width) * 3);
border-radius: var(--sl-border-radius-medium);
box-shadow: var(--box-shadow);
@@ -24,7 +24,7 @@ export default css`
font-size: var(--sl-font-size-small);
font-weight: var(--sl-font-weight-normal);
line-height: 1.6;
color: rgb(var(--sl-color-neutral-700));
color: var(--sl-color-neutral-700);
margin: inherit;
}
@@ -40,43 +40,43 @@ export default css`
}
.alert--primary {
border-top-color: rgb(var(--sl-color-primary-600));
border-top-color: var(--sl-color-primary-600);
}
.alert--primary .alert__icon {
color: rgb(var(--sl-color-primary-600));
color: var(--sl-color-primary-600);
}
.alert--success {
border-top-color: rgb(var(--sl-color-success-600));
border-top-color: var(--sl-color-success-600);
}
.alert--success .alert__icon {
color: rgb(var(--sl-color-success-600));
color: var(--sl-color-success-600);
}
.alert--neutral {
border-top-color: rgb(var(--sl-color-neutral-600));
border-top-color: var(--sl-color-neutral-600);
}
.alert--neutral .alert__icon {
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
}
.alert--warning {
border-top-color: rgb(var(--sl-color-warning-600));
border-top-color: var(--sl-color-warning-600);
}
.alert--warning .alert__icon {
color: rgb(var(--sl-color-warning-600));
color: var(--sl-color-warning-600);
}
.alert--danger {
border-top-color: rgb(var(--sl-color-danger-600));
border-top-color: var(--sl-color-danger-600);
}
.alert--danger .alert__icon {
color: rgb(var(--sl-color-danger-600));
color: var(--sl-color-danger-600);
}
.alert__message {

View File

@@ -5,7 +5,7 @@ export default css`
${componentStyles}
:host {
--control-box-size: 2.5rem;
--control-box-size: 3rem;
--icon-size: calc(var(--control-box-size) * 0.625);
display: inline-flex;
position: relative;
@@ -33,10 +33,10 @@ export default css`
height: var(--control-box-size);
font-size: var(--icon-size);
background: none;
border: none;
background-color: rgb(var(--sl-color-neutral-1000) / 50%);
border: solid 2px currentColor;
background-color: rgb(0 0 0 /50%);
border-radius: var(--sl-border-radius-circle);
color: rgb(var(--sl-color-neutral-0));
color: white;
pointer-events: none;
transition: var(--sl-transition-fast) opacity;
}

View File

@@ -13,7 +13,8 @@ import styles from './animation.styles';
* @event sl-finish - Emitted when the animation finishes.
* @event sl-start - Emitted when the animation starts or restarts.
*
* @slot - The element to animate. If multiple elements are to be animated, wrap them in a single container.
* @slot - The element to animate. If multiple elements are to be animated, wrap them in a single container or use
* multiple animation elements.
*/
@customElement('sl-animation')
export default class SlAnimation extends LitElement {

View File

@@ -17,11 +17,11 @@ export default css`
position: relative;
width: var(--size);
height: var(--size);
background-color: rgb(var(--sl-color-neutral-400));
background-color: var(--sl-color-neutral-400);
font-family: var(--sl-font-sans);
font-size: calc(var(--size) * 0.5);
font-weight: var(--sl-font-weight-normal);
color: rgb(var(--sl-color-neutral-0));
color: var(--sl-color-neutral-0);
overflow: hidden;
user-select: none;
vertical-align: middle;

View File

@@ -8,7 +8,7 @@ describe('<sl-avatar>', () => {
describe('when provided no parameters', async () => {
before(async () => {
el = await fixture<SlAvatar>(html` <sl-avatar></sl-avatar> `);
el = await fixture<SlAvatar>(html` <sl-avatar alt="Avatar"></sl-avatar> `);
});
it('passes accessibility test', async () => {
@@ -71,7 +71,7 @@ describe('<sl-avatar>', () => {
describe('when provided initials parameter', async () => {
const initials = 'SL';
before(async () => {
el = await fixture<SlAvatar>(html`<sl-avatar initials="${initials}"></sl-avatar>`);
el = await fixture<SlAvatar>(html`<sl-avatar initials="${initials}" alt="Avatar"></sl-avatar>`);
});
it('passes accessibility test', async () => {
@@ -88,7 +88,7 @@ describe('<sl-avatar>', () => {
['square', 'rounded', 'circle'].forEach(shape => {
describe(`when passed a shape attribute ${shape}`, () => {
before(async () => {
el = await fixture<SlAvatar>(html`<sl-avatar shape="${shape}"></sl-avatar>`);
el = await fixture<SlAvatar>(html`<sl-avatar shape="${shape as any}" alt="Shaped avatar"></sl-avatar>`);
});
it('passes accessibility test', async () => {
@@ -106,7 +106,7 @@ describe('<sl-avatar>', () => {
describe('when passed a <span>, on slot "icon"', async () => {
before(async () => {
el = await fixture<SlAvatar>(html`<sl-avatar><span slot="icon">random content</span></sl-avatar>`);
el = await fixture<SlAvatar>(html`<sl-avatar alt="Avatar"><span slot="icon">random content</span></sl-avatar>`);
});
it('passes accessibility test', async () => {

View File

@@ -48,12 +48,13 @@ export default class SlAvatar extends LitElement {
'avatar--rounded': this.shape === 'rounded',
'avatar--square': this.shape === 'square'
})}
role="img"
aria-label=${this.alt}
>
${this.initials
? html` <div part="initials" class="avatar__initials">${this.initials}</div> `
: html`
<div part="icon" class="avatar__icon">
<div part="icon" class="avatar__icon" aria-hidden="true">
<slot name="icon">
<sl-icon name="person-fill" library="system"></sl-icon>
</slot>

View File

@@ -17,7 +17,7 @@ export default css`
letter-spacing: var(--sl-letter-spacing-normal);
line-height: 1;
border-radius: var(--sl-border-radius-small);
border: solid 1px rgb(var(--sl-color-neutral-0));
border: solid 1px var(--sl-color-neutral-0);
white-space: nowrap;
padding: 3px 6px;
user-select: none;
@@ -26,28 +26,28 @@ export default css`
/* Type modifiers */
.badge--primary {
background-color: rgb(var(--sl-color-primary-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
.badge--success {
background-color: rgb(var(--sl-color-success-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-success-600);
color: var(--sl-color-neutral-0);
}
.badge--neutral {
background-color: rgb(var(--sl-color-neutral-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-neutral-600);
color: var(--sl-color-neutral-0);
}
.badge--warning {
background-color: rgb(var(--sl-color-warning-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-warning-600);
color: var(--sl-color-neutral-0);
}
.badge--danger {
background-color: rgb(var(--sl-color-danger-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-danger-600);
color: var(--sl-color-neutral-0);
}
/* Pill modifier */
@@ -61,23 +61,23 @@ export default css`
}
.badge--pulse.badge--primary {
--pulse-color: rgb(var(--sl-color-primary-600));
--pulse-color: var(--sl-color-primary-600);
}
.badge--pulse.badge--success {
--pulse-color: rgb(var(--sl-color-success-600));
--pulse-color: var(--sl-color-success-600);
}
.badge--pulse.badge--neutral {
--pulse-color: rgb(var(--sl-color-neutral-600));
--pulse-color: var(--sl-color-neutral-600);
}
.badge--pulse.badge--warning {
--pulse-color: rgb(var(--sl-color-warning-600));
--pulse-color: var(--sl-color-warning-600);
}
.badge--pulse.badge--danger {
--pulse-color: rgb(var(--sl-color-danger-600));
--pulse-color: var(--sl-color-danger-600);
}
@keyframes pulse {

View File

@@ -15,7 +15,7 @@ export default css`
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-small);
font-weight: var(--sl-font-weight-semibold);
color: rgb(var(--sl-color-neutral-600));
color: var(--sl-color-neutral-600);
line-height: var(--sl-line-height-normal);
white-space: nowrap;
}
@@ -38,15 +38,15 @@ export default css`
}
:host(:not(:last-of-type)) .breadcrumb-item__label {
color: rgb(var(--sl-color-primary-600));
color: var(--sl-color-primary-600);
}
:host(:not(:last-of-type)) .breadcrumb-item__label:hover {
color: rgb(var(--sl-color-primary-500));
color: var(--sl-color-primary-500);
}
:host(:not(:last-of-type)) .breadcrumb-item__label:active {
color: rgb(var(--sl-color-primary-600));
color: var(--sl-color-primary-600);
}
.breadcrumb-item__label${focusVisibleSelector} {

View File

@@ -70,157 +70,157 @@ export default css`
/* Default */
.button--standard.button--default {
background-color: rgb(var(--sl-color-neutral-0));
border-color: rgb(var(--sl-color-neutral-300));
color: rgb(var(--sl-color-neutral-700));
background-color: var(--sl-color-neutral-0);
border-color: var(--sl-color-neutral-300);
color: var(--sl-color-neutral-700);
}
.button--standard.button--default:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-primary-50));
border-color: rgb(var(--sl-color-primary-300));
color: rgb(var(--sl-color-primary-700));
background-color: var(--sl-color-primary-50);
border-color: var(--sl-color-primary-300);
color: var(--sl-color-primary-700);
}
.button--standard.button--default${focusVisibleSelector}:not(.button--disabled) {
background-color: rgb(var(--sl-color-primary-50));
border-color: rgb(var(--sl-color-primary-400));
color: rgb(var(--sl-color-primary-700));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-primary-500) / var(--sl-focus-ring-alpha));
background-color: var(--sl-color-primary-50);
border-color: var(--sl-color-primary-400);
color: var(--sl-color-primary-700);
box-shadow: var(--sl-focus-ring);
}
.button--standard.button--default:active:not(.button--disabled) {
background-color: rgb(var(--sl-color-primary-100));
border-color: rgb(var(--sl-color-primary-400));
color: rgb(var(--sl-color-primary-700));
background-color: var(--sl-color-primary-100);
border-color: var(--sl-color-primary-400);
color: var(--sl-color-primary-700);
}
/* Primary */
.button--standard.button--primary {
background-color: rgb(var(--sl-color-primary-600));
border-color: rgb(var(--sl-color-primary-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-primary-600);
border-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
.button--standard.button--primary:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-primary-500));
border-color: rgb(var(--sl-color-primary-500));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-primary-500);
border-color: var(--sl-color-primary-500);
color: var(--sl-color-neutral-0);
}
.button--standard.button--primary${focusVisibleSelector}:not(.button--disabled) {
background-color: rgb(var(--sl-color-primary-500));
border-color: rgb(var(--sl-color-primary-500));
color: rgb(var(--sl-color-neutral-0));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-primary-500) / var(--sl-focus-ring-alpha));
background-color: var(--sl-color-primary-500);
border-color: var(--sl-color-primary-500);
color: var(--sl-color-neutral-0);
box-shadow: var(--sl-focus-ring);
}
.button--standard.button--primary:active:not(.button--disabled) {
background-color: rgb(var(--sl-color-primary-600));
border-color: rgb(var(--sl-color-primary-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-primary-600);
border-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
/* Success */
.button--standard.button--success {
background-color: rgb(var(--sl-color-success-600));
border-color: rgb(var(--sl-color-success-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-success-600);
border-color: var(--sl-color-success-600);
color: var(--sl-color-neutral-0);
}
.button--standard.button--success:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-success-500));
border-color: rgb(var(--sl-color-success-500));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-success-500);
border-color: var(--sl-color-success-500);
color: var(--sl-color-neutral-0);
}
.button--standard.button--success${focusVisibleSelector}:not(.button--disabled) {
background-color: rgb(var(--sl-color-success-600));
border-color: rgb(var(--sl-color-success-600));
color: rgb(var(--sl-color-neutral-0));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-success-500) / var(--sl-focus-ring-alpha));
background-color: var(--sl-color-success-600);
border-color: var(--sl-color-success-600);
color: var(--sl-color-neutral-0);
box-shadow: var(--sl-focus-ring);
}
.button--standard.button--success:active:not(.button--disabled) {
background-color: rgb(var(--sl-color-success-600));
border-color: rgb(var(--sl-color-success-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-success-600);
border-color: var(--sl-color-success-600);
color: var(--sl-color-neutral-0);
}
/* Neutral */
.button--standard.button--neutral {
background-color: rgb(var(--sl-color-neutral-600));
border-color: rgb(var(--sl-color-neutral-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-neutral-600);
border-color: var(--sl-color-neutral-600);
color: var(--sl-color-neutral-0);
}
.button--standard.button--neutral:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-neutral-500));
border-color: rgb(var(--sl-color-neutral-500));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-neutral-500);
border-color: var(--sl-color-neutral-500);
color: var(--sl-color-neutral-0);
}
.button--standard.button--neutral${focusVisibleSelector}:not(.button--disabled) {
background-color: rgb(var(--sl-color-neutral-500));
border-color: rgb(var(--sl-color-neutral-500));
color: rgb(var(--sl-color-neutral-0));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-neutral-500) / var(--sl-focus-ring-alpha));
background-color: var(--sl-color-neutral-500);
border-color: var(--sl-color-neutral-500);
color: var(--sl-color-neutral-0);
box-shadow: var(--sl-focus-ring);
}
.button--standard.button--neutral:active:not(.button--disabled) {
background-color: rgb(var(--sl-color-neutral-600));
border-color: rgb(var(--sl-color-neutral-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-neutral-600);
border-color: var(--sl-color-neutral-600);
color: var(--sl-color-neutral-0);
}
/* Warning */
.button--standard.button--warning {
background-color: rgb(var(--sl-color-warning-600));
border-color: rgb(var(--sl-color-warning-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-warning-600);
border-color: var(--sl-color-warning-600);
color: var(--sl-color-neutral-0);
}
.button--standard.button--warning:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-warning-500));
border-color: rgb(var(--sl-color-warning-500));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-warning-500);
border-color: var(--sl-color-warning-500);
color: var(--sl-color-neutral-0);
}
.button--standard.button--warning${focusVisibleSelector}:not(.button--disabled) {
background-color: rgb(var(--sl-color-warning-500));
border-color: rgb(var(--sl-color-warning-500));
color: rgb(var(--sl-color-neutral-0));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-warning-500) / var(--sl-focus-ring-alpha));
background-color: var(--sl-color-warning-500);
border-color: var(--sl-color-warning-500);
color: var(--sl-color-neutral-0);
box-shadow: var(--sl-focus-ring);
}
.button--standard.button--warning:active:not(.button--disabled) {
background-color: rgb(var(--sl-color-warning-600));
border-color: rgb(var(--sl-color-warning-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-warning-600);
border-color: var(--sl-color-warning-600);
color: var(--sl-color-neutral-0);
}
/* Danger */
.button--standard.button--danger {
background-color: rgb(var(--sl-color-danger-600));
border-color: rgb(var(--sl-color-danger-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-danger-600);
border-color: var(--sl-color-danger-600);
color: var(--sl-color-neutral-0);
}
.button--standard.button--danger:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-danger-500));
border-color: rgb(var(--sl-color-danger-500));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-danger-500);
border-color: var(--sl-color-danger-500);
color: var(--sl-color-neutral-0);
}
.button--standard.button--danger${focusVisibleSelector}:not(.button--disabled) {
background-color: rgb(var(--sl-color-danger-500));
border-color: rgb(var(--sl-color-danger-500));
color: rgb(var(--sl-color-neutral-0));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-danger-500) / var(--sl-focus-ring-alpha));
background-color: var(--sl-color-danger-500);
border-color: var(--sl-color-danger-500);
color: var(--sl-color-neutral-0);
box-shadow: var(--sl-focus-ring);
}
.button--standard.button--danger:active:not(.button--disabled) {
background-color: rgb(var(--sl-color-danger-600));
border-color: rgb(var(--sl-color-danger-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-danger-600);
border-color: var(--sl-color-danger-600);
color: var(--sl-color-neutral-0);
}
/*
@@ -234,135 +234,135 @@ export default css`
/* Default */
.button--outline.button--default {
border-color: rgb(var(--sl-color-neutral-300));
color: rgb(var(--sl-color-neutral-700));
border-color: var(--sl-color-neutral-300);
color: var(--sl-color-neutral-700);
}
.button--outline.button--default:hover:not(.button--disabled) {
border-color: rgb(var(--sl-color-primary-600));
background-color: rgb(var(--sl-color-primary-600));
color: rgb(var(--sl-color-neutral-0));
border-color: var(--sl-color-primary-600);
background-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
.button--outline.button--default${focusVisibleSelector}:not(.button--disabled) {
border-color: rgb(var(--sl-color-primary-500));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-primary-500) / var(--sl-focus-ring-alpha));
border-color: var(--sl-color-primary-500);
box-shadow: var(--sl-focus-ring);
}
.button--outline.button--default:active:not(.button--disabled) {
border-color: rgb(var(--sl-color-primary-700));
background-color: rgb(var(--sl-color-primary-700));
color: rgb(var(--sl-color-neutral-0));
border-color: var(--sl-color-primary-700);
background-color: var(--sl-color-primary-700);
color: var(--sl-color-neutral-0);
}
/* Primary */
.button--outline.button--primary {
border-color: rgb(var(--sl-color-primary-600));
color: rgb(var(--sl-color-primary-600));
border-color: var(--sl-color-primary-600);
color: var(--sl-color-primary-600);
}
.button--outline.button--primary:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-primary-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
.button--outline.button--primary${focusVisibleSelector}:not(.button--disabled) {
border-color: rgb(var(--sl-color-primary-500));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-primary-500) / var(--sl-focus-ring-alpha));
border-color: var(--sl-color-primary-500);
box-shadow: var(--sl-focus-ring);
}
.button--outline.button--primary:active:not(.button--disabled) {
border-color: rgb(var(--sl-color-primary-700));
background-color: rgb(var(--sl-color-primary-700));
color: rgb(var(--sl-color-neutral-0));
border-color: var(--sl-color-primary-700);
background-color: var(--sl-color-primary-700);
color: var(--sl-color-neutral-0);
}
/* Success */
.button--outline.button--success {
border-color: rgb(var(--sl-color-success-600));
color: rgb(var(--sl-color-success-600));
border-color: var(--sl-color-success-600);
color: var(--sl-color-success-600);
}
.button--outline.button--success:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-success-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-success-600);
color: var(--sl-color-neutral-0);
}
.button--outline.button--success${focusVisibleSelector}:not(.button--disabled) {
border-color: rgb(var(--sl-color-success-500));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-success-500) / var(--sl-focus-ring-alpha));
border-color: var(--sl-color-success-500);
box-shadow: var(--sl-focus-ring);
}
.button--outline.button--success:active:not(.button--disabled) {
border-color: rgb(var(--sl-color-success-700));
background-color: rgb(var(--sl-color-success-700));
color: rgb(var(--sl-color-neutral-0));
border-color: var(--sl-color-success-700);
background-color: var(--sl-color-success-700);
color: var(--sl-color-neutral-0);
}
/* Neutral */
.button--outline.button--neutral {
border-color: rgb(var(--sl-color-neutral-600));
color: rgb(var(--sl-color-neutral-600));
border-color: var(--sl-color-neutral-600);
color: var(--sl-color-neutral-600);
}
.button--outline.button--neutral:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-neutral-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-neutral-600);
color: var(--sl-color-neutral-0);
}
.button--outline.button--neutral${focusVisibleSelector}:not(.button--disabled) {
border-color: rgb(var(--sl-color-neutral-500));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-neutral-500) / var(--sl-focus-ring-alpha));
border-color: var(--sl-color-neutral-500);
box-shadow: var(--sl-focus-ring);
}
.button--outline.button--neutral:active:not(.button--disabled) {
border-color: rgb(var(--sl-color-neutral-700));
background-color: rgb(var(--sl-color-neutral-700));
color: rgb(var(--sl-color-neutral-0));
border-color: var(--sl-color-neutral-700);
background-color: var(--sl-color-neutral-700);
color: var(--sl-color-neutral-0);
}
/* Warning */
.button--outline.button--warning {
border-color: rgb(var(--sl-color-warning-600));
color: rgb(var(--sl-color-warning-600));
border-color: var(--sl-color-warning-600);
color: var(--sl-color-warning-600);
}
.button--outline.button--warning:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-warning-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-warning-600);
color: var(--sl-color-neutral-0);
}
.button--outline.button--warning${focusVisibleSelector}:not(.button--disabled) {
border-color: rgb(var(--sl-color-warning-500));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-warning-500) / var(--sl-focus-ring-alpha));
border-color: var(--sl-color-warning-500);
box-shadow: var(--sl-focus-ring);
}
.button--outline.button--warning:active:not(.button--disabled) {
border-color: rgb(var(--sl-color-warning-700));
background-color: rgb(var(--sl-color-warning-700));
color: rgb(var(--sl-color-neutral-0));
border-color: var(--sl-color-warning-700);
background-color: var(--sl-color-warning-700);
color: var(--sl-color-neutral-0);
}
/* Danger */
.button--outline.button--danger {
border-color: rgb(var(--sl-color-danger-600));
color: rgb(var(--sl-color-danger-600));
border-color: var(--sl-color-danger-600);
color: var(--sl-color-danger-600);
}
.button--outline.button--danger:hover:not(.button--disabled) {
background-color: rgb(var(--sl-color-danger-600));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-danger-600);
color: var(--sl-color-neutral-0);
}
.button--outline.button--danger${focusVisibleSelector}:not(.button--disabled) {
border-color: rgb(var(--sl-color-danger-500));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-danger-500) / var(--sl-focus-ring-alpha));
border-color: var(--sl-color-danger-500);
box-shadow: var(--sl-focus-ring);
}
.button--outline.button--danger:active:not(.button--disabled) {
border-color: rgb(var(--sl-color-danger-700));
background-color: rgb(var(--sl-color-danger-700));
color: rgb(var(--sl-color-neutral-0));
border-color: var(--sl-color-danger-700);
background-color: var(--sl-color-danger-700);
color: var(--sl-color-neutral-0);
}
/*
@@ -372,26 +372,26 @@ export default css`
.button--text {
background-color: transparent;
border-color: transparent;
color: rgb(var(--sl-color-primary-600));
color: var(--sl-color-primary-600);
}
.button--text:hover:not(.button--disabled) {
background-color: transparent;
border-color: transparent;
color: rgb(var(--sl-color-primary-500));
color: var(--sl-color-primary-500);
}
.button--text${focusVisibleSelector}:not(.button--disabled) {
background-color: transparent;
border-color: transparent;
color: rgb(var(--sl-color-primary-500));
box-shadow: 0 0 0 var(--sl-focus-ring-width) rgb(var(--sl-color-primary-500) / var(--sl-focus-ring-alpha));
color: var(--sl-color-primary-500);
box-shadow: var(--sl-focus-ring);
}
.button--text:active:not(.button--disabled) {
background-color: transparent;
border-color: transparent;
color: rgb(var(--sl-color-primary-700));
color: var(--sl-color-primary-700);
}
/*
@@ -625,7 +625,8 @@ export default css`
top: 0;
left: 0;
bottom: 0;
border-left: solid 1px rgb(var(--sl-color-neutral-0) / 20%);
border-left: solid 1px rgb(128 128 128 / 33%);
mix-blend-mode: multiply;
}
/* Bump focused buttons up so their focus ring isn't clipped */

View File

@@ -5,7 +5,7 @@ export default css`
${componentStyles}
:host {
--border-color: rgb(var(--sl-color-neutral-200));
--border-color: var(--sl-color-neutral-200);
--border-radius: var(--sl-border-radius-medium);
--border-width: 1px;
--padding: var(--sl-spacing-large);
@@ -16,7 +16,7 @@ export default css`
.card {
display: flex;
flex-direction: column;
background-color: rgb(var(--sl-surface-base-alt));
background-color: var(--sl-panel-background-color);
box-shadow: var(--sl-shadow-x-small);
border: solid var(--border-width) var(--border-color);
border-radius: var(--border-radius);

View File

@@ -15,7 +15,7 @@ export default css`
font-family: var(--sl-input-font-family);
font-size: var(--sl-input-font-size-medium);
font-weight: var(--sl-input-font-weight);
color: rgb(var(--sl-input-color));
color: var(--sl-input-color);
vertical-align: middle;
cursor: pointer;
}
@@ -28,10 +28,10 @@ export default css`
justify-content: center;
width: var(--sl-toggle-size);
height: var(--sl-toggle-size);
border: solid var(--sl-input-border-width) rgb(var(--sl-input-border-color));
border: solid var(--sl-input-border-width) var(--sl-input-border-color);
border-radius: 2px;
background-color: rgb(var(--sl-input-background-color));
color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-input-background-color);
color: var(--sl-color-neutral-0);
transition: var(--sl-transition-fast) border-color, var(--sl-transition-fast) background-color,
var(--sl-transition-fast) color, var(--sl-transition-fast) box-shadow;
}
@@ -57,31 +57,31 @@ export default css`
/* Hover */
.checkbox:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover {
border-color: rgb(var(--sl-input-border-color-hover));
background-color: rgb(var(--sl-input-background-color-hover));
border-color: var(--sl-input-border-color-hover);
background-color: var(--sl-input-background-color-hover);
}
/* Focus */
.checkbox:not(.checkbox--checked):not(.checkbox--disabled)
.checkbox__input${focusVisibleSelector}
~ .checkbox__control {
border-color: rgb(var(--sl-input-border-color-focus));
background-color: rgb(var(--sl-input-background-color-focus));
border-color: var(--sl-input-border-color-focus);
background-color: var(--sl-input-background-color-focus);
box-shadow: var(--sl-focus-ring);
}
/* Checked/indeterminate */
.checkbox--checked .checkbox__control,
.checkbox--indeterminate .checkbox__control {
border-color: rgb(var(--sl-color-primary-600));
background-color: rgb(var(--sl-color-primary-600));
border-color: var(--sl-color-primary-600);
background-color: var(--sl-color-primary-600);
}
/* Checked/indeterminate + hover */
.checkbox.checkbox--checked:not(.checkbox--disabled) .checkbox__control:hover,
.checkbox.checkbox--indeterminate:not(.checkbox--disabled) .checkbox__control:hover {
border-color: rgb(var(--sl-color-primary-500));
background-color: rgb(var(--sl-color-primary-500));
border-color: var(--sl-color-primary-500);
background-color: var(--sl-color-primary-500);
}
/* Checked/indeterminate + focus */
@@ -89,8 +89,8 @@ export default css`
.checkbox.checkbox--indeterminate:not(.checkbox--disabled)
.checkbox__input${focusVisibleSelector}
~ .checkbox__control {
border-color: rgb(var(--sl-color-primary-500));
background-color: rgb(var(--sl-color-primary-500));
border-color: var(--sl-color-primary-500);
background-color: var(--sl-color-primary-500);
box-shadow: var(--sl-focus-ring);
}

View File

@@ -22,13 +22,13 @@ export default css`
font-size: var(--sl-font-size-medium);
font-weight: var(--sl-font-weight-normal);
color: var(--color);
background-color: rgb(var(--sl-panel-background-color));
background-color: var(--sl-panel-background-color);
border-radius: var(--sl-border-radius-medium);
user-select: none;
}
.color-picker--inline {
border: solid var(--sl-panel-border-width) rgb(var(--sl-panel-border-color));
border: solid var(--sl-panel-border-width) var(--sl-panel-border-color);
}
.color-picker__grid {
@@ -60,7 +60,7 @@ export default css`
.color-picker__grid-handle${focusVisibleSelector} {
outline: none;
box-shadow: 0 0 0 1px rgb(var(--sl-color-primary-500)), var(--sl-focus-ring);
box-shadow: 0 0 0 1px var(--sl-color-primary-500), var(--sl-focus-ring);
}
.color-picker__controls {
@@ -97,7 +97,7 @@ export default css`
.color-picker__slider-handle${focusVisibleSelector} {
outline: none;
box-shadow: 0 0 0 1px rgb(var(--sl-color-primary-500)), var(--sl-focus-ring);
box-shadow: 0 0 0 1px var(--sl-color-primary-500), var(--sl-focus-ring);
}
.color-picker__hue {
@@ -171,7 +171,7 @@ export default css`
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgb(var(--sl-focus-ring-color));
box-shadow: 0 0 0 0 var(--sl-color-primary-500);
}
70% {
box-shadow: 0 0 0 0.5rem transparent;
@@ -206,7 +206,7 @@ export default css`
grid-template-columns: repeat(8, 1fr);
grid-gap: 0.5rem;
justify-items: center;
border-top: solid 1px rgb(var(--sl-color-neutral-200));
border-top: solid 1px var(--sl-color-neutral-200);
padding: var(--sl-spacing-small);
}
@@ -234,10 +234,10 @@ export default css`
}
.color-picker__transparent-bg {
background-image: linear-gradient(45deg, rgb(var(--sl-color-neutral-300)) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgb(var(--sl-color-neutral-300)) 75%),
linear-gradient(45deg, transparent 75%, rgb(var(--sl-color-neutral-300)) 75%),
linear-gradient(45deg, rgb(var(--sl-color-neutral-300)) 25%, transparent 25%);
background-image: linear-gradient(45deg, var(--sl-color-neutral-300) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, var(--sl-color-neutral-300) 75%),
linear-gradient(45deg, transparent 75%, var(--sl-color-neutral-300) 75%),
linear-gradient(45deg, var(--sl-color-neutral-300) 25%, transparent 25%);
background-size: 10px 10px;
background-position: 0 0, 0 0, -5px -5px, 5px 5px;
}
@@ -302,7 +302,7 @@ export default css`
height: 100%;
border-radius: inherit;
background-color: currentColor;
box-shadow: inset 0 0 0 1px rgb(var(--sl-color-neutral-1000) / 25%);
box-shadow: inset 0 0 0 2px var(--sl-input-border-color), inset 0 0 0 4px var(--sl-color-neutral-0);
transition: inherit;
}
@@ -316,7 +316,7 @@ export default css`
}
.color-dropdown__trigger${focusVisibleSelector}:not(.color-dropdown__trigger--disabled):before {
box-shadow: inset 0 0 0 1px rgb(var(--sl-color-primary-500));
box-shadow: inset 0 0 0 1px var(--sl-color-primary-500);
}
.color-dropdown__trigger.color-dropdown__trigger--disabled {

View File

@@ -1,43 +0,0 @@
import { css } from 'lit';
import componentStyles from '../../styles/component.styles';
export default css`
${componentStyles}
:host {
display: contents;
}
::slotted(sl-menu) {
min-width: 180px;
background: rgb(var(--sl-panel-background-color));
border: solid var(--sl-panel-border-width) rgb(var(--sl-panel-border-color));
border-radius: var(--sl-border-radius-medium);
box-shadow: var(--sl-shadow-large);
}
.context-menu {
position: relative;
z-index: var(--sl-z-index-dropdown);
}
.context-menu__locater {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
pointer-events: none;
}
.dropdown__positioner {
position: absolute;
}
.context-menu__menu {
position: relative;
top: 0;
left: 0;
pointer-events: all;
}
`;

View File

@@ -1,292 +0,0 @@
import { LitElement, html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { emit, waitForEvent } from '../../internal/event';
import { watch } from '../../internal/watch';
import { Instance as PopperInstance, createPopper } from '@popperjs/core/dist/esm';
import { animateTo, stopAnimations } from '../../internal/animate';
import { setDefaultAnimation, getAnimation } from '../../utilities/animation-registry';
import type SlMenu from '../menu/menu';
import styles from './context-menu.styles';
import '../menu/menu';
/**
* @since 2.0
* @status experimental
*
* @dependency sl-menu
*
* @event sl-event-name - Emitted as an example.
*
* @slot - Content that will activate the context menu when right-clicked.
* @slot menu - The menu to show when the context menu is activated, an `<sl-menu>` element.
*
* @event sl-show - Emitted when the context menu opens.
* @event sl-after-show - Emitted after the context menu opens and all animations are complete.
* @event sl-hide - Emitted when the context menu closes.
* @event sl-after-hide - Emitted after the context menu closes and all animations are complete.
*
* @animation contextMenu.show - The animation to use when showing the context menu.
* @animation contextMenu.hide - The animation to use when hiding the context menu.
*/
@customElement('sl-context-menu')
export default class SlContextMenu extends LitElement {
static styles = styles;
@query('.context-menu') wrapper: HTMLElement;
@query('.context-menu__locater') locater: HTMLElement;
@query('.context-menu__menu') menu: HTMLSlotElement;
@query('.context-menu__positioner') positioner: HTMLElement;
private popover: PopperInstance;
/**
* The preferred placement of the context menu. Note that the actual placement may vary as needed to keep the menu
* inside of the viewport.
*/
@property() placement:
| 'top'
| 'top-start'
| 'top-end'
| 'right'
| 'right-start'
| 'right-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end' = 'bottom-start';
/** Disables the context menu so it won't show when triggered. */
@property({ type: Boolean, reflect: true }) disabled = false;
/** The distance in pixels from which to offset the context menu away from its target. */
@property({ type: Number }) distance = 0;
/** Indicates whether or not the context menu is open. You can use this in lieu of the show/hide methods. */
@property({ type: Boolean, reflect: true }) open = false;
/** The distance in pixels from which to offset the context menu along its target. */
@property({ type: Number }) skidding = 0;
/**
* Enable this option to prevent the menu from being clipped when the component is placed inside a container with
* `overflow: auto|hidden|scroll`.
*/
@property({ type: Boolean }) hoist = false;
connectedCallback() {
super.connectedCallback();
this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
this.handleDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
}
firstUpdated() {
this.menu.hidden = !this.open;
}
getMenu() {
const slot = this.menu.querySelector('slot')!;
return slot.assignedElements({ flatten: true }).filter(el => el.tagName.toLowerCase() === 'sl-menu')[0] as SlMenu;
}
async handleContextMenu(event: MouseEvent) {
const target = event.target as HTMLElement;
const targetRect = target.getBoundingClientRect();
const wrapperRect = this.wrapper.getBoundingClientRect();
const { offsetX, offsetY } = event;
const x = targetRect.left + offsetX - wrapperRect.left;
const y = targetRect.top + offsetY - wrapperRect.top;
event.preventDefault();
if (this.open) {
await this.hide();
}
this.show(x, y);
}
handleDocumentKeyDown(event: KeyboardEvent) {
const menu = this.getMenu();
const menuItems = menu ? menu.getAllItems() : [];
const firstMenuItem = menuItems[0];
const lastMenuItem = menuItems[menuItems.length - 1];
// Close when escape is pressed
if (event.key === 'Escape') {
this.hide();
return;
}
// Forward key presses that don't originate from the menu to allow keyboard selection and type-to-select
if (menu && !event.composedPath().includes(this.menu)) {
// Focus on a menu item
if (['ArrowDown', 'Home'].includes(event.key) && firstMenuItem) {
event.preventDefault();
const menu = this.getMenu();
menu.setCurrentItem(firstMenuItem);
firstMenuItem.focus();
return;
}
if (['ArrowUp', 'End'].includes(event.key) && lastMenuItem) {
event.preventDefault();
menu.setCurrentItem(lastMenuItem);
lastMenuItem.focus();
return;
}
// Other keys bring focus to the menu and initiate type-to-select behavior
const ignoredKeys = ['Tab', 'Shift', 'Meta', 'Ctrl', 'Alt'];
if (!ignoredKeys.includes(event.key)) {
menu.typeToSelect(event.key);
return;
}
}
}
handleDocumentMouseDown(event: MouseEvent) {
const path = event.composedPath() as Array<EventTarget>;
//
// Close the context menu when clicking outside of it. We use a setTimeout here because mousedown fires before
// contextmenu and, if the menu is already open and the user-right clicks again, we want the menu to re-open in the
// new position instead of closing.
//
setTimeout(() => {
if (this.open && !path.includes(this.menu)) {
this.hide();
return;
}
});
}
handleMenuSelect() {
// Close the context menu when a menu item is selected
this.hide();
}
@watch('open', { waitUntilFirstUpdate: true })
async handleOpenChange() {
if (this.disabled) {
return;
}
if (this.open) {
// Show
emit(this, 'sl-show');
document.addEventListener('keydown', this.handleDocumentKeyDown);
document.addEventListener('mousedown', this.handleDocumentMouseDown);
await stopAnimations(this);
this.popover = createPopper(this.locater, this.positioner, {
placement: this.placement,
strategy: this.hoist ? 'fixed' : 'absolute',
modifiers: [
{
name: 'flip',
options: {
boundary: 'viewport'
}
},
{
name: 'offset',
options: {
offset: [this.skidding, this.distance]
}
}
]
});
this.menu.hidden = false;
const { keyframes, options } = getAnimation(this, 'contextMenu.show');
await animateTo(this.menu, keyframes, options);
emit(this, 'sl-after-show');
} else {
// Hide
emit(this, 'sl-hide');
document.removeEventListener('keydown', this.handleDocumentKeyDown);
document.removeEventListener('mousedown', this.handleDocumentMouseDown);
await stopAnimations(this);
const { keyframes, options } = getAnimation(this, 'contextMenu.hide');
await animateTo(this.menu, keyframes, options);
this.menu.hidden = true;
this.locater.style.top = '0px';
this.locater.style.left = '0px';
this.popover.destroy();
emit(this, 'sl-after-hide');
}
}
/** Shows the context menu */
async show(offsetX?: number, offsetY?: number) {
if (this.open) {
return;
}
this.locater.style.top = `${offsetY || 0}px`;
this.locater.style.left = `${offsetX || 0}px`;
this.open = true;
return waitForEvent(this, 'sl-after-show');
}
/** Hides the dropdown panel */
async hide() {
if (!this.open) {
return;
}
this.open = false;
return waitForEvent(this, 'sl-after-hide');
}
render() {
return html`
<div class="context-menu">
<slot @contextmenu=${this.handleContextMenu}></slot>
<div class="context-menu__locater"></div>
<!-- Position the menu with a wrapper since the popover makes use of translate. This let's us add animations
on the menu without interfering with the position. -->
<div class="context-menu__positioner">
<div class="context-menu__menu" hidden @sl-select=${this.handleMenuSelect}>
<slot name="menu"></slot>
</div>
</div>
</div>
`;
}
}
setDefaultAnimation('contextMenu.show', {
keyframes: [
{ opacity: 0, transform: 'scale(0.9)' },
{ opacity: 1, transform: 'scale(1)' }
],
options: { duration: 50, easing: 'ease' }
});
setDefaultAnimation('contextMenu.hide', {
keyframes: [
{ opacity: 1, transform: 'scale(1)' },
{ opacity: 0, transform: 'scale(0.9)' }
],
options: { duration: 150, easing: 'ease' }
});
declare global {
interface HTMLElementTagNameMap {
'sl-context-menu': SlContextMenu;
}
}

View File

@@ -10,9 +10,9 @@ export default css`
}
.details {
border: solid 1px rgb(var(--sl-color-neutral-200));
border: solid 1px var(--sl-color-neutral-200);
border-radius: var(--sl-border-radius-medium);
background-color: rgb(var(--sl-color-neutral-0));
background-color: var(--sl-color-neutral-0);
overflow-anchor: none;
}

View File

@@ -32,7 +32,7 @@ export default css`
width: var(--width);
max-width: calc(100% - var(--sl-spacing-2x-large));
max-height: calc(100% - var(--sl-spacing-2x-large));
background-color: rgb(var(--sl-panel-background-color));
background-color: var(--sl-panel-background-color);
border-radius: var(--sl-border-radius-medium);
box-shadow: var(--sl-shadow-x-large);
}
@@ -101,6 +101,6 @@ export default css`
right: 0;
bottom: 0;
left: 0;
background-color: rgb(var(--sl-overlay-background-color) / var(--sl-overlay-opacity));
background-color: var(--sl-overlay-background-color);
}
`;

View File

@@ -5,7 +5,7 @@ export default css`
${componentStyles}
:host {
--color: rgb(var(--sl-panel-border-color));
--color: var(--sl-panel-border-color);
--width: var(--sl-panel-border-width);
--spacing: var(--sl-spacing-medium);
}

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