Showing preview only (2,381K chars total). Download the full file or copy to clipboard to get everything.
Repository: shoelace-style/shoelace
Branch: next
Commit: 4fc0fc332ccc
Files: 492
Total size: 2.2 MB
Directory structure:
gitextract_b6qx7r1_/
├── .editorconfig
├── .eslintignore
├── .eslintrc.cjs
├── .github/
│ ├── CODE_OF_CONDUCT.md
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── config.yml
│ ├── SECURITY.md
│ └── workflows/
│ ├── node.js.yml
│ └── release.yml
├── .gitignore
├── .gitpod.yml
├── .husky/
│ └── pre-commit
├── .prettierignore
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── cspell.json
├── custom-elements-manifest.config.js
├── docs/
│ ├── _includes/
│ │ ├── component.njk
│ │ ├── default.njk
│ │ ├── sidebar.njk
│ │ └── wa-logo-icon.njk
│ ├── _utilities/
│ │ ├── active-links.cjs
│ │ ├── anchor-headings.cjs
│ │ ├── cem.cjs
│ │ ├── code-previews.cjs
│ │ ├── copy-code-buttons.cjs
│ │ ├── external-links.cjs
│ │ ├── highlight-code.cjs
│ │ ├── markdown.cjs
│ │ ├── prettier.cjs
│ │ ├── replacer.cjs
│ │ ├── scrolling-tables.cjs
│ │ ├── strings.cjs
│ │ ├── table-of-contents.cjs
│ │ └── typography.cjs
│ ├── assets/
│ │ ├── examples/
│ │ │ └── include.html
│ │ ├── scripts/
│ │ │ ├── code-previews.js
│ │ │ ├── docs.js
│ │ │ ├── search.js
│ │ │ └── turbo.js
│ │ └── styles/
│ │ ├── code-previews.css
│ │ ├── docs.css
│ │ └── search.css
│ ├── eleventy.config.cjs
│ └── pages/
│ ├── 404.md
│ ├── components/
│ │ ├── alert.md
│ │ ├── animated-image.md
│ │ ├── animation.md
│ │ ├── avatar.md
│ │ ├── badge.md
│ │ ├── breadcrumb-item.md
│ │ ├── breadcrumb.md
│ │ ├── button-group.md
│ │ ├── button.md
│ │ ├── card.md
│ │ ├── carousel-item.md
│ │ ├── carousel.md
│ │ ├── checkbox.md
│ │ ├── color-picker.md
│ │ ├── copy-button.md
│ │ ├── details.md
│ │ ├── dialog.md
│ │ ├── divider.md
│ │ ├── drawer.md
│ │ ├── dropdown.md
│ │ ├── format-bytes.md
│ │ ├── format-date.md
│ │ ├── format-number.md
│ │ ├── icon-button.md
│ │ ├── icon.md
│ │ ├── image-comparer.md
│ │ ├── include.md
│ │ ├── input.md
│ │ ├── menu-item.md
│ │ ├── menu-label.md
│ │ ├── menu.md
│ │ ├── mutation-observer.md
│ │ ├── option.md
│ │ ├── popup.md
│ │ ├── progress-bar.md
│ │ ├── progress-ring.md
│ │ ├── qr-code.md
│ │ ├── radio-button.md
│ │ ├── radio-group.md
│ │ ├── radio.md
│ │ ├── range.md
│ │ ├── rating.md
│ │ ├── relative-time.md
│ │ ├── resize-observer.md
│ │ ├── select.md
│ │ ├── skeleton.md
│ │ ├── spinner.md
│ │ ├── split-panel.md
│ │ ├── switch.md
│ │ ├── tab-group.md
│ │ ├── tab-panel.md
│ │ ├── tab.md
│ │ ├── tag.md
│ │ ├── textarea.md
│ │ ├── tooltip.md
│ │ ├── tree-item.md
│ │ ├── tree.md
│ │ └── visually-hidden.md
│ ├── frameworks/
│ │ ├── angular.md
│ │ ├── react.md
│ │ ├── svelte.md
│ │ ├── vue-2.md
│ │ └── vue.md
│ ├── getting-started/
│ │ ├── customizing.md
│ │ ├── form-controls.md
│ │ ├── installation.md
│ │ ├── localization.md
│ │ ├── themes.md
│ │ └── usage.md
│ ├── index.md
│ ├── resources/
│ │ ├── accessibility.md
│ │ ├── changelog.md
│ │ ├── community.md
│ │ └── contributing.md
│ ├── tokens/
│ │ ├── border-radius.md
│ │ ├── color.md
│ │ ├── elevation.md
│ │ ├── more.md
│ │ ├── spacing.md
│ │ ├── transition.md
│ │ ├── typography.md
│ │ └── z-index.md
│ └── tutorials/
│ ├── integrating-with-astro.md
│ ├── integrating-with-laravel.md
│ ├── integrating-with-nextjs.md
│ └── integrating-with-rails.md
├── lint-staged.config.js
├── package.json
├── prettier.config.js
├── scripts/
│ ├── build.js
│ ├── make-icons.js
│ ├── make-metadata.js
│ ├── make-react.js
│ ├── make-themes.js
│ ├── plop/
│ │ ├── plopfile.js
│ │ └── templates/
│ │ └── component/
│ │ ├── component.hbs
│ │ ├── define.hbs
│ │ ├── docs.hbs
│ │ ├── styles.hbs
│ │ └── tests.hbs
│ └── shared.js
├── src/
│ ├── components/
│ │ ├── alert/
│ │ │ ├── alert.component.ts
│ │ │ ├── alert.styles.ts
│ │ │ ├── alert.test.ts
│ │ │ └── alert.ts
│ │ ├── animated-image/
│ │ │ ├── animated-image.component.ts
│ │ │ ├── animated-image.styles.ts
│ │ │ ├── animated-image.test.ts
│ │ │ └── animated-image.ts
│ │ ├── animation/
│ │ │ ├── animation.component.ts
│ │ │ ├── animation.styles.ts
│ │ │ ├── animation.test.ts
│ │ │ ├── animation.ts
│ │ │ └── animations.ts
│ │ ├── avatar/
│ │ │ ├── avatar.component.ts
│ │ │ ├── avatar.styles.ts
│ │ │ ├── avatar.test.ts
│ │ │ └── avatar.ts
│ │ ├── badge/
│ │ │ ├── badge.component.ts
│ │ │ ├── badge.styles.ts
│ │ │ ├── badge.test.ts
│ │ │ └── badge.ts
│ │ ├── breadcrumb/
│ │ │ ├── breadcrumb.component.ts
│ │ │ ├── breadcrumb.styles.ts
│ │ │ ├── breadcrumb.test.ts
│ │ │ └── breadcrumb.ts
│ │ ├── breadcrumb-item/
│ │ │ ├── breadcrumb-item.component.ts
│ │ │ ├── breadcrumb-item.styles.ts
│ │ │ ├── breadcrumb-item.test.ts
│ │ │ └── breadcrumb-item.ts
│ │ ├── button/
│ │ │ ├── button.component.ts
│ │ │ ├── button.styles.ts
│ │ │ ├── button.test.ts
│ │ │ └── button.ts
│ │ ├── button-group/
│ │ │ ├── button-group.component.ts
│ │ │ ├── button-group.styles.ts
│ │ │ ├── button-group.test.ts
│ │ │ └── button-group.ts
│ │ ├── card/
│ │ │ ├── card.component.ts
│ │ │ ├── card.styles.ts
│ │ │ ├── card.test.ts
│ │ │ └── card.ts
│ │ ├── carousel/
│ │ │ ├── autoplay-controller.ts
│ │ │ ├── carousel.component.ts
│ │ │ ├── carousel.styles.ts
│ │ │ ├── carousel.test.ts
│ │ │ └── carousel.ts
│ │ ├── carousel-item/
│ │ │ ├── carousel-item.component.ts
│ │ │ ├── carousel-item.styles.ts
│ │ │ ├── carousel-item.test.ts
│ │ │ └── carousel-item.ts
│ │ ├── checkbox/
│ │ │ ├── checkbox.component.ts
│ │ │ ├── checkbox.styles.ts
│ │ │ ├── checkbox.test.ts
│ │ │ └── checkbox.ts
│ │ ├── color-picker/
│ │ │ ├── color-picker.component.ts
│ │ │ ├── color-picker.styles.ts
│ │ │ ├── color-picker.test.ts
│ │ │ └── color-picker.ts
│ │ ├── copy-button/
│ │ │ ├── copy-button.component.ts
│ │ │ ├── copy-button.styles.ts
│ │ │ ├── copy-button.test.ts
│ │ │ └── copy-button.ts
│ │ ├── details/
│ │ │ ├── details.component.ts
│ │ │ ├── details.styles.ts
│ │ │ ├── details.test.ts
│ │ │ └── details.ts
│ │ ├── dialog/
│ │ │ ├── dialog.component.ts
│ │ │ ├── dialog.styles.ts
│ │ │ ├── dialog.test.ts
│ │ │ └── dialog.ts
│ │ ├── divider/
│ │ │ ├── divider.component.ts
│ │ │ ├── divider.styles.ts
│ │ │ ├── divider.test.ts
│ │ │ └── divider.ts
│ │ ├── drawer/
│ │ │ ├── drawer.component.ts
│ │ │ ├── drawer.styles.ts
│ │ │ ├── drawer.test.ts
│ │ │ └── drawer.ts
│ │ ├── dropdown/
│ │ │ ├── dropdown.component.ts
│ │ │ ├── dropdown.styles.ts
│ │ │ ├── dropdown.test.ts
│ │ │ └── dropdown.ts
│ │ ├── format-bytes/
│ │ │ ├── format-bytes.component.ts
│ │ │ ├── format-bytes.test.ts
│ │ │ └── format-bytes.ts
│ │ ├── format-date/
│ │ │ ├── format-date.component.ts
│ │ │ ├── format-date.test.ts
│ │ │ └── format-date.ts
│ │ ├── format-number/
│ │ │ ├── format-number.component.ts
│ │ │ ├── format-number.test.ts
│ │ │ └── format-number.ts
│ │ ├── icon/
│ │ │ ├── icon.component.ts
│ │ │ ├── icon.styles.ts
│ │ │ ├── icon.test.ts
│ │ │ ├── icon.ts
│ │ │ ├── library.default.ts
│ │ │ ├── library.system.ts
│ │ │ └── library.ts
│ │ ├── icon-button/
│ │ │ ├── icon-button.component.ts
│ │ │ ├── icon-button.styles.ts
│ │ │ ├── icon-button.test.ts
│ │ │ └── icon-button.ts
│ │ ├── image-comparer/
│ │ │ ├── image-comparer.component.ts
│ │ │ ├── image-comparer.styles.ts
│ │ │ ├── image-comparer.test.ts
│ │ │ └── image-comparer.ts
│ │ ├── include/
│ │ │ ├── include.component.ts
│ │ │ ├── include.styles.ts
│ │ │ ├── include.test.ts
│ │ │ ├── include.ts
│ │ │ └── request.ts
│ │ ├── input/
│ │ │ ├── input.component.ts
│ │ │ ├── input.styles.ts
│ │ │ ├── input.test.ts
│ │ │ └── input.ts
│ │ ├── menu/
│ │ │ ├── menu.component.ts
│ │ │ ├── menu.styles.ts
│ │ │ ├── menu.test.ts
│ │ │ └── menu.ts
│ │ ├── menu-item/
│ │ │ ├── menu-item.component.ts
│ │ │ ├── menu-item.styles.ts
│ │ │ ├── menu-item.test.ts
│ │ │ ├── menu-item.ts
│ │ │ └── submenu-controller.ts
│ │ ├── menu-label/
│ │ │ ├── menu-label.component.ts
│ │ │ ├── menu-label.styles.ts
│ │ │ ├── menu-label.test.ts
│ │ │ └── menu-label.ts
│ │ ├── mutation-observer/
│ │ │ ├── mutation-observer.component.ts
│ │ │ ├── mutation-observer.styles.ts
│ │ │ ├── mutation-observer.test.ts
│ │ │ └── mutation-observer.ts
│ │ ├── option/
│ │ │ ├── option.component.ts
│ │ │ ├── option.styles.ts
│ │ │ ├── option.test.ts
│ │ │ └── option.ts
│ │ ├── popup/
│ │ │ ├── popup.component.ts
│ │ │ ├── popup.styles.ts
│ │ │ ├── popup.test.ts
│ │ │ └── popup.ts
│ │ ├── progress-bar/
│ │ │ ├── progress-bar.component.ts
│ │ │ ├── progress-bar.styles.ts
│ │ │ ├── progress-bar.test.ts
│ │ │ └── progress-bar.ts
│ │ ├── progress-ring/
│ │ │ ├── progress-ring.component.ts
│ │ │ ├── progress-ring.styles.ts
│ │ │ ├── progress-ring.test.ts
│ │ │ └── progress-ring.ts
│ │ ├── qr-code/
│ │ │ ├── qr-code.component.ts
│ │ │ ├── qr-code.styles.ts
│ │ │ ├── qr-code.test.ts
│ │ │ └── qr-code.ts
│ │ ├── radio/
│ │ │ ├── radio.component.ts
│ │ │ ├── radio.styles.ts
│ │ │ ├── radio.test.ts
│ │ │ └── radio.ts
│ │ ├── radio-button/
│ │ │ ├── radio-button.component.ts
│ │ │ ├── radio-button.styles.ts
│ │ │ ├── radio-button.test.ts
│ │ │ └── radio-button.ts
│ │ ├── radio-group/
│ │ │ ├── radio-group.component.ts
│ │ │ ├── radio-group.styles.ts
│ │ │ ├── radio-group.test.ts
│ │ │ └── radio-group.ts
│ │ ├── range/
│ │ │ ├── range.component.ts
│ │ │ ├── range.styles.ts
│ │ │ ├── range.test.ts
│ │ │ └── range.ts
│ │ ├── rating/
│ │ │ ├── rating.component.ts
│ │ │ ├── rating.styles.ts
│ │ │ ├── rating.test.ts
│ │ │ └── rating.ts
│ │ ├── relative-time/
│ │ │ ├── relative-time.component.ts
│ │ │ ├── relative-time.test.ts
│ │ │ └── relative-time.ts
│ │ ├── resize-observer/
│ │ │ ├── resize-observer.component.ts
│ │ │ ├── resize-observer.styles.ts
│ │ │ └── resize-observer.ts
│ │ ├── select/
│ │ │ ├── select.component.ts
│ │ │ ├── select.styles.ts
│ │ │ ├── select.test.ts
│ │ │ └── select.ts
│ │ ├── skeleton/
│ │ │ ├── skeleton.component.ts
│ │ │ ├── skeleton.styles.ts
│ │ │ ├── skeleton.test.ts
│ │ │ └── skeleton.ts
│ │ ├── spinner/
│ │ │ ├── spinner.component.ts
│ │ │ ├── spinner.styles.ts
│ │ │ ├── spinner.test.ts
│ │ │ └── spinner.ts
│ │ ├── split-panel/
│ │ │ ├── split-panel.component.ts
│ │ │ ├── split-panel.styles.ts
│ │ │ ├── split-panel.test.ts
│ │ │ └── split-panel.ts
│ │ ├── switch/
│ │ │ ├── switch.component.ts
│ │ │ ├── switch.styles.ts
│ │ │ ├── switch.test.ts
│ │ │ └── switch.ts
│ │ ├── tab/
│ │ │ ├── tab.component.ts
│ │ │ ├── tab.styles.ts
│ │ │ ├── tab.test.ts
│ │ │ └── tab.ts
│ │ ├── tab-group/
│ │ │ ├── tab-group.component.ts
│ │ │ ├── tab-group.styles.ts
│ │ │ ├── tab-group.test.ts
│ │ │ └── tab-group.ts
│ │ ├── tab-panel/
│ │ │ ├── tab-panel.component.ts
│ │ │ ├── tab-panel.styles.ts
│ │ │ ├── tab-panel.test.ts
│ │ │ └── tab-panel.ts
│ │ ├── tag/
│ │ │ ├── tag.component.ts
│ │ │ ├── tag.styles.ts
│ │ │ ├── tag.test.ts
│ │ │ └── tag.ts
│ │ ├── textarea/
│ │ │ ├── textarea.component.ts
│ │ │ ├── textarea.styles.ts
│ │ │ ├── textarea.test.ts
│ │ │ └── textarea.ts
│ │ ├── tooltip/
│ │ │ ├── tooltip.component.ts
│ │ │ ├── tooltip.styles.ts
│ │ │ ├── tooltip.test.ts
│ │ │ └── tooltip.ts
│ │ ├── tree/
│ │ │ ├── tree.component.ts
│ │ │ ├── tree.styles.ts
│ │ │ ├── tree.test.ts
│ │ │ └── tree.ts
│ │ ├── tree-item/
│ │ │ ├── tree-item.component.ts
│ │ │ ├── tree-item.styles.ts
│ │ │ ├── tree-item.test.ts
│ │ │ └── tree-item.ts
│ │ └── visually-hidden/
│ │ ├── visually-hidden.component.ts
│ │ ├── visually-hidden.styles.ts
│ │ ├── visually-hidden.test.ts
│ │ └── visually-hidden.ts
│ ├── declaration.d.ts
│ ├── events/
│ │ ├── events.ts
│ │ ├── sl-after-collapse.ts
│ │ ├── sl-after-expand.ts
│ │ ├── sl-after-hide.ts
│ │ ├── sl-after-show.ts
│ │ ├── sl-blur.ts
│ │ ├── sl-cancel.ts
│ │ ├── sl-change.ts
│ │ ├── sl-clear.ts
│ │ ├── sl-close.ts
│ │ ├── sl-collapse.ts
│ │ ├── sl-copy.ts
│ │ ├── sl-error.ts
│ │ ├── sl-expand.ts
│ │ ├── sl-finish.ts
│ │ ├── sl-focus.ts
│ │ ├── sl-hide.ts
│ │ ├── sl-hover.ts
│ │ ├── sl-initial-focus.ts
│ │ ├── sl-input.ts
│ │ ├── sl-invalid.ts
│ │ ├── sl-lazy-change.ts
│ │ ├── sl-lazy-load.ts
│ │ ├── sl-load.ts
│ │ ├── sl-mutation.ts
│ │ ├── sl-remove.ts
│ │ ├── sl-reposition.ts
│ │ ├── sl-request-close.ts
│ │ ├── sl-resize.ts
│ │ ├── sl-select.ts
│ │ ├── sl-selection-change.ts
│ │ ├── sl-show.ts
│ │ ├── sl-slide-change.ts
│ │ ├── sl-start.ts
│ │ ├── sl-tab-hide.ts
│ │ └── sl-tab-show.ts
│ ├── internal/
│ │ ├── active-elements.ts
│ │ ├── animate.ts
│ │ ├── closeActiveElement.ts
│ │ ├── debounce.ts
│ │ ├── default-value.ts
│ │ ├── drag.ts
│ │ ├── event.ts
│ │ ├── form.test.ts
│ │ ├── form.ts
│ │ ├── math.ts
│ │ ├── modal.ts
│ │ ├── offset.ts
│ │ ├── scroll.ts
│ │ ├── scrollend-polyfill.ts
│ │ ├── shoelace-element.test.ts
│ │ ├── shoelace-element.ts
│ │ ├── slot.ts
│ │ ├── string.ts
│ │ ├── tabbable.test.ts
│ │ ├── tabbable.ts
│ │ ├── test/
│ │ │ ├── data-testid-helpers.ts
│ │ │ ├── element-visible-overflow.ts
│ │ │ ├── form-control-base-tests.ts
│ │ │ └── wait-for-scrolling.ts
│ │ ├── test.ts
│ │ └── watch.ts
│ ├── shoelace-autoloader.ts
│ ├── shoelace.ts
│ ├── styles/
│ │ ├── component.styles.ts
│ │ └── form-control.styles.ts
│ ├── themes/
│ │ ├── _utility.css
│ │ ├── dark.css
│ │ └── light.css
│ ├── translations/
│ │ ├── ar.ts
│ │ ├── cs.ts
│ │ ├── da.ts
│ │ ├── de-ch.ts
│ │ ├── de.ts
│ │ ├── en-gb.ts
│ │ ├── en.ts
│ │ ├── es.ts
│ │ ├── fa.ts
│ │ ├── fi.ts
│ │ ├── fr.ts
│ │ ├── he.ts
│ │ ├── hr.ts
│ │ ├── hu.ts
│ │ ├── id.ts
│ │ ├── it.ts
│ │ ├── ja.ts
│ │ ├── nb.ts
│ │ ├── nl.ts
│ │ ├── nn.ts
│ │ ├── pl.ts
│ │ ├── pt.ts
│ │ ├── ru.ts
│ │ ├── sl.ts
│ │ ├── sv.ts
│ │ ├── tr.ts
│ │ ├── uk.ts
│ │ ├── zh-cn.ts
│ │ └── zh-tw.ts
│ └── utilities/
│ ├── animation-registry.ts
│ ├── animation.ts
│ ├── base-path.ts
│ ├── form.ts
│ ├── icon-library.ts
│ └── localize.ts
├── tsconfig.json
├── tsconfig.prod.json
└── web-test-runner.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
================================================
FILE: .eslintignore
================================================
.cache
docs/dist
docs/search.json
docs/**/*.min.js
dist
examples
node_modules
src/react
scripts
================================================
FILE: .eslintrc.cjs
================================================
/* eslint-env node */
module.exports = {
plugins: [
'@typescript-eslint',
'wc',
'lit',
'lit-a11y',
'chai-expect',
'chai-friendly',
'import',
'sort-imports-es6-autofix'
],
extends: [
'eslint:recommended',
'plugin:wc/recommended',
'plugin:wc/best-practice',
'plugin:lit/recommended',
'plugin:lit-a11y/recommended'
],
env: {
es2021: true,
browser: true
},
parserOptions: {
sourceType: 'module'
},
overrides: [
{
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking'
],
parser: '@typescript-eslint/parser',
parserOptions: {
sourceType: 'module',
project: './tsconfig.json',
tsconfigRootDir: __dirname
},
files: ['*.ts'],
rules: {
'default-param-last': 'off',
'@typescript-eslint/default-param-last': 'error',
'no-empty-function': 'off',
'@typescript-eslint/no-empty-function': 'warn',
'no-implied-eval': 'off',
'@typescript-eslint/no-implied-eval': 'error',
'no-invalid-this': 'off',
'@typescript-eslint/no-invalid-this': 'error',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'no-throw-literal': 'off',
'@typescript-eslint/no-throw-literal': 'error',
'no-unused-expressions': 'off',
'@typescript-eslint/prefer-regexp-exec': 'off',
'@typescript-eslint/no-unused-expressions': 'error',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-misused-promises': [
'error',
{
checksVoidReturn: false
}
],
'@typescript-eslint/consistent-type-assertions': [
'warn',
{
assertionStyle: 'as',
objectLiteralTypeAssertions: 'never'
}
],
'@typescript-eslint/consistent-type-imports': 'warn',
'@typescript-eslint/no-base-to-string': 'error',
'@typescript-eslint/no-confusing-non-null-assertion': 'error',
'@typescript-eslint/no-invalid-void-type': 'error',
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn',
'@typescript-eslint/no-unnecessary-condition': 'off',
'@typescript-eslint/no-unnecessary-qualifier': 'warn',
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
'@typescript-eslint/non-nullable-type-assertion-style': 'warn',
'@typescript-eslint/prefer-for-of': 'warn',
'@typescript-eslint/prefer-optional-chain': 'warn',
'@typescript-eslint/prefer-ts-expect-error': 'warn',
'@typescript-eslint/prefer-return-this-type': 'error',
'@typescript-eslint/prefer-string-starts-ends-with': 'warn',
'@typescript-eslint/require-array-sort-compare': 'error',
'@typescript-eslint/unified-signatures': 'warn',
'@typescript-eslint/array-type': 'warn',
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/member-delimiter-style': 'warn',
'@typescript-eslint/method-signature-style': 'warn',
'@typescript-eslint/no-extraneous-class': 'error',
'@typescript-eslint/no-redundant-type-constituents': 'off',
'@typescript-eslint/parameter-properties': 'error',
'@typescript-eslint/strict-boolean-expressions': 'off'
}
},
{
files: ['**/*.cjs'],
env: {
node: true
}
},
{
extends: ['plugin:chai-expect/recommended', 'plugin:chai-friendly/recommended'],
files: ['*.test.ts'],
rules: {
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unused-expressions': 'off'
}
}
],
rules: {
'no-template-curly-in-string': 'error',
'array-callback-return': 'error',
'comma-dangle': 'off',
'consistent-return': 'error',
curly: 'off',
'default-param-last': 'error',
eqeqeq: 'error',
'lit-a11y/click-events-have-key-events': 'off',
'no-constructor-return': 'error',
'no-empty-function': 'warn',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-floating-decimal': 'error',
'no-implicit-coercion': 'off',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-invalid-this': 'error',
'no-labels': 'error',
'no-lone-blocks': 'error',
'no-new': 'error',
'no-new-func': 'error',
'no-new-wrappers': 'error',
'no-octal-escape': 'error',
'no-proto': 'error',
'no-return-assign': 'warn',
'no-script-url': 'error',
'no-self-compare': 'warn',
'no-sequences': 'warn',
'no-throw-literal': 'error',
'no-unmodified-loop-condition': 'error',
'no-unused-expressions': 'warn',
'no-useless-call': 'error',
'no-useless-concat': 'error',
'no-useless-return': 'warn',
'prefer-promise-reject-errors': 'error',
radix: 'off',
'require-await': 'error',
'wrap-iife': ['warn', 'inside'],
'no-shadow': 'error',
'no-array-constructor': 'error',
'no-bitwise': 'error',
'no-multi-assign': 'warn',
'no-new-object': 'error',
'no-useless-computed-key': 'warn',
'no-useless-rename': 'warn',
'no-var': 'error',
'prefer-const': 'warn',
'prefer-numeric-literals': 'warn',
'prefer-object-spread': 'warn',
'prefer-rest-params': 'warn',
'prefer-spread': 'warn',
'prefer-template': 'off',
'no-else-return': 'off',
'func-names': ['warn', 'never'],
'one-var': ['warn', 'never'],
'operator-assignment': 'warn',
'prefer-arrow-callback': 'warn',
'no-restricted-imports': [
'warn',
{
paths: [
{
name: '.',
message: 'Usage of local index imports is not allowed.'
},
{
name: './index',
message: 'Import from the source file instead.'
}
]
}
],
'import/extensions': [
'error',
'always',
{
ignorePackages: true,
pattern: {
js: 'always',
ts: 'never'
}
}
],
'import/no-duplicates': 'warn',
'sort-imports-es6-autofix/sort-imports-es6': [
2,
{
ignoreCase: true,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single']
}
],
'wc/guard-super-call': 'off'
}
};
================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cory@abeautifulsite.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4.
================================================
FILE: .github/FUNDING.yml
================================================
github: [claviska]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Create a bug report to help us fix a demonstrable problem with code in the library.
title: ''
labels: bug
assignees:
---
### Describe the bug
A bug is _a demonstrable problem_ caused by code in the library. Please provide a clear and concise description of what the bug is here.
### To Reproduce
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
### Demo
If the bug isn't obvious, please provide a link to a CodePen or Fiddle with a minimal reproduction. Bugs that have repros get attention faster than those that don't.
Tip: use the CodePen button on any example in the docs!
### Screenshots
If applicable, add screenshots to help explain the bug.
### Browser / OS
- OS: [e.g. Mac, Windows]
- Browser: [e.g. Chrome, Firefox, Safari]
- Browser version: [e.g. 22]
### Additional information
Provide any additional information about the bug here.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
contact_links:
- name: Feature Requests
url: https://github.com/shoelace-style/shoelace/discussions/categories/ideas
about: All requests for new features should go here.
- name: Help & Support
url: https://github.com/shoelace-style/shoelace/discussions/categories/help
about: Please don't create issues for personal help requests. Instead, ask your question on the discussion forum.
================================================
FILE: .github/SECURITY.md
================================================
# Reporting Security Issues
We take security issues in Shoelace very seriously and appreciate your efforts to disclose your findings responsibly.
To report a security issue, email [cory@abeautifulsite.net](mailto:cory@abeautifulsite.net) and include "SHOELACE SECURITY" in the subject line.
We'll respond as soon as possible and keep you updated throughout the process.
================================================
FILE: .github/workflows/node.js.yml
================================================
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
push:
branches: [next]
pull_request:
branches: [next]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Update system packages
run: |
sudo apt-get update
sudo apt-get upgrade -y
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npx playwright install --with-deps
- run: npm ci
- run: npm run verify
================================================
FILE: .github/workflows/release.yml
================================================
# This workflow will create a GitHub release every time a tag is pushed
name: Create GitHub Release
on:
push:
tags:
- "v2.*"
- "v3.*"
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: "marvinpinto/action-automatic-releases@v1.2.1"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
================================================
FILE: .gitignore
================================================
_site
.cache
.DS_Store
cdn
dist
docs/assets/images/sprite.svg
node_modules
src/react
================================================
FILE: .gitpod.yml
================================================
tasks:
- init: npm install && npm run build
command: npm run start
ports:
- port: 3001
onOpen: ignore
- port: 4000-4999
onOpen: open-preview
github:
prebuilds:
# enable for the master/default branch (defaults to true)
master: true
# enable for all branches in this repo (defaults to false)
branches: true
# enable for pull requests coming from this repo (defaults to true)
pullRequests: true
# enable for pull requests coming from forks (defaults to false)
pullRequestsFromForks: true
# add a check to pull requests (defaults to true)
addCheck: true
# add a "Review in Gitpod" button as a comment to pull requests (defaults to false)
addComment: false
# add a "Review in Gitpod" button to the pull request's description (defaults to false)
addBadge: true
# add a label once the prebuild is ready to pull requests (defaults to false)
addLabel: true
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install lint-staged
================================================
FILE: .prettierignore
================================================
*.hbs
.cache
.github
cspell.json
dist
docs/search.json
src/components/icon/icons
src/react/index.ts
node_modules
package.json
package-lock.json
tsconfig.json
cdn
_site
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bierner.lit-html",
"bashmish.es6-string-css",
"streetsidesoftware.code-spell-checker"
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Shoelace
Before contributing, please review the contributions guidelines at:
[shoelace.style/resources/contributing](https://shoelace.style/resources/contributing)
================================================
FILE: LICENSE.md
================================================
Copyright (c) 2020 A Beautiful Site, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Shoelace is now Web Awesome 🧡!
> [!IMPORTANT]
> **Shoelace is in maintenance mode (LTS)**. It is no longer actively being developed but remains available for use under the MIT license. Critical fixes may be released as needed; there is no fixed end date.
> For active development and new features, check out Web Awesome at [https://webawesome.com](https://webawesome.com) and [https://github.com/shoelace-style/webawesome](https://github.com/shoelace-style/webawesome).
Web Awesome has an even larger library of free web [components](https://webawesome.com/docs/components/), plus [themes](https://webawesome.com/docs/themes/), [utilities](https://webawesome.com/docs/utilities/), [patterns](https://webawesome.com/docs/patterns/), and more.
---
# Shoelace
A forward-thinking library of web components.
- Works with all frameworks 🧩
- Works with CDNs 🚛
- Fully customizable with CSS 🎨
- Includes an official dark theme 🌛
- Built with accessibility in mind ♿️
- Open source 😸
---
- Documentation: [shoelace.style](https://shoelace.style)
- Shoelace Source (Maintenance Mode - LTS): [github.com/shoelace-style/shoelace](https://github.com/shoelace-style/shoelace)
- Web Awesome Source (Active Development): [github.com/shoelace-style/webawesome](https://github.com/shoelace-style/webawesome)
---
## Shoemakers 🥾
Shoemakers, or "Shoelace developers," can use this documentation to learn how to build Shoelace from source. You will need Node >= 14.17 to build and run the project locally.
**You don't need to do any of this to use Shoelace!** This page is for people who want to contribute to the project, tinker with the source, or create a custom build of Shoelace.
If that's not what you're trying to do, the [documentation website](https://shoelace.style) is where you want to be.
### What are you using to build Shoelace?
Components are built with [LitElement](https://lit-element.polymer-project.org/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
### Forking the Repo
Start by [forking the repo](https://github.com/shoelace-style/shoelace/fork) on GitHub, then clone it locally and install dependencies.
```bash
git clone https://github.com/YOUR_GITHUB_USERNAME/shoelace
cd shoelace
npm install
```
### Developing
Once you've cloned the repo, run the following command.
```bash
npm start
```
This will spin up the dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browsers don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically.
### Building
To generate a production build, run the following command.
```bash
npm run build
```
### Creating New Components
To scaffold a new component, run the following command, replacing `sl-tag-name` with the desired tag name.
```bash
npm run create sl-tag-name
```
This will generate a source file, a stylesheet, and a docs page for you. When you start the dev server, you'll find the new component in the "Components" section of the sidebar.
### Contributing
Shoelace is open source under the MIT license. Bug fixes and maintenance updates may still be considered; for new features and active development, see [Web Awesome](https://webawesome.com). If you want to contribute here, please review the [contribution guidelines](CONTRIBUTING.md) first.
## License
Shoelace is available under the terms of the MIT license.
Whether you're building Shoelace or building something _with_ Shoelace — have fun creating! 🥾
================================================
FILE: cspell.json
================================================
{
"version": "0.2",
"words": [
"activedescendant",
"allowfullscreen",
"animationend",
"Animista",
"apos",
"atrule",
"autocorrect",
"autofix",
"autoload",
"autoloader",
"autoloading",
"autoplay",
"bezier",
"Bokmål",
"boxicons",
"CACHEABLE",
"callout",
"callouts",
"cdndir",
"chatbubble",
"checkmark",
"claviska",
"Clippy",
"codebases",
"codepen",
"colocated",
"colour",
"combobox",
"Commonmark",
"Composability",
"Consolas",
"contenteditable",
"copydir",
"Cotte",
"coverpage",
"crossorigin",
"crutchcorn",
"csspart",
"cssproperty",
"datetime",
"describedby",
"Docsify",
"dogfood",
"dropdowns",
"easings",
"endraw",
"enterkeyhint",
"eqeqeq",
"erroneou",
"errormessage",
"esbuild",
"exportmaps",
"exportparts",
"fieldsets",
"formaction",
"formdata",
"formenctype",
"formmethod",
"formnovalidate",
"formtarget",
"FOUC",
"FOUCE",
"fullscreen",
"gestern",
"giga",
"globby",
"Grayscale",
"haspopup",
"heroicons",
"hexa",
"Iconoir",
"Iframes",
"iife",
"inputmode",
"ionicon",
"ionicons",
"jsDelivr",
"jsfiddle",
"keydown",
"keyframes",
"Kool",
"labelledby",
"Laravel",
"LaViska",
"linkify",
"listbox",
"listitem",
"litelement",
"lowercasing",
"Lucide",
"maxlength",
"Menlo",
"menuitemcheckbox",
"menuitemradio",
"middlewares",
"minlength",
"monospace",
"mousedown",
"mousemove",
"mouseout",
"mouseup",
"multiselectable",
"nextjs",
"nocheck",
"noopener",
"noreferrer",
"novalidate",
"npmdir",
"Numberish",
"onscrollend",
"outdir",
"ParamagicDev",
"peta",
"petabit",
"prismjs",
"progressbar",
"radiogroup",
"Railsbyte",
"remixicon",
"reregister",
"resizer",
"resizers",
"retargeted",
"RETRYABLE",
"rgba",
"roadmap",
"Roboto",
"roledescription",
"Sapan",
"saturationl",
"Schilp",
"scrollbars",
"scrollend",
"scroller",
"Segoe",
"semibold",
"sitedir",
"slotchange",
"smartquotes",
"spacebar",
"stylesheet",
"Tabbable",
"tabindex",
"tabler",
"tablist",
"tabpanel",
"templating",
"tera",
"testid",
"textareas",
"textfield",
"tinycolor",
"transitionend",
"treeitem",
"treeshaking",
"Triaging",
"turbolinks",
"typeof",
"unbundles",
"unbundling",
"unicons",
"unsanitized",
"unsupportive",
"valpha",
"valuenow",
"valuetext",
"vuejs",
"WEBP",
"Webpacker",
"wordmark"
],
"ignorePaths": [
"package.json",
"package-lock.json",
"docs/assets/examples/include.html",
".vscode/**",
"src/translations/!(en).ts",
"**/*.min.js"
],
"ignoreRegExpList": [
"(^|[^a-z])sl[a-z]*(^|[^a-z])"
],
"useGitignore": true
}
================================================
FILE: custom-elements-manifest.config.js
================================================
import * as path from 'path';
import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';
import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
import { customElementVuejsPlugin } from 'custom-element-vuejs-integration';
import { parse } from 'comment-parser';
import { pascalCase } from 'pascal-case';
import commandLineArgs from 'command-line-args';
import fs from 'fs';
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const { name, description, version, author, homepage, license } = packageData;
const { outdir } = commandLineArgs([
{ name: 'litelement', type: String },
{ name: 'analyze', defaultOption: true },
{ name: 'outdir', type: String }
]);
function noDash(string) {
return string.replace(/^\s?-/, '').trim();
}
function replace(string, terms) {
terms.forEach(({ from, to }) => {
string = string?.replace(from, to);
});
return string;
}
export default {
globs: ['src/components/**/*.component.ts'],
exclude: ['**/*.styles.ts', '**/*.test.ts'],
plugins: [
// Append package data
{
name: 'shoelace-package-data',
packageLinkPhase({ customElementsManifest }) {
customElementsManifest.package = { name, description, version, author, homepage, license };
}
},
// Infer tag names because we no longer use @customElement decorators.
{
name: 'shoelace-infer-tag-names',
analyzePhase({ ts, node, moduleDoc }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
const className = node.name.getText();
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
const importPath = moduleDoc.path;
// This is kind of a best guess at components. "thing.component.ts"
if (!importPath.endsWith('.component.ts')) {
return;
}
const tagNameWithoutPrefix = path.basename(importPath, '.component.ts');
const tagName = 'sl-' + tagNameWithoutPrefix;
classDoc.tagNameWithoutPrefix = tagNameWithoutPrefix;
classDoc.tagName = tagName;
// This used to be set to true by @customElement
classDoc.customElement = true;
}
}
}
},
// Parse custom jsDoc tags
{
name: 'shoelace-custom-tags',
analyzePhase({ ts, node, moduleDoc }) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
const className = node.name.getText();
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
const customTags = ['animation', 'dependency', 'documentation', 'since', 'status', 'title'];
let customComments = '/**';
node.jsDoc?.forEach(jsDoc => {
jsDoc?.tags?.forEach(tag => {
const tagName = tag.tagName.getText();
if (customTags.includes(tagName)) {
customComments += `\n * @${tagName} ${tag.comment}`;
}
});
});
// This is what allows us to map JSDOC comments to ReactWrappers.
classDoc['jsDoc'] = node.jsDoc?.map(jsDoc => jsDoc.getFullText()).join('\n');
const parsed = parse(`${customComments}\n */`);
parsed[0].tags?.forEach(t => {
switch (t.tag) {
// Animations
case 'animation':
if (!Array.isArray(classDoc['animations'])) {
classDoc['animations'] = [];
}
classDoc['animations'].push({
name: t.name,
description: noDash(t.description)
});
break;
// Dependencies
case 'dependency':
if (!Array.isArray(classDoc['dependencies'])) {
classDoc['dependencies'] = [];
}
classDoc['dependencies'].push(t.name);
break;
// Value-only metadata tags
case 'documentation':
case 'since':
case 'status':
case 'title':
classDoc[t.tag] = t.name;
break;
// All other tags
default:
if (!Array.isArray(classDoc[t.tag])) {
classDoc[t.tag] = [];
}
classDoc[t.tag].push({
name: t.name,
description: t.description,
type: t.type || undefined
});
}
});
}
}
}
},
{
name: 'shoelace-react-event-names',
analyzePhase({ ts, node, moduleDoc }) {
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.forEach(event => {
event.reactName = `on${pascalCase(event.name)}`;
event.eventName = `${pascalCase(event.name)}Event`;
});
}
}
}
}
},
{
name: 'shoelace-translate-module-paths',
packageLinkPhase({ customElementsManifest }) {
customElementsManifest?.modules?.forEach(mod => {
//
// CEM paths look like this:
//
// src/components/button/button.ts
//
// But we want them to look like this:
//
// components/button/button.js
//
const terms = [
{ from: /^src\//, to: '' }, // Strip the src/ prefix
{ from: /\.component.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
];
mod.path = replace(mod.path, terms);
for (const ex of mod.exports ?? []) {
ex.declaration.module = replace(ex.declaration.module, terms);
}
for (const dec of mod.declarations ?? []) {
if (dec.kind === 'class') {
for (const member of dec.members ?? []) {
if (member.inheritedFrom) {
member.inheritedFrom.module = replace(member.inheritedFrom.module, terms);
}
}
}
}
});
}
},
// Generate custom VS Code data
customElementVsCodePlugin({
outdir,
cssFileName: null,
referencesTemplate: (_, tag) => [
{
name: 'Documentation',
url: `https://shoelace.style/components/${tag.replace('sl-', '')}`
}
]
}),
customElementJetBrainsPlugin({
outdir: './dist',
excludeCss: true,
packageJson: false,
referencesTemplate: (_, tag) => {
return {
name: 'Documentation',
url: `https://shoelace.style/components/${tag.replace('sl-', '')}`
};
}
}),
customElementVuejsPlugin({
outdir: './dist/types/vue',
fileName: 'index.d.ts',
componentTypePath: (_, tag) => `../../components/${tag.replace('sl-', '')}/${tag.replace('sl-', '')}.component.js`
})
]
};
================================================
FILE: docs/_includes/component.njk
================================================
{% extends "default.njk" %}
{# Find the component based on the `tag` front matter #}
{% set component = getComponent('sl-' + page.fileSlug) %}
{% block content %}
{# Determine the badge variant #}
{% if component.status == 'stable' %}
{% set badgeVariant = 'primary' %}
{% elseif component.status == 'experimental' %}
{% set badgeVariant = 'warning' %}
{% elseif component.status == 'planned' %}
{% set badgeVariant = 'neutral' %}
{% elseif component.status == 'deprecated' %}
{% set badgeVariant = 'danger' %}
{% else %}
{% set badgeVariant = 'neutral' %}
{% endif %}
{# Header #}
<header class="component-header">
<h1>{{ component.name | classNameToComponentName }}</h1>
<div class="component-header__tag">
<code><{{ component.tagName }}> | {{ component.name }}</code>
</div>
<div class="component-header__info">
<sl-badge variant="neutral" pill>
Since {{component.since or '?' }}
</sl-badge>
<sl-badge variant="{{ badgeVariant }}" pill style="text-transform: capitalize;">
{{ component.status }}
</sl-badge>
</div>
</header>
<p class="component-summary">
{% if component.summary %}
{{ component.summary | markdownInline | safe }}
{% endif %}
</p>
{# Markdown content #}
{{ content | safe }}
{# Importing #}
<h2>Importing</h2>
<p>
If you're using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use
any of the following snippets to <a href="/getting-started/installation#cherry-picking">cherry pick</a> this component.
</p>
<sl-tab-group>
<sl-tab slot="nav" panel="script">Script</sl-tab>
<sl-tab slot="nav" panel="import">Import</sl-tab>
<sl-tab slot="nav" panel="bundler">Bundler</sl-tab>
<sl-tab slot="nav" panel="react">React</sl-tab>
<sl-tab-panel name="script">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a script tag:
</p>
<pre><code class="language-html"><script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}"></script></code></pre>
</sl-tab-panel>
<sl-tab-panel name="import">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a JavaScript import:
</p>
<pre><code class="language-js">import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}';</code></pre>
</sl-tab-panel>
<sl-tab-panel name="bundler">
<p>
To import this component using <a href="{{ rootUrl('/getting-started/installation#bundling') }}">a bundler</a>:
</p>
<pre><code class="language-js">import '@shoelace-style/shoelace/{{ meta.npmdir }}/{{ component.path }}';</code></pre>
</sl-tab-panel>
<sl-tab-panel name="react">
<p>
To import this component as a <a href="/frameworks/react">React component</a>:
</p>
<pre><code class="language-js">import {{ component.name }} from '@shoelace-style/shoelace/{{ meta.npmdir }}/react/{{ component.tagNameWithoutPrefix }}';</code></pre>
</sl-tab-panel>
</sl-tab-group>
{# Slots #}
{% if component.slots.length %}
<h2>Slots</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for slot in component.slots %}
<tr>
<td class="nowrap">
{% if slot.name %}
<code>{{ slot.name }}</code>
{% else %}
(default)
{% endif %}
</td>
<td>{{ slot.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#slots') }}">using slots</a>.</em></p>
{% endif %}
{# Properties #}
{% if component.properties.length %}
<h2>Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-reflects">Reflects</th>
<th class="table-type">Type</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for prop in component.properties %}
<tr>
<td>
<code class="nowrap">{{ prop.name }}</code>
{% if prop.attribute | length > 0 %}
{% if prop.attribute != prop.name %}
<br>
<sl-tooltip content="This attribute is different from its property">
<small>
<code class="nowrap">
{{ prop.attribute }}
</code>
</small>
</sl-tooltip>
{% endif %}
{% endif %}
</td>
<td>
{{ prop.description | markdownInline | safe }}
</td>
<td style="text-align: center;">
{% if prop.reflects %}
<sl-icon label="yes" name="check-lg"></sl-icon>
{% endif %}
</td>
<td>
{% if prop.type.text %}
<code>{{ prop.type.text | trimPipes | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
<td>
{% if prop.default %}
<code>{{ prop.default | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
<tr>
<td class="nowrap"><code>updateComplete</code></td>
<td>
A read-only promise that resolves when the component has
<a href="/getting-started/usage?#component-rendering-and-updating">finished updating</a>.
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#attributes-and-properties') }}">attributes and properties</a>.</em></p>
{% endif %}
{# Events #}
{% if component.events.length %}
<h2>Events</h2>
<table>
<thead>
<tr>
<th class="table-name" data-flavor="html">Name</th>
<th class="table-name" data-flavor="react">React Event</th>
<th class="table-description">Description</th>
<th class="table-event-detail">Event Detail</th>
</tr>
</thead>
<tbody>
{% for event in component.events %}
<tr>
<td data-flavor="html"><code class="nowrap">{{ event.name }}</code></td>
<td data-flavor="react"><code class="nowrap">{{ event.reactName }}</code></td>
<td>{{ event.description | markdownInline | safe }}</td>
<td>
{% if event.type.text %}
<code>{{ event.type.text | trimPipes }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#events') }}">events</a>.</em></p>
{% endif %}
{# Methods #}
{% if component.methods.length %}
<h2>Methods</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-arguments">Arguments</th>
</tr>
</thead>
<tbody>
{% for method in component.methods %}
<tr>
<td class="nowrap"><code>{{ method.name }}()</code></td>
<td>{{ method.description | markdownInline | safe }}</td>
<td>
{% if method.parameters.length %}
<code>
{% for param in method.parameters %}
{{ param.name }}: {{ param.type.text | trimPipes }}{% if not loop.last %},{% endif %}
{% endfor %}
</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#methods') }}">methods</a>.</em></p>
{% endif %}
{# Custom Properties #}
{% if component.cssProperties.length %}
<h2>Custom Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for cssProperty in component.cssProperties %}
<tr>
<td class="nowrap"><code>{{ cssProperty.name }}</code></td>
<td>{{ cssProperty.description | markdownInline | safe }}</td>
<td>{{ cssProperty.default }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
{% endif %}
{# CSS Parts #}
{% if component.cssParts.length %}
<h2>Parts</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for cssPart in component.cssParts %}
<tr>
<td class="nowrap"><code>{{ cssPart.name }}</code></td>
<td>{{ cssPart.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing/#css-parts') }}">customizing CSS parts</a>.</em></p>
{% endif %}
{# Animations #}
{% if component.animations.length %}
<h2>Animations</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for animation in component.animations %}
<tr>
<td class="nowrap"><code>{{ animation.name }}</code></td>
<td>{{ animation.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing#animations') }}">customizing animations</a>.</em></p>
{% endif %}
{# Dependencies #}
{% if component.dependencies.length %}
<h2>Dependencies</h2>
<p>This component automatically imports the following dependencies.</p>
<ul>
{% for dependency in component.dependencies %}
<li><code><{{ dependency }}></code></li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}
================================================
FILE: docs/_includes/default.njk
================================================
<!DOCTYPE html>
<html
lang="en"
data-layout="{{ layout }}"
data-shoelace-version="{{ meta.version }}"
>
<head>
{# Metadata #}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{ meta.description }}" />
<title>{{ meta.title }}</title>
{# Opt out of Turbo caching #}
<meta name="turbo-cache-control">
{# Stylesheets #}
<link rel="stylesheet" href="{{ assetUrl('styles/docs.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/code-previews.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/search.css') }}" />
{# Favicons #}
<link rel="icon" href="{{ assetUrl('images/logo.svg') }}" type="image/x-icon" />
{# Twitter Cards #}
<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content="shoelace_style" />
<meta name="twitter:image" content="{{ assetUrl(meta.image, true) }}" />
{# OpenGraph #}
<meta property="og:url" content="{{ rootUrl(page.url, true) }}" />
<meta property="og:title" content="{{ meta.title }}" />
<meta property="og:description" content="{{ meta.description }}" />
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
{# Shoelace #}
<link rel="stylesheet" href="/dist/themes/light.css" />
<link rel="stylesheet" href="/dist/themes/dark.css" />
<script type="module" src="/dist/shoelace-autoloader.js"></script>
{# Set the initial theme and menu states here to prevent flashing #}
<script>
(() => {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = localStorage.getItem('theme') || 'auto';
document.documentElement.classList.toggle('sl-theme-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
})();
</script>
{# Turbo + Scroll positioning #}
<script src="{{ assetUrl('scripts/turbo.js') }}" type="module"></script>
<script src="{{ assetUrl('scripts/docs.js') }}" defer></script>
<script src="{{ assetUrl('scripts/code-previews.js') }}" defer></script>
<script src="{{ assetUrl('scripts/lunr.js') }}" defer></script>
<script src="{{ assetUrl('scripts/search.js') }}" defer></script>
</head>
<body>
<a id="skip-to-main" class="visually-hidden" href="#main-content" data-smooth-link="false">
Skip to main content
</a>
{# Menu toggle #}
<button id="menu-toggle" type="button" aria-label="Menu">
<svg width="148" height="148" viewBox="0 0 148 148" xmlns="http://www.w3.org/2000/svg">
<g stroke="currentColor" stroke-width="18" fill="none" fill-rule="evenodd" stroke-linecap="round">
<path d="M9.5 125.5h129M9.5 74.5h129M9.5 23.5h129"></path>
</g>
</svg>
</button>
{# Icon toolbar #}
<div id="icon-toolbar">
{# GitHub #}
<a href="https://github.com/shoelace-style/shoelace" title="View Shoelace on GitHub">
<sl-icon name="github"></sl-icon>
</a>
{# Twitter #}
<a href="https://twitter.com/shoelace_style" title="Follow Shoelace on Twitter">
<sl-icon name="twitter"></sl-icon>
</a>
{# Theme selector #}
<sl-dropdown id="theme-selector" placement="bottom-end" distance="3">
<sl-button slot="trigger" size="small" variant="text" caret title="Press \ to toggle">
<sl-icon class="only-light" name="sun-fill"></sl-icon>
<sl-icon class="only-dark" name="moon-fill"></sl-icon>
</sl-button>
<sl-menu>
<sl-menu-item type="checkbox" value="light">Light</sl-menu-item>
<sl-menu-item type="checkbox" value="dark">Dark</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item type="checkbox" value="auto">System</sl-menu-item>
</sl-menu>
</sl-dropdown>
</div>
<sl-dialog id="wa-dialog" label="Shoelace is now Web Awesome!">
<div class="dialog-billboard">
<div class="dialog-billboard-content">
{% include 'wa-logo-icon.njk' %}
<h2>Shoelace is now Web Awesome!</h2>
</div>
</div>
<div class="dialog-body">
<p>Web Awesome has an even bigger library of free <a href="https://webawesome.com/docs/components/?utm_source=shoelace-docs&utm_medium=web&utm_campaign=wa-dialog" target="_blank">web components</a>. Plus <a href="https://webawesome.com/docs/themes/?utm_source=shoelace-docs&utm_medium=web&utm_campaign=wa-dialog" target="_blank">themes</a>, <a href="https://webawesome.com/docs/utilities/?utm_source=shoelace-docs&utm_medium=web&utm_campaign=wa-dialog" target="_blank">utilities</a>, <a href="https://webawesome.com/docs/patterns/?utm_source=shoelace-docs&utm_medium=web&utm_campaign=wa-dialog" target="_blank">patterns</a>, and more!</p>
<p><strong>Still open source. Now with more awesome.</strong></p>
<sl-divider></sl-divider>
<div class="dialog-actions">
<sl-button variant="text" class="close-wa-dialog">Okay, got it</sl-button>
<div class="wa-ctas">
<sl-button variant="primary" size="large" class="wa-pro-cta" href="https://webawesome.com/docs?utm_source=shoelace-docs&utm_medium=web&utm_campaign=wa-dialog" target="_blank">
See What's New
<sl-icon slot="suffix" name="arrow-right"></sl-icon>
</sl-button>
</div>
</div>
</div>
<div slot="footer">
<div class="sl-status">
<strong>FYI, Shoelace is no longer actively being developed</strong>
<p>But it is still available for use and may receive updates as needed.</p>
</div>
</div>
</sl-dialog>
<script>
if (!sessionStorage.getItem('wa-dialog-dismissed')) {
customElements.whenDefined('sl-dialog').then(() => {
const dialog = document.getElementById('wa-dialog');
const closeButton = dialog.querySelector('sl-button.close-wa-dialog');
closeButton.addEventListener('click', () => dialog.hide());
dialog.addEventListener('sl-after-hide', () => {
sessionStorage.setItem('wa-dialog-dismissed', 'true');
}, { once: true });
dialog.show();
});
}
</script>
<aside id="sidebar" data-preserve-scroll>
<header>
<a href="/">
<img src="{{ assetUrl('images/wordmark.svg') }}" alt="Shoelace" />
</a>
<div class="sidebar-version">
{{ meta.version }}
</div>
</header>
<div class="sidebar-buttons">
<sl-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace" target="_blank">
<sl-icon slot="prefix" name="github"></sl-icon> Code
</sl-button>
<sl-button size="small" class="repo-button repo-button--star" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<sl-icon slot="prefix" name="star-fill"></sl-icon> Star
</sl-button>
<sl-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<sl-icon slot="prefix" name="twitter"></sl-icon> Follow
</sl-button>
</div>
<button class="search-box" type="button" title="Press / to search" aria-label="Search" data-plugin="search">
<sl-icon name="search"></sl-icon>
<span>Search</span>
</button>
<nav>
{% include 'sidebar.njk' %}
</nav>
</aside>
{# Content #}
<main>
<a id="main-content"></a>
<article id="content" class="content{% if toc %} content--with-toc{% endif %}">
{% if toc %}
<div class="content__toc">
<ul>
<li class="top"><a href="#">{{ meta.title }}</a></li>
</ul>
</div>
{% endif %}
<div class="content__body">
{% block content %}
{{ content | safe }}
{% endblock %}
</div>
</article>
</main>
</body>
</html>
================================================
FILE: docs/_includes/sidebar.njk
================================================
<ul>
<li>
<h2>Getting Started</h2>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/getting-started/installation">Installation</a></li>
<li><a href="/getting-started/usage">Usage</a></li>
<li><a href="/getting-started/themes">Themes</a></li>
<li><a href="/getting-started/customizing">Customizing</a></li>
<li><a href="/getting-started/form-controls">Form Controls</a></li>
<li><a href="/getting-started/localization">Localization</a></li>
</ul>
</li>
<li>
<h2>Frameworks</h2>
<ul>
<li><a href="/frameworks/react">React</a></li>
<li><a href="/frameworks/vue">Vue</a></li>
<li><a href="/frameworks/angular">Angular</a></li>
<li><a href="/frameworks/svelte">Svelte</a></li>
</ul>
</li>
<li>
<h2>Resources</h2>
<ul>
<li><a href="/resources/community">Community</a></li>
<li><a href="https://github.com/shoelace-style/shoelace/discussions">Help & Support</a></li>
<li><a href="/resources/accessibility">Accessibility</a></li>
<li><a href="/resources/contributing">Contributing</a></li>
<li><a href="/resources/changelog">Changelog</a></li>
</ul>
</li>
<li>
<h2>Components</h2>
<ul>
{% for component in meta.components %}
<li>
<a href="/components/{{ component.tagName | removeSlPrefix }}">
{{ component.name | classNameToComponentName }}
</a>
</li>
{% endfor %}
</ul>
</li>
<li>
<h2>Design Tokens</h2>
<ul>
<li><a href="/tokens/typography">Typography</a></li>
<li><a href="/tokens/color">Color</a></li>
<li><a href="/tokens/spacing">Spacing</a></li>
<li><a href="/tokens/elevation">Elevation</a></li>
<li><a href="/tokens/border-radius">Border Radius</a></li>
<li><a href="/tokens/transition">Transition</a></li>
<li><a href="/tokens/z-index">Z-index</a></li>
<li><a href="/tokens/more">More Tokens</a></li>
</ul>
</li>
<li>
<h2>Tutorials</h2>
<ul>
<li><a href="/tutorials/integrating-with-astro">Integrating with Astro</a></li>
<li><a href="/tutorials/integrating-with-laravel">Integrating with Laravel</a></li>
<li><a href="/tutorials/integrating-with-nextjs">Integrating with NextJS</a></li>
<li><a href="/tutorials/integrating-with-rails">Integrating with Rails</a></li>
</ul>
</li>
</ul>
================================================
FILE: docs/_includes/wa-logo-icon.njk
================================================
<svg class="wa-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" aria-hidden="true"><path fill="currentColor" d="M372.2 116C372.2 136.9 359.8 155 342 163.2L448 256L552.4 235.1C547.1 227.4 544 218 544 208C544 181.5 565.5 160 592 160C618.5 160 640 181.5 640 208C640 234 619.4 255.1 593.6 256L481 506.3C470.7 529.3 447.8 544 422.6 544L217.4 544C192.2 544 169.4 529.2 159 506.3L46.4 256C20.6 255.1 0 234 0 208C0 181.5 21.5 160 48 160C74.5 160 96 181.5 96 208C96 218.1 92.9 227.4 87.6 235.1L192 256L298.1 163.1C280.4 154.8 268.1 136.8 268.1 116C268.1 87.3 291.4 64 320.1 64C348.8 64 372.1 87.3 372.1 116L372.2 116z"/></svg>
================================================
FILE: docs/_utilities/active-links.cjs
================================================
function normalizePathname(pathname) {
// Remove /index.html
if (pathname.endsWith('/index.html')) {
pathname = pathname.replace(/\/index\.html/, '');
}
// Remove trailing slashes
return pathname.replace(/\/$/, '');
}
/**
* Adds a class name to links that are currently active.
*/
module.exports = function (doc, options) {
options = {
className: 'active-link', // the class to add to active links
pathname: undefined, // the current pathname to compare
within: 'body', // element containing the target links
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('a').forEach(link => {
if (normalizePathname(options.pathname) === normalizePathname(link.pathname)) {
link.classList.add(options.className);
}
});
return doc;
};
================================================
FILE: docs/_utilities/anchor-headings.cjs
================================================
const { createSlug } = require('./strings.cjs');
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
levels: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], // the headings to convert
className: 'anchor-heading', // the class name to add
within: 'body', // the element containing the target headings
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
const hasAnchor = heading.querySelector('a');
const anchor = doc.createElement('a');
let id = heading.textContent ?? '';
let suffix = 0;
// Skip heading levels we don't care about
if (!options.levels?.includes(heading.tagName.toLowerCase())) {
return;
}
// Convert dots to underscores
id = id.replace(/\./g, '_');
// Turn it into a slug
id = createSlug(id);
// Make sure it starts with a letter
if (!/^[a-z]/i.test(id)) {
id = `id_${id}`;
}
// Make sure the id is unique
const originalId = id;
while (doc.getElementById(id) !== null) {
id = `${originalId}-${++suffix}`;
}
if (hasAnchor || !id) return;
heading.setAttribute('id', id);
anchor.setAttribute('href', `#${encodeURIComponent(id)}`);
anchor.setAttribute('aria-label', `Direct link to "${heading.textContent}"`);
if (options.className) {
heading.classList.add(options.className);
}
// Append the anchor
heading.append(anchor);
});
return doc;
};
================================================
FILE: docs/_utilities/cem.cjs
================================================
const customElementsManifest = require('../../dist/custom-elements.json');
//
// Export it here so we can import it elsewhere and use the same version
//
module.exports.customElementsManifest = customElementsManifest;
//
// Gets all components from custom-elements.json and returns them in a more documentation-friendly format.
//
module.exports.getAllComponents = function () {
const allComponents = [];
customElementsManifest.modules?.forEach(module => {
module.declarations?.forEach(declaration => {
if (declaration.customElement) {
// Generate the dist path based on the src path and attach it to the component
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
// Remove members that are private or don't have a description
const members = declaration.members?.filter(member => member.description && member.privacy !== 'private');
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
const properties = members?.filter(prop => {
// Look for a corresponding attribute
const attribute = declaration.attributes?.find(attr => attr.fieldName === prop.name);
if (attribute) {
prop.attribute = attribute.name || attribute.fieldName;
}
return prop.kind === 'field' && prop.privacy !== 'private';
});
allComponents.push({
...declaration,
methods,
properties
});
}
});
});
// Build dependency graphs
allComponents.forEach(component => {
const dependencies = [];
// Recursively fetch sub-dependencies
function getDependencies(tag) {
const cmp = allComponents.find(c => c.tagName === tag);
if (!cmp || !Array.isArray(component.dependencies)) {
return;
}
cmp.dependencies?.forEach(dependentTag => {
if (!dependencies.includes(dependentTag)) {
dependencies.push(dependentTag);
}
getDependencies(dependentTag);
});
}
getDependencies(component.tagName);
component.dependencies = dependencies.sort();
});
// Sort by name
return allComponents.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
};
================================================
FILE: docs/_utilities/code-previews.cjs
================================================
let count = 1;
function escapeHtml(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
/**
* Turns code fields with the :preview suffix into interactive code previews.
*/
module.exports = function (doc, options) {
options = {
within: 'body', // the element containing the code fields to convert
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('[class*=":preview"]').forEach(code => {
const pre = code.closest('pre');
if (!pre) {
return;
}
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
const sourceGroupId = `code-preview-source-group-${count}`;
const isExpanded = code.getAttribute('class').includes(':expanded');
const noCodePen = code.getAttribute('class').includes(':no-codepen');
count++;
const htmlButton = `
<button type="button"
title="Show HTML code"
class="code-preview__button code-preview__button--html"
>
HTML
</button>
`;
const reactButton = `
<button type="button" title="Show React code" class="code-preview__button code-preview__button--react">
React
</button>
`;
const codePenButton = `
<button type="button" class="code-preview__button code-preview__button--codepen" title="Edit on CodePen">
<svg
width="138"
height="26"
viewBox="0 0 138 26"
fill="none"
stroke="currentColor"
stroke-width="2.3"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
</svg>
</button>
`;
const codePreview = `
<div class="code-preview ${isExpanded ? 'code-preview--expanded' : ''}">
<div class="code-preview__preview">
${code.textContent}
<div class="code-preview__resizer">
<sl-icon name="grip-vertical"></sl-icon>
</div>
</div>
<div class="code-preview__source-group" id="${sourceGroupId}">
<div class="code-preview__source code-preview__source--html" ${reactCode ? 'data-flavor="html"' : ''}>
<pre><code class="language-html">${escapeHtml(code.textContent)}</code></pre>
</div>
${
reactCode
? `
<div class="code-preview__source code-preview__source--react" data-flavor="react">
<pre><code class="language-jsx">${escapeHtml(reactCode.textContent)}</code></pre>
</div>
`
: ''
}
</div>
<div class="code-preview__buttons">
<button
type="button"
class="code-preview__button code-preview__toggle"
aria-expanded="${isExpanded ? 'true' : 'false'}"
aria-controls="${sourceGroupId}"
>
Source
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
${reactCode ? ` ${htmlButton} ${reactButton} ` : ''}
${noCodePen ? '' : codePenButton}
</div>
</div>
`;
pre.insertAdjacentHTML('afterend', codePreview);
pre.remove();
if (adjacentPre) {
adjacentPre.remove();
}
});
// Wrap code preview scripts in anonymous functions so they don't run in the global scope
doc.querySelectorAll('.code-preview__preview script').forEach(script => {
if (script.type === 'module') {
// Modules are already scoped
script.textContent = script.innerHTML;
} else {
// Wrap non-modules in an anonymous function so they don't run in the global scope
script.textContent = `(() => { ${script.innerHTML} })();`;
}
});
return doc;
};
================================================
FILE: docs/_utilities/copy-code-buttons.cjs
================================================
let codeBlockId = 0;
/**
* Adds copy code buttons to code fields. The provided doc should be a document object provided by JSDOM. The same
* document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
const button = doc.createElement('sl-copy-button');
if (!code.id) {
code.id = `code-block-${++codeBlockId}`;
}
button.classList.add('copy-code-button');
button.setAttribute('from', code.id);
pre.append(button);
});
return doc;
};
================================================
FILE: docs/_utilities/external-links.cjs
================================================
const { isExternalLink } = require('./strings.cjs');
/**
* Transforms external links to make them safer and optionally add a target. The provided doc should be a document
* object provided by JSDOM. The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
className: 'external-link', // the class name to add to links
noopener: true, // sets rel="noopener"
noreferrer: true, // sets rel="noreferrer"
ignore: () => false, // callback function to filter links that should be ignored
within: 'body', // element that contains the target links
target: '', // sets the target attribute
...options
};
const within = doc.querySelector(options.within);
if (within) {
within.querySelectorAll('a').forEach(link => {
if (isExternalLink(link) && !options.ignore(link)) {
link.classList.add(options.className);
const rel = [];
if (options.noopener) rel.push('noopener');
if (options.noreferrer) rel.push('noreferrer');
if (rel.length) {
link.setAttribute('rel', rel.join(' '));
}
if (options.target) {
link.setAttribute('target', options.target);
}
}
});
}
return doc;
};
================================================
FILE: docs/_utilities/highlight-code.cjs
================================================
const Prism = require('prismjs');
const PrismLoader = require('prismjs/components/index.js');
PrismLoader('diff');
PrismLoader.silent = true;
/** Highlights a code string. */
function highlight(code, language) {
const alias = language.replace(/^diff-/, '');
const isDiff = /^diff-/i.test(language);
// Auto-load the target language
if (!Prism.languages[alias]) {
PrismLoader(alias);
if (!Prism.languages[alias]) {
throw new Error(`Unsupported language for code highlighting: "${language}"`);
}
}
// Register diff-* languages to use the diff grammar
if (isDiff) {
Prism.languages[language] = Prism.languages.diff;
}
return Prism.highlight(code, Prism.languages[language], language);
}
/**
* Highlights all code fields that have a language parameter. If the language has a colon in its name, the first chunk
* will be the language used and additional chunks will be applied as classes to the `<pre>`. For example, a code field
* tagged with "html:preview" will be rendered as `<pre class="language-html preview">`.
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code[class]').forEach(code => {
// Look for class="language-*" and split colons into separate classes
code.classList.forEach(className => {
if (className.startsWith('language-')) {
//
// We use certain suffixes to indicate code previews, expanded states, etc. The class might look something like
// this:
//
// class="language-html:preview:expanded"
//
// The language will always come first, so we need to drop the "language-" prefix and everything after the first
// color to get the highlighter language.
//
const language = className.replace(/^language-/, '').split(':')[0];
try {
code.innerHTML = highlight(code.textContent ?? '', language);
} catch (err) {
// Language not found, skip it
}
}
});
});
return doc;
};
================================================
FILE: docs/_utilities/markdown.cjs
================================================
const MarkdownIt = require('markdown-it');
const markdownItContainer = require('markdown-it-container');
const markdownItIns = require('markdown-it-ins');
const markdownItKbd = require('markdown-it-kbd');
const markdownItMark = require('markdown-it-mark');
const markdownItReplaceIt = require('markdown-it-replace-it');
const markdown = MarkdownIt({
html: true,
xhtmlOut: false,
breaks: false,
langPrefix: 'language-',
linkify: false,
typographer: false
});
// Third-party plugins
markdown.use(markdownItContainer);
markdown.use(markdownItIns);
markdown.use(markdownItKbd);
markdown.use(markdownItMark);
markdown.use(markdownItReplaceIt);
// Callouts
['tip', 'warning', 'danger'].forEach(type => {
markdown.use(markdownItContainer, type, {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<div role="alert" class="callout callout--${type}">`;
}
return '</div>\n';
}
});
});
// Asides
markdown.use(markdownItContainer, 'aside', {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<aside>`;
}
return '</aside>\n';
}
});
// Details
markdown.use(markdownItContainer, 'details', {
validate: params => params.trim().match(/^details\s+(.*)$/),
render: (tokens, idx) => {
const m = tokens[idx].info.trim().match(/^details\s+(.*)$/);
if (tokens[idx].nesting === 1) {
return `<details>\n<summary><span>${markdown.utils.escapeHtml(m[1])}</span></summary>\n`;
}
return '</details>\n';
}
});
// Replace [#1234] with a link to GitHub issues
markdownItReplaceIt.replacements.push({
name: 'github-issues',
re: /\[#([0-9]+)\]/gs,
sub: '<a href="https://github.com/shoelace-style/shoelace/issues/$1">#$1</a>',
html: true,
default: true
});
module.exports = markdown;
================================================
FILE: docs/_utilities/prettier.cjs
================================================
const { format } = require('prettier');
/** Formats markup using prettier. */
module.exports = function (content, options) {
options = {
arrowParens: 'avoid',
bracketSpacing: true,
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
bracketSameLine: false,
jsxSingleQuote: false,
parser: 'html',
printWidth: 120,
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'none',
useTabs: false,
...options
};
return format(content, options);
};
================================================
FILE: docs/_utilities/replacer.cjs
================================================
/**
* @typedef {object} Replacement
* @property {string | RegExp} pattern
* @property {string} replacement
*/
/**
* @typedef {Array<Replacement>} Replacements
*/
/**
* @param {String} rawContent
* @param {Replacements} replacements
*/
module.exports = function (rawContent, replacements) {
let content = rawContent;
replacements.forEach(replacement => {
content = content.replaceAll(replacement.pattern, replacement.replacement);
});
return content;
};
================================================
FILE: docs/_utilities/scrolling-tables.cjs
================================================
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
const tables = [...doc.querySelectorAll('table')];
options = {
className: 'table-scroll', // the class name to add to the table's container
...options
};
tables.forEach(table => {
const div = doc.createElement('div');
div.classList.add(options.className);
table.insertAdjacentElement('beforebegin', div);
div.append(table);
});
return doc;
};
================================================
FILE: docs/_utilities/strings.cjs
================================================
const slugify = require('slugify');
/** Creates a slug from an arbitrary string of text. */
module.exports.createSlug = function (text) {
return slugify(String(text), {
remove: /[^\w|\s]/g,
lower: true
});
};
/** Determines whether or not a link is external. */
module.exports.isExternalLink = function (link) {
// We use the "internal" hostname when initializing JSDOM so we know that those are local links
if (!link.hostname || link.hostname === 'internal') return false;
return true;
};
================================================
FILE: docs/_utilities/table-of-contents.cjs
================================================
/**
* Generates an in-page table of contents based on headings.
*/
module.exports = function (doc, options) {
options = {
levels: ['h2'], // headings to include (they must have an id)
container: 'nav', // the container to append links to
listItem: true, // if true, links will be wrapped in <li>
within: 'body', // the element containing the headings to summarize
...options
};
const container = doc.querySelector(options.container);
const within = doc.querySelector(options.within);
const headingSelector = options.levels.map(h => `${h}[id]`).join(', ');
if (!container || !within) {
return doc;
}
within.querySelectorAll(headingSelector).forEach(heading => {
const listItem = doc.createElement('li');
const link = doc.createElement('a');
const level = heading.tagName.slice(1);
link.href = `#${heading.id}`;
link.textContent = heading.textContent;
if (options.listItem) {
// List item + link
listItem.setAttribute('data-level', level);
listItem.append(link);
container.append(listItem);
} else {
// Link only
link.setAttribute('data-level', level);
container.append(link);
}
});
return doc;
};
================================================
FILE: docs/_utilities/typography.cjs
================================================
const smartquotes = require('smartquotes');
smartquotes.replacements.push([/---/g, '\u2014']); // em dash
smartquotes.replacements.push([/--/g, '\u2013']); // en dash
smartquotes.replacements.push([/\.\.\./g, '\u2026']); // ellipsis
smartquotes.replacements.push([/\(c\)/gi, '\u00A9']); // copyright
smartquotes.replacements.push([/\(r\)/gi, '\u00AE']); // registered trademark
smartquotes.replacements.push([/\?!/g, '\u2048']); // ?!
smartquotes.replacements.push([/!!/g, '\u203C']); // !!
smartquotes.replacements.push([/\?\?/g, '\u2047']); // ??
smartquotes.replacements.push([/([0-9]\s?)-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
/**
* Improves typography by adding smart quotes and similar corrections within the specified element(s).
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc, selector = 'body') {
const elements = [...doc.querySelectorAll(selector)];
elements.forEach(el => smartquotes.element(el));
return doc;
};
================================================
FILE: docs/assets/examples/include.html
================================================
<p style="margin-top: 0">
The content in this example was included from
<a href="/assets/examples/include.html" target="_blank">a separate file</a>. 🤯
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Lectus vestibulum mattis ullamcorper velit sed ullamcorper morbi. Fringilla urna porttitor rhoncus dolor purus
non enim. Nullam vehicula ipsum a arcu cursus vitae congue mauris. Gravida in fermentum et sollicitudin.
</p>
<p>
Cursus sit amet dictum sit amet justo donec enim. Sed id semper risus in hendrerit gravida. Viverra accumsan in nisl
nisi scelerisque eu ultrices vitae. Et molestie ac feugiat sed lectus vestibulum mattis ullamcorper velit. Nec
ullamcorper sit amet risus nullam. Et egestas quis ipsum suspendisse ultrices gravida dictum. Lorem donec massa sapien
faucibus et molestie. A cras semper auctor neque vitae.
</p>
<script>
console.log('This will only execute if the `allow-scripts` prop is present');
</script>
================================================
FILE: docs/assets/scripts/code-previews.js
================================================
(() => {
function convertModuleLinks(html) {
html = html
.replace(/@shoelace-style\/shoelace/g, `https://esm.sh/@shoelace-style/shoelace@${shoelaceVersion}`)
.replace(/from 'react'/g, `from 'https://esm.sh/react@${reactVersion}'`)
.replace(/from "react"/g, `from "https://esm.sh/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');
if (script.type === 'module') {
newScript.type = 'module';
newScript.textContent = script.innerHTML;
} else {
newScript.appendChild(document.createTextNode(`(() => { ${script.innerHTML} })();`));
}
script.parentNode.replaceChild(newScript, script);
}
function getFlavor() {
return sessionStorage.getItem('flavor') || 'html';
}
function setFlavor(newFlavor) {
flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html';
sessionStorage.setItem('flavor', flavor);
// Set the flavor class on the body
document.documentElement.classList.toggle('flavor-html', flavor === 'html');
document.documentElement.classList.toggle('flavor-react', flavor === 'react');
}
function syncFlavor() {
setFlavor(getFlavor());
document.querySelectorAll('.code-preview__button--html').forEach(preview => {
if (flavor === 'html') {
preview.classList.add('code-preview__button--selected');
}
});
document.querySelectorAll('.code-preview__button--react').forEach(preview => {
if (flavor === 'react') {
preview.classList.add('code-preview__button--selected');
}
});
}
const shoelaceVersion = document.documentElement.getAttribute('data-shoelace-version');
const reactVersion = '^18';
const cdndir = 'cdn';
const npmdir = 'dist';
let flavor = getFlavor();
let count = 1;
// We need the version to open
if (!shoelaceVersion) {
throw new Error('The data-shoelace-version attribute is missing from <html>.');
}
// Sync flavor UI on page load
syncFlavor();
//
// Resizing previews
//
document.addEventListener('mousedown', handleResizerDrag);
document.addEventListener('touchstart', handleResizerDrag, { passive: true });
function handleResizerDrag(event) {
const resizer = event.target.closest('.code-preview__resizer');
const preview = event.target.closest('.code-preview__preview');
if (!resizer || !preview) return;
let startX = event.changedTouches ? event.changedTouches[0].pageX : event.clientX;
let startWidth = parseInt(document.defaultView.getComputedStyle(preview).width, 10);
event.preventDefault();
preview.classList.add('code-preview__preview--dragging');
document.documentElement.addEventListener('mousemove', dragMove);
document.documentElement.addEventListener('touchmove', dragMove);
document.documentElement.addEventListener('mouseup', dragStop);
document.documentElement.addEventListener('touchend', dragStop);
function dragMove(event) {
const width = startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX;
preview.style.width = `${width}px`;
}
function dragStop() {
preview.classList.remove('code-preview__preview--dragging');
document.documentElement.removeEventListener('mousemove', dragMove);
document.documentElement.removeEventListener('touchmove', dragMove);
document.documentElement.removeEventListener('mouseup', dragStop);
document.documentElement.removeEventListener('touchend', dragStop);
}
}
//
// Toggle source mode
//
document.addEventListener('click', event => {
const button = event.target.closest('.code-preview__button');
const codeBlock = button?.closest('.code-preview');
if (button?.classList.contains('code-preview__button--html')) {
// Show HTML
setFlavor('html');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-preview__button--react')) {
// Show React
setFlavor('react');
toggleSource(codeBlock, true);
} else if (button?.classList.contains('code-preview__toggle')) {
// Toggle source
toggleSource(codeBlock);
} else {
return;
}
// Update flavor buttons
[...document.querySelectorAll('.code-preview')].forEach(cb => {
cb.querySelector('.code-preview__button--html')?.classList.toggle(
'code-preview__button--selected',
flavor === 'html'
);
cb.querySelector('.code-preview__button--react')?.classList.toggle(
'code-preview__button--selected',
flavor === 'react'
);
});
});
function toggleSource(codeBlock, force) {
codeBlock.classList.toggle('code-preview--expanded', force);
event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-preview--expanded'));
}
//
// Open in CodePen
//
document.addEventListener('click', event => {
const button = event.target.closest('button');
if (button?.classList.contains('code-preview__button--codepen')) {
const codeBlock = button.closest('.code-preview');
const htmlExample = codeBlock.querySelector('.code-preview__source--html > pre > code')?.textContent;
const reactExample = codeBlock.querySelector('.code-preview__source--react > pre > code')?.textContent;
const isReact = flavor === 'react' && typeof reactExample === 'string';
const theme = document.documentElement.classList.contains('sl-theme-dark') ? 'dark' : 'light';
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const isDark = theme === 'dark' || (theme === 'auto' && prefersDark);
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@${shoelaceVersion}/${cdndir}/shoelace.js"></script>\n` +
`\n${htmlExample}`;
jsTemplate = '';
}
// React templates
if (isReact) {
htmlTemplate = '<div id="root"></div>';
jsTemplate =
`import React from 'https://esm.sh/react@${reactVersion}';\n` +
`import ReactDOM from 'https://esm.sh/react-dom@${reactVersion}';\n` +
`import { setBasePath } from 'https://esm.sh/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/utilities/base-path';\n` +
`\n` +
`// Set the base path for Shoelace assets\n` +
`setBasePath('https://esm.sh/@shoelace-style/shoelace@${shoelaceVersion}/${npmdir}/')\n` +
`\n${convertModuleLinks(reactExample)}\n` +
`\n` +
`ReactDOM.render(<App />, document.getElementById('root'));`;
}
// CSS templates
cssTemplate =
`@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${shoelaceVersion}/${cdndir}/themes/${
isDark ? 'dark' : 'light'
}.css';\n` +
'\n' +
'body {\n' +
' font: 16px sans-serif;\n' +
' background-color: var(--sl-color-neutral-0);\n' +
' color: var(--sl-color-neutral-900);\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">`,
html_classes: `sl-theme-${isDark ? 'dark' : 'light'}`,
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.documentElement.append(form);
form.submit();
form.remove();
}
});
// Set the initial flavor
window.addEventListener('turbo:load', syncFlavor);
})();
================================================
FILE: docs/assets/scripts/docs.js
================================================
//
// Sidebar
//
// When the sidebar is hidden, we apply the inert attribute to prevent focus from reaching it. Due to the many states
// the sidebar can have (e.g. static, hidden, expanded), we test for visibility by checking to see if it's placed
// offscreen or not. Then, on resize/transition we make sure to update the attribute accordingly.
//
(() => {
function getSidebar() {
return document.getElementById('sidebar');
}
function isSidebarOpen() {
return document.documentElement.classList.contains('sidebar-open');
}
function isSidebarVisible() {
return getSidebar().getBoundingClientRect().x >= 0;
}
function toggleSidebar(force) {
const isOpen = typeof force === 'boolean' ? force : !isSidebarOpen();
return document.documentElement.classList.toggle('sidebar-open', isOpen);
}
function updateInert() {
getSidebar().inert = !isSidebarVisible();
}
// Toggle the menu
document.addEventListener('click', event => {
const menuToggle = event.target.closest('#menu-toggle');
if (!menuToggle) return;
toggleSidebar();
});
// Update the sidebar's inert state when the window resizes and when the sidebar transitions
window.addEventListener('resize', () => toggleSidebar(false));
document.addEventListener('transitionend', event => {
const sidebar = event.target.closest('#sidebar');
if (!sidebar) return;
updateInert();
});
// Close when a menu item is selected on mobile
document.addEventListener('click', event => {
const sidebar = event.target.closest('#sidebar');
const link = event.target.closest('a');
if (!sidebar || !link) return;
if (isSidebarOpen()) {
toggleSidebar();
}
});
// Close when open and escape is pressed
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && isSidebarOpen()) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
// Close when clicking outside of the sidebar
document.addEventListener('mousedown', event => {
if (isSidebarOpen() & !event.target?.closest('#sidebar, #menu-toggle')) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
updateInert();
})();
//
// Theme selector
//
(() => {
function getTheme() {
return localStorage.getItem('theme') || 'auto';
}
function isDark() {
if (theme === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
return theme === 'dark';
}
function setTheme(newTheme) {
theme = newTheme;
localStorage.setItem('theme', theme);
// Update the UI
updateSelection();
// Toggle the dark mode class
document.documentElement.classList.toggle('sl-theme-dark', isDark());
}
function updateSelection() {
const menu = document.querySelector('#theme-selector sl-menu');
if (!menu) return;
[...menu.querySelectorAll('sl-menu-item')].map(item => (item.checked = item.getAttribute('value') === theme));
}
let theme = getTheme();
// Selection is not preserved when changing page, so update when opening dropdown
document.addEventListener('sl-show', event => {
const themeSelector = event.target.closest('#theme-selector');
if (!themeSelector) return;
updateSelection();
});
// Listen for selections
document.addEventListener('sl-select', event => {
const menu = event.target.closest('#theme-selector sl-menu');
if (!menu) return;
setTheme(event.detail.item.value);
});
// Update the theme when the preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTheme(theme));
// Toggle with backslash
document.addEventListener('keydown', event => {
if (
event.key === '\\' &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
setTheme(isDark() ? 'light' : 'dark');
}
});
// Set the initial theme and sync the UI
setTheme(theme);
})();
//
// Open details when printing
//
(() => {
const detailsOpenOnPrint = new Set();
window.addEventListener('beforeprint', () => {
detailsOpenOnPrint.clear();
document.querySelectorAll('details').forEach(details => {
if (details.open) {
detailsOpenOnPrint.add(details);
}
details.open = true;
});
});
window.addEventListener('afterprint', () => {
document.querySelectorAll('details').forEach(details => {
details.open = detailsOpenOnPrint.has(details);
});
detailsOpenOnPrint.clear();
});
})();
//
// Smooth links
//
(() => {
document.addEventListener('click', event => {
const link = event.target.closest('a');
const id = (link?.hash ?? '').substr(1);
const isFragment = link?.hasAttribute('href') && link?.getAttribute('href').startsWith('#');
if (!link || !isFragment || link.getAttribute('data-smooth-link') === 'false') {
return;
}
// Scroll to the top
if (link.hash === '') {
event.preventDefault();
window.scroll({ top: 0, behavior: 'smooth' });
history.pushState(undefined, undefined, location.pathname);
}
// Scroll to an id
if (id) {
const target = document.getElementById(id);
if (target) {
event.preventDefault();
window.scroll({ top: target.offsetTop, behavior: 'smooth' });
history.pushState(undefined, undefined, `#${id}`);
}
}
});
})();
//
// Table of Contents scrollspy
//
(() => {
// This will be stale if its not a function.
const getLinks = () => [...document.querySelectorAll('.content__toc a')];
const linkTargets = new WeakMap();
const visibleTargets = new WeakSet();
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '0px 0px' });
let debounce;
function handleIntersect(entries) {
entries.forEach(entry => {
// Remember which targets are visible
if (entry.isIntersecting) {
visibleTargets.add(entry.target);
} else {
visibleTargets.delete(entry.target);
}
});
updateActiveLinks();
}
function updateActiveLinks() {
const links = getLinks();
// Find the first visible target and activate the respective link
links.find(link => {
const target = linkTargets.get(link);
if (target && visibleTargets.has(target)) {
links.forEach(el => el.classList.toggle('active', el === link));
return true;
}
return false;
});
}
// Observe link targets
function observeLinks() {
getLinks().forEach(link => {
const hash = link.hash.slice(1);
const target = hash ? document.querySelector(`.content__body #${hash}`) : null;
if (target) {
linkTargets.set(link, target);
observer.observe(target);
}
});
}
observeLinks();
document.addEventListener('turbo:load', updateActiveLinks);
document.addEventListener('turbo:load', observeLinks);
})();
//
// Show custom versions in the sidebar
//
(() => {
function updateVersion() {
const el = document.querySelector('.sidebar-version');
if (!el) return;
if (location.hostname === 'next.shoelace.style') el.textContent = 'Next';
if (location.hostname === 'localhost') el.textContent = 'Development';
}
updateVersion();
document.addEventListener('turbo:load', updateVersion);
})();
================================================
FILE: docs/assets/scripts/search.js
================================================
(() => {
// Append the search dialog to the body
const siteSearch = document.createElement('div');
const scrollbarWidth = Math.abs(window.innerWidth - document.documentElement.clientWidth);
siteSearch.classList.add('search');
siteSearch.innerHTML = `
<div class="search__overlay"></div>
<dialog id="search-dialog" class="search__dialog">
<div class="search__content">
<div class="search__header">
<div id="search-combobox" class="search__input-wrapper">
<sl-icon name="search"></sl-icon>
<input
id="search-input"
class="search__input"
type="search"
placeholder="Search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
enterkeyhint="go"
spellcheck="false"
maxlength="100"
role="combobox"
aria-autocomplete="list"
aria-expanded="true"
aria-controls="search-listbox"
aria-haspopup="listbox"
aria-activedescendant
>
<button type="button" class="search__clear-button" aria-label="Clear entry" tabindex="-1" hidden>
<sl-icon name="x-circle-fill"></sl-icon>
</button>
</div>
</div>
<div class="search__body">
<ul
id="search-listbox"
class="search__results"
role="listbox"
aria-label="Search results"
></ul>
<div class="search__empty">No matching pages</div>
</div>
<footer class="search__footer">
<small><kbd>↑</kbd> <kbd>↓</kbd> Navigate</small>
<small><kbd>↲</kbd> Select</small>
<small><kbd>Esc</kbd> Close</small>
</footer>
</div>
</dialog>
`;
const overlay = siteSearch.querySelector('.search__overlay');
const dialog = siteSearch.querySelector('.search__dialog');
const input = siteSearch.querySelector('.search__input');
const clearButton = siteSearch.querySelector('.search__clear-button');
const results = siteSearch.querySelector('.search__results');
const version = document.documentElement.getAttribute('data-shoelace-version');
const key = `search_${version}`;
const searchDebounce = 50;
const animationDuration = 150;
let isShowing = false;
let searchTimeout;
let searchIndex;
let map;
const loadSearchIndex = new Promise(resolve => {
const cache = localStorage.getItem(key);
const wait = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame;
// Cleanup older search indices (everything before this version)
try {
const items = { ...localStorage };
Object.keys(items).forEach(k => {
if (key > k) {
localStorage.removeItem(k);
}
});
} catch {
/* do nothing */
}
// Look for a cached index
try {
if (cache) {
const data = JSON.parse(cache);
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
return resolve();
}
} catch {
/* do nothing */
}
// Wait until idle to fetch the index
wait(() => {
fetch('/assets/search.json')
.then(res => res.json())
.then(data => {
if (!window.lunr) {
console.error('The Lunr search client has not yet been loaded.');
}
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
// Cache the search index for this version
if (version) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (err) {
console.warn(`Unable to cache the search index: ${err}`);
}
}
resolve();
});
});
});
async function show() {
isShowing = true;
document.body.append(siteSearch);
document.body.classList.add('search-visible');
document.body.style.setProperty('--docs-search-scroll-lock-size', `${scrollbarWidth}px`);
clearButton.hidden = true;
requestAnimationFrame(() => input.focus());
updateResults();
dialog.showModal();
await Promise.all([
dialog.animate(
[
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' },
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
]);
dialog.addEventListener('mousedown', handleMouseDown);
dialog.addEventListener('keydown', handleKeyDown);
}
async function hide() {
isShowing = false;
await Promise.all([
dialog.animate(
[
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' },
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
]);
dialog.close();
input.blur(); // otherwise Safari will scroll to the bottom of the page on close
input.value = '';
document.body.classList.remove('search-visible');
document.body.style.removeProperty('--docs-search-scroll-lock-size');
siteSearch.remove();
updateResults();
dialog.removeEventListener('mousedown', handleMouseDown);
dialog.removeEventListener('keydown', handleKeyDown);
}
function handleInput() {
clearButton.hidden = input.value === '';
// Debounce search queries
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
}
function handleClear() {
clearButton.hidden = true;
input.value = '';
input.focus();
updateResults();
}
function handleMouseDown(event) {
if (!event.target.closest('.search__content')) {
hide();
}
}
function handleKeyDown(event) {
// Close when pressing escape
if (event.key === 'Escape') {
event.preventDefault(); // prevent <dialog> from closing immediately so it can animate
event.stopImmediatePropagation();
hide();
return;
}
// Handle keyboard selections
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
event.preventDefault();
const currentEl = results.querySelector('[data-selected="true"]');
const items = [...results.querySelectorAll('li')];
const index = items.indexOf(currentEl);
let nextEl;
if (items.length === 0) {
return;
}
switch (event.key) {
case 'ArrowUp':
nextEl = items[Math.max(0, index - 1)];
break;
case 'ArrowDown':
nextEl = items[Math.min(items.length - 1, index + 1)];
break;
case 'Home':
nextEl = items[0];
break;
case 'End':
nextEl = items[items.length - 1];
break;
case 'Enter':
currentEl?.querySelector('a')?.click();
break;
}
// Update the selected item
items.forEach(item => {
if (item === nextEl) {
input.setAttribute('aria-activedescendant', item.id);
item.setAttribute('data-selected', 'true');
nextEl.scrollIntoView({ block: 'nearest' });
} else {
item.setAttribute('data-selected', 'false');
}
});
}
}
async function updateResults(query = '') {
try {
await loadSearchIndex;
const hasQuery = query.length > 0;
const searchTerms = query
.split(' ')
.map((term, index, arr) => {
// Search API: https://lunrjs.com/guides/searching.html
if (index === arr.length - 1) {
// The last term is not mandatory and 1x fuzzy. We also duplicate it with a wildcard to match partial words
// as the user types.
return `${term}~1 ${term}*`;
} else {
// All other terms are mandatory and 1x fuzzy
return `+${term}~1`;
}
})
.join(' ');
const matches = hasQuery ? searchIndex.search(searchTerms) : [];
const hasResults = hasQuery && matches.length > 0;
siteSearch.classList.toggle('search--has-results', hasQuery && hasResults);
siteSearch.classList.toggle('search--no-results', hasQuery && !hasResults);
input.setAttribute('aria-activedescendant', '');
results.innerHTML = '';
matches.forEach((match, index) => {
const page = map[match.ref];
const li = document.createElement('li');
const a = document.createElement('a');
const displayTitle = page.title ?? '';
const displayDescription = page.description ?? '';
const displayUrl = page.url.replace(/^\//, '').replace(/\/$/, '');
let icon = 'file-text';
a.setAttribute('role', 'option');
a.setAttribute('id', `search-result-item-${match.ref}`);
if (page.url.includes('getting-started/')) {
icon = 'lightbulb';
}
if (page.url.includes('resources/')) {
icon = 'book';
}
if (page.url.includes('components/')) {
icon = 'puzzle';
}
if (page.url.includes('tokens/')) {
icon = 'palette2';
}
if (page.url.includes('utilities/')) {
icon = 'wrench';
}
if (page.url.includes('tutorials/')) {
icon = 'joystick';
}
li.classList.add('search__result');
li.setAttribute('role', 'option');
li.setAttribute('id', `search-result-item-${match.ref}`);
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
a.href = page.url;
a.innerHTML = `
<div class="search__result-icon" aria-hidden="true">
<sl-icon name="${icon}"></sl-icon>
</div>
<div class="search__result__details">
<div class="search__result-title"></div>
<div class="search__result-description"></div>
<div class="search__result-url"></div>
</div>
`;
a.querySelector('.search__result-title').textContent = displayTitle;
a.querySelector('.search__result-description').textContent = displayDescription;
a.querySelector('.search__result-url').textContent = displayUrl;
li.appendChild(a);
results.appendChild(li);
});
} catch {
// Ignore query errors as the user types
}
}
// Show the search dialog when clicking on data-plugin="search"
document.addEventListener('click', event => {
const searchButton = event.target.closest('[data-plugin="search"]');
if (searchButton) {
show();
}
});
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
document.addEventListener('keydown', event => {
if (
!isShowing &&
(event.key === '/' || (event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
show();
}
});
// Purge cache when we press CMD+CTRL+R
document.addEventListener('keydown', event => {
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'r') {
localStorage.clear();
}
});
input.addEventListener('input', handleInput);
clearButton.addEventListener('click', handleClear);
// Close when a result is selected
results.addEventListener('click', event => {
if (event.target.closest('a')) {
hide();
}
});
// We're using Turbo, so when a user searches for something, visits a result, and presses the back button, the search
// UI will still be visible but not interactive. This removes the search UI when Turbo renders a page so they don't
// get trapped.
window.addEventListener('turbo:render', () => {
document.body.classList.remove('search-visible');
document.querySelectorAll('.search__overlay, .search__dialog').forEach(el => el.remove());
});
})();
================================================
FILE: docs/assets/scripts/turbo.js
================================================
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.10/+esm';
(() => {
if (!window.scrollPositions) {
window.scrollPositions = {};
}
function preserveScroll() {
document.querySelectorAll('[data-preserve-scroll]').forEach(element => {
scrollPositions[element.id] = element.scrollTop;
});
}
function restoreScroll(event) {
document.querySelectorAll('[data-preserve-scroll]').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
if (event.detail && event.detail.newBody) {
event.detail.newBody.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
}
}
window.addEventListener('turbo:before-cache', preserveScroll);
window.addEventListener('turbo:before-render', restoreScroll);
window.addEventListener('turbo:render', restoreScroll);
})();
================================================
FILE: docs/assets/styles/code-previews.css
================================================
/* Interactive code blocks */
.code-preview {
position: relative;
border-radius: 3px;
background-color: var(--sl-color-neutral-50);
margin-bottom: 1.5rem;
}
.code-preview__preview {
position: relative;
border: solid 1px var(--sl-color-neutral-200);
border-bottom: none;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background-color: var(--sl-color-neutral-0);
min-width: 20rem;
max-width: 100%;
padding: 1.5rem 3.25rem 1.5rem 1.5rem;
}
/* Block the preview while dragging to prevent iframes from intercepting drag events */
.code-preview__preview--dragging:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: ew-resize;
}
.code-preview__resizer {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 1.75rem;
font-size: 20px;
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;
}
@media screen and (max-width: 600px) {
.code-preview__preview {
padding-right: 1.5rem;
}
.code-preview__resizer {
display: none;
}
}
.code-preview__source {
border: solid 1px var(--sl-color-neutral-200);
border-bottom: none;
border-radius: 0 !important;
display: none;
}
.code-preview--expanded .code-preview__source {
display: block;
}
.code-preview__source pre {
margin: 0;
}
.code-preview__buttons {
position: relative;
border: solid 1px var(--sl-color-neutral-200);
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
display: flex;
}
.code-preview__button {
flex: 0 0 auto;
height: 2.5rem;
min-width: 2.5rem;
border: none;
border-radius: 0;
background: var(--sl-color-neutral-0);
font: inherit;
font-size: 0.7rem;
font-weight: 500;
text-transform: uppercase;
color: var(--sl-color-neutral-600);
padding: 0 1rem;
cursor: pointer;
}
.code-preview__button:not(:last-of-type) {
border-right: solid 1px var(--sl-color-neutral-200);
}
.code-preview__button--html,
.code-preview__button--react {
width: 70px;
display: flex;
place-items: center;
justify-content: center;
}
.code-preview__button--selected {
font-weight: 700;
color: var(--sl-color-primary-600);
}
.code-preview__button--codepen {
display: flex;
place-items: center;
width: 6rem;
}
.code-preview__button:first-of-type {
border-bottom-left-radius: 3px;
}
.code-preview__button:last-of-type {
border-bottom-right-radius: 3px;
}
.code-preview__button:hover,
.code-preview__button:active {
box-shadow: 0 0 0 1px var(--sl-color-primary-400);
border-right-color: transparent;
background-color: var(--sl-color-primary-50);
color: var(--sl-color-primary-600);
z-index: 1;
}
.code-preview__button:focus-visible {
outline: none;
outline: var(--sl-focus-ring);
z-index: 2;
}
.code-preview__toggle {
position: relative;
display: flex;
flex: 1 1 auto;
align-items: center;
justify-content: center;
width: 100%;
color: var(--sl-color-neutral-600);
cursor: pointer;
}
.code-preview__toggle svg {
width: 1em;
height: 1em;
margin-left: 0.25rem;
}
.code-preview--expanded .code-preview__toggle svg {
transform: rotate(180deg);
}
/* We can apply data-flavor="html|react" to any element on the page to toggle it when the flavor changes */
.flavor-html [data-flavor]:not([data-flavor='html']) {
display: none;
}
.flavor-react [data-flavor]:not([data-flavor='react']) {
display: none;
}
================================================
FILE: docs/assets/styles/docs.css
================================================
:root {
--docs-background-color: var(--sl-color-neutral-0);
--docs-border-color: var(--sl-color-neutral-200);
--docs-border-width: 1px;
--docs-border-radius: var(--sl-border-radius-medium);
--docs-content-max-width: 860px;
--docs-sidebar-width: 320px;
--docs-sidebar-transition-speed: 250ms;
--docs-content-toc-max-width: 260px;
--docs-content-padding: 2rem;
--docs-content-vertical-spacing: 2rem;
--docs-search-overlay-background: rgb(0 0 0 / 0.2);
--docs-skip-to-main-width: 200px;
}
/* Light theme */
:root {
color-scheme: normal;
--sl-color-primary-50: var(--sl-color-sky-50);
--sl-color-primary-100: var(--sl-color-sky-100);
--sl-color-primary-200: var(--sl-color-sky-200);
--sl-color-primary-300: var(--sl-color-sky-300);
--sl-color-primary-400: var(--sl-color-sky-400);
--sl-color-primary-500: var(--sl-color-sky-500);
--sl-color-primary-600: var(--sl-color-sky-600);
--sl-color-primary-700: var(--sl-color-sky-700);
--sl-color-primary-800: var(--sl-color-sky-800);
--sl-color-primary-900: var(--sl-color-sky-900);
--sl-color-primary-950: var(--sl-color-sky-950);
--docs-overlay-color: hsl(240 3.8% 46.1% / 33%);
--docs-shadow-x-small: 0 1px 2px hsl(240 3.8% 46.1% / 12%);
--docs-shadow-small: 0 1px 2px hsl(240 3.8% 46.1% / 24%);
--docs-shadow-medium: 0 2px 4px hsl(240 3.8% 46.1% / 24%);
--docs-shadow-large: 0 2px 8px hsl(240 3.8% 46.1% / 24%);
--docs-shadow-x-large: 0 4px 16px hsl(240 3.8% 46.1% / 24%);
}
/* Dark theme */
.sl-theme-dark {
color-scheme: dark;
--docs-overlay-color: hsl(0 0% 0% / 66%);
--docs-shadow-x-small: 0 1px 2px rgb(0 0 0 / 36%);
--docs-shadow-small: 0 1px 2px rgb(0 0 0 / 48%);
--docs-shadow-medium: 0 2px 4px rgb(0 0 0 / 48%);
--docs-shadow-large: 0 2px 8px rgb(0 0 0 / 48%);
--docs-shadow-x-large: 0 4px 16px rgb(0 0 0 / 48%);
}
/* Utils */
html.sl-theme-dark .only-light,
html:not(.sl-theme-dark) .only-dark {
display: none !important;
}
.visually-hidden:not(:focus-within) {
position: absolute !important;
width: 1px !important;
height: 1px !important;
clip: rect(0 0 0 0) !important;
clip-path: inset(50%) !important;
border: none !important;
overflow: hidden !important;
white-space: nowrap !important;
padding: 0 !important;
}
.nowrap {
white-space: nowrap;
}
@media screen and (max-width: 900px) {
:root {
--docs-content-padding: 1rem;
}
}
/* Bare styles */
*,
*:before,
*:after {
box-sizing: inherit;
}
a:focus,
button:focus {
outline: none;
}
a:focus-visible,
button:focus-visible {
outline: var(--sl-focus-ring);
outline-offset: var(--sl-focus-ring-offset);
}
::selection {
background-color: var(--sl-color-primary-200);
color: var(--sl-color-neutral-900);
}
/* Show custom elements only after they're registered */
:not(:defined),
:not(:defined) * {
opacity: 0;
}
:defined {
opacity: 1;
transition: 0.1s opacity;
}
html {
height: 100%;
box-sizing: border-box;
line-height: var(--sl-line-height-normal);
padding: 0;
margin: 0;
}
body {
height: 100%;
font: 16px var(--sl-font-sans);
font-weight: var(--sl-font-weight-normal);
background-color: var(--docs-background-color);
line-height: var(--sl-line-height-normal);
color: var(--sl-color-neutral-900);
padding: 0;
margin: 0;
overflow-x: hidden;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
/* Common elements */
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--sl-font-sans);
font-weight: var(--sl-font-weight-semibold);
margin: 3rem 0 1.5rem 0;
}
h1:first-of-type {
margin-top: 1rem;
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 1.875rem;
border-bottom: solid var(--docs-border-width) var(--docs-border-color);
}
h3 {
font-size: 1.625rem;
}
h4 {
font-size: 1.375rem;
}
h5 {
font-size: 1.125rem;
}
h6 {
font-size: 0.875rem;
}
p {
margin-block-end: 1.5em;
}
img {
max-width: 100%;
}
.badges img {
border-radius: var(--sl-border-radius-medium);
}
.callout img,
details img {
width: 100%;
margin-left: 0;
margin-right: 0;
}
details pre {
border: solid var(--docs-border-width) var(--docs-border-color);
}
a {
color: var(--sl-color-primary-700);
}
a:hover {
color: var(--sl-color-primary-800);
}
abbr[title] {
text-decoration: none;
border-bottom: dashed 1px var(--sl-color-primary-700);
cursor: help;
}
em {
font-style: italic;
}
strong {
font-weight: var(--sl-font-weight-bold);
}
code {
font-family: var(--sl-font-mono);
font-size: 0.9125em;
background-color: rgba(0 0 0 / 0.025);
background-blend-mode: darken;
border-radius: var(--docs-border-radius);
padding: 0.125em 0.25em;
}
.sl-theme-dark code {
background-color: rgba(255 255 255 / 0.03);
}
kbd {
background: var(--sl-color-neutral-100);
border: solid 1px var(--sl-color-neutral-200);
box-shadow:
inset 0 1px 0 0 var(--sl-color-neutral-0),
inset 0 -1px 0 0 var(--sl-color-neutral-200);
font-family: var(--sl-font-mono);
font-size: 0.9125em;
border-radius: var(--docs-border-radius);
color: var(--sl-color-neutral-800);
padding: 0.125em 0.4em;
}
ins {
background-color: var(--sl-color-green-200);
color: var(--sl-color-green-900);
border-radius: var(--docs-border-radius);
text-decoration: none;
padding: 0.125em 0.25em;
}
s {
background-color: var(--sl-color-red-200);
color: var(--sl-color-red-900);
border-radius: var(--docs-border-radius);
text-decoration: none;
padding: 0.125em 0.25em;
}
mark {
background-color: var(--sl-color-yellow-200);
border-radius: var(--docs-border-radius);
padding: 0.125em 0.25em;
}
hr {
border: none;
border-bottom: solid var(--docs-border-width) var(--docs-border-color);
margin: calc(var(--docs-content-vertical-spacing) * 2) 0;
}
/* Block quotes */
blockquote {
position: relative;
font-family: var(--sl-font-serif);
font-size: 1.33rem;
font-style: italic;
color: var(--sl-color-neutral-700);
background-color: var(--sl-color-neutral-100);
border-radius: var(--docs-border-radius);
border-left: solid 6px var(--sl-color-neutral-300);
padding: 1.5rem;
margin: 0 0 1.5rem 0;
}
blockquote > :first-child {
margin-top: 0;
}
blockquote > :last-child {
margin-bottom: 0;
}
/* Lists */
ul,
ol {
padding: 0;
margin: 0 0 var(--docs-content-vertical-spacing) 2rem;
}
ul {
list-style: disc;
}
li {
padding: 0;
margin: 0 0 0.25rem 0;
}
li ul,
li ol {
margin-top: 0.25rem;
}
ul ul:last-child,
ul ol:last-child,
ol ul:last-child,
ol ol:last-child {
margin-bottom: 0;
}
/* Anchor headings */
.anchor-heading {
position: relative;
color: inherit;
text-decoration: none;
}
.anchor-heading a {
text-decoration: none;
color: inherit;
}
.anchor-heading a::after {
content: '#';
color: var(--sl-color-primary-700);
margin-inline: 0.5rem;
opacity: 0;
transition: 100ms opacity;
}
.anchor-heading:hover a::after,
.anchor-heading:focus-within a::after {
opacity: 1;
}
/* External links */
.external-link__icon {
width: 0.75em;
height: 0.75em;
vertical-align: 0;
margin-left: 0.25em;
margin-right: 0.125em;
}
/* Tables */
table {
max-width: 100%;
border: none;
border-collapse: collapse;
color: inherit;
margin-bottom: var(--docs-content-vertical-spacing);
}
table tr {
border-bottom: solid var(--docs-border-width) var(--docs-border-color);
}
table th {
font-weight: var(--sl-font-weight-semibold);
text-align: left;
}
table td,
table th {
line-height: var(--sl-line-height-normal);
padding: 1rem 1rem;
}
table th p:first-child,
table td p:first-child {
margin-top: 0;
}
table th p:last-child,
table td p:last-child {
margin-bottom: 0;
}
.table-scroll {
max-width: 100%;
overflow-x: auto;
}
.table-scroll code {
white-space: nowrap;
}
th.table-name,
th.table-event-detail {
min-width: 15ch;
}
th.table-description {
min-width: 50ch !important;
max-width: 70ch;
}
/* Code blocks */
pre {
position: relative;
background-color: var(--sl-color-neutral-50);
font-family: var(--sl-font-mono);
color: var(--sl-color-neutral-900);
border-radius: var(--docs-border-radius);
padding: 1rem;
white-space: pre;
}
.sl-theme-dark pre {
background-color: var(--sl-color-neutral-50);
}
pre:not(:last-child) {
margin-bottom: 1.5rem;
}
pre > code {
display: block;
background: none !important;
border-radius: 0;
hyphens: none;
tab-size: 2;
white-space: pre;
padding: 1rem;
margin: -1rem;
overflow: auto;
}
pre .token.comment {
color: var(--sl-color-neutral-600);
}
pre .token.prolog,
pre .token.doctype,
pre .token.cdata,
pre .token.operator,
pre .token.punctuation {
color: var(--sl-color-neutral-700);
}
.namespace {
opacity: 0.7;
}
pre .token.property,
pre .token.keyword,
pre .token.tag,
pre .token.url {
color: var(--sl-color-blue-700);
}
pre .token.symbol,
pre .token.deleted {
color: var(--sl-color-red-700);
}
pre .token.boolean,
pre .token.constant,
pre .token.selector,
pre .token.attr-name,
pre .token.string,
pre .token.char,
pre .token.builtin,
pre .token.inserted {
color: var(--sl-color-emerald-700);
}
pre .token.atrule,
pre .token.attr-value,
pre .token.number,
pre .token.variable {
color: var(--sl-color-violet-700);
}
pre .token.function,
pre .token.class-name,
pre .token.regex {
color: var(--sl-color-orange-700);
}
pre .token.important {
color: var(--sl-color-red-700);
}
pre .token.important,
pre .token.bold {
font-weight: bold;
}
pre .token.italic {
font-style: italic;
}
/* Copy code button */
.copy-code-button {
position: absolute;
top: 0;
right: 0;
white-space: normal;
color: var(--sl-color-neutral-800);
transition:
150ms opacity,
150ms scale;
}
.copy-code-button::part(button) {
background-color: var(--sl-color-neutral-50);
border-radius: 0 var(--docs-border-radius) 0 var(--docs-border-radius);
padding: 0.75rem;
}
.copy-code-button::part(button):hover {
background-color: color-mix(in oklch, var(--sl-color-neutral-50), var(--sl-color-neutral-1000) 3%);
}
.copy-code-button::part(button):active {
background-color: color-mix(in oklch, var(--sl-color-neutral-50), var(--sl-color-neutral-1000) 6%);
}
pre .copy-code-button {
opacity: 0;
scale: 0.75;
}
pre:hover .copy-code-button,
.copy-code-button:focus-within {
opacity: 1;
scale: 1;
}
/* Callouts */
.callout {
position: relative;
background-color: var(--sl-color-neutral-100);
border-left: solid 4px var(--sl-color-neutral-500);
border-radius: var(--docs-border-radius);
color: var(--sl-color-neutral-800);
padding: 1.5rem 1.5rem 1.5rem 2rem;
margin-bottom: var(--docs-content-vertical-spacing);
}
.callout > :first-child {
margin-top: 0;
}
.callout > :last-child {
margin-bottom: 0;
}
.callout--tip {
background-color: var(--sl-color-primary-100);
border-left-color: var(--sl-color-primary-600);
color: var(--sl-color-primary-800);
}
.callout::before {
content: '';
position: absolute;
top: calc(50% - 0.8rem);
left: calc(-0.8rem - 2px);
width: 1.6rem;
height: 1.6rem;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--sl-font-serif);
font-weight: var(--sl-font-weight-bold);
color: var(--sl-color-neutral-0);
clip-path: circle(50% at 50% 50%);
}
.callout--tip::before {
content: 'i';
font-style: italic;
background-color: var(--sl-color-primary-600);
}
.callout--warning {
background-color: var(--sl-color-warning-100);
border-left-color: var(--sl-color-warning-600);
color: var(--sl-color-warning-800);
}
.callout--warning::before {
content: '!';
background-color: var(--sl-color-warning-600);
}
.callout--danger {
background-color: var(--sl-color-danger-100);
border-left-color: var(--sl-color-danger-600);
color: var(--sl-color-danger-800);
}
.callout--danger::before {
content: '‼';
background-color: var(--sl-color-danger-600);
}
.callout + .callout {
margin-top: calc(-0.5 * var(--docs-content-vertical-spacing));
}
.callout a {
color: inherit;
}
/* Aside */
.content aside {
float: right;
min-width: 300px;
max-width: 50%;
background: var(--sl-color-neutral-100);
border-radius: var(--docs-border-radius);
padding: 1rem;
margin-left: 1rem;
}
.content aside > :first-child {
margin-top: 0;
}
.content aside > :last-child {
margin-bottom: 0;
}
@media screen and (max-width: 600px) {
.content aside {
float: none;
width: calc(100% + (var(--docs-content-padding) * 2));
max-width: none;
margin: var(--docs-content-vertical-spacing) calc(-1 * var(--docs-content-padding));
}
}
/* Details */
.content details {
background-color: var(--sl-color-neutral-100);
border-radius: var(--docs-border-radius);
padding: 1rem;
margin: 0 0 var(--docs-content-vertical-spacing) 0;
}
.content details summary {
font-weight: var(--sl-font-weight-semibold);
border-radius: var(--docs-border-radius);
padding: 1rem;
margin: -1rem;
cursor: pointer;
user-select: none;
-webkit-user-select: none;
}
.content details summary span {
padding-left: 0.5rem;
}
.content details[open] summary {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin-bottom: 1rem;
}
.content details[open] summary:focus-visible {
border-radius: var(--docs-border-radius);
}
.content details > :last-child {
margin-bottom: 0;
}
.content details > :nth-child(2) {
margin-top: 0;
}
.content details + details {
margin-top: calc(-1 * var(--docs-content-vertical-spacing) + (2 * var(--docs-border-width)));
}
/* Sidebar */
#sidebar {
position: fixed;
flex: 0;
top: 0;
left: 0;
bottom: 0;
z-index: 20;
width: var(--docs-sidebar-width);
background-color: var(--docs-background-color);
border-right: solid var(--docs-border-width) var(--docs-border-color);
border-radius: 0;
padding: 2rem;
margin: 0;
overflow: auto;
scrollbar-width: thin;
transition: var(--docs-sidebar-transition-speed) translate ease-in-out;
}
#sidebar::-webkit-scrollbar {
width: 4px;
}
#sidebar::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 9999px;
}
#sidebar:hover::-webkit-scrollbar-thumb {
background: var(--sl-color-neutral-400);
}
#sidebar:hover::-webkit-scrollbar-track {
background: var(--sl-color-neutral-100);
}
#sidebar > header {
margin-bottom: 1.5rem;
}
#sidebar > header h1 {
margin: 0;
}
#sidebar > header a {
display: block;
}
#sidebar nav a {
text-decoration: none;
}
#sidebar nav h2 {
font-size: var(--sl-font-size-medium);
font-weight: var(--sl-font-weight-semibold);
border-bottom: solid var(--docs-border-width) var(--docs-border-color);
margin: 1.5rem 0 0.5rem 0;
}
#sidebar ul {
padding: 0;
margin: 0;
}
#sidebar ul li {
list-style: none;
padding: 0;
margin: 0.125rem 0.5rem;
}
#sidebar ul ul ul {
margin-left: 0.75rem;
}
#sidebar ul li a {
line-height: 1.33;
color: inherit;
display: inline-block;
padding: 0;
}
#sidebar ul li a:not(.active-link):hover {
color: var(--sl-color-primary-700);
}
#sidebar nav .active-link {
color: var(--sl-color-primary-700);
border-bottom: dashed 1px var(--sl-color-primary-700);
}
#sidebar > header img {
display: block;
width: 100%;
height: auto;
margin: 0 auto;
}
@media screen and (max-width: 900px) {
#sidebar {
translate: -100%;
}
.sidebar-open #sidebar {
translate: 0;
}
}
.sidebar-version {
font-size: var(--sl-font-size-x-small);
color: var(--sl-color-neutral-500);
text-align: right;
margin-top: -0.5rem;
margin-right: 1rem;
margin-bottom: -0.5rem;
}
.sidebar-buttons {
display: flex;
justify-content: space-between;
}
/* Main content */
main {
position: relative;
padding: var(--docs-content-vertical-spacing) var(--docs-content-padding)
calc(var(--docs-content-vertical-spacing) * 2) var(--docs-content-padding);
margin-left: var(--docs-sidebar-width);
}
.sidebar-open .content {
margin-left: 0;
}
.content__body > :last-child {
margin-bottom: 0;
}
@media screen and (max-width: 900px) {
main {
margin-left: 0;
}
}
/* Component layouts */
.content {
display: grid;
grid-template-columns: 100%;
gap: 2rem;
position: relative;
max-width: var(--docs-content-max-width);
margin: 0 auto;
}
.content--with-toc {
/* There's a 2rem gap, so we need to remove it from the column */
grid-template-columns: calc(75% - 2rem) min(25%, var(--docs-content-toc-max-width));
max-width: calc(var(--docs-content-max-width) + var(--docs-content-toc-max-width));
}
.content__body {
order: 1;
width: 100%;
}
.content:not(.content--with-toc) .content__toc {
display: none;
}
.content__toc {
order: 2;
display: flex;
flex-direction: column;
margin-top: 0;
}
.content__toc ul {
position: sticky;
top: 5rem;
max-height: calc(100vh - 6rem);
font-size: var(--sl-font-size-small);
line-height: 1.33;
border-left: solid 1px var(--sl-color-neutral-200);
list-style: none;
padding: 1rem 0;
margin: 0;
padding-left: 1rem;
overflow-y: auto;
}
.content__toc li {
padding: 0 0 0 0.5rem;
margin: 0;
}
.content__toc li[data-level='3'] {
margin-left: 1rem;
}
/* We don't use them, but just in case */
.content__toc li[data-level='4'],
.content__toc li[data-level='5'],
.content__toc li[data-level='6'] {
margin-left: 2rem;
}
.content__toc li:not(:last-of-type) {
margin-bottom: 0.6rem;
}
.content__toc a {
color: var(--sl-color-neutral-700);
text-decoration: none;
}
.content__toc a:hover {
color: var(--sl-color-primary-700);
}
.content__toc a.active {
color: var(--sl-color-primary-700);
border-bottom: dashed 1px var(--sl-color-primary-700);
}
.content__toc .top a {
font-weight: var(--sl-font-weight-semibold);
color: var(--sl-color-neutral-500);
}
@media screen and (max-width: 1024px) {
.content {
grid-template-columns: 100%;
gap: 0;
}
.content__toc {
position: relative;
order: 1;
}
.content__toc ul {
display: flex;
justify-content: start;
gap: 1rem 1.5rem;
position: static;
border: none;
border-bottom: solid 1px var(--sl-color-neutral-200);
border-radius: 0;
padding: 1rem 1.5rem 1rem 0.5rem; /* extra right padding to hide the fade effect */
margin-top: 1rem;
overflow-x: auto;
}
.content__toc ul::after {
content: '';
position: absolute;
top: 0;
bottom: 1rem; /* don't cover the scrollbar */
right: 0;
width: 2rem;
background: linear-gradient(90deg, rgba(0 0 0 / 0) 0%, var(--sl-color-neutral-0) 100%);
}
.content__toc li {
white-space: nowrap;
}
.content__toc li:not(:last-of-type) {
margin-bottom: 0;
}
.content__toc [data-level]:not([data-level='2']) {
display: none;
}
.content__body {
order: 2;
}
}
/* Menu toggle */
#menu-toggle {
display: none;
position: fixed;
z-index: 30;
top: 0.25rem;
left: 0.25rem;
height: auto;
width: auto;
color: black;
border: none;
border-radius: 50%;
background: var(--sl-color-neutral-0);
padding: 0.5rem;
margin: 0;
cursor: pointer;
transition:
250ms scale ease,
250ms rotate ease;
}
@media screen and (max-width: 900px) {
#menu-toggle {
display: flex;
}
}
.sl-theme-dark #menu-toggle {
color: white;
}
#menu-toggle:hover {
scale: 1.1;
}
#menu-toggle svg {
width: 1.25rem;
height: 1.25rem;
}
html.sidebar-open #menu-toggle {
rotate: 180deg;
}
/* Skip to main content */
#skip-to-main {
position: fixed;
top: 0.25rem;
left: calc(50% - var(--docs-skip-to-main-width) / 2);
z-index: 100;
width: var(--docs-skip-to-main-width);
text-align: center;
text-decoration: none;
border-radius: 9999px;
background: var(--sl-color-neutral-0);
color: var(--sl-color-neutral-1000);
padding: 0.5rem;
}
/* Icon toolbar */
#icon-toolbar {
display: flex;
position: fixed;
top: 0;
right: 1rem;
z-index: 10;
background: var(--sl-color-neutral-800);
border-bottom-left-radius: calc(var(--docs-border-radius) * 2);
border-bottom-right-radius: calc(var(--docs-border-radius) * 2);
padding: 0.125rem 0.25rem;
}
#icon-toolbar button,
#icon-toolbar a {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
width: auto;
height: auto;
background: transparent;
border: none;
border-radius: var(--docs-border-radius);
font-size: 1.25rem;
color: var(--sl-color-neutral-0);
text-decoration: none;
padding: 0.5rem;
margin: 0;
cursor: pointer;
}
#theme-selector:not(:defined) {
/* Hide when not defined to prevent extra wide icon toolbar while loading */
display: none;
}
#theme-selector sl-menu {
/* Set an initial size to prevent width being too small when first opening on small screen width */
width: 140px;
}
#theme-selector sl-button {
transition: 250ms scale ease;
}
#theme-selector sl-button::part(base) {
color: var(--sl-color-neutral-0);
}
#theme-selector sl-button::part(label) {
display: flex;
padding: 0.5rem;
}
#theme-selector sl-icon {
font-size: 1.25rem;
}
.sl-theme-dark #theme-selector sl-button::part(base) {
color: var(--sl-color-neutral-1000);
}
.sl-theme-dark #icon-toolbar {
background: var(--sl-color-neutral-200);
}
.sl-theme-dark #icon-toolbar button,
.sl-theme-dark #icon-toolbar a {
color: var(--sl-color-neutral-1000);
}
#icon-toolbar a:not(:last-child),
#icon-toolbar button:not(:last-child) {
margin-right: 0.25rem;
}
@media screen and (max-width: 900px) {
#icon-toolbar {
right: 0;
border-bottom-right-radius: 0;
}
#icon-toolbar button,
#icon-toolbar a {
font-size: 1rem;
padding: 0.5rem;
}
#theme-selector sl-icon {
font-size: 1rem;
}
}
/* Sidebar addons */
#sidebar-addons:not(:empty) {
margin-bottom: var(--docs-content-vertical-spacing);
}
/* Print styles */
@media print {
a:not(.anchor-heading)[href]::after {
content: ' (' attr(href) ')';
}
details,
pre,
.callout {
border: solid var(--docs-border-width) var(--docs-border-color);
}
details summary {
list-style: none;
}
details summary span {
padding-left: 0;
}
details summary::marker,
details summary::-webkit-details-marker {
display: none;
}
.callout::before {
border: solid var(--docs-border-width) var(--docs-border-color);
}
.component-page__navigation,
.copy-code-button,
.code-preview__buttons,
.code-preview__resizer {
display: none !important;
}
.flavor-html .code-preview__source--html,
.flavor-react .code-preview__source--react {
display: block !important;
}
.flavor-html .code-preview__source--html > pre,
.flavor-react .code-preview__source--react > pre {
border: none;
}
.code-preview__source-group {
border-bottom: solid 1px var(--sl-color-neutral-200);
border-bottom-left-radius: var(--sl-border-radius-medium);
border-bottom-right-radius: var(--sl-border-radius-medium);
}
#sidebar {
display: none;
}
#content {
margin-left: 0;
}
#menu-toggle,
#icon-toolbar,
.external-link__icon {
display: none;
}
}
/* Splash */
.splash {
display: flex;
padding-top: 2rem;
}
.splash-start {
min-width: 420px;
}
.splash-start h1 {
font-size: var(--sl-font-size-large);
font-weight: var(--sl-font-weight-normal);
}
.splash li img {
width: 1em;
height: 1em;
vertical-align: -2px;
}
.splash-end {
display: flex;
align-items: flex-end;
width: auto;
padding-left: 1rem;
}
.splash-image {
width: 100%;
height: auto;
}
.splash-logo {
max-width: 22rem;
}
.splash-start h1:first-of-type {
font-size: var(--sl-font-size-large);
margin: 0 0 0.5rem 0;
}
@media screen and (max-width: 1280px) {
.splash {
display: block;
}
.splash-start {
min-width: 0;
padding-bottom: 1rem;
}
.splash-end {
padding: 0;
}
.splash-image {
display: block;
max-width: 400px;
}
/* Shields */
.splash + p {
margin-top: 2rem;
}
}
/* Component headers */
.component-header h1 {
margin-bottom: 0;
}
.component-header__tag {
margin-top: -0.5rem;
margin-bottom: 0.5rem;
}
.component-header__tag code {
background: none;
color: var(--sl-color-neutral-600);
font-size: var(--sl-font-size-large);
padding: 0;
margin: 0;
}
.component-header__info {
margin-bottom: var(--sl-spacing-x-large);
}
.component-summary {
font-size: var(--sl-font-size-large);
line-height: 1.6;
margin: 2rem 0;
}
/* Repo buttons */
.sidebar-buttons {
display: flex;
gap: 0.125rem;
justify-content: space-between;
}
.sidebar-buttons .repo-button {
flex: 1 1 auto;
}
.repo-button--github sl-icon {
color: var(--sl-color-neutral-700);
}
.repo-button--star sl-icon {
color: var(--sl-color-yellow-500);
}
.repo-button--twitter sl-icon {
color: var(--sl-color-sky-500);
}
@media screen and (max-width: 400px) {
:not(.sidebar-buttons) > .repo-button {
width: 100%;
margin-bottom: 1rem;
}
}
body[data-page^='/tokens/'] .table-wrapper td:first-child,
body[data-page^='/tokens/'] .table-wrapper td:first-child code {
white-space: nowrap;
}
/* Border radius demo */
.border-radius-demo {
width: 3rem;
height: 3rem;
background: var(--sl-color-primary-600);
}
/* Transition demo */
.transition-demo {
position: relative;
background: var(--sl-color-neutral-200);
width: 8rem;
height: 2rem;
}
.transition-demo:after {
content: '';
position: absolute;
background-color: var(--sl-color-primary-600);
top: 0;
left: 0;
width: 0;
height: 100%;
transition-duration: inherit;
transition-property: width;
}
.transition-demo:hover:after {
width: 100%;
}
/* Spacing demo */
.spacing-demo {
width: 100px;
background: var(--sl-color-primary-600);
}
/* Elevation demo */
.elevation-demo {
background: transparent;
border-radius: 3px;
width: 4rem;
height: 4rem;
margin: 1rem;
}
/* Color palettes */
.color-palette {
display: grid;
grid-template-columns: 200px repeat(11, 1fr);
gap: 1rem var(--sl-spacing-2x-small);
margin: 2rem 0;
}
.color-palette__name {
font-size: var(--sl-font-size-medium);
font-weight: var(--sl-font-weight-semibold);
grid-template-columns: repeat(11, 1fr);
}
.color-palette__name code {
background: none;
font-size: var(--sl-font-size-x-small);
}
.color-palette__example {
font-size: var(--sl-font-size-x-small);
text-align: center;
}
.color-palette__swatch {
height: 3rem;
border-radius: var(--sl-border-radius-small);
}
.color-palette__swatch--border {
box-shadow: inset 0 0 0 1px var(--sl-color-neutral-300);
}
@media screen and (max-width: 1200px) {
.color-palette {
grid-template-columns: repeat(6, 1fr);
}
.color-palette__name {
grid-column-start: span 6;
}
}
/* custom styling - WA dialog */
#wa-dialog {
--action-color: #cd491c;
--action-color-hover: color-mix(in oklab, var(--action-color), var(--sl-color-neutral-1000) 5%);
}
#wa-dialog a {
color: var(--action-color);
}
#wa-dialog a:hover {
color: var(--action-color-hover);
}
#wa-dialog::part(header) {
display: none;
}
#wa-dialog::part(body) {
padding: 0;
}
#wa-dialog::part(footer) {
background-color: var(--sl-color-gray-50);
font-size: var(--sl-font-size-small);
text-align: start;
}
#wa-dialog::part(panel) {
overflow: clip; /* preserve border-radius of panel w/ footer + billboard backgrounds */
}
#wa-dialog .dialog-body {
padding: var(--sl-spacing-x-large) var(--body-spacing);
}
#wa-dialog .dialog-billboard {
position: relative;
padding: var(--sl-spacing-3x-large) var(--sl-spacing-2x-large);
gap: var(--sl-spacing-medium);
background: linear-gradient(to bottom, #f46a45, #cd491c);
color: var(--sl-color-neutral-0);
}
#wa-dialog .dialog-billboard::after {
content: '';
position: absolute;
inset: 0;
z-index: 0;
background-color: transparent;
background-image: url('/assets/images/wa-bg-pattern.svg');
background-repeat: repeat;
opacity: 0.3;
}
#wa-dialog .dialog-billboard-content {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--sl-spacing-x-small);
}
#wa-dialog .dialog-billboard-content .wa-logo {
inline-size: auto;
max-inline-size: 100%;
block-size: 4rem;
}
#wa-dialog .dialog-billboard-content h2 {
margin-block: 0;
border-bottom: none;
text-align: center;
line-height: 1.4;
font-weight: var(--sl-font-weight-bold);
font-size: var(--sl-font-size-x-large);
}
#wa-dialog p {
margin-block-end: var(--sl-spacing-small);
}
#wa-dialog p:last-of-type {
margin-block-end: 0;
}
#wa-dialog h4,
#wa-dialog p {
margin-block-start: 0;
}
#wa-dialog .dialog-actions {
display: flex;
flex-wrap: wrap;
gap: var(--sl-spacing-x-small);
justify-content: space-between;
align-items: center;
}
#wa-dialog .dialog-actions .wa-ctas {
display: flex;
flex-wrap: wrap;
gap: var(--sl-spacing-x-small);
align-items: center;
}
#wa-dialog sl-divider {
--spacing: var(--sl-spacing-x-large);
}
#wa-dialog .wa-cta::part(base) {
border-color: var(--action-color);
}
#wa-dialog .close-wa-dialog::part(label) {
color: var(--action-color);
}
#wa-dialog .close-wa-dialog::part(label):hover {
color: var(--action-color-hover);
}
#wa-dialog .close-wa-cta::part(label):hover {
color: var(--sl-color-neutral-0);
}
#wa-dialog .wa-cta::part(base) {
color: var(--action-color);
}
#wa-dialog .wa-pro-cta::part(base) {
background-color: var(--action-color);
border-color: var(--action-color);
}
#wa-dialog .wa-cta::part(base):hover,
#wa-dialog .wa-pro-cta::part(base):hover {
background-color: var(--action-color-hover);
border-color: var(--action-color-hover);
color: var(--sl-color-neutral-0);
}
================================================
FILE: docs/assets/styles/search.css
================================================
/* Search plugin */
:root,
:root.sl-theme-dark {
--docs-search-box-background: var(--sl-color-neutral-0);
--docs-search-box-border-width: 1px;
--docs-search-box-border-color: var(--sl-color-neutral-300);
--docs-search-box-color: var(--sl-color-neutral-600);
--docs-search-dialog-background: var(--sl-color-neutral-0);
--docs-search-border-width: var(--docs-border-width);
--docs-search-border-color: var(--docs-border-color);
--docs-search-text-color: var(--sl-color-neutral-900);
--docs-search-text-color-muted: var(--sl-color-neutral-500);
--docs-search-font-weight-normal: var(--sl-font-weight-normal);
--docs-search-font-weight-semibold: var(--sl-font-weight-semibold);
--docs-search-border-radius: calc(2 * var(--docs-border-radius));
--docs-search-accent-color: var(--sl-color-primary-600);
--docs-search-icon-color: var(--sl-color-neutral-500);
--docs-search-icon-color-active: var(--sl-color-neutral-600);
--docs-search-shadow: var(--docs-shadow-x-large);
--docs-search-result-background-hover: var(--sl-color-neutral-100);
--docs-search-result-color-hover: var(--sl-color-neutral-900);
--docs-search-result-background-active: var(--sl-color-primary-600);
--docs-search-result-color-active: var(--sl-color-neutral-0);
--docs-search-focus-ring: var(--sl-focus-ring);
--docs-search-overlay-background: rgb(0 0 0 / 0.33);
}
:root.sl-theme-dark {
--docs-search-overlay-background: rgb(71 71 71 / 0.33);
}
body.search-visible {
padding-right: var(--docs-search-scroll-lock-size) !important;
overflow: hidden !important;
}
/* Search box */
.search-box {
flex: 1 1 auto;
display: flex;
align-items: center;
width: 100%;
border: none;
border-radius: 9999px;
background: var(--docs-search-box-background);
border: solid var(--docs-search-box-border-width) var(--docs-search-box-border-color);
font: inherit;
color: var(--docs-search-box-color);
padding: 0.75rem 1rem;
margin: var(--sl-spacing-large) 0;
cursor: pointer;
}
.search-box span {
flex: 1 1 auto;
width: 1rem;
height: 1rem;
text-align: left;
line-height: 1;
margin: 0 0.75rem;
}
.search-box:focus {
outline: none;
}
.search-box:focus-visible {
outline: var(--docs-search-focus-ring);
}
/* Site search */
.search {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
}
.search[hidden] {
display: none;
}
.search__overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--docs-search-overlay-background);
z-index: -1;
}
.search__dialog {
width: 100%;
height: 100%;
max-width: none;
max-height: none;
background: transparent;
border: none;
padding: 0;
margin: 0;
}
.search__dialog:focus {
outline: none;
}
.search__dialog::backdrop {
display: none;
}
/* Fixes an iOS Safari 16.4 bug that draws the parent element's border radius incorrectly when showing/hiding results */
.search__header {
background-color: var(--docs-search-dialog-background);
border-radius: var(--docs-search-border-radius);
}
.search--has-results .search__header {
border-top-left-radius: var(--docs-search-border-radius);
border-top-right-radius: var(--docs-search-border-radius);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.search__content {
display: flex;
flex-direction: column;
width: 100%;
max-width: 500px;
max-height: calc(100vh - 20rem);
background-color: var(--docs-search-dialog-background);
border-radius: var(--docs-search-border-radius);
box-shadow: var(--docs-search-shadow);
padding: 0;
margin: 10rem auto;
}
@media screen and (max-width: 900px) {
.search__content {
max-width: calc(100% - 2rem);
max-height: calc(90svh);
margin: 4vh 1rem;
}
}
.search__input-wrapper {
display: flex;
align-items: center;
}
.search__input-wrapper sl-icon {
width: 1.5rem;
height: 1.5rem;
flex: 0 0 auto;
color: var(--docs-search-icon-color);
margin: 0 1.5rem;
}
.search__clear-button {
display: flex;
background: none;
border: none;
font: inherit;
padding: 0;
margin: 0;
cursor: pointer;
}
.search__clear-button[hidden] {
display: none;
}
.search__clear-button:active sl-icon {
color: var(--docs-search-icon-color-active);
}
.search__input {
flex: 1 1 auto;
min-width: 0;
border: none;
font: inherit;
font-size: 1.5rem;
font-weight: var(--docs-search-font-weight-normal);
color: var(--docs-search-text-color);
background: transparent;
padding: 1rem 0;
margin: 0;
}
.search__input::placeholder {
color: var(--docs-search-text-color-muted);
}
.search__input::-webkit-search-decoration,
.search__input::-webkit-search-cancel-button,
.search__input::-webkit-search-results-button,
.search__input::-webkit-search-results-decoration {
display: none;
}
.search__input:focus,
.search__input:focus-visible {
outline: none;
}
.search__body {
flex: 1 1 auto;
overflow: auto;
}
.search--has-results .search__body {
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
}
.search__results {
display: none;
line-height: 1.2;
list-style: none;
padding: 0.5rem 0;
margin: 0;
}
.search--has-results .search__results {
display: block;
}
.search__results a {
display: block;
text-decoration: none;
padding: 0.5rem 1.5rem;
}
.search__results a:focus-visible {
outline: var(--docs-search-focus-ring);
}
.search__results li a:hover,
.search__results li a:hover small {
background-color: var(--docs-search-result-background-hover);
color: var(--docs-search-result-color-hover);
}
.search__results li[data-selected='true'] a,
.search__results li[data-selected='true'] a * {
outline: none;
background-color: var(--docs-search-result-background-active);
color: var(--docs-search-result-color-active);
}
.search__results h3 {
font-weight: var(--docs-search-font-weight-semibold);
margin: 0;
}
.search__results small {
display: block;
color: var(--docs-search-text-color-muted);
}
.search__result {
padding: 0;
margin: 0;
}
.search__result a {
display: flex;
align-items: center;
gap: 1rem;
}
.search__result-icon {
flex: 0 0 auto;
display: flex;
color: var(--docs-search-text-color-muted);
}
.search__result-icon sl-icon {
font-size: 1.5rem;
}
.search__result__details {
width: calc(100% - 3rem);
}
.search__result-title,
.search__result-description,
.search__result-url {
max-width: 400px;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.search__result-title {
font-size: 1.2rem;
font-weight: var(--docs-search-font-weight-semibold);
color: var(--docs-search-accent-color);
}
.search__result-description {
font-size: 0.875rem;
color: var(--docs-search-text-color);
}
.search__result-url {
font-size: 0.875rem;
color: var(--docs-search-text-color-muted);
}
.search__empty {
display: none;
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
text-align: center;
color: var(--docs-search-text-color-muted);
padding: 2rem;
}
.search--no-results .search__empty {
display: block;
}
.search__footer {
display: flex;
justify-content: center;
gap: 2rem;
border-top: solid var(--docs-search-border-width) var(--docs-search-border-color);
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
padding: 1rem;
}
.search__footer small {
color: var(--docs-search-text-color-muted);
}
.search__footer small kbd:last-of-type {
margin-right: 0.25rem;
}
@media screen and (max-width: 900px) {
.search__footer {
display: none;
}
}
================================================
FILE: docs/eleventy.config.cjs
================================================
/* eslint-disable no-invalid-this */
const fs = require('fs');
const path = require('path');
const lunr = require('lunr');
const { capitalCase } = require('change-case');
const { JSDOM } = require('jsdom');
const { customElementsManifest, getAllComponents } = require('./_utilities/cem.cjs');
const shoelaceFlavoredMarkdown = require('./_utilities/markdown.cjs');
const activeLinks = require('./_utilities/active-links.cjs');
const anchorHeadings = require('./_utilities/anchor-headings.cjs');
const codePreviews = require('./_utilities/code-previews.cjs');
const copyCodeButtons = require('./_utilities/copy-code-buttons.cjs');
const externalLinks = require('./_utilities/external-links.cjs');
const highlightCodeBlocks = require('./_utilities/highlight-code.cjs');
const tableOfContents = require('./_utilities/table-of-contents.cjs');
const prettier = require('./_utilities/prettier.cjs');
const scrollingTables = require('./_utilities/scrolling-tables.cjs');
const typography = require('./_utilities/typography.cjs');
const replacer = require('./_utilities/replacer.cjs');
const assetsDir = 'assets';
const cdndir = 'cdn';
const npmdir = 'dist';
const allComponents = getAllComponents();
let hasBuiltSearchIndex = false;
module.exports = function (eleventyConfig) {
//
// Global data
//
eleventyConfig.addGlobalData('baseUrl', 'https://shoelace.style/'); // the production URL
eleventyConfig.addGlobalData('layout', 'default'); // make 'default' the default layout
eleventyConfig.addGlobalData('toc', true); // enable the table of contents
eleventyConfig.addGlobalData('meta', {
title: 'Shoelace',
description: 'A forward-thinking library of web components.',
image: 'images/og-image.png',
version: customElementsManifest.package.version,
components: allComponents,
cdndir,
npmdir
});
//
// Layout aliases
//
eleventyConfig.addLayoutAlias('default', 'default.njk');
//
// Copy assets
//
eleventyConfig.addPassthroughCopy(assetsDir);
eleventyConfig.setServerPassthroughCopyBehavior('passthrough'); // emulates passthrough copy during --serve
//
// Functions
//
// Generates a URL relative to the site's root
eleventyConfig.addNunjucksGlobal('rootUrl', (value = '', absolute = false) => {
value = path.join('/', value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Generates a URL relative to the site's asset directory
eleventyConfig.addNunjucksGlobal('assetUrl', (value = '', absolute = false) => {
value = path.join(`/${assetsDir}`, value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Fetches a specific component's metadata
eleventyConfig.addNunjucksGlobal('getComponent', tagName => {
const component = allComponents.find(c => c.tagName === tagName);
if (!component) {
throw new Error(
`Unable to find a component called "${tagName}". Make sure the file name is the same as the component's tag ` +
`name (minus the sl- prefix).`
);
}
return component;
});
//
// Custom markdown syntaxes
//
eleventyConfig.setLibrary('md', shoelaceFlavoredMarkdown);
//
// Filters
//
eleventyConfig.addFilter('markdown', content => {
return shoelaceFlavoredMarkdown.render(content);
});
eleventyConfig.addFilter('markdownInline', content => {
return shoelaceFlavoredMarkdown.renderInline(content);
});
// Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited.
// With Prettier 3, this means a leading pipe will exist if the line wraps.
eleventyConfig.addFilter('trimPipes', content => {
return typeof content === 'string' ? content.replace(/^(\s|\|)/g, '').replace(/(\s|\|)$/g, '') : content;
});
eleventyConfig.addFilter('classNameToComponentName', className => {
let name = capitalCase(className.replace(/^Sl/, ''));
if (name === 'Qr Code') name = 'QR Code'; // manual override
return name;
});
eleventyConfig.addFilter('removeSlPrefix', tagName => {
return tagName.replace(/^sl-/, '');
});
//
// Transforms
//
eleventyConfig.addTransform('html-transform', function (rawContent) {
let content = replacer(rawContent, [
{ pattern: '%VERSION%', replacement: customElementsManifest.package.version },
{ pattern: '%CDNDIR%', replacement: cdndir },
{ pattern: '%NPMDIR%', replacement: npmdir }
]);
// Parse the template and get a Document object
const doc = new JSDOM(content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
// DOM transforms
activeLinks(doc, { pathname: this.page.url });
anchorHeadings(doc, {
within: '#content .content__body',
levels: ['h2', 'h3', 'h4', 'h5']
});
tableOfContents(doc, {
levels: ['h2', 'h3'],
container: '#content .content__toc > ul',
within: '#content .content__body'
});
codePreviews(doc);
externalLinks(doc, { target: '_blank' });
highlightCodeBlocks(doc);
scrollingTables(doc);
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
typography(doc, '#content');
// Serialize the Document object to an HTML string and prepend the doctype
content = `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
// String transforms
content = prettier(content);
return content;
});
//
// Build a search index
//
eleventyConfig.on('eleventy.after', ({ results }) => {
// We only want to build the search index on the first run so all pages get indexed.
if (hasBuiltSearchIndex) {
return;
}
const map = {};
const searchIndexFilename = path.join(eleventyConfig.dir.output, assetsDir, 'search.json');
const lunrInput = path.resolve('../node_modules/lunr/lunr.min.js');
const lunrOutput = path.join(eleventyConfig.dir.output, assetsDir, 'scripts/lunr.js');
const searchIndex = lunr(function () {
// The search index uses these field names extensively, so shortening them can save some serious bytes. The
// initial index file went from 468 KB => 401 KB by using single-character names!
this.ref('id'); // id
this.field('t', { boost: 50 }); // title
this.field('h', { boost: 25 }); // headings
this.field('c'); // content
results.forEach((result, index) => {
const url = path
.join('/', path.relative(eleventyConfig.dir.output, result.outputPath))
.replace(/\\/g, '/') // convert backslashes to forward slashes
.replace(/\/index.html$/, '/'); // convert trailing /index.html to /
const doc = new JSDOM(result.content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
const content = doc.querySelector('#content');
// Get title and headings
const title = (doc.querySelector('title')?.textContent || path.basename(result.outputPath)).trim();
const headings = [...content.querySelectorAll('h1, h2, h3, h4')]
.map(heading => heading.textContent)
.join(' ')
.replace(/\s+/g, ' ')
.trim();
// Remove code blocks and whitespace from content
[...content.querySelectorAll('code[class|=language]')].forEach(code => code.remove());
const textContent = content.textContent.replace(/\s+/g, ' ').trim();
// Update the index and map
this.add({ id: index, t: title, h: headings, c: textContent });
map[index] = { title, url };
});
});
// Copy the Lunr search client and write the index
fs.mkdirSync(path.dirname(lunrOutput), { recursive: true });
fs.copyFileSync(lunrInput, lunrOutput);
fs.writeFileSync(searchIndexFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
hasBuiltSearchIndex = true;
});
//
// Send a signal to stdout that let's the build know we've reached this point
//
eleventyConfig.on('eleventy.after', () => {
console.log('[eleventy.after]');
});
//
// Dev server options (see https://www.11ty.dev/docs/dev-server/#options)
//
eleventyConfig.setServerOptions({
domDiff: false, // disable dom diffing so custom elements don't break on reload,
port: 4000, // if port 4000 is taken, 11ty will use the next one available
watch: ['cdn/**/*'] // additional files to watch that will trigger server updates (array of paths or globs)
});
//
// 11ty config
//
return {
dir: {
input: 'pages',
output: '../_site',
includes: '../_includes' // resolved relative to the input dir
},
markdownTemplateEngine: 'njk', // use Nunjucks instead of Liquid for markdown files
templateEngineOverride: ['njk'] // just Nunjucks and then markdown
};
};
================================================
FILE: docs/pages/404.md
================================================
---
meta:
title: Page Not Found
description: "The page you were looking for couldn't be found."
permalink: 404.html
toc: false
---
<div style="text-align: center;">
# Page Not Found

The page you were looking for couldn't be found.
Press [[/]] to search, or [head back to the homepage](/).
</div>
================================================
FILE: docs/pages/components/alert.md
================================================
---
meta:
title: Alert
description: Alerts are used to display important messages inline or as toast notifications.
layout: component
---
```html:preview
<sl-alert open>
<sl-icon slot="icon" name="info-circle"></sl-icon>
This is a standard alert. You can customize its content and even the icon.
</sl-alert>
```
```jsx:react
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
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>
);
```
:::tip
Alerts will not be visible if the `open` attribute is not present.
:::
## Examples
### Variants
Set the `variant` attribute to change the alert's variant.
```html:preview
<sl-alert variant="primary" open>
<sl-icon slot="icon" name="info-circle"></sl-icon>
<strong>This is super informative</strong><br />
You can tell by how pretty the alert is.
</sl-alert>
<br />
<sl-alert variant="success" open>
<sl-icon slot="icon" name="check2-circle"></sl-icon>
<strong>Your changes have been saved</strong><br />
You can safely exit the app now.
</sl-alert>
<br />
<sl-alert variant="neutral" open>
<sl-icon slot="icon" name="gear"></sl-icon>
<strong>Your settings have been updated</strong><br />
Settings will take effect on next login.
</sl-alert>
<br />
<sl-alert variant="warning" open>
<sl-icon slot="icon" name="exclamation-triangle"></sl-icon>
<strong>Your session has ended</strong><br />
Please login again to continue.
</sl-alert>
<br />
<sl-alert variant="danger" open>
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
<strong>Your account has been deleted</strong><br />
We're very sorry to see you go!
</sl-alert>
```
```jsx:react
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<>
<SlAlert variant="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 variant="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 variant="neutral" open>
<SlIcon slot="icon" name="gear" />
<strong>Your settings have been updated</strong>
<br />
Settings will take effect on next login.
</SlAlert>
<br />
<SlAlert variant="warning" open>
<SlIcon slot="icon" name="exclamation-triangle" />
<strong>Your session has ended</strong>
<br />
Please login again to continue.
</SlAlert>
<br />
<SlAlert variant="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.
```html:preview
<sl-alert variant="primary" open closable class="alert-closable">
<sl-icon slot="icon" name="info-circle"></sl-icon>
You can close this alert any time!
</sl-alert>
<script>
const alert = document.querySelector('.alert-closable');
alert.addEventListener('sl-after-hide', () => {
setTimeout(() => (alert.open = true), 2000);
});
</script>
```
```jsx:react
import { useState } from 'react';
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
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.
```html:preview
<sl-alert variant="primary" open> Nothing fancy here, just a simple alert. </sl-alert>
```
```jsx:react
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
const App = () => (
<SlAlert variant="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.
```html:preview
<div class="alert-duration">
<sl-button variant="primary">Show Alert</sl-button>
<sl-alert variant="primary" duration="3000" closable>
<sl-icon slot="icon" name="info-circle"></sl-icon>
This alert will automatically hide itself after three seconds, unless you interact with it.
</sl-alert>
</div>
<script>
const container = document.querySelector('.alert-duration');
const button = container.querySelector('sl-button');
const alert = container.querySelector('sl-alert');
button.addEventListener('click', () => alert.show());
</script>
<style>
.alert-duration sl-alert {
margin-top: var(--sl-spacing-medium);
}
</style>
```
```jsx:react
import { useState } from 'react';
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
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 variant="primary" onClick={() => setOpen(true)}>
Show Alert
</SlButton>
<SlAlert variant="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>
</>
);
};
```
### Countdown
Set the `countdown` attribute to display a loading bar that indicates the alert remaining time. This is useful for alerts with relatively long duration.
```html:preview
<div class="alert-countdown">
<sl-button variant="primary">Show Alert</sl-button>
<sl-alert variant="primary" duration="10000" countdown="rtl" closable>
<sl-icon slot="icon" name="info-circle"></sl-icon>
You're not stuck, the alert will close after a pretty long duration.
</sl-alert>
</div>
<script>
const container = document.querySelector('.alert-countdown');
const button = container.querySelector('sl-button');
const alert = container.querySelector('sl-alert');
button.addEventListener('click', () => alert.show());
</script>
<style>
.alert-countdown sl-alert {
margin-top: var(--sl-spacing-medium);
}
</style>
```
```jsx:react
import { useState } from 'react';
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const css = `
.alert-countdown sl-alert {
margin-top: var(--sl-spacing-medium);
}
`;
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<div className="alert-countdown">
<SlButton variant="primary" onClick={() => setOpen(true)}>
Show Alert
</SlButton>
<SlAlert variant="primary" duration="3000" countdown="rtl" open={open} closable onSlAfterHide={() => setOpen(false)}>
<SlIcon slot="icon" name="info-circle" />
You're not stuck, the alert will close after a pretty long duration.
</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.
You should always use the `closable` attribute so users can dismiss the notification. It's also common to set a reasonable `duration` when the notification doesn't require acknowledgement.
```html:preview
<div class="alert-toast">
<sl-button variant="primary">Primary</sl-button>
<sl-button variant="success">Success</sl-button>
<sl-button variant="neutral">Neutral</sl-button>
<sl-button variant="warning">Warning</sl-button>
<sl-button variant="danger">Danger</sl-button>
<sl-alert variant="primary" duration="3000" closable>
<sl-icon slot="icon" name="info-circle"></sl-icon>
<strong>This is super informative</strong><br />
You can tell by how pretty the alert is.
</sl-alert>
<sl-alert variant="success" duration="3000" closable>
<sl-icon slot="icon" name="check2-circle"></sl-icon>
<strong>Your changes have been saved</strong><br />
You can safely exit the app now.
</sl-alert>
<sl-alert variant="neutral" duration="3000" closable>
<sl-icon slot="icon" name="gear"></sl-icon>
<strong>Your settings have been updated</strong><br />
Settings will take effect on next login.
</sl-alert>
<sl-alert variant="warning" duration="3000" closable>
<sl-icon slot="icon" name="exclamation-triangle"></sl-icon>
<strong>Your session has ended</strong><br />
Please login again to continue.
</sl-alert>
<sl-alert variant="danger" duration="3000" closable>
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
<strong>Your account has been deleted</strong><br />
We're very sorry to see you go!
</sl-alert>
</div>
<script>
const container = document.querySelector('.alert-toast');
['primary', 'success', 'neutral', 'warning', 'danger'].map(variant => {
const button = container.querySelector(`sl-button[variant="${variant}"]`);
const alert = container.querySelector(`sl-alert[variant="${variant}"]`);
button.addEventListener('click', () => alert.toast());
});
</script>
```
```jsx:react
import { useRef } from 'react';
import SlAlert from '@shoelace-style/shoelace/dist/react/alert';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
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 variant="primary" onClick={() => primary.current.toast()}>
Primary
</SlButton>
<SlButton variant="success" onClick={() => success.current.toast()}>
Success
</SlButton>
<SlButton variant="neutral" onClick={() => neutral.current.toast()}>
Neutral
</SlButton>
<SlButton variant="warning" onClick={() => warning.current.toast()}>
Warning
</SlButton>
<SlButton variant="danger" onClick={() => danger.current.toast()}>
Danger
</SlButton>
<SlAlert ref={primary} variant="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} variant="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} variant="neutral" duration="3000" closable>
<SlIcon slot="icon" name="gear" />
<strong>Your settings have been updated</strong>
<br />
Settings will take effect on next login.
</SlAlert>
<SlAlert ref={warning} variant="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} variant="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.
```html:preview
<div class="alert-toast-wrapper">
<sl-button variant="primary">Create Toast</sl-button>
</div>
<script>
const container = document.querySelector('.alert-toast-wrapper');
const button = container.querySelector('sl-button');
let count = 0;
// Always escape HTML for text arguments!
function escapeHtml(html) {
const div = document.createElement('div');
div.textContent = html;
return div.innerHTML;
}
// Custom function to emit toast notifications
function notify(message, variant = 'primary', icon = 'info-circle', duration = 3000) {
const alert = Object.assign(document.createElement('sl-alert'), {
variant,
closable: true,
duration: duration,
innerHTML: `
<sl-icon name="${icon}" slot="icon"></sl-icon>
${escapeHtml(message)}
`
});
document.body.append(alert);
return alert.toast();
}
button.addEventListener('click', () => {
notify(`This is custom toast #${++count}`);
});
</script>
```
### The Toast Stack
The toast stack is a fixed position singleton element created and managed internally by the alert component. It will be added and removed from the DOM as needed when toasts are shown. When more than one toast is visible, they will stack vertically in the toast stack.
By default, the toast stack is positioned at the top-right of the viewport. You can change its position by targeting `.sl-toast-stack` in your stylesheet. To make toasts appear at the top-left of the viewport, for example, use the following styles.
```css
.sl-toast-stack {
left: 0;
right: auto;
}
```
:::tip
By design, it is not possible to show toasts in more than one stack simultaneously. Such behavior is confusing and makes for a poor user experience.
:::
================================================
FILE: docs/pages/components/animated-image.md
================================================
---
meta:
title: Animated Image
description: A component for displaying animated GIFs and WEBPs that play and pause on interaction.
layout: component
---
```html:preview
<sl-animated-image
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/animated-image';
const App = () => (
<SlAnimatedImage
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
/>
);
```
:::tip
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
Both GIF and WEBP images are supported.
```html:preview
<sl-animated-image
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/animated-image';
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="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
style="width: 150px; height: 200px;"
>
</sl-animated-image>
```
{% raw %}
```jsx:react
import SlAnimatedImage from '@shoelace-style/shoelace/dist/react/animated-image';
const App = () => (
<SlAnimatedImage
src="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
style={{ width: '150px', height: '200px' }}
/>
);
```
{% endraw %}
### 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="https://shoelace.style/assets/images/walk.gif"
alt="Animation of untied shoes walking on pavement"
class="animated-image-custom-control-box"
></sl-animated-image>
<style>
.animated-image-custom-control-box::part(control-box) {
top: auto;
right: auto;
bottom: 1rem;
left: 1rem;
background-color: deeppink;
border: none;
color: pink;
}
</style>
```
```jsx:react
import SlAnimatedImage from '@shoelace-style/shoelace/dist/react/animated-image';
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>
</>
);
```
================================================
FILE: docs/pages/components/animation.md
================================================
---
meta:
title: Animation
description: Animate elements declaratively with nearly 100 baked-in presets, or roll your own with custom keyframes.
layout: component
---
To animate an element, wrap it in `<sl-animation>` and set an animation `name`. The animation will not start until you add the `play` attribute. Refer to the [properties table](#properties) for a list of all animation options.
```html:preview
<div class="animation-overview">
<sl-animation name="bounce" duration="2000" play><div class="box"></div></sl-animation>
<sl-animation name="jello" duration="2000" play><div class="box"></div></sl-animation>
<sl-animation name="heartBeat" duration="2000" play><div class="box"></div></sl-animation>
<sl-animation name="flip" duration="2000" play><div class="box"></div></sl-animation>
</div>
<style>
.animation-overview .box {
display: inline-block;
width: 100px;
height: 100px;
background-color: var(--sl-color-primary-600);
margin: 1.5rem;
}
</style>
```
```jsx:react
import SlAnimation from '@shoelace-style/shoelace/dist/react/animation';
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>
</>
);
```
:::tip
The animation will only be applied to the first child element found in `<sl-animation>`.
:::
## Examples
### Animations & Easings
This example demonstrates all of the baked-in animations and easings. Animations are based on those found in the popular [Animate.css](https://animate.style/) library.
```html:preview
<div class="animation-sandbox">
<sl-animation name="bounce" easing="ease-in-out" duration="2000" play>
<div class="box"></div>
</sl-animation>
<div class="controls">
<sl-select label="Animation" value="bounce"></sl-select>
<sl-select label="Easing" value="linear"></sl-select>
<sl-input label="Playback Rate" type="number" min="0" max="2" step=".25" value="1"></sl-input>
</div>
</div>
<script type="module">
import { getAnimationNames, getEasingNames } from '/dist/utilities/animation.js';
const container = document.querySelector('.animation-sandbox');
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-input[type="number"]');
const animations = getAnimationNames();
const easings = getEasingNames();
animations.map(name => {
const option = Object.assign(document.createElement('sl-option'), {
textContent: name,
value: name
});
animationName.appendChild(option);
});
easings.map(name => {
const option = Object.assign(document.createElement('sl-option'), {
textContent: name,
value: name
});
easingName.appendChild(option);
});
animationName.addEventListener('sl-change', () => (animation.name = animationName.value));
easingName.addEventListener('sl-change', () => (animation.easing = easingName.value));
playbackRate.addEventListener('sl-input', () => (animation.playbackRate = playbackRate.value));
</script>
<style>
.animation-sandbox .box {
width: 100px;
height: 100px;
background-color: var(--sl-color-primary-600);
}
.animation-sandbox .controls {
max-width: 300px;
margin-top: 2rem;
}
.animation-sandbox .controls sl-select {
margin-bottom: 1rem;
}
</style>
```
### Using Intersection Observer
Use an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to control the animation when an element enters or exits the viewport. For example, scroll the box below in and out of your screen. The animation stops when the box exits the viewport and restarts each time it enters the viewport.
```html:preview
<div class="animation-scroll">
<sl-animation name="jackInTheBox" duration="2000" iterations="1"><div class="box"></div></sl-animation>
</div>
<script>
const container = document.querySelector('.animation-scroll');
const animation = container.querySelector('sl-animation');
const box = animation.querySelector('.box');
// Watch for the box to enter and exit the viewport. Note that we're observing the box, not the animation element!
const observer = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
// Start the animation when the box enters the viewport
animation.play = true;
} else {
animation.play = false;
animation.currentTime = 0;
}
});
observer.observe(box);
</script>
<style>
.animation-scroll .box {
display: inline-block;
width: 100px;
height: 100px;
background-color: var(--sl-color-primary-600);
}
</style>
```
```jsx:react
import { useEffect, useRef, useState } from 'react';
import SlAnimation from '@shoelace-style/shoelace/dist/react/animation';
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.
```html:preview
<div class="animation-keyframes">
<sl-animation easing="ease-in-out" duration="2000" play>
<div class="box"></div>
</sl-animation>
</div>
<script>
const animation = document.querySelector('.animation-keyframes sl-animation');
animation.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)'
}
];
</script>
<style>
.animation-keyframes .box {
width: 100px;
height: 100px;
background-color: var(--sl-color-primary-600);
}
</style>
```
```jsx:react
import SlAnimation from '@shoelace-style/shoelace/dist/react/animation';
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.
```html:preview
<div class="animation-form">
<sl-animation name="rubberBand" duration="1000" iterations="1">
<sl-button variant="primary">Click me</sl-button>
</sl-animation>
</div>
<script>
const container = document.querySelector('.animation-form');
const animation = container.querySelector('sl-animation');
const button = container.querySelector('sl-button');
button.addEventListener('click', () => {
animation.play = true;
});
</script>
```
```jsx:react
import { useState } from 'react';
import SlAnimation from '@shoelace-style/shoelace/dist/react/animation';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
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 variant="primary" onClick={() => setPlay(true)}>
Click me
</SlButton>
</SlAnimation>
</div>
);
};
```
================================================
FILE: docs/pages/components/avatar.md
================================================
---
meta:
title: Avatar
description: Avatars are used to represent a person or object.
layout: component
---
By default, a generic icon will be shown. You can personalize avatars by adding custom icons, initials, and images. You should always provide a `label` for assistive devices.
```html:preview
<sl-avatar label="User avatar"></sl-avatar>
```
```jsx:react
import SlAvatar from '@shoelace-style/shoelace/dist/react/avatar';
const App = () => <SlAvatar label="User avatar" />;
```
## Examples
### Images
To use an image for the avatar, set the `image` and `label` attributes. This will take priority and be shown over initials and icons.
Avatar images can be lazily loaded by setting the `loading` attribute to `lazy`.
```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"
label="Avatar of a gray tabby kitten looking down"
></sl-avatar>
<sl-avatar
image="https://images.unsplash.com/photo-1591871937573-74dbba515c4c?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
label="Avatar of a white and grey kitten on grey textile"
loading="lazy"
></sl-avatar>
```
```jsx:react
import SlAvatar from '@shoelace-style/shoelace/dist/react/avatar';
const App = () => (
<SlAvatar
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
label="Avatar of a gray tabby kitten looking down"
/>
<SlAvatar
image="https://images.unsplash.com/photo-1591871937573-74dbba515c4c?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
label="Avatar of a white and grey kitten on grey textile"
loading="lazy"
/>
);
```
### 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" label="Avatar with initials: SL"></sl-avatar>
```
```jsx:react
import SlAvatar from '@shoelace-style/shoelace/dist/react/avatar';
const App = () => <SlAvatar initials="SL" label="Avatar with initials: SL" />;
```
### Custom Icons
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 label="Avatar with an image icon">
<sl-icon slot="icon" name="image"></sl-icon>
</sl-avatar>
<sl-avatar label="Avatar with an archive icon">
<sl-icon slot="icon" name="archive"></sl-icon>
</sl-avatar>
<sl-avatar label="Avatar with a briefcase icon">
<sl-icon slot="icon" name="briefcase"></sl-icon>
</sl-avatar>
```
```jsx:react
import SlAvatar from '@shoelace-style/shoelace/dist/react/avatar';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<>
<SlAvatar label="Avatar with an image icon">
<SlIcon slot="icon" name="image" />
</SlAvatar>
<SlAvatar label="Avatar with an archive icon">
<SlIcon slot="icon" name="archive" />
</SlAvatar>
<SlAvatar label="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" label="Square avatar"></sl-avatar>
<sl-avatar shape="rounded" label="Rounded avatar"></sl-avatar>
<sl-avatar shape="circle" label="Circle avatar"></sl-avatar>
```
```jsx:react
import SlAvatar from '@shoelace-style/shoelace/dist/react/avatar';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
const App = () => (
<>
<SlAvatar shape="square" label="Square avatar" />
<SlAvatar shape="rounded" label="Rounded avatar" />
<SlAvatar shape="circle" label="Circle avatar" />
</>
);
```
### Avatar Groups
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"
label="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"
label="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"
label="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"
label="Avatar 4 of 4"
></sl-avatar>
</div>
<style>
.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);
}
</style>
```
```jsx:react
import SlAvatar from '@shoelace-style/shoelace/dist/react/avatar';
import SlIcon from '@shoelace-style/shoelace/dist/react/icon';
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"
label="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"
label="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"
label="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"
label="Avatar 4 of 4"
/>
</div>
<style>{css}</style>
</>
);
```
================================================
FILE: docs/pages/components/badge.md
================================================
---
meta:
title: Badge
description: Badges are used to draw attention and display statuses or counts.
layout: component
---
```html:preview
<sl-badge>Badge</sl-badge>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const App = () => <SlBadge>Badge</SlBadge>;
```
## Examples
### Variants
Set the `variant` attribute to change the badge's variant.
```html:preview
<sl-badge variant="primary">Primary</sl-badge>
<sl-badge variant="success">Success</sl-badge>
<sl-badge variant="neutral">Neutral</sl-badge>
<sl-badge variant="warning">Warning</sl-badge>
<sl-badge variant="danger">Danger</sl-badge>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const App = () => (
<>
<SlBadge variant="primary">Primary</SlBadge>
<SlBadge variant="success">Success</SlBadge>
<SlBadge variant="neutral">Neutral</SlBadge>
<SlBadge variant="warning">Warning</SlBadge>
<SlBadge variant="danger">Danger</SlBadge>
</>
);
```
### Pill Badges
Use the `pill` attribute to give badges rounded edges.
```html:preview
<sl-badge variant="primary" pill>Primary</sl-badge>
<sl-badge variant="success" pill>Success</sl-badge>
<sl-badge variant="neutral" pill>Neutral</sl-badge>
<sl-badge variant="warning" pill>Warning</sl-badge>
<sl-badge variant="danger" pill>Danger</sl-badge>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const App = () => (
<>
<SlBadge variant="primary" pill>
Primary
</SlBadge>
<SlBadge variant="success" pill>
Success
</SlBadge>
<SlBadge variant="neutral" pill>
Neutral
</SlBadge>
<SlBadge variant="warning" pill>
Warning
</SlBadge>
<SlBadge variant="danger" pill>
Danger
</SlBadge>
</>
);
```
### Pulsating Badges
Use the `pulse` attribute to draw attention to the badge with a subtle animation.
```html:preview
<div class="badge-pulse">
<sl-badge variant="primary" pill pulse>1</sl-badge>
<sl-badge variant="success" pill pulse>1</sl-badge>
<sl-badge variant="neutral" pill pulse>1</sl-badge>
<sl-badge variant="warning" pill pulse>1</sl-badge>
<sl-badge variant="danger" pill pulse>1</sl-badge>
</div>
<style>
.badge-pulse sl-badge:not(:last-of-type) {
margin-right: 1rem;
}
</style>
```
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
const css = `
.badge-pulse sl-badge:not(:last-of-type) {
margin-right: 1rem;
}
`;
const App = () => (
<>
<div className="badge-pulse">
<SlBadge variant="primary" pill pulse>
1
</SlBadge>
<SlBadge variant="success" pill pulse>
1
</SlBadge>
<SlBadge variant="neutral" pill pulse>
1
</SlBadge>
<SlBadge variant="warning" pill pulse>
1
</SlBadge>
<SlBadge variant="danger" pill pulse>
1
</SlBadge>
</div>
<style>{css}</style>
</>
);
```
### With Buttons
One of the most common use cases for badges is attaching them to buttons. To make this easier, badges will be automatically positioned at the top-right when they're a child of a button.
```html:preview
<sl-button>
Requests
<sl-badge pill>30</sl-badge>
</sl-button>
<sl-button style="margin-inline-start: 1rem;">
Warnings
<sl-badge variant="warning" pill>8</sl-badge>
</sl-button>
<sl-button style="margin-inline-start: 1rem;">
Errors
<sl-badge variant="danger" pill>6</sl-badge>
</sl-button>
```
{% raw %}
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
const App = () => (
<>
<SlButton>
Requests
<SlBadge pill>30</SlBadge>
</SlButton>
<SlButton style={{ marginInlineStart: '1rem' }}>
Warnings
<SlBadge variant="warning" pill>
8
</SlBadge>
</SlButton>
<SlButton style={{ marginInlineStart: '1rem' }}>
Errors
<SlBadge variant="danger" pill>
6
</SlBadge>
</SlButton>
</>
);
```
{% endraw %}
### With Menu Items
When including badges in menu items, use the `suffix` slot to make sure they're aligned correctly.
```html:preview
<sl-menu style="max-width: 240px;">
<sl-menu-label>Messages</sl-menu-label>
<sl-menu-item>Comments <sl-badge slot="suffix" variant="neutral" pill>4</sl-badge></sl-menu-item>
<sl-menu-item>Replies <sl-badge slot="suffix" variant="neutral" pill>12</sl-badge></sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlBadge from '@shoelace-style/shoelace/dist/react/badge';
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
import SlMenuLabel from '@shoelace-style/shoelace/dist/react/menu-label';
const App = () => (
<SlMenu
style={{
maxWidth: '240px',
border: 'solid 1px var(--sl-panel-border-color)',
borderRadius: 'var(--sl-border-radius-medium)'
}}
>
<SlMenuLabel>Messages</SlMenuLabel>
<SlMenuItem>
Comments
<SlBadge slot="suffix" variant="neutral" pill>
4
</SlBadge>
</SlMenuItem>
<SlMenuItem>
Replies
<SlBadge slot="suffix" variant="neutral" pill>
12
</SlBadge>
</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
================================================
FILE: docs/pages/components/breadcrumb-item.md
================================================
---
meta:
title: Breadcrumb Item
description: Breadcrumb Items are used inside breadcrumbs to represent different links.
layout: component
---
```html:preview
<sl-breadcrumb>
<sl-breadcrumb-item>
gitextract_b6qx7r1_/ ├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .github/ │ ├── CODE_OF_CONDUCT.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── config.yml │ ├── SECURITY.md │ └── workflows/ │ ├── node.js.yml │ └── release.yml ├── .gitignore ├── .gitpod.yml ├── .husky/ │ └── pre-commit ├── .prettierignore ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── cspell.json ├── custom-elements-manifest.config.js ├── docs/ │ ├── _includes/ │ │ ├── component.njk │ │ ├── default.njk │ │ ├── sidebar.njk │ │ └── wa-logo-icon.njk │ ├── _utilities/ │ │ ├── active-links.cjs │ │ ├── anchor-headings.cjs │ │ ├── cem.cjs │ │ ├── code-previews.cjs │ │ ├── copy-code-buttons.cjs │ │ ├── external-links.cjs │ │ ├── highlight-code.cjs │ │ ├── markdown.cjs │ │ ├── prettier.cjs │ │ ├── replacer.cjs │ │ ├── scrolling-tables.cjs │ │ ├── strings.cjs │ │ ├── table-of-contents.cjs │ │ └── typography.cjs │ ├── assets/ │ │ ├── examples/ │ │ │ └── include.html │ │ ├── scripts/ │ │ │ ├── code-previews.js │ │ │ ├── docs.js │ │ │ ├── search.js │ │ │ └── turbo.js │ │ └── styles/ │ │ ├── code-previews.css │ │ ├── docs.css │ │ └── search.css │ ├── eleventy.config.cjs │ └── pages/ │ ├── 404.md │ ├── components/ │ │ ├── alert.md │ │ ├── animated-image.md │ │ ├── animation.md │ │ ├── avatar.md │ │ ├── badge.md │ │ ├── breadcrumb-item.md │ │ ├── breadcrumb.md │ │ ├── button-group.md │ │ ├── button.md │ │ ├── card.md │ │ ├── carousel-item.md │ │ ├── carousel.md │ │ ├── checkbox.md │ │ ├── color-picker.md │ │ ├── copy-button.md │ │ ├── details.md │ │ ├── dialog.md │ │ ├── divider.md │ │ ├── drawer.md │ │ ├── dropdown.md │ │ ├── format-bytes.md │ │ ├── format-date.md │ │ ├── format-number.md │ │ ├── icon-button.md │ │ ├── icon.md │ │ ├── image-comparer.md │ │ ├── include.md │ │ ├── input.md │ │ ├── menu-item.md │ │ ├── menu-label.md │ │ ├── menu.md │ │ ├── mutation-observer.md │ │ ├── option.md │ │ ├── popup.md │ │ ├── progress-bar.md │ │ ├── progress-ring.md │ │ ├── qr-code.md │ │ ├── radio-button.md │ │ ├── radio-group.md │ │ ├── radio.md │ │ ├── range.md │ │ ├── rating.md │ │ ├── relative-time.md │ │ ├── resize-observer.md │ │ ├── select.md │ │ ├── skeleton.md │ │ ├── spinner.md │ │ ├── split-panel.md │ │ ├── switch.md │ │ ├── tab-group.md │ │ ├── tab-panel.md │ │ ├── tab.md │ │ ├── tag.md │ │ ├── textarea.md │ │ ├── tooltip.md │ │ ├── tree-item.md │ │ ├── tree.md │ │ └── visually-hidden.md │ ├── frameworks/ │ │ ├── angular.md │ │ ├── react.md │ │ ├── svelte.md │ │ ├── vue-2.md │ │ └── vue.md │ ├── getting-started/ │ │ ├── customizing.md │ │ ├── form-controls.md │ │ ├── installation.md │ │ ├── localization.md │ │ ├── themes.md │ │ └── usage.md │ ├── index.md │ ├── resources/ │ │ ├── accessibility.md │ │ ├── changelog.md │ │ ├── community.md │ │ └── contributing.md │ ├── tokens/ │ │ ├── border-radius.md │ │ ├── color.md │ │ ├── elevation.md │ │ ├── more.md │ │ ├── spacing.md │ │ ├── transition.md │ │ ├── typography.md │ │ └── z-index.md │ └── tutorials/ │ ├── integrating-with-astro.md │ ├── integrating-with-laravel.md │ ├── integrating-with-nextjs.md │ └── integrating-with-rails.md ├── lint-staged.config.js ├── package.json ├── prettier.config.js ├── scripts/ │ ├── build.js │ ├── make-icons.js │ ├── make-metadata.js │ ├── make-react.js │ ├── make-themes.js │ ├── plop/ │ │ ├── plopfile.js │ │ └── templates/ │ │ └── component/ │ │ ├── component.hbs │ │ ├── define.hbs │ │ ├── docs.hbs │ │ ├── styles.hbs │ │ └── tests.hbs │ └── shared.js ├── src/ │ ├── components/ │ │ ├── alert/ │ │ │ ├── alert.component.ts │ │ │ ├── alert.styles.ts │ │ │ ├── alert.test.ts │ │ │ └── alert.ts │ │ ├── animated-image/ │ │ │ ├── animated-image.component.ts │ │ │ ├── animated-image.styles.ts │ │ │ ├── animated-image.test.ts │ │ │ └── animated-image.ts │ │ ├── animation/ │ │ │ ├── animation.component.ts │ │ │ ├── animation.styles.ts │ │ │ ├── animation.test.ts │ │ │ ├── animation.ts │ │ │ └── animations.ts │ │ ├── avatar/ │ │ │ ├── avatar.component.ts │ │ │ ├── avatar.styles.ts │ │ │ ├── avatar.test.ts │ │ │ └── avatar.ts │ │ ├── badge/ │ │ │ ├── badge.component.ts │ │ │ ├── badge.styles.ts │ │ │ ├── badge.test.ts │ │ │ └── badge.ts │ │ ├── breadcrumb/ │ │ │ ├── breadcrumb.component.ts │ │ │ ├── breadcrumb.styles.ts │ │ │ ├── breadcrumb.test.ts │ │ │ └── breadcrumb.ts │ │ ├── breadcrumb-item/ │ │ │ ├── breadcrumb-item.component.ts │ │ │ ├── breadcrumb-item.styles.ts │ │ │ ├── breadcrumb-item.test.ts │ │ │ └── breadcrumb-item.ts │ │ ├── button/ │ │ │ ├── button.component.ts │ │ │ ├── button.styles.ts │ │ │ ├── button.test.ts │ │ │ └── button.ts │ │ ├── button-group/ │ │ │ ├── button-group.component.ts │ │ │ ├── button-group.styles.ts │ │ │ ├── button-group.test.ts │ │ │ └── button-group.ts │ │ ├── card/ │ │ │ ├── card.component.ts │ │ │ ├── card.styles.ts │ │ │ ├── card.test.ts │ │ │ └── card.ts │ │ ├── carousel/ │ │ │ ├── autoplay-controller.ts │ │ │ ├── carousel.component.ts │ │ │ ├── carousel.styles.ts │ │ │ ├── carousel.test.ts │ │ │ └── carousel.ts │ │ ├── carousel-item/ │ │ │ ├── carousel-item.component.ts │ │ │ ├── carousel-item.styles.ts │ │ │ ├── carousel-item.test.ts │ │ │ └── carousel-item.ts │ │ ├── checkbox/ │ │ │ ├── checkbox.component.ts │ │ │ ├── checkbox.styles.ts │ │ │ ├── checkbox.test.ts │ │ │ └── checkbox.ts │ │ ├── color-picker/ │ │ │ ├── color-picker.component.ts │ │ │ ├── color-picker.styles.ts │ │ │ ├── color-picker.test.ts │ │ │ └── color-picker.ts │ │ ├── copy-button/ │ │ │ ├── copy-button.component.ts │ │ │ ├── copy-button.styles.ts │ │ │ ├── copy-button.test.ts │ │ │ └── copy-button.ts │ │ ├── details/ │ │ │ ├── details.component.ts │ │ │ ├── details.styles.ts │ │ │ ├── details.test.ts │ │ │ └── details.ts │ │ ├── dialog/ │ │ │ ├── dialog.component.ts │ │ │ ├── dialog.styles.ts │ │ │ ├── dialog.test.ts │ │ │ └── dialog.ts │ │ ├── divider/ │ │ │ ├── divider.component.ts │ │ │ ├── divider.styles.ts │ │ │ ├── divider.test.ts │ │ │ └── divider.ts │ │ ├── drawer/ │ │ │ ├── drawer.component.ts │ │ │ ├── drawer.styles.ts │ │ │ ├── drawer.test.ts │ │ │ └── drawer.ts │ │ ├── dropdown/ │ │ │ ├── dropdown.component.ts │ │ │ ├── dropdown.styles.ts │ │ │ ├── dropdown.test.ts │ │ │ └── dropdown.ts │ │ ├── format-bytes/ │ │ │ ├── format-bytes.component.ts │ │ │ ├── format-bytes.test.ts │ │ │ └── format-bytes.ts │ │ ├── format-date/ │ │ │ ├── format-date.component.ts │ │ │ ├── format-date.test.ts │ │ │ └── format-date.ts │ │ ├── format-number/ │ │ │ ├── format-number.component.ts │ │ │ ├── format-number.test.ts │ │ │ └── format-number.ts │ │ ├── icon/ │ │ │ ├── icon.component.ts │ │ │ ├── icon.styles.ts │ │ │ ├── icon.test.ts │ │ │ ├── icon.ts │ │ │ ├── library.default.ts │ │ │ ├── library.system.ts │ │ │ └── library.ts │ │ ├── icon-button/ │ │ │ ├── icon-button.component.ts │ │ │ ├── icon-button.styles.ts │ │ │ ├── icon-button.test.ts │ │ │ └── icon-button.ts │ │ ├── image-comparer/ │ │ │ ├── image-comparer.component.ts │ │ │ ├── image-comparer.styles.ts │ │ │ ├── image-comparer.test.ts │ │ │ └── image-comparer.ts │ │ ├── include/ │ │ │ ├── include.component.ts │ │ │ ├── include.styles.ts │ │ │ ├── include.test.ts │ │ │ ├── include.ts │ │ │ └── request.ts │ │ ├── input/ │ │ │ ├── input.component.ts │ │ │ ├── input.styles.ts │ │ │ ├── input.test.ts │ │ │ └── input.ts │ │ ├── menu/ │ │ │ ├── menu.component.ts │ │ │ ├── menu.styles.ts │ │ │ ├── menu.test.ts │ │ │ └── menu.ts │ │ ├── menu-item/ │ │ │ ├── menu-item.component.ts │ │ │ ├── menu-item.styles.ts │ │ │ ├── menu-item.test.ts │ │ │ ├── menu-item.ts │ │ │ └── submenu-controller.ts │ │ ├── menu-label/ │ │ │ ├── menu-label.component.ts │ │ │ ├── menu-label.styles.ts │ │ │ ├── menu-label.test.ts │ │ │ └── menu-label.ts │ │ ├── mutation-observer/ │ │ │ ├── mutation-observer.component.ts │ │ │ ├── mutation-observer.styles.ts │ │ │ ├── mutation-observer.test.ts │ │ │ └── mutation-observer.ts │ │ ├── option/ │ │ │ ├── option.component.ts │ │ │ ├── option.styles.ts │ │ │ ├── option.test.ts │ │ │ └── option.ts │ │ ├── popup/ │ │ │ ├── popup.component.ts │ │ │ ├── popup.styles.ts │ │ │ ├── popup.test.ts │ │ │ └── popup.ts │ │ ├── progress-bar/ │ │ │ ├── progress-bar.component.ts │ │ │ ├── progress-bar.styles.ts │ │ │ ├── progress-bar.test.ts │ │ │ └── progress-bar.ts │ │ ├── progress-ring/ │ │ │ ├── progress-ring.component.ts │ │ │ ├── progress-ring.styles.ts │ │ │ ├── progress-ring.test.ts │ │ │ └── progress-ring.ts │ │ ├── qr-code/ │ │ │ ├── qr-code.component.ts │ │ │ ├── qr-code.styles.ts │ │ │ ├── qr-code.test.ts │ │ │ └── qr-code.ts │ │ ├── radio/ │ │ │ ├── radio.component.ts │ │ │ ├── radio.styles.ts │ │ │ ├── radio.test.ts │ │ │ └── radio.ts │ │ ├── radio-button/ │ │ │ ├── radio-button.component.ts │ │ │ ├── radio-button.styles.ts │ │ │ ├── radio-button.test.ts │ │ │ └── radio-button.ts │ │ ├── radio-group/ │ │ │ ├── radio-group.component.ts │ │ │ ├── radio-group.styles.ts │ │ │ ├── radio-group.test.ts │ │ │ └── radio-group.ts │ │ ├── range/ │ │ │ ├── range.component.ts │ │ │ ├── range.styles.ts │ │ │ ├── range.test.ts │ │ │ └── range.ts │ │ ├── rating/ │ │ │ ├── rating.component.ts │ │ │ ├── rating.styles.ts │ │ │ ├── rating.test.ts │ │ │ └── rating.ts │ │ ├── relative-time/ │ │ │ ├── relative-time.component.ts │ │ │ ├── relative-time.test.ts │ │ │ └── relative-time.ts │ │ ├── resize-observer/ │ │ │ ├── resize-observer.component.ts │ │ │ ├── resize-observer.styles.ts │ │ │ └── resize-observer.ts │ │ ├── select/ │ │ │ ├── select.component.ts │ │ │ ├── select.styles.ts │ │ │ ├── select.test.ts │ │ │ └── select.ts │ │ ├── skeleton/ │ │ │ ├── skeleton.component.ts │ │ │ ├── skeleton.styles.ts │ │ │ ├── skeleton.test.ts │ │ │ └── skeleton.ts │ │ ├── spinner/ │ │ │ ├── spinner.component.ts │ │ │ ├── spinner.styles.ts │ │ │ ├── spinner.test.ts │ │ │ └── spinner.ts │ │ ├── split-panel/ │ │ │ ├── split-panel.component.ts │ │ │ ├── split-panel.styles.ts │ │ │ ├── split-panel.test.ts │ │ │ └── split-panel.ts │ │ ├── switch/ │ │ │ ├── switch.component.ts │ │ │ ├── switch.styles.ts │ │ │ ├── switch.test.ts │ │ │ └── switch.ts │ │ ├── tab/ │ │ │ ├── tab.component.ts │ │ │ ├── tab.styles.ts │ │ │ ├── tab.test.ts │ │ │ └── tab.ts │ │ ├── tab-group/ │ │ │ ├── tab-group.component.ts │ │ │ ├── tab-group.styles.ts │ │ │ ├── tab-group.test.ts │ │ │ └── tab-group.ts │ │ ├── tab-panel/ │ │ │ ├── tab-panel.component.ts │ │ │ ├── tab-panel.styles.ts │ │ │ ├── tab-panel.test.ts │ │ │ └── tab-panel.ts │ │ ├── tag/ │ │ │ ├── tag.component.ts │ │ │ ├── tag.styles.ts │ │ │ ├── tag.test.ts │ │ │ └── tag.ts │ │ ├── textarea/ │ │ │ ├── textarea.component.ts │ │ │ ├── textarea.styles.ts │ │ │ ├── textarea.test.ts │ │ │ └── textarea.ts │ │ ├── tooltip/ │ │ │ ├── tooltip.component.ts │ │ │ ├── tooltip.styles.ts │ │ │ ├── tooltip.test.ts │ │ │ └── tooltip.ts │ │ ├── tree/ │ │ │ ├── tree.component.ts │ │ │ ├── tree.styles.ts │ │ │ ├── tree.test.ts │ │ │ └── tree.ts │ │ ├── tree-item/ │ │ │ ├── tree-item.component.ts │ │ │ ├── tree-item.styles.ts │ │ │ ├── tree-item.test.ts │ │ │ └── tree-item.ts │ │ └── visually-hidden/ │ │ ├── visually-hidden.component.ts │ │ ├── visually-hidden.styles.ts │ │ ├── visually-hidden.test.ts │ │ └── visually-hidden.ts │ ├── declaration.d.ts │ ├── events/ │ │ ├── events.ts │ │ ├── sl-after-collapse.ts │ │ ├── sl-after-expand.ts │ │ ├── sl-after-hide.ts │ │ ├── sl-after-show.ts │ │ ├── sl-blur.ts │ │ ├── sl-cancel.ts │ │ ├── sl-change.ts │ │ ├── sl-clear.ts │ │ ├── sl-close.ts │ │ ├── sl-collapse.ts │ │ ├── sl-copy.ts │ │ ├── sl-error.ts │ │ ├── sl-expand.ts │ │ ├── sl-finish.ts │ │ ├── sl-focus.ts │ │ ├── sl-hide.ts │ │ ├── sl-hover.ts │ │ ├── sl-initial-focus.ts │ │ ├── sl-input.ts │ │ ├── sl-invalid.ts │ │ ├── sl-lazy-change.ts │ │ ├── sl-lazy-load.ts │ │ ├── sl-load.ts │ │ ├── sl-mutation.ts │ │ ├── sl-remove.ts │ │ ├── sl-reposition.ts │ │ ├── sl-request-close.ts │ │ ├── sl-resize.ts │ │ ├── sl-select.ts │ │ ├── sl-selection-change.ts │ │ ├── sl-show.ts │ │ ├── sl-slide-change.ts │ │ ├── sl-start.ts │ │ ├── sl-tab-hide.ts │ │ └── sl-tab-show.ts │ ├── internal/ │ │ ├── active-elements.ts │ │ ├── animate.ts │ │ ├── closeActiveElement.ts │ │ ├── debounce.ts │ │ ├── default-value.ts │ │ ├── drag.ts │ │ ├── event.ts │ │ ├── form.test.ts │ │ ├── form.ts │ │ ├── math.ts │ │ ├── modal.ts │ │ ├── offset.ts │ │ ├── scroll.ts │ │ ├── scrollend-polyfill.ts │ │ ├── shoelace-element.test.ts │ │ ├── shoelace-element.ts │ │ ├── slot.ts │ │ ├── string.ts │ │ ├── tabbable.test.ts │ │ ├── tabbable.ts │ │ ├── test/ │ │ │ ├── data-testid-helpers.ts │ │ │ ├── element-visible-overflow.ts │ │ │ ├── form-control-base-tests.ts │ │ │ └── wait-for-scrolling.ts │ │ ├── test.ts │ │ └── watch.ts │ ├── shoelace-autoloader.ts │ ├── shoelace.ts │ ├── styles/ │ │ ├── component.styles.ts │ │ └── form-control.styles.ts │ ├── themes/ │ │ ├── _utility.css │ │ ├── dark.css │ │ └── light.css │ ├── translations/ │ │ ├── ar.ts │ │ ├── cs.ts │ │ ├── da.ts │ │ ├── de-ch.ts │ │ ├── de.ts │ │ ├── en-gb.ts │ │ ├── en.ts │ │ ├── es.ts │ │ ├── fa.ts │ │ ├── fi.ts │ │ ├── fr.ts │ │ ├── he.ts │ │ ├── hr.ts │ │ ├── hu.ts │ │ ├── id.ts │ │ ├── it.ts │ │ ├── ja.ts │ │ ├── nb.ts │ │ ├── nl.ts │ │ ├── nn.ts │ │ ├── pl.ts │ │ ├── pt.ts │ │ ├── ru.ts │ │ ├── sl.ts │ │ ├── sv.ts │ │ ├── tr.ts │ │ ├── uk.ts │ │ ├── zh-cn.ts │ │ └── zh-tw.ts │ └── utilities/ │ ├── animation-registry.ts │ ├── animation.ts │ ├── base-path.ts │ ├── form.ts │ ├── icon-library.ts │ └── localize.ts ├── tsconfig.json ├── tsconfig.prod.json └── web-test-runner.config.js
SYMBOL INDEX (1000 symbols across 205 files)
FILE: custom-elements-manifest.config.js
function noDash (line 19) | function noDash(string) {
function replace (line 23) | function replace(string, terms) {
method packageLinkPhase (line 38) | packageLinkPhase({ customElementsManifest }) {
method analyzePhase (line 46) | analyzePhase({ ts, node, moduleDoc }) {
method analyzePhase (line 75) | analyzePhase({ ts, node, moduleDoc }) {
method analyzePhase (line 146) | analyzePhase({ ts, node, moduleDoc }) {
method packageLinkPhase (line 165) | packageLinkPhase({ customElementsManifest }) {
FILE: docs/_utilities/active-links.cjs
function normalizePathname (line 1) | function normalizePathname(pathname) {
FILE: docs/_utilities/cem.cjs
function getDependencies (line 46) | function getDependencies(tag) {
FILE: docs/_utilities/code-previews.cjs
function escapeHtml (line 3) | function escapeHtml(str) {
FILE: docs/_utilities/highlight-code.cjs
function highlight (line 8) | function highlight(code, language) {
FILE: docs/assets/scripts/code-previews.js
function convertModuleLinks (line 2) | function convertModuleLinks(html) {
function getAdjacentExample (line 11) | function getAdjacentExample(name, pre) {
function runScript (line 25) | function runScript(script) {
function getFlavor (line 38) | function getFlavor() {
function setFlavor (line 42) | function setFlavor(newFlavor) {
function syncFlavor (line 51) | function syncFlavor() {
function handleResizerDrag (line 88) | function handleResizerDrag(event) {
function toggleSource (line 153) | function toggleSource(codeBlock, force) {
FILE: docs/assets/scripts/docs.js
function getSidebar (line 9) | function getSidebar() {
function isSidebarOpen (line 13) | function isSidebarOpen() {
function isSidebarVisible (line 17) | function isSidebarVisible() {
function toggleSidebar (line 21) | function toggleSidebar(force) {
function updateInert (line 26) | function updateInert() {
function getTheme (line 80) | function getTheme() {
function isDark (line 84) | function isDark() {
function setTheme (line 91) | function setTheme(newTheme) {
function updateSelection (line 102) | function updateSelection() {
function handleIntersect (line 210) | function handleIntersect(entries) {
function updateActiveLinks (line 223) | function updateActiveLinks() {
function observeLinks (line 239) | function observeLinks() {
function updateVersion (line 261) | function updateVersion() {
FILE: docs/assets/scripts/search.js
function show (line 126) | async function show() {
function hide (line 152) | async function hide() {
function handleInput (line 179) | function handleInput() {
function handleClear (line 187) | function handleClear() {
function handleMouseDown (line 194) | function handleMouseDown(event) {
function handleKeyDown (line 200) | function handleKeyDown(event) {
function updateResults (line 253) | async function updateResults(query = '') {
FILE: docs/assets/scripts/turbo.js
function preserveScroll (line 8) | function preserveScroll() {
function restoreScroll (line 14) | function restoreScroll(event) {
FILE: scripts/build.js
function buildTheDocs (line 34) | async function buildTheDocs(watch = false) {
function buildTheSource (line 89) | async function buildTheSource() {
function handleCleanup (line 158) | function handleCleanup() {
function nextTask (line 171) | async function nextTask(label, action) {
FILE: scripts/shared.js
function getAllComponents (line 2) | function getAllComponents(metadata) {
FILE: src/components/alert/alert.component.ts
class SlAlert (line 42) | class SlAlert extends ShoelaceElement {
method toastStack (line 54) | private static get toastStack() {
method firstUpdated (line 94) | firstUpdated() {
method restartAutoHide (line 98) | private restartAutoHide() {
method pauseAutoHide (line 111) | private pauseAutoHide() {
method resumeAutoHide (line 117) | private resumeAutoHide() {
method handleCountdownChange (line 127) | private handleCountdownChange() {
method handleCloseClick (line 139) | private handleCloseClick() {
method handleOpenChange (line 144) | async handleOpenChange() {
method handleDurationChange (line 177) | handleDurationChange() {
method show (line 182) | async show() {
method hide (line 192) | async hide() {
method toast (line 206) | async toast() {
method render (line 238) | render() {
FILE: src/components/alert/alert.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/animated-image/animated-image.component.ts
class SlAnimatedImage (line 29) | class SlAnimatedImage extends ShoelaceElement {
method handleClick (line 47) | private handleClick() {
method handleLoad (line 51) | private handleLoad() {
method handleError (line 65) | private handleError() {
method handlePlayChange (line 70) | handlePlayChange() {
method handleSrcChange (line 80) | handleSrcChange() {
method render (line 84) | render() {
FILE: src/components/animated-image/animated-image.test.ts
function loadImage (line 67) | async function loadImage(animatedImage: SlAnimatedImage, file: string) {
FILE: src/components/animated-image/animated-image.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/animation/animation.component.ts
class SlAnimation (line 23) | class SlAnimation extends ShoelaceElement {
method currentTime (line 81) | get currentTime(): CSSNumberish {
method currentTime (line 85) | set currentTime(time: number) {
method connectedCallback (line 91) | connectedCallback() {
method disconnectedCallback (line 96) | disconnectedCallback() {
method handleSlotChange (line 113) | private handleSlotChange() {
method createAnimation (line 118) | private async createAnimation() {
method destroyAnimation (line 153) | private destroyAnimation() {
method handleAnimationChange (line 174) | handleAnimationChange() {
method handlePlayChange (line 183) | handlePlayChange() {
method handlePlaybackRateChange (line 202) | handlePlaybackRateChange() {
method cancel (line 209) | cancel() {
method finish (line 214) | finish() {
method render (line 218) | render() {
FILE: src/components/animation/animation.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/animation/animations.ts
function getAnimationNames (line 6) | function getAnimationNames() {
function getEasingNames (line 13) | function getEasingNames() {
FILE: src/components/avatar/avatar.component.ts
class SlAvatar (line 31) | class SlAvatar extends ShoelaceElement {
method handleImageChange (line 55) | handleImageChange() {
method handleImageLoadError (line 60) | private handleImageLoadError() {
method render (line 65) | render() {
FILE: src/components/avatar/avatar.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/badge/badge.component.ts
class SlBadge (line 19) | class SlBadge extends ShoelaceElement {
method render (line 31) | render() {
FILE: src/components/badge/badge.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/breadcrumb-item/breadcrumb-item.component.ts
class SlBreadcrumbItem (line 30) | class SlBreadcrumbItem extends ShoelaceElement {
method setRenderType (line 51) | private setRenderType() {
method hrefChanged (line 70) | hrefChanged() {
method handleSlotChange (line 74) | handleSlotChange() {
method render (line 78) | render() {
FILE: src/components/breadcrumb-item/breadcrumb-item.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/breadcrumb/breadcrumb.component.ts
class SlBreadcrumb (line 24) | class SlBreadcrumb extends ShoelaceElement {
method getSeparator (line 41) | private getSeparator() {
method handleSlotChange (line 53) | private handleSlotChange() {
method render (line 80) | render() {
FILE: src/components/breadcrumb/breadcrumb.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/button-group/button-group.component.ts
class SlButtonGroup (line 18) | class SlButtonGroup extends ShoelaceElement {
method handleFocus (line 31) | private handleFocus(event: Event) {
method handleBlur (line 36) | private handleBlur(event: Event) {
method handleMouseOver (line 41) | private handleMouseOver(event: Event) {
method handleMouseOut (line 46) | private handleMouseOut(event: Event) {
method handleSlotChange (line 51) | private handleSlotChange() {
method render (line 71) | render() {
function findButton (line 90) | function findButton(el: HTMLElement) {
FILE: src/components/button-group/button-group.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/button/button.component.ts
class SlButton (line 41) | class SlButton extends ShoelaceElement implements ShoelaceFormControl {
method validity (line 147) | get validity() {
method validationMessage (line 156) | get validationMessage() {
method firstUpdated (line 164) | firstUpdated() {
method handleBlur (line 170) | private handleBlur() {
method handleFocus (line 175) | private handleFocus() {
method handleClick (line 180) | private handleClick() {
method handleInvalid (line 190) | private handleInvalid(event: Event) {
method isButton (line 195) | private isButton() {
method isLink (line 199) | private isLink() {
method handleDisabledChange (line 204) | handleDisabledChange() {
method click (line 212) | click() {
method focus (line 217) | focus(options?: FocusOptions) {
method blur (line 222) | blur() {
method checkValidity (line 227) | checkValidity() {
method getForm (line 236) | getForm(): HTMLFormElement | null {
method reportValidity (line 241) | reportValidity() {
method setCustomValidity (line 250) | setCustomValidity(message: string) {
method render (line 257) | render() {
FILE: src/components/button/button.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/card/card.component.ts
class SlCard (line 31) | class SlCard extends ShoelaceElement {
method render (line 36) | render() {
FILE: src/components/card/card.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/carousel-item/carousel-item.component.ts
class SlCarouselItem (line 18) | class SlCarouselItem extends ShoelaceElement {
method connectedCallback (line 21) | connectedCallback() {
method render (line 25) | render() {
FILE: src/components/carousel-item/carousel-item.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/carousel/autoplay-controller.ts
class AutoplayController (line 7) | class AutoplayController implements ReactiveController {
method constructor (line 16) | constructor(host: ReactiveElement, tickCallback: () => void) {
method hostConnected (line 23) | hostConnected(): void {
method hostDisconnected (line 32) | hostDisconnected(): void {
method start (line 43) | start(interval: number) {
method stop (line 54) | stop() {
FILE: src/components/carousel/carousel.component.ts
class SlCarousel (line 50) | class SlCarousel extends ShoelaceElement {
method connectedCallback (line 100) | connectedCallback(): void {
method disconnectedCallback (line 106) | disconnectedCallback(): void {
method firstUpdated (line 111) | protected firstUpdated(): void {
method willUpdate (line 120) | protected willUpdate(changedProperties: PropertyValueMap<SlCarousel> |...
method getPageCount (line 127) | private getPageCount() {
method getCurrentPage (line 136) | private getCurrentPage() {
method canScrollNext (line 140) | private canScrollNext(): boolean {
method canScrollPrev (line 144) | private canScrollPrev(): boolean {
method getSlides (line 149) | private getSlides({ excludeClones = true }: { excludeClones?: boolean ...
method handleClick (line 155) | private handleClick(event: MouseEvent) {
method handleKeyDown (line 169) | private handleKeyDown(event: KeyboardEvent) {
method handleMouseDragStart (line 211) | private handleMouseDragStart(event: PointerEvent) {
method handleScroll (line 280) | private handleScroll() {
method synchronizeSlides (line 288) | private synchronizeSlides() {
method handleScrollEnd (line 337) | private handleScrollEnd() {
method isCarouselItem (line 344) | private isCarouselItem(node: Node): node is SlCarouselItem {
method initializeSlides (line 365) | initializeSlides() {
method createClones (line 398) | private createClones() {
method handleSlideChange (line 419) | handleSlideChange() {
method updateSlidesSnap (line 437) | updateSlidesSnap() {
method handleAutoplayChange (line 452) | handleAutoplayChange() {
method previous (line 464) | previous(behavior: ScrollBehavior = 'smooth') {
method next (line 473) | next(behavior: ScrollBehavior = 'smooth') {
method goToSlide (line 483) | goToSlide(index: number, behavior: ScrollBehavior = 'smooth') {
method scrollToSlide (line 516) | private scrollToSlide(slide: HTMLElement, behavior: ScrollBehavior = '...
method render (line 549) | render() {
FILE: src/components/carousel/carousel.test.ts
method constructor (line 22) | constructor(callback: IntersectionObserverCallback, options?: Intersecti...
FILE: src/components/carousel/carousel.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/checkbox/checkbox.component.ts
class SlCheckbox (line 44) | class SlCheckbox extends ShoelaceElement implements ShoelaceFormControl {
method validity (line 99) | get validity() {
method validationMessage (line 104) | get validationMessage() {
method firstUpdated (line 108) | firstUpdated() {
method handleClick (line 112) | private handleClick() {
method handleBlur (line 118) | private handleBlur() {
method handleInput (line 123) | private handleInput() {
method handleInvalid (line 127) | private handleInvalid(event: Event) {
method handleFocus (line 132) | private handleFocus() {
method handleDisabledChange (line 138) | handleDisabledChange() {
method handleStateChange (line 144) | handleStateChange() {
method click (line 151) | click() {
method focus (line 156) | focus(options?: FocusOptions) {
method blur (line 161) | blur() {
method checkValidity (line 166) | checkValidity() {
method getForm (line 171) | getForm(): HTMLFormElement | null {
method reportValidity (line 176) | reportValidity() {
method setCustomValidity (line 184) | setCustomValidity(message: string) {
method render (line 189) | render() {
FILE: src/components/checkbox/checkbox.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/color-picker/color-picker.component.ts
type EyeDropperConstructor (line 29) | interface EyeDropperConstructor {
type EyeDropperInterface (line 33) | interface EyeDropperInterface {
class SlColorPicker (line 93) | class SlColorPicker extends ShoelaceElement implements ShoelaceFormContr...
method validity (line 191) | get validity() {
method validationMessage (line 196) | get validationMessage() {
method constructor (line 200) | constructor() {
method firstUpdated (line 206) | firstUpdated() {
method handleCopy (line 212) | private handleCopy() {
method handleFormatToggle (line 234) | private handleFormatToggle() {
method handleAlphaDrag (line 243) | private handleAlphaDrag(event: PointerEvent) {
method handleHueDrag (line 273) | private handleHueDrag(event: PointerEvent) {
method handleGridDrag (line 303) | private handleGridDrag(event: PointerEvent) {
method handleAlphaKeyDown (line 337) | private handleAlphaKeyDown(event: KeyboardEvent) {
method handleHueKeyDown (line 371) | private handleHueKeyDown(event: KeyboardEvent) {
method handleGridKeyDown (line 405) | private handleGridKeyDown(event: KeyboardEvent) {
method handleInputChange (line 439) | private handleInputChange(event: SlChangeEvent) {
method handleInputInput (line 459) | private handleInputInput(event: SlInputEvent) {
method handleInputKeyDown (line 466) | private handleInputKeyDown(event: KeyboardEvent) {
method handleInputInvalid (line 486) | private handleInputInvalid(event: Event) {
method handleTouchMove (line 492) | private handleTouchMove(event: TouchEvent) {
method parseColor (line 496) | private parseColor(colorString: string) {
method setColor (line 576) | private setColor(colorString: string) {
method setLetterCase (line 593) | private setLetterCase(string: string) {
method syncValues (line 600) | private async syncValues() {
method handleAfterHide (line 629) | private handleAfterHide() {
method handleEyeDropper (line 633) | private handleEyeDropper() {
method selectSwatch (line 657) | private selectSwatch(color: string) {
method getHexString (line 671) | private getHexString(hue: number, saturation: number, brightness: numb...
method stopNestedEventPropagation (line 681) | private stopNestedEventPropagation(event: CustomEvent) {
method handleFormatChange (line 686) | handleFormatChange() {
method handleOpacityChange (line 691) | handleOpacityChange() {
method handleValueChange (line 696) | handleValueChange(oldValue: string | undefined, newValue: string) {
method focus (line 723) | focus(options?: FocusOptions) {
method blur (line 732) | blur() {
method getFormattedValue (line 749) | getFormattedValue(format: 'hex' | 'hexa' | 'rgb' | 'rgba' | 'hsl' | 'h...
method checkValidity (line 781) | checkValidity() {
method getForm (line 786) | getForm(): HTMLFormElement | null {
method reportValidity (line 791) | reportValidity() {
method setCustomValidity (line 809) | setCustomValidity(message: string) {
method render (line 814) | render() {
FILE: src/components/color-picker/color-picker.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/copy-button/copy-button.component.ts
class SlCopyButton (line 44) | class SlCopyButton extends ShoelaceElement {
method handleCopy (line 97) | private async handleCopy() {
method showStatus (line 165) | private async showStatus(status: 'success' | 'error') {
method render (line 195) | render() {
FILE: src/components/copy-button/copy-button.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/details/details.component.ts
class SlDetails (line 42) | class SlDetails extends ShoelaceElement {
method firstUpdated (line 70) | firstUpdated() {
method disconnectedCallback (line 90) | disconnectedCallback() {
method handleSummaryClick (line 95) | private handleSummaryClick(event: MouseEvent) {
method handleSummaryKeyDown (line 108) | private handleSummaryKeyDown(event: KeyboardEvent) {
method handleOpenChange (line 131) | async handleOpenChange() {
method show (line 170) | async show() {
method hide (line 180) | async hide() {
method render (line 189) | render() {
FILE: src/components/details/details.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/dialog/dialog.component.ts
class SlDialog (line 70) | class SlDialog extends ShoelaceElement {
method firstUpdated (line 104) | firstUpdated() {
method disconnectedCallback (line 114) | disconnectedCallback() {
method requestClose (line 121) | private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
method addOpenListeners (line 136) | private addOpenListeners() {
method removeOpenListeners (line 146) | private removeOpenListeners() {
method handleOpenChange (line 159) | async handleOpenChange() {
method show (line 252) | async show() {
method hide (line 262) | async hide() {
method render (line 271) | render() {
FILE: src/components/dialog/dialog.test.ts
class AContainer (line 153) | class AContainer extends LitElement {
method dialog (line 154) | get dialog() {
method openDialog (line 158) | openDialog() {
method render (line 162) | render() {
function pressTab (line 266) | async function pressTab() {
FILE: src/components/dialog/dialog.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/divider/divider.component.ts
class SlDivider (line 18) | class SlDivider extends ShoelaceElement {
method connectedCallback (line 24) | connectedCallback() {
method handleVerticalChange (line 30) | handleVerticalChange() {
FILE: src/components/divider/divider.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/drawer/drawer.component.ts
class SlDrawer (line 78) | class SlDrawer extends ShoelaceElement {
method firstUpdated (line 119) | firstUpdated() {
method disconnectedCallback (line 132) | disconnectedCallback() {
method requestClose (line 138) | private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
method addOpenListeners (line 153) | private addOpenListeners() {
method removeOpenListeners (line 165) | private removeOpenListeners() {
method handleOpenChange (line 183) | async handleOpenChange() {
method handleNoModalChange (line 285) | handleNoModalChange() {
method show (line 298) | async show() {
method hide (line 308) | async hide() {
method render (line 317) | render() {
FILE: src/components/drawer/drawer.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/dropdown/dropdown.component.ts
class SlDropdown (line 46) | class SlDropdown extends ShoelaceElement {
method connectedCallback (line 113) | connectedCallback() {
method firstUpdated (line 121) | firstUpdated() {
method disconnectedCallback (line 131) | disconnectedCallback() {
method focusOnTrigger (line 137) | focusOnTrigger() {
method getMenu (line 144) | getMenu() {
method handleTriggerClick (line 232) | handleTriggerClick() {
method handleTriggerKeyDown (line 241) | async handleTriggerKeyDown(event: KeyboardEvent) {
method handleTriggerKeyUp (line 289) | handleTriggerKeyUp(event: KeyboardEvent) {
method handleTriggerSlotChange (line 296) | handleTriggerSlotChange() {
method updateAccessibleTrigger (line 310) | updateAccessibleTrigger() {
method show (line 333) | async show() {
method hide (line 343) | async hide() {
method reposition (line 356) | reposition() {
method addOpenListeners (line 360) | addOpenListeners() {
method removeOpenListeners (line 376) | removeOpenListeners() {
method handleOpenChange (line 387) | async handleOpenChange() {
method render (line 422) | render() {
FILE: src/components/dropdown/dropdown.test.ts
class Wrapper (line 362) | @customElement('custom-wrapper')
method render (line 364) | render() {
class NestedDropdown (line 371) | @customElement('nested-dropdown')
method render (line 373) | render() {
class WrapperArbitrary (line 408) | @customElement('custom-wrapper-arbitrary')
method render (line 410) | render() {
class NestedDropdownArbitrary (line 417) | @customElement('nested-dropdown-arbitrary')
method render (line 419) | render() {
FILE: src/components/dropdown/dropdown.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/format-bytes/format-bytes.component.ts
class SlFormatBytes (line 11) | class SlFormatBytes extends ShoelaceElement {
method render (line 23) | render() {
FILE: src/components/format-bytes/format-bytes.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/format-date/format-date.component.ts
class SlFormatDate (line 12) | class SlFormatDate extends ShoelaceElement {
method render (line 55) | render() {
FILE: src/components/format-date/format-date.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/format-number/format-number.component.ts
class SlFormatNumber (line 11) | class SlFormatNumber extends ShoelaceElement {
method render (line 44) | render() {
FILE: src/components/format-number/format-number.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/icon-button/icon-button.component.ts
class SlIconButton (line 24) | class SlIconButton extends ShoelaceElement {
method handleBlur (line 62) | private handleBlur() {
method handleFocus (line 67) | private handleFocus() {
method handleClick (line 72) | private handleClick(event: MouseEvent) {
method click (line 80) | click() {
method focus (line 85) | focus(options?: FocusOptions) {
method blur (line 90) | blur() {
method render (line 94) | render() {
FILE: src/components/icon-button/icon-button.test.ts
type LinkTarget (line 6) | type LinkTarget = '_self' | '_blank' | '_parent' | '_top';
FILE: src/components/icon-button/icon-button.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/icon/icon.component.ts
constant CACHEABLE_ERROR (line 11) | const CACHEABLE_ERROR = Symbol();
constant RETRYABLE_ERROR (line 12) | const RETRYABLE_ERROR = Symbol();
type SVGResult (line 13) | type SVGResult = HTMLTemplateResult | SVGSVGElement | typeof RETRYABLE_E...
type IconSource (line 18) | interface IconSource {
class SlIcon (line 35) | class SlIcon extends ShoelaceElement {
method resolveIcon (line 41) | private async resolveIcon(url: string, library?: IconLibrary): Promise...
method connectedCallback (line 99) | connectedCallback() {
method firstUpdated (line 104) | firstUpdated() {
method disconnectedCallback (line 109) | disconnectedCallback() {
method getIconSource (line 114) | private getIconSource(): IconSource {
method handleLabelChange (line 130) | handleLabelChange() {
method setIcon (line 145) | async setIcon() {
method render (line 207) | render() {
FILE: src/components/icon/icon.test.ts
method resolver (line 245) | resolver(name) {
method mutator (line 248) | mutator(svg) {
FILE: src/components/icon/icon.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/icon/library.ts
type IconLibraryResolver (line 5) | type IconLibraryResolver = (name: string) => string;
type IconLibraryMutator (line 6) | type IconLibraryMutator = (svg: SVGElement) => void;
type IconLibrary (line 7) | interface IconLibrary {
function watchIcon (line 18) | function watchIcon(icon: SlIcon) {
function unwatchIcon (line 23) | function unwatchIcon(icon: SlIcon) {
function getIconLibrary (line 28) | function getIconLibrary(name?: string) {
function registerIconLibrary (line 33) | function registerIconLibrary(name: string, options: Omit<IconLibrary, 'n...
function unregisterIconLibrary (line 51) | function unregisterIconLibrary(name: string) {
FILE: src/components/image-comparer/image-comparer.component.ts
class SlImageComparer (line 38) | class SlImageComparer extends ShoelaceElement {
method handleDrag (line 50) | private handleDrag(event: PointerEvent) {
method handleKeyDown (line 65) | private handleKeyDown(event: KeyboardEvent) {
method handlePositionChange (line 94) | handlePositionChange() {
method render (line 98) | render() {
FILE: src/components/image-comparer/image-comparer.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/include/include.component.ts
class SlInclude (line 19) | class SlInclude extends ShoelaceElement {
method executeScript (line 37) | private executeScript(script: HTMLScriptElement) {
method handleSrcChange (line 46) | async handleSrcChange() {
method render (line 73) | render() {
FILE: src/components/include/include.test.ts
function delayResolve (line 24) | async function delayResolve(resolveValue: string) {
FILE: src/components/include/include.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/include/request.ts
type IncludeFile (line 1) | interface IncludeFile {
function requestInclude (line 10) | function requestInclude(src: string, mode: 'cors' | 'no-cors' | 'same-or...
FILE: src/components/input/input.component.ts
class SlInput (line 53) | class SlInput extends ShoelaceElement implements ShoelaceFormControl {
method valueAsDate (line 206) | get valueAsDate() {
method valueAsDate (line 212) | set valueAsDate(newValue: Date | null) {
method valueAsNumber (line 219) | get valueAsNumber() {
method valueAsNumber (line 224) | set valueAsNumber(newValue: number) {
method validity (line 230) | get validity() {
method validationMessage (line 235) | get validationMessage() {
method firstUpdated (line 239) | firstUpdated() {
method handleBlur (line 243) | private handleBlur() {
method handleChange (line 248) | private handleChange() {
method handleClearClick (line 253) | private handleClearClick(event: MouseEvent) {
method handleFocus (line 266) | private handleFocus() {
method handleInput (line 271) | private handleInput() {
method handleInvalid (line 277) | private handleInvalid(event: Event) {
method handleKeyDown (line 282) | private handleKeyDown(event: KeyboardEvent) {
method handlePasswordToggle (line 302) | private handlePasswordToggle() {
method handleDisabledChange (line 307) | handleDisabledChange() {
method handleStepChange (line 313) | handleStepChange() {
method handleValueChange (line 321) | async handleValueChange() {
method focus (line 327) | focus(options?: FocusOptions) {
method blur (line 332) | blur() {
method select (line 337) | select() {
method setSelectionRange (line 342) | setSelectionRange(
method setRangeText (line 351) | setRangeText(
method showPicker (line 368) | showPicker() {
method stepUp (line 375) | stepUp() {
method stepDown (line 383) | stepDown() {
method checkValidity (line 391) | checkValidity() {
method getForm (line 396) | getForm(): HTMLFormElement | null {
method reportValidity (line 401) | reportValidity() {
method setCustomValidity (line 406) | setCustomValidity(message: string) {
method render (line 411) | render() {
FILE: src/components/input/input.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/menu-item/menu-item.component.ts
class SlMenuItem (line 42) | class SlMenuItem extends ShoelaceElement {
method connectedCallback (line 74) | connectedCallback() {
method disconnectedCallback (line 80) | disconnectedCallback() {
method handleDefaultSlotChange (line 86) | private handleDefaultSlotChange() {
method handleCheckedChange (line 116) | handleCheckedChange() {
method handleDisabledChange (line 133) | handleDisabledChange() {
method handleTypeChange (line 138) | handleTypeChange() {
method getTextLabel (line 149) | getTextLabel() {
method isSubmenu (line 153) | isSubmenu() {
method render (line 157) | render() {
FILE: src/components/menu-item/menu-item.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/menu-item/submenu-controller.ts
class SubmenuController (line 9) | class SubmenuController implements ReactiveController {
method constructor (line 19) | constructor(host: ReactiveControllerHost & SlMenuItem, hasSlotControll...
method hostConnected (line 24) | hostConnected() {
method hostDisconnected (line 30) | hostDisconnected() {
method hostUpdated (line 34) | hostUpdated() {
method addListeners (line 43) | private addListeners() {
method removeListeners (line 64) | private removeListeners() {
method handleSubmenuEntry (line 94) | private handleSubmenuEntry(event: KeyboardEvent) {
method setSubmenuState (line 211) | private setSubmenuState(state: boolean) {
method enableSubmenu (line 222) | private enableSubmenu(delay = true) {
method disableSubmenu (line 233) | private disableSubmenu() {
method updateSkidding (line 239) | private updateSkidding(): void {
method isExpanded (line 257) | isExpanded(): boolean {
method renderSubmenu (line 261) | renderSubmenu() {
FILE: src/components/menu-label/menu-label.component.ts
class SlMenuLabel (line 17) | class SlMenuLabel extends ShoelaceElement {
method render (line 20) | render() {
type HTMLElementTagNameMap (line 26) | interface HTMLElementTagNameMap {
FILE: src/components/menu-label/menu-label.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/menu/menu.component.ts
type MenuSelectEventDetail (line 9) | interface MenuSelectEventDetail {
class SlMenu (line 23) | class SlMenu extends ShoelaceElement {
method connectedCallback (line 28) | connectedCallback() {
method handleClick (line 33) | private handleClick(event: MouseEvent) {
method handleKeyDown (line 57) | private handleKeyDown(event: KeyboardEvent) {
method handleMouseDown (line 101) | private handleMouseDown(event: MouseEvent) {
method handleSlotChange (line 109) | private handleSlotChange() {
method isMenuItem (line 118) | private isMenuItem(item: HTMLElement) {
method getAllItems (line 126) | getAllItems() {
method getCurrentItem (line 139) | getCurrentItem() {
method setCurrentItem (line 147) | setCurrentItem(item: SlMenuItem) {
method render (line 156) | render() {
FILE: src/components/menu/menu.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/mutation-observer/mutation-observer.component.ts
class SlMutationObserver (line 19) | class SlMutationObserver extends ShoelaceElement {
method connectedCallback (line 45) | connectedCallback() {
method disconnectedCallback (line 55) | disconnectedCallback() {
method startObserver (line 66) | private startObserver() {
method stopObserver (line 89) | private stopObserver() {
method handleDisabledChange (line 94) | handleDisabledChange() {
method handleChange (line 107) | handleChange() {
method render (line 112) | render() {
FILE: src/components/mutation-observer/mutation-observer.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/option/option.component.ts
class SlOption (line 30) | class SlOption extends ShoelaceElement {
method connectedCallback (line 55) | connectedCallback() {
method handleDefaultSlotChange (line 61) | private handleDefaultSlotChange() {
method handleMouseEnter (line 75) | private handleMouseEnter() {
method handleMouseLeave (line 79) | private handleMouseLeave() {
method handleDisabledChange (line 84) | handleDisabledChange() {
method handleSelectedChange (line 89) | handleSelectedChange() {
method handleValueChange (line 94) | handleValueChange() {
method getTextLabel (line 108) | getTextLabel() {
method render (line 127) | render() {
FILE: src/components/option/option.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/popup/popup.component.ts
type VirtualElement (line 12) | interface VirtualElement {
function isVirtualElement (line 17) | function isVirtualElement(e: unknown): e is VirtualElement {
class SlPopup (line 55) | class SlPopup extends ShoelaceElement {
method connectedCallback (line 210) | async connectedCallback() {
method disconnectedCallback (line 218) | disconnectedCallback() {
method updated (line 223) | async updated(changedProps: Map<string, unknown>) {
method handleAnchorChange (line 247) | private async handleAnchorChange() {
method start (line 274) | private start() {
method stop (line 285) | private async stop(): Promise<void> {
method reposition (line 301) | reposition() {
method render (line 549) | render() {
FILE: src/components/popup/popup.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/progress-bar/progress-bar.component.ts
class SlProgressBar (line 29) | class SlProgressBar extends ShoelaceElement {
method render (line 42) | render() {
FILE: src/components/progress-bar/progress-bar.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/progress-ring/progress-ring.component.ts
class SlProgressRing (line 27) | class SlProgressRing extends ShoelaceElement {
method updated (line 42) | updated(changedProps: Map<string, unknown>) {
method render (line 59) | render() {
FILE: src/components/progress-ring/progress-ring.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/qr-code/qr-code.component.ts
class SlQrCode (line 19) | class SlQrCode extends ShoelaceElement {
method firstUpdated (line 45) | firstUpdated() {
method generate (line 50) | generate() {
method render (line 70) | render() {
FILE: src/components/qr-code/qr-code.test.ts
class Color (line 16) | class Color {
method constructor (line 22) | constructor(r: number, g: number, b: number, alpha: number) {
method equals (line 29) | equals(other: Color): boolean {
method toString (line 35) | toString(): string {
type QrCodeColors (line 40) | interface QrCodeColors {
FILE: src/components/qr-code/qr-code.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/radio-button/radio-button.component.ts
class SlRadioButton (line 32) | class SlRadioButton extends ShoelaceElement {
method connectedCallback (line 63) | connectedCallback() {
method handleBlur (line 68) | private handleBlur() {
method handleClick (line 73) | private handleClick(e: MouseEvent) {
method handleFocus (line 83) | private handleFocus() {
method handleDisabledChange (line 89) | handleDisabledChange() {
method focus (line 94) | focus(options?: FocusOptions) {
method blur (line 99) | blur() {
method render (line 103) | render() {
FILE: src/components/radio-button/radio-button.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/radio-group/radio-group.component.ts
class SlRadioGroup (line 46) | class SlRadioGroup extends ShoelaceElement implements ShoelaceFormControl {
method validity (line 91) | get validity() {
method validationMessage (line 105) | get validationMessage() {
method connectedCallback (line 118) | connectedCallback() {
method firstUpdated (line 123) | firstUpdated() {
method getAllRadios (line 127) | private getAllRadios() {
method handleRadioClick (line 131) | private handleRadioClick(event: MouseEvent) {
method handleKeyDown (line 149) | private handleKeyDown(event: KeyboardEvent) {
method handleLabelClick (line 194) | private handleLabelClick() {
method handleInvalid (line 198) | private handleInvalid(event: Event) {
method syncRadioElements (line 203) | private async syncRadioElements() {
method syncRadios (line 238) | private syncRadios() {
method updateCheckedRadio (line 258) | private updateCheckedRadio() {
method handleSizeChange (line 265) | handleSizeChange() {
method handleValueChange (line 270) | handleValueChange() {
method checkValidity (line 277) | checkValidity() {
method getForm (line 290) | getForm(): HTMLFormElement | null {
method reportValidity (line 295) | reportValidity(): boolean {
method setCustomValidity (line 314) | setCustomValidity(message = '') {
method focus (line 322) | public focus(options?: FocusOptions) {
method render (line 335) | render() {
FILE: src/components/radio-group/radio-group.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/radio/radio.component.ts
class SlRadio (line 30) | class SlRadio extends ShoelaceElement {
method constructor (line 49) | constructor() {
method connectedCallback (line 56) | connectedCallback() {
method setInitialAttributes (line 77) | private setInitialAttributes() {
method handleCheckedChange (line 84) | handleCheckedChange() {
method handleDisabledChange (line 90) | handleDisabledChange() {
method render (line 94) | render() {
FILE: src/components/radio/radio.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/range/range.component.ts
class SlRange (line 48) | class SlRange extends ShoelaceElement implements ShoelaceFormControl {
method validity (line 107) | get validity() {
method validationMessage (line 112) | get validationMessage() {
method connectedCallback (line 116) | connectedCallback() {
method disconnectedCallback (line 133) | disconnectedCallback() {
method handleChange (line 138) | private handleChange() {
method handleInput (line 142) | private handleInput() {
method handleBlur (line 148) | private handleBlur() {
method handleFocus (line 154) | private handleFocus() {
method handleThumbDragStart (line 161) | private handleThumbDragStart() {
method handleThumbDragEnd (line 165) | private handleThumbDragEnd() {
method syncProgress (line 169) | private syncProgress(percent: number) {
method syncTooltip (line 173) | private syncTooltip(percent: number) {
method handleValueChange (line 195) | handleValueChange() {
method handleDisabledChange (line 207) | handleDisabledChange() {
method syncRange (line 213) | syncRange() {
method handleInvalid (line 224) | private handleInvalid(event: Event) {
method focus (line 230) | focus(options?: FocusOptions) {
method blur (line 235) | blur() {
method stepUp (line 240) | stepUp() {
method stepDown (line 248) | stepDown() {
method checkValidity (line 256) | checkValidity() {
method getForm (line 261) | getForm(): HTMLFormElement | null {
method reportValidity (line 266) | reportValidity() {
method setCustomValidity (line 271) | setCustomValidity(message: string) {
method render (line 276) | render() {
FILE: src/components/range/range.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/rating/rating.component.ts
class SlRating (line 35) | class SlRating extends ShoelaceElement {
method getValueFromMousePosition (line 74) | private getValueFromMousePosition(event: MouseEvent) {
method getValueFromTouchPosition (line 78) | private getValueFromTouchPosition(event: TouchEvent) {
method getValueFromXCoordinate (line 82) | private getValueFromXCoordinate(coordinate: number) {
method handleClick (line 92) | private handleClick(event: MouseEvent) {
method setValue (line 101) | private setValue(newValue: number) {
method handleKeyDown (line 110) | private handleKeyDown(event: KeyboardEvent) {
method handleMouseEnter (line 146) | private handleMouseEnter(event: MouseEvent) {
method handleMouseMove (line 151) | private handleMouseMove(event: MouseEvent) {
method handleMouseLeave (line 155) | private handleMouseLeave() {
method handleTouchStart (line 159) | private handleTouchStart(event: TouchEvent) {
method handleTouchMove (line 168) | private handleTouchMove(event: TouchEvent) {
method handleTouchEnd (line 172) | private handleTouchEnd(event: TouchEvent) {
method roundToPrecision (line 181) | private roundToPrecision(numberToRound: number, precision = 0.5) {
method handleHoverValueChange (line 187) | handleHoverValueChange() {
method handleIsHoveringChange (line 197) | handleIsHoveringChange() {
method focus (line 207) | focus(options?: FocusOptions) {
method blur (line 212) | blur() {
method render (line 216) | render() {
FILE: src/components/rating/rating.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/relative-time/relative-time.component.ts
type UnitConfig (line 6) | interface UnitConfig {
class SlRelativeTime (line 27) | class SlRelativeTime extends ShoelaceElement {
method disconnectedCallback (line 53) | disconnectedCallback() {
method render (line 58) | render() {
function getTimeUntilNextUnit (line 108) | function getTimeUntilNextUnit(unit: 'second' | 'minute' | 'hour' | 'day') {
FILE: src/components/relative-time/relative-time.test.ts
type SlRelativeTimeTestCase (line 6) | interface SlRelativeTimeTestCase {
FILE: src/components/relative-time/relative-time.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/resize-observer/resize-observer.component.ts
class SlResizeObserver (line 19) | class SlResizeObserver extends ShoelaceElement {
method connectedCallback (line 28) | connectedCallback() {
method disconnectedCallback (line 39) | disconnectedCallback() {
method handleSlotChange (line 44) | private handleSlotChange() {
method startObserver (line 50) | private startObserver() {
method stopObserver (line 68) | private stopObserver() {
method handleDisabledChange (line 73) | handleDisabledChange() {
method render (line 81) | render() {
type HTMLElementTagNameMap (line 87) | interface HTMLElementTagNameMap {
FILE: src/components/resize-observer/resize-observer.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/select/select.component.ts
class SlSelect (line 72) | class SlSelect extends ShoelaceElement implements ShoelaceFormControl {
method value (line 106) | get value() {
method value (line 116) | set value(val: string | string[]) {
method validity (line 221) | get validity() {
method validationMessage (line 226) | get validationMessage() {
method connectedCallback (line 230) | connectedCallback() {
method addOpenListeners (line 241) | private addOpenListeners() {
method removeOpenListeners (line 268) | private removeOpenListeners() {
method handleFocus (line 280) | private handleFocus() {
method handleBlur (line 286) | private handleBlur() {
method handleLabelClick (line 437) | private handleLabelClick() {
method handleComboboxMouseDown (line 441) | private handleComboboxMouseDown(event: MouseEvent) {
method handleComboboxKeyDown (line 455) | private handleComboboxKeyDown(event: KeyboardEvent) {
method handleClearClick (line 464) | private handleClearClick(event: MouseEvent) {
method handleClearMouseDown (line 482) | private handleClearMouseDown(event: MouseEvent) {
method handleOptionClick (line 488) | private handleOptionClick(event: MouseEvent) {
method handleDefaultSlotChange (line 520) | public handleDefaultSlotChange() {
method handleTagRemove (line 537) | private handleTagRemove(event: SlRemoveEvent, option: SlOption) {
method getAllOptions (line 554) | private getAllOptions() {
method getFirstOption (line 559) | private getFirstOption() {
method setCurrentOption (line 565) | private setCurrentOption(option: SlOption | null) {
method setSelectedOptions (line 584) | private setSelectedOptions(option: SlOption | SlOption[]) {
method toggleOptionSelection (line 601) | private toggleOptionSelection(option: SlOption, force?: boolean) {
method selectionChanged (line 613) | private selectionChanged() {
method tags (line 644) | protected get tags() {
method handleInvalid (line 660) | private handleInvalid(event: Event) {
method handleDisabledChange (line 666) | handleDisabledChange() {
method attributeChangedCallback (line 674) | attributeChangedCallback(name: string, oldVal: string | null, newVal: ...
method handleValueChange (line 688) | handleValueChange() {
method handleOpenChange (line 705) | async handleOpenChange() {
method show (line 748) | async show() {
method hide (line 759) | async hide() {
method checkValidity (line 770) | checkValidity() {
method getForm (line 775) | getForm(): HTMLFormElement | null {
method reportValidity (line 780) | reportValidity() {
method setCustomValidity (line 785) | setCustomValidity(message: string) {
method focus (line 791) | focus(options?: FocusOptions) {
method blur (line 796) | blur() {
method render (line 800) | render() {
FILE: src/components/select/select.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/skeleton/skeleton.component.ts
class SlSkeleton (line 22) | class SlSkeleton extends ShoelaceElement {
method render (line 28) | render() {
FILE: src/components/skeleton/skeleton.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/spinner/spinner.component.ts
class SlSpinner (line 21) | class SlSpinner extends ShoelaceElement {
method render (line 26) | render() {
FILE: src/components/spinner/spinner.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/split-panel/split-panel.component.ts
type SnapFunctionParams (line 13) | interface SnapFunctionParams {
type SnapFunction (line 27) | type SnapFunction = (opt: SnapFunctionParams) => number | null;
class SlSplitPanel (line 55) | class SlSplitPanel extends ShoelaceElement {
method toSnapFunction (line 100) | private toSnapFunction(snap: string): SnapFunction {
method snap (line 143) | set snap(snap: string | SnapFunction | null | undefined) {
method snap (line 152) | get snap(): string | SnapFunction {
method connectedCallback (line 159) | connectedCallback() {
method disconnectedCallback (line 168) | disconnectedCallback() {
method detectSize (line 173) | private detectSize() {
method percentageToPixels (line 178) | private percentageToPixels(value: number) {
method pixelsToPercentage (line 182) | private pixelsToPercentage(value: number) {
method handleDrag (line 186) | private handleDrag(event: PointerEvent) {
method handleKeyDown (line 223) | private handleKeyDown(event: KeyboardEvent) {
method handleResize (line 272) | private handleResize(entries: ResizeObserverEntry[]) {
method handlePositionChange (line 291) | handlePositionChange() {
method handlePositionInPixelsChange (line 300) | handlePositionInPixelsChange() {
method handleVerticalChange (line 305) | handleVerticalChange() {
method render (line 309) | render() {
type HTMLElementTagNameMap (line 368) | interface HTMLElementTagNameMap {
FILE: src/components/split-panel/split-panel.test.ts
constant DIVIDER_WIDTH_IN_PX (line 8) | const DIVIDER_WIDTH_IN_PX = 4;
FILE: src/components/split-panel/split-panel.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/switch/switch.component.ts
class SlSwitch (line 42) | class SlSwitch extends ShoelaceElement implements ShoelaceFormControl {
method validity (line 89) | get validity() {
method validationMessage (line 94) | get validationMessage() {
method firstUpdated (line 98) | firstUpdated() {
method handleBlur (line 102) | private handleBlur() {
method handleInput (line 107) | private handleInput() {
method handleInvalid (line 111) | private handleInvalid(event: Event) {
method handleClick (line 116) | private handleClick() {
method handleFocus (line 121) | private handleFocus() {
method handleKeyDown (line 126) | private handleKeyDown(event: KeyboardEvent) {
method handleCheckedChange (line 143) | handleCheckedChange() {
method handleDisabledChange (line 149) | handleDisabledChange() {
method click (line 155) | click() {
method focus (line 160) | focus(options?: FocusOptions) {
method blur (line 165) | blur() {
method checkValidity (line 170) | checkValidity() {
method getForm (line 175) | getForm(): HTMLFormElement | null {
method reportValidity (line 180) | reportValidity() {
method setCustomValidity (line 185) | setCustomValidity(message: string) {
method render (line 190) | render() {
type HTMLElementTagNameMap (line 259) | interface HTMLElementTagNameMap {
FILE: src/components/switch/switch.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/tab-group/tab-group.component.ts
class SlTabGroup (line 45) | class SlTabGroup extends ShoelaceElement {
method connectedCallback (line 82) | connectedCallback() {
method disconnectedCallback (line 161) | disconnectedCallback() {
method getAllTabs (line 170) | private getAllTabs() {
method getAllPanels (line 176) | private getAllPanels() {
method getActiveTab (line 180) | private getActiveTab() {
method handleClick (line 184) | private handleClick(event: MouseEvent) {
method handleKeyDown (line 199) | private handleKeyDown(event: KeyboardEvent) {
method handleScrollToStart (line 266) | private handleScrollToStart() {
method handleScrollToEnd (line 276) | private handleScrollToEnd() {
method setActiveTab (line 286) | private setActiveTab(tab: SlTab, options?: { emitEvents?: boolean; scr...
method setAriaLabels (line 320) | private setAriaLabels() {
method repositionIndicator (line 331) | private repositionIndicator() {
method syncTabsAndPanels (line 372) | private syncTabsAndPanels() {
method findNextFocusableTab (line 383) | private findNextFocusableTab(currentIndex: number, direction: 'forward...
method updateScrollButtons (line 418) | private updateScrollButtons() {
method isScrolledToEnd (line 425) | private isScrolledToEnd() {
method scrollFromStart (line 429) | private scrollFromStart() {
method updateScrollControls (line 434) | updateScrollControls() {
method syncIndicator (line 451) | syncIndicator() {
method show (line 463) | show(panel: string) {
method render (line 471) | render() {
type HTMLElementTagNameMap (line 547) | interface HTMLElementTagNameMap {
FILE: src/components/tab-group/tab-group.test.ts
type ClientRectangles (line 15) | interface ClientRectangles {
FILE: src/components/tab-group/tab-group.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/tab-panel/tab-panel.component.ts
class SlTabPanel (line 24) | class SlTabPanel extends ShoelaceElement {
method connectedCallback (line 36) | connectedCallback() {
method handleActiveChange (line 43) | handleActiveChange() {
method render (line 47) | render() {
FILE: src/components/tab-panel/tab-panel.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/tab/tab.component.ts
class SlTab (line 30) | class SlTab extends ShoelaceElement {
method connectedCallback (line 59) | connectedCallback() {
method handleCloseClick (line 64) | private handleCloseClick(event: Event) {
method handleActiveChange (line 70) | handleActiveChange() {
method handleDisabledChange (line 75) | handleDisabledChange() {
method render (line 85) | render() {
type HTMLElementTagNameMap (line 120) | interface HTMLElementTagNameMap {
FILE: src/components/tab/tab.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/tag/tag.component.ts
class SlTag (line 28) | class SlTag extends ShoelaceElement {
method handleRemoveClick (line 46) | private handleRemoveClick() {
method render (line 50) | render() {
FILE: src/components/tag/tag.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/textarea/textarea.component.ts
class SlTextarea (line 39) | class SlTextarea extends ShoelaceElement implements ShoelaceFormControl {
method validity (line 142) | get validity() {
method validationMessage (line 147) | get validationMessage() {
method connectedCallback (line 151) | connectedCallback() {
method firstUpdated (line 161) | firstUpdated() {
method disconnectedCallback (line 165) | disconnectedCallback() {
method handleBlur (line 172) | private handleBlur() {
method handleChange (line 177) | private handleChange() {
method handleFocus (line 183) | private handleFocus() {
method handleInput (line 188) | private handleInput() {
method handleInvalid (line 193) | private handleInvalid(event: Event) {
method setTextareaHeight (line 198) | private setTextareaHeight() {
method handleDisabledChange (line 210) | handleDisabledChange() {
method handleRowsChange (line 216) | handleRowsChange() {
method handleValueChange (line 221) | async handleValueChange() {
method focus (line 228) | focus(options?: FocusOptions) {
method blur (line 233) | blur() {
method select (line 238) | select() {
method scrollPosition (line 243) | scrollPosition(position?: { top?: number; left?: number }): { top: num...
method setSelectionRange (line 257) | setSelectionRange(
method setRangeText (line 266) | setRangeText(
method checkValidity (line 284) | checkValidity() {
method getForm (line 289) | getForm(): HTMLFormElement | null {
method reportValidity (line 294) | reportValidity() {
method setCustomValidity (line 299) | setCustomValidity(message: string) {
method render (line 304) | render() {
FILE: src/components/textarea/textarea.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/tooltip/tooltip.component.ts
class SlTooltip (line 43) | class SlTooltip extends ShoelaceElement {
method constructor (line 102) | constructor() {
method disconnectedCallback (line 111) | disconnectedCallback() {
method firstUpdated (line 118) | firstUpdated() {
method hasTrigger (line 174) | private hasTrigger(triggerType: string) {
method handleOpenChange (line 180) | async handleOpenChange() {
method handleOptionsChange (line 223) | async handleOptionsChange() {
method handleDisabledChange (line 231) | handleDisabledChange() {
method show (line 238) | async show() {
method hide (line 248) | async hide() {
method render (line 263) | render() {
FILE: src/components/tooltip/tooltip.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/tree-item/tree-item.component.ts
class SlTreeItem (line 62) | class SlTreeItem extends ShoelaceElement {
method isTreeItem (line 70) | static isTreeItem(node: Node) {
method connectedCallback (line 99) | connectedCallback() {
method firstUpdated (line 110) | firstUpdated() {
method animateCollapse (line 118) | private async animateCollapse() {
method isNestedItem (line 135) | private isNestedItem(): boolean {
method handleChildrenSlotChange (line 140) | private handleChildrenSlotChange() {
method willUpdate (line 145) | protected willUpdate(changedProperties: PropertyValueMap<SlTreeItem> |...
method animateExpand (line 151) | private async animateExpand() {
method handleLoadingChange (line 169) | handleLoadingChange() {
method handleDisabledChange (line 178) | handleDisabledChange() {
method handleSelectedChange (line 183) | handleSelectedChange() {
method handleExpandedChange (line 188) | handleExpandedChange() {
method handleExpandAnimation (line 197) | handleExpandAnimation() {
method handleLazyChange (line 212) | handleLazyChange() {
method getChildrenItems (line 217) | getChildrenItems({ includeDisabled = true }: { includeDisabled?: boole...
method render (line 225) | render() {
FILE: src/components/tree-item/tree-item.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/tree/tree.component.ts
function syncCheckboxes (line 12) | function syncCheckboxes(changedTreeItem: SlTreeItem, initialSync = false) {
class SlTree (line 73) | class SlTree extends ShoelaceElement {
method constructor (line 95) | constructor() {
method connectedCallback (line 102) | async connectedCallback() {
method disconnectedCallback (line 114) | disconnectedCallback() {
method getExpandButtonIcon (line 120) | private getExpandButtonIcon(status: 'expand' | 'collapse') {
method selectItem (line 175) | private selectItem(selectedItem: SlTreeItem) {
method getAllTreeItems (line 206) | private getAllTreeItems() {
method focusItem (line 210) | private focusItem(item?: SlTreeItem | null) {
method handleKeyDown (line 214) | private handleKeyDown(event: KeyboardEvent) {
method handleClick (line 286) | private handleClick(event: Event) {
method handleMouseDown (line 311) | handleMouseDown(event: MouseEvent) {
method handleSlotChange (line 345) | private handleSlotChange() {
method handleSelectionChange (line 351) | async handleSelectionChange() {
method selectedItems (line 371) | get selectedItems(): SlTreeItem[] {
method getFocusableItems (line 379) | getFocusableItems() {
method render (line 397) | render() {
FILE: src/components/tree/tree.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/components/visually-hidden/visually-hidden.component.ts
class SlVisuallyHidden (line 15) | class SlVisuallyHidden extends ShoelaceElement {
method render (line 18) | render() {
FILE: src/components/visually-hidden/visually-hidden.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
FILE: src/declaration.d.ts
type Assertion (line 7) | interface Assertion {
type HTMLInputElement (line 14) | interface HTMLInputElement {
type CloseWatcher (line 19) | interface CloseWatcher extends EventTarget {
type CloseWatcherOptions (line 31) | interface CloseWatcherOptions {
type Window (line 35) | interface Window {
FILE: src/events/sl-after-collapse.ts
type SlAfterCollapseEvent (line 1) | type SlAfterCollapseEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-after-expand.ts
type SlAfterExpandEvent (line 1) | type SlAfterExpandEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-after-hide.ts
type SlAfterHideEvent (line 1) | type SlAfterHideEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-after-show.ts
type SlAfterShowEvent (line 1) | type SlAfterShowEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-blur.ts
type SlBlurEvent (line 1) | type SlBlurEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-cancel.ts
type SlCancelEvent (line 1) | type SlCancelEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-change.ts
type SlChangeEvent (line 1) | type SlChangeEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-clear.ts
type SlClearEvent (line 1) | type SlClearEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-close.ts
type SlCloseEvent (line 1) | type SlCloseEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-collapse.ts
type SlCollapseEvent (line 1) | type SlCollapseEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-copy.ts
type SlCopyEvent (line 1) | type SlCopyEvent = CustomEvent<{ value: string }>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-error.ts
type SlErrorEvent (line 1) | type SlErrorEvent = CustomEvent<{ status?: number }>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-expand.ts
type SlExpandEvent (line 1) | type SlExpandEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-finish.ts
type SlFinishEvent (line 1) | type SlFinishEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-focus.ts
type SlFocusEvent (line 1) | type SlFocusEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-hide.ts
type SlHideEvent (line 1) | type SlHideEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-hover.ts
type SlHoverEvent (line 1) | type SlHoverEvent = CustomEvent<{
type GlobalEventHandlersEventMap (line 7) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-initial-focus.ts
type SlInitialFocusEvent (line 1) | type SlInitialFocusEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-input.ts
type SlInputEvent (line 1) | type SlInputEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-invalid.ts
type SlInvalidEvent (line 1) | type SlInvalidEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-lazy-change.ts
type SlLazyChangeEvent (line 1) | type SlLazyChangeEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-lazy-load.ts
type SlLazyLoadEvent (line 1) | type SlLazyLoadEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-load.ts
type SlLoadEvent (line 1) | type SlLoadEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-mutation.ts
type SlMutationEvent (line 1) | type SlMutationEvent = CustomEvent<{ mutationList: MutationRecord[] }>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-remove.ts
type SlRemoveEvent (line 1) | type SlRemoveEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-reposition.ts
type SlRepositionEvent (line 1) | type SlRepositionEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-request-close.ts
type SlRequestCloseEvent (line 1) | type SlRequestCloseEvent = CustomEvent<{ source: 'close-button' | 'keybo...
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-resize.ts
type SlResizeEvent (line 1) | type SlResizeEvent = CustomEvent<{ entries: ResizeObserverEntry[] }>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-select.ts
type SlSelectEvent (line 3) | type SlSelectEvent = CustomEvent<{ item: SlMenuItem }>;
type GlobalEventHandlersEventMap (line 6) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-selection-change.ts
type SlSelectionChangeEvent (line 3) | type SlSelectionChangeEvent = CustomEvent<{ selection: SlTreeItem[] }>;
type GlobalEventHandlersEventMap (line 6) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-show.ts
type SlShowEvent (line 1) | type SlShowEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-slide-change.ts
type SlSlideChangeEvent (line 3) | type SlSlideChangeEvent = CustomEvent<{ index: number; slide: SlCarousel...
type GlobalEventHandlersEventMap (line 6) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-start.ts
type SlStartEvent (line 1) | type SlStartEvent = CustomEvent<Record<PropertyKey, never>>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-tab-hide.ts
type SlTabHideEvent (line 1) | type SlTabHideEvent = CustomEvent<{ name: string }>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/events/sl-tab-show.ts
type SlTabShowEvent (line 1) | type SlTabShowEvent = CustomEvent<{ name: string }>;
type GlobalEventHandlersEventMap (line 4) | interface GlobalEventHandlersEventMap {
FILE: src/internal/active-elements.ts
function getDeepestActiveElement (line 24) | function getDeepestActiveElement() {
FILE: src/internal/animate.ts
function animateTo (line 4) | function animateTo(el: HTMLElement, keyframes: Keyframe[], options?: Key...
function parseDuration (line 21) | function parseDuration(delay: number | string) {
function prefersReducedMotion (line 36) | function prefersReducedMotion() {
function stopAnimations (line 44) | function stopAnimations(el: HTMLElement) {
function shimKeyframesHeightAuto (line 59) | function shimKeyframesHeightAuto(keyframes: Keyframe[], calculatedHeight...
FILE: src/internal/debounce.ts
constant TIMER_ID_KEY (line 16) | const TIMER_ID_KEY = Symbol();
FILE: src/internal/drag.ts
type DragOptions (line 1) | interface DragOptions {
function drag (line 15) | function drag(container: HTMLElement, options?: Partial<DragOptions>) {
FILE: src/internal/event.ts
function waitForEvent (line 2) | function waitForEvent(el: HTMLElement, eventName: string) {
FILE: src/internal/form.ts
type FormControlControllerOptions (line 30) | interface FormControlControllerOptions {
class FormControlController (line 61) | class FormControlController implements ReactiveController {
method constructor (line 66) | constructor(host: ReactiveControllerHost & ShoelaceFormControl, option...
method hostConnected (line 97) | hostConnected() {
method hostDisconnected (line 111) | hostDisconnected() {
method hostUpdated (line 121) | hostUpdated() {
method attachForm (line 140) | private attachForm(form?: HTMLFormElement) {
method detachForm (line 171) | private detachForm() {
method setUserInteracted (line 331) | private setUserInteracted(el: ShoelaceFormControl, hasInteracted: bool...
method doAction (line 341) | private doAction(type: 'submit' | 'reset', submitter?: HTMLInputElemen...
method getForm (line 371) | getForm() {
method reset (line 376) | reset(submitter?: HTMLInputElement | SlButton) {
method submit (line 381) | submit(submitter?: HTMLInputElement | SlButton) {
method setValidity (line 391) | setValidity(isValid: boolean) {
method updateValidity (line 414) | updateValidity() {
method emitInvalidEvent (line 426) | emitInvalidEvent(originalInvalidEvent?: Event) {
FILE: src/internal/math.ts
function clamp (line 2) | function clamp(value: number, min: number, max: number) {
FILE: src/internal/modal.ts
class Modal (line 6) | class Modal {
method constructor (line 14) | constructor(element: HTMLElement) {
method activate (line 21) | activate() {
method deactivate (line 29) | deactivate() {
method isActive (line 38) | isActive() {
method activateExternal (line 44) | activateExternal() {
method deactivateExternal (line 49) | deactivateExternal() {
method checkFocus (line 53) | private checkFocus() {
method possiblyHasTabbableChildren (line 74) | private possiblyHasTabbableChildren(element: HTMLElement) {
FILE: src/internal/offset.ts
function getOffset (line 8) | function getOffset(element: HTMLElement, parent: HTMLElement) {
FILE: src/internal/scroll.ts
function getScrollbarWidth (line 6) | function getScrollbarWidth() {
function getExistingBodyPadding (line 14) | function getExistingBodyPadding() {
function lockBodyScrolling (line 28) | function lockBodyScrolling(lockingEl: HTMLElement) {
function unlockBodyScrolling (line 58) | function unlockBodyScrolling(lockingEl: HTMLElement) {
function scrollIntoView (line 68) | function scrollIntoView(
FILE: src/internal/scrollend-polyfill.ts
type GenericCallback (line 1) | type GenericCallback = (this: unknown, ...args: unknown[]) => unknown;
type MethodOf (line 3) | type MethodOf<T, K extends keyof T> = T[K] extends GenericCallback ? T[K...
FILE: src/internal/shoelace-element.test.ts
function stubCustomElements (line 23) | function stubCustomElements() {
class MyButton (line 50) | class MyButton extends SlButton {
class MyButton (line 78) | class MyButton extends SlButton {}
class MyElement (line 94) | class MyElement extends ShoelaceElement {
FILE: src/internal/shoelace-element.ts
type EventTypeRequiresDetail (line 5) | type EventTypeRequiresDetail<T> = T extends keyof GlobalEventHandlersEve...
type EventTypeDoesNotRequireDetail (line 19) | type EventTypeDoesNotRequireDetail<T> = T extends keyof GlobalEventHandl...
type EventTypesWithRequiredDetail (line 30) | type EventTypesWithRequiredDetail = {
type EventTypesWithoutRequiredDetail (line 35) | type EventTypesWithoutRequiredDetail = {
type WithRequired (line 40) | type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
type SlEventInit (line 45) | type SlEventInit<T> = T extends keyof GlobalEventHandlersEventMap
type GetCustomEventType (line 56) | type GetCustomEventType<T> = T extends keyof GlobalEventHandlersEventMap
type ValidEventTypeMap (line 63) | type ValidEventTypeMap = EventTypesWithRequiredDetail | EventTypesWithou...
class ShoelaceElement (line 65) | class ShoelaceElement extends LitElement {
method emit (line 79) | emit<T extends string & keyof ValidEventTypeMap>(
method define (line 101) | static define(name: string, elementConstructor = this, options: Elemen...
method constructor (line 143) | constructor() {
method attributeChangedCallback (line 155) | attributeChangedCallback(name: string, oldValue: string | null, newVal...
method willUpdate (line 172) | protected willUpdate(changedProperties: Parameters<LitElement['willUpd...
type ShoelaceFormControl (line 187) | interface ShoelaceFormControl extends ShoelaceElement {
FILE: src/internal/slot.ts
class HasSlotController (line 4) | class HasSlotController implements ReactiveController {
method constructor (line 8) | constructor(host: ReactiveControllerHost & Element, ...slotNames: stri...
method hasDefaultSlot (line 13) | private hasDefaultSlot() {
method hasNamedSlot (line 38) | private hasNamedSlot(name: string) {
method test (line 42) | test(slotName: string) {
method hostConnected (line 46) | hostConnected() {
method hostDisconnected (line 50) | hostDisconnected() {
function getInnerHTML (line 67) | function getInnerHTML(slot: HTMLSlotElement): string {
function getTextContent (line 88) | function getTextContent(slot: HTMLSlotElement | undefined | null): string {
FILE: src/internal/string.ts
function uppercaseFirstLetter (line 2) | function uppercaseFirstLetter(string: string) {
FILE: src/internal/tabbable.test.ts
function holdShiftKey (line 11) | async function holdShiftKey(callback: () => Promise<void>) {
function activeElementsArray (line 21) | function activeElementsArray() {
method constructor (line 28) | constructor() {
method connectedCallback (line 32) | connectedCallback() {
FILE: src/internal/tabbable.ts
function getCachedComputedStyle (line 5) | function getCachedComputedStyle(el: HTMLElement): CSSStyleDeclaration {
function isVisible (line 16) | function isVisible(el: HTMLElement): boolean {
function isOverflowingAndTabbable (line 32) | function isOverflowingAndTabbable(el: HTMLElement): boolean {
function isTabbable (line 62) | function isTabbable(el: HTMLElement) {
function getTabbableBoundary (line 142) | function getTabbableBoundary(root: HTMLElement | ShadowRoot) {
function getSlottedChildrenOutsideRootElement (line 157) | function getSlottedChildrenOutsideRootElement(slotElement: HTMLSlotEleme...
function getTabbableElements (line 161) | function getTabbableElements(root: HTMLElement | ShadowRoot) {
FILE: src/internal/test.ts
function determineMousePosition (line 3) | function determineMousePosition(el: Element, position: string, offsetX: ...
function clickOnElement (line 38) | async function clickOnElement(
function moveMouseOnElement (line 54) | async function moveMouseOnElement(
function dragElement (line 70) | async function dragElement(
FILE: src/internal/test/form-control-base-tests.ts
type CreateControlFn (line 4) | type CreateControlFn = () => Promise<ShoelaceFormControl>;
function runFormControlBaseTests (line 7) | function runFormControlBaseTests<T extends ShoelaceFormControl = Shoelac...
function runAllValidityTests (line 49) | function runAllValidityTests(
function runSpecialTests_slButtonOfTypeButton (line 168) | function runSpecialTests_slButtonOfTypeButton(createControl: CreateContr...
function runSpecialTests_slButtonWithHref (line 210) | function runSpecialTests_slButtonWithHref(createControl: CreateControlFn) {
function runSpecialTests_standard (line 243) | function runSpecialTests_standard(createControl: CreateControlFn) {
function createFormControl (line 286) | async function createFormControl<T extends ShoelaceFormControl = Shoelac...
function checkEventEmissions (line 292) | function checkEventEmissions(control: ShoelaceFormControl, eventType: st...
function getMode (line 311) | function getMode(control: ShoelaceFormControl) {
FILE: src/internal/test/wait-for-scrolling.ts
function checkScrollingChanged (line 19) | function checkScrollingChanged() {
FILE: src/internal/watch.ts
type UpdateHandler (line 3) | type UpdateHandler = (prev?: unknown, next?: unknown) => void;
type NonUndefined (line 5) | type NonUndefined<A> = A extends undefined ? never : A;
type UpdateHandlerFunctionKeys (line 7) | type UpdateHandlerFunctionKeys<T extends object> = {
type WatchOptions (line 11) | interface WatchOptions {
function watch (line 30) | function watch(propertyName: string | string[], options?: WatchOptions) {
FILE: src/shoelace-autoloader.ts
function discover (line 16) | async function discover(root: Element | ShadowRoot) {
function register (line 37) | function register(tagName: string): Promise<void> {
FILE: src/utilities/animation-registry.ts
type ElementAnimation (line 1) | interface ElementAnimation {
type ElementAnimationMap (line 7) | interface ElementAnimationMap {
type GetAnimationOptions (line 11) | interface GetAnimationOptions {
function ensureAnimation (line 22) | function ensureAnimation(animation: ElementAnimation | null) {
function getLogicalAnimation (line 30) | function getLogicalAnimation(animation: ElementAnimation, dir: string) {
function setDefaultAnimation (line 45) | function setDefaultAnimation(animationName: string, animation: ElementAn...
function setAnimation (line 50) | function setAnimation(el: Element, animationName: string, animation: Ele...
function getAnimation (line 55) | function getAnimation(el: Element, animationName: string, options: GetAn...
FILE: src/utilities/base-path.ts
function setBasePath (line 4) | function setBasePath(path: string) {
function getBasePath (line 23) | function getBasePath(subpath = '') {
FILE: src/utilities/form.ts
function serialize (line 7) | function serialize(form: HTMLFormElement) {
function getFormControls (line 32) | function getFormControls(form: HTMLFormElement) {
FILE: src/utilities/localize.ts
class LocalizeController (line 6) | class LocalizeController extends DefaultLocalizationController<Translati...
type Translation (line 19) | interface Translation extends DefaultTranslation {
Condensed preview — 492 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,411K chars).
[
{
"path": ".editorconfig",
"chars": 245,
"preview": "# https://editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninser"
},
{
"path": ".eslintignore",
"chars": 97,
"preview": ".cache\ndocs/dist\ndocs/search.json\ndocs/**/*.min.js\ndist\nexamples\nnode_modules\nsrc/react\nscripts\n\n"
},
{
"path": ".eslintrc.cjs",
"chars": 6730,
"preview": "/* eslint-env node */\n\nmodule.exports = {\n plugins: [\n '@typescript-eslint',\n 'wc',\n 'lit',\n 'lit-a11y',\n "
},
{
"path": ".github/CODE_OF_CONDUCT.md",
"chars": 3156,
"preview": "\n# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, "
},
{
"path": ".github/FUNDING.yml",
"chars": 19,
"preview": "github: [claviska]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 939,
"preview": "---\nname: Bug Report\nabout: Create a bug report to help us fix a demonstrable problem with code in the library.\ntitle: '"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 403,
"preview": "contact_links:\n - name: Feature Requests\n url: https://github.com/shoelace-style/shoelace/discussions/categories/ide"
},
{
"path": ".github/SECURITY.md",
"chars": 373,
"preview": "# Reporting Security Issues\n\nWe take security issues in Shoelace very seriously and appreciate your efforts to disclose "
},
{
"path": ".github/workflows/node.js.yml",
"chars": 841,
"preview": "# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests ac"
},
{
"path": ".github/workflows/release.yml",
"chars": 364,
"preview": "# This workflow will create a GitHub release every time a tag is pushed\nname: Create GitHub Release\n\non:\n push:\n tag"
},
{
"path": ".gitignore",
"chars": 85,
"preview": "_site\n.cache\n.DS_Store\ncdn\ndist\ndocs/assets/images/sprite.svg\nnode_modules\nsrc/react\n"
},
{
"path": ".gitpod.yml",
"chars": 934,
"preview": "tasks:\n - init: npm install && npm run build\n command: npm run start\n\nports:\n - port: 3001\n onOpen: ignore\n - p"
},
{
"path": ".husky/pre-commit",
"chars": 71,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx --no-install lint-staged\n"
},
{
"path": ".prettierignore",
"chars": 168,
"preview": "*.hbs\n.cache\n.github\ncspell.json\ndist\ndocs/search.json\nsrc/components/icon/icons\nsrc/react/index.ts\nnode_modules\npackage"
},
{
"path": ".vscode/extensions.json",
"chars": 190,
"preview": "{\n \"recommendations\": [\n \"dbaeumer.vscode-eslint\",\n \"esbenp.prettier-vscode\",\n \"bierner.lit-html\",\n \"bashmi"
},
{
"path": ".vscode/settings.json",
"chars": 165,
"preview": "{\n \"editor.formatOnSave\": true,\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n \"editor.codeActionsOnSave\": {\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 184,
"preview": "# Contributing to Shoelace\n\nBefore contributing, please review the contributions guidelines at:\n\n[shoelace.style/resourc"
},
{
"path": "LICENSE.md",
"chars": 1065,
"preview": "Copyright (c) 2020 A Beautiful Site, LLC\n\nPermission is hereby granted, free of charge, to any person obtaining a copy o"
},
{
"path": "README.md",
"chars": 3665,
"preview": "# Shoelace is now Web Awesome 🧡!\n\n> [!IMPORTANT] \n> **Shoelace is in maintenance mode (LTS)**. It is no longer actively"
},
{
"path": "cspell.json",
"chars": 3105,
"preview": "{\n \"version\": \"0.2\",\n \"words\": [\n \"activedescendant\",\n \"allowfullscreen\",\n \"animationend\",\n \"Animista\",\n "
},
{
"path": "custom-elements-manifest.config.js",
"chars": 7405,
"preview": "import * as path from 'path';\nimport { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';\nimpo"
},
{
"path": "docs/_includes/component.njk",
"chars": 11228,
"preview": "{% extends \"default.njk\" %}\n\n{# Find the component based on the `tag` front matter #}\n{% set component = getComponent('s"
},
{
"path": "docs/_includes/default.njk",
"chars": 8109,
"preview": "<!DOCTYPE html>\n<html\n lang=\"en\"\n data-layout=\"{{ layout }}\"\n data-shoelace-version=\"{{ meta.version }}\"\n>\n <head>\n "
},
{
"path": "docs/_includes/sidebar.njk",
"chars": 2417,
"preview": "<ul>\n <li>\n <h2>Getting Started</h2>\n <ul>\n <li><a href=\"/\">Home</a></li>\n <li><a href=\"/getting-starte"
},
{
"path": "docs/_includes/wa-logo-icon.njk",
"chars": 632,
"preview": "<svg class=\"wa-logo\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\" aria-hidden=\"true\"><path fill=\"currentColo"
},
{
"path": "docs/_utilities/active-links.cjs",
"chars": 861,
"preview": "function normalizePathname(pathname) {\n // Remove /index.html\n if (pathname.endsWith('/index.html')) {\n pathname = "
},
{
"path": "docs/_utilities/anchor-headings.cjs",
"chars": 1746,
"preview": "const { createSlug } = require('./strings.cjs');\n\n/**\n * Turns headings into clickable, deep linkable anchors. The provi"
},
{
"path": "docs/_utilities/cem.cjs",
"chars": 2322,
"preview": "const customElementsManifest = require('../../dist/custom-elements.json');\n\n//\n// Export it here so we can import it els"
},
{
"path": "docs/_utilities/code-previews.cjs",
"chars": 4491,
"preview": "let count = 1;\n\nfunction escapeHtml(str) {\n return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/"
},
{
"path": "docs/_utilities/copy-code-buttons.cjs",
"chars": 608,
"preview": "let codeBlockId = 0;\n\n/**\n * Adds copy code buttons to code fields. The provided doc should be a document object provide"
},
{
"path": "docs/_utilities/external-links.cjs",
"chars": 1288,
"preview": "const { isExternalLink } = require('./strings.cjs');\n\n/**\n * Transforms external links to make them safer and optionally"
},
{
"path": "docs/_utilities/highlight-code.cjs",
"chars": 2155,
"preview": "const Prism = require('prismjs');\nconst PrismLoader = require('prismjs/components/index.js');\n\nPrismLoader('diff');\nPris"
},
{
"path": "docs/_utilities/markdown.cjs",
"chars": 1815,
"preview": "const MarkdownIt = require('markdown-it');\nconst markdownItContainer = require('markdown-it-container');\nconst markdownI"
},
{
"path": "docs/_utilities/prettier.cjs",
"chars": 595,
"preview": "const { format } = require('prettier');\n\n/** Formats markup using prettier. */\nmodule.exports = function (content, optio"
},
{
"path": "docs/_utilities/replacer.cjs",
"chars": 477,
"preview": "/**\n * @typedef {object} Replacement\n * @property {string | RegExp} pattern\n * @property {string} replacement\n */\n\n/**\n "
},
{
"path": "docs/_utilities/scrolling-tables.cjs",
"chars": 632,
"preview": "/**\n * Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JS"
},
{
"path": "docs/_utilities/strings.cjs",
"chars": 510,
"preview": "const slugify = require('slugify');\n\n/** Creates a slug from an arbitrary string of text. */\nmodule.exports.createSlug ="
},
{
"path": "docs/_utilities/table-of-contents.cjs",
"chars": 1222,
"preview": "/**\n * Generates an in-page table of contents based on headings.\n */\nmodule.exports = function (doc, options) {\n option"
},
{
"path": "docs/_utilities/typography.cjs",
"chars": 1089,
"preview": "const smartquotes = require('smartquotes');\n\nsmartquotes.replacements.push([/---/g, '\\u2014']); // em dash\nsmartquotes.r"
},
{
"path": "docs/assets/examples/include.html",
"chars": 1035,
"preview": "<p style=\"margin-top: 0\">\n The content in this example was included from\n <a href=\"/assets/examples/include.html\" targ"
},
{
"path": "docs/assets/scripts/code-previews.js",
"chars": 8687,
"preview": "(() => {\n function convertModuleLinks(html) {\n html = html\n .replace(/@shoelace-style\\/shoelace/g, `https://esm"
},
{
"path": "docs/assets/scripts/docs.js",
"chars": 7374,
"preview": "//\n// Sidebar\n//\n// When the sidebar is hidden, we apply the inert attribute to prevent focus from reaching it. Due to t"
},
{
"path": "docs/assets/scripts/search.js",
"chars": 12263,
"preview": "(() => {\n // Append the search dialog to the body\n const siteSearch = document.createElement('div');\n const scrollbar"
},
{
"path": "docs/assets/scripts/turbo.js",
"chars": 924,
"preview": "import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.10/+esm';\n\n(() => {\n if (!window.scrollPositio"
},
{
"path": "docs/assets/styles/code-previews.css",
"chars": 3606,
"preview": "/* Interactive code blocks */\n.code-preview {\n position: relative;\n border-radius: 3px;\n background-color: var(--sl-c"
},
{
"path": "docs/assets/styles/docs.css",
"chars": 29637,
"preview": ":root {\n --docs-background-color: var(--sl-color-neutral-0);\n --docs-border-color: var(--sl-color-neutral-200);\n --do"
},
{
"path": "docs/assets/styles/search.css",
"chars": 7615,
"preview": "/* Search plugin */\n:root,\n:root.sl-theme-dark {\n --docs-search-box-background: var(--sl-color-neutral-0);\n --docs-sea"
},
{
"path": "docs/eleventy.config.cjs",
"chars": 9168,
"preview": "/* eslint-disable no-invalid-this */\nconst fs = require('fs');\nconst path = require('path');\nconst lunr = require('lunr'"
},
{
"path": "docs/pages/404.md",
"chars": 388,
"preview": "---\nmeta:\n title: Page Not Found\n description: \"The page you were looking for couldn't be found.\"\npermalink: 404.html\n"
},
{
"path": "docs/pages/components/alert.md",
"chars": 14767,
"preview": "---\nmeta:\n title: Alert\n description: Alerts are used to display important messages inline or as toast notifications.\n"
},
{
"path": "docs/pages/components/animated-image.md",
"chars": 3031,
"preview": "---\nmeta:\n title: Animated Image\n description: A component for displaying animated GIFs and WEBPs that play and pause "
},
{
"path": "docs/pages/components/animation.md",
"chars": 9693,
"preview": "---\nmeta:\n title: Animation\n description: Animate elements declaratively with nearly 100 baked-in presets, or roll you"
},
{
"path": "docs/pages/components/avatar.md",
"chars": 6474,
"preview": "---\nmeta:\n title: Avatar\n description: Avatars are used to represent a person or object.\nlayout: component\n---\n\nBy def"
},
{
"path": "docs/pages/components/badge.md",
"chars": 5399,
"preview": "---\nmeta:\n title: Badge\n description: Badges are used to draw attention and display statuses or counts.\nlayout: compon"
},
{
"path": "docs/pages/components/breadcrumb-item.md",
"chars": 1021,
"preview": "---\nmeta:\n title: Breadcrumb Item\n description: Breadcrumb Items are used inside breadcrumbs to represent different li"
},
{
"path": "docs/pages/components/breadcrumb.md",
"chars": 9519,
"preview": "---\nmeta:\n title: Breadcrumb\n description: Breadcrumbs provide a group of links so users can easily navigate a website"
},
{
"path": "docs/pages/components/button-group.md",
"chars": 13917,
"preview": "---\nmeta:\n title: Button Group\n description: Button groups can be used to group related buttons into sections.\nlayout:"
},
{
"path": "docs/pages/components/button.md",
"chars": 14109,
"preview": "---\nmeta:\n title: Button\n description: Buttons represent actions that are available to the user.\nlayout: component\n---"
},
{
"path": "docs/pages/components/card.md",
"chars": 6510,
"preview": "---\nmeta:\n title: Card\n description: Cards can be used to group related subjects in a container.\nlayout: component\n---"
},
{
"path": "docs/pages/components/carousel-item.md",
"chars": 2566,
"preview": "---\nmeta:\n title: Carousel Item\n description: A carousel item represent a slide within a carousel.\nlayout: component\n-"
},
{
"path": "docs/pages/components/carousel.md",
"chars": 38559,
"preview": "---\nmeta:\n title: Carousel\n description: Carousels display an arbitrary number of content slides along a horizontal or"
},
{
"path": "docs/pages/components/checkbox.md",
"chars": 4528,
"preview": "---\nmeta:\n title: Checkbox\n description: Checkboxes allow the user to toggle an option on or off.\nlayout: component\n--"
},
{
"path": "docs/pages/components/color-picker.md",
"chars": 4356,
"preview": "---\nmeta:\n title: Color Picker\n description: Color pickers allow the user to select a color.\nlayout: component\n---\n\n``"
},
{
"path": "docs/pages/components/copy-button.md",
"chars": 7317,
"preview": "---\nmeta:\n title: Copy Button\n description: Copies data to the clipboard when the user clicks the button.\nlayout: comp"
},
{
"path": "docs/pages/components/details.md",
"chars": 4905,
"preview": "---\nmeta:\n title: Details\n description: Details show a brief summary and expand to show additional content.\nlayout: co"
},
{
"path": "docs/pages/components/dialog.md",
"chars": 10587,
"preview": "---\nmeta:\n title: Dialog\n description: 'Dialogs, sometimes called \"modals\", appear above the page and require the user"
},
{
"path": "docs/pages/components/divider.md",
"chars": 3706,
"preview": "---\nmeta:\n title: Divider\n description: Dividers are used to visually separate or group elements.\nlayout: component\n--"
},
{
"path": "docs/pages/components/drawer.md",
"chars": 17300,
"preview": "---\nmeta:\n title: Drawer\n description: Drawers slide in from a container to expose additional options and information."
},
{
"path": "docs/pages/components/dropdown.md",
"chars": 15074,
"preview": "---\nmeta:\n title: Dropdown\n description: 'Dropdowns expose additional content that \"drops down\" in a panel.'\nlayout: c"
},
{
"path": "docs/pages/components/format-bytes.md",
"chars": 3421,
"preview": "---\nmeta:\n title: Format Bytes\n description: Formats a number as a human readable bytes value.\nlayout: component\n---\n\n"
},
{
"path": "docs/pages/components/format-date.md",
"chars": 4094,
"preview": "---\nmeta:\n title: Format Date\n description: Formats a date/time using the specified locale and options.\nlayout: compon"
},
{
"path": "docs/pages/components/format-number.md",
"chars": 4510,
"preview": "---\nmeta:\n title: Format Number\n description: Formats a number using the specified locale and options.\nlayout: compone"
},
{
"path": "docs/pages/components/icon-button.md",
"chars": 3976,
"preview": "---\nmeta:\n title: Icon Button\n description: Icons buttons are simple, icon-only buttons that can be used for actions a"
},
{
"path": "docs/pages/components/icon.md",
"chars": 33542,
"preview": "---\nmeta:\n title: Icon\n description: Icons are symbols that can be used to represent various options within an applica"
},
{
"path": "docs/pages/components/image-comparer.md",
"chars": 3018,
"preview": "---\nmeta:\n title: Image Comparer\n description: Compare visual differences between similar photos with a sliding panel."
},
{
"path": "docs/pages/components/include.md",
"chars": 1589,
"preview": "---\nmeta:\n title: Include\n description: Includes give you the power to embed external HTML files into the page.\nlayout"
},
{
"path": "docs/pages/components/input.md",
"chars": 7154,
"preview": "---\nmeta:\n title: Input\n description: Inputs collect data from the user.\nlayout: component\n---\n\n```html:preview\n<sl-in"
},
{
"path": "docs/pages/components/menu-item.md",
"chars": 7348,
"preview": "---\nmeta:\n title: Menu Item\n description: Menu items provide options for the user to pick from in a menu.\nlayout: comp"
},
{
"path": "docs/pages/components/menu-label.md",
"chars": 1435,
"preview": "---\nmeta:\n title: Menu Label\n description: Menu labels are used to describe a group of menu items.\nlayout: component\n-"
},
{
"path": "docs/pages/components/menu.md",
"chars": 4934,
"preview": "---\nmeta:\n title: Menu\n description: Menus provide a list of options for the user to choose from.\nlayout: component\n--"
},
{
"path": "docs/pages/components/mutation-observer.md",
"chars": 5357,
"preview": "---\nmeta:\n title: Mutation Observer\n description: The Mutation Observer component offers a thin, declarative interface"
},
{
"path": "docs/pages/components/option.md",
"chars": 2111,
"preview": "---\nmeta:\n title: Option\n description: Options define the selectable items within various form controls such as select"
},
{
"path": "docs/pages/components/popup.md",
"chars": 53495,
"preview": "---\nmeta:\n title: Popup\n description: 'Popup is a utility that lets you declaratively anchor \"popup\" containers to ano"
},
{
"path": "docs/pages/components/progress-bar.md",
"chars": 3342,
"preview": "---\nmeta:\n title: Progress Bar\n description: Progress bars are used to show the status of an ongoing operation.\nlayout"
},
{
"path": "docs/pages/components/progress-ring.md",
"chars": 4111,
"preview": "---\nmeta:\n title: Progress Ring\n description: Progress rings are used to show the progress of a determinate operation "
},
{
"path": "docs/pages/components/qr-code.md",
"chars": 4376,
"preview": "---\nmeta:\n title: QR Code\n description: Generates a QR code and renders it using the Canvas API.\nlayout: component\n---"
},
{
"path": "docs/pages/components/radio-button.md",
"chars": 9602,
"preview": "---\nmeta:\n title: Radio Button\n description: Radios buttons allow the user to select a single option from a group usin"
},
{
"path": "docs/pages/components/radio-group.md",
"chars": 9133,
"preview": "---\nmeta:\n title: Radio Group\n description: Radio groups are used to group multiple radios or radio buttons so they fu"
},
{
"path": "docs/pages/components/radio.md",
"chars": 3898,
"preview": "---\nmeta:\n title: Radio\n description: Radios allow the user to select a single option from a group.\nlayout: component\n"
},
{
"path": "docs/pages/components/range.md",
"chars": 4557,
"preview": "---\nmeta:\n title: Range\n description: Ranges allow the user to select a single value within a given range using a slid"
},
{
"path": "docs/pages/components/rating.md",
"chars": 6223,
"preview": "---\nmeta:\n title: Rating\n description: Ratings give users a way to quickly view and provide feedback.\nlayout: componen"
},
{
"path": "docs/pages/components/relative-time.md",
"chars": 4018,
"preview": "---\nmeta:\n title: Relative Time\n description: Outputs a localized time phrase relative to the current date and time.\nl"
},
{
"path": "docs/pages/components/resize-observer.md",
"chars": 1786,
"preview": "---\nmeta:\n title: Resize Observer\n description: The Resize Observer component offers a thin, declarative interface to "
},
{
"path": "docs/pages/components/select.md",
"chars": 20286,
"preview": "---\nmeta:\n title: Select\n description: Selects allow you to choose items from a menu of predefined options.\nlayout: co"
},
{
"path": "docs/pages/components/skeleton.md",
"chars": 9480,
"preview": "---\nmeta:\n title: Skeleton\n description: Skeletons are used to provide a visual representation of where content will e"
},
{
"path": "docs/pages/components/spinner.md",
"chars": 1842,
"preview": "---\nmeta:\n title: Spinner\n description: Spinners are used to show the progress of an indeterminate operation.\nlayout: "
},
{
"path": "docs/pages/components/split-panel.md",
"chars": 22667,
"preview": "---\nmeta:\n title: Split Panel\n description: Split panels display two adjacent panels, allowing the user to reposition "
},
{
"path": "docs/pages/components/switch.md",
"chars": 2475,
"preview": "---\nmeta:\n title: Switch\n description: Switches allow the user to toggle an option on or off.\nlayout: component\n---\n\n`"
},
{
"path": "docs/pages/components/tab-group.md",
"chars": 21323,
"preview": "---\nmeta:\n title: Tab Group\n description: Tab groups organize content into a container that shows one section at a tim"
},
{
"path": "docs/pages/components/tab-panel.md",
"chars": 1676,
"preview": "---\nmeta:\n title: Tab Panel\n description: Tab panels are used inside tab groups to display tabbed content.\nlayout: com"
},
{
"path": "docs/pages/components/tab.md",
"chars": 233,
"preview": "---\nmeta:\n title: Tab\n description: Tabs are used inside tab groups to represent and activate tab panels.\nlayout: comp"
},
{
"path": "docs/pages/components/tag.md",
"chars": 3025,
"preview": "---\nmeta:\n title: Tag\n description: Tags are used as labels to organize things or to indicate a selection.\nlayout: com"
},
{
"path": "docs/pages/components/textarea.md",
"chars": 3829,
"preview": "---\nmeta:\n title: Textarea\n description: Textareas collect data from the user and allow multiple lines of text.\nlayout"
},
{
"path": "docs/pages/components/tooltip.md",
"chars": 11902,
"preview": "---\nmeta:\n title: Tooltip\n description: Tooltips display additional information based on a specific action.\nlayout: co"
},
{
"path": "docs/pages/components/tree-item.md",
"chars": 4003,
"preview": "---\nmeta:\n title: Tree Item\n description: A tree item serves as a hierarchical node that lives inside a tree.\nlayout: "
},
{
"path": "docs/pages/components/tree.md",
"chars": 12586,
"preview": "---\nmeta:\n title: Tree\n description: Trees allow you to display a hierarchical list of selectable tree items. Items wi"
},
{
"path": "docs/pages/components/visually-hidden.md",
"chars": 1960,
"preview": "---\nmeta:\n title: Visually Hidden\n description: The visually hidden utility makes content accessible to assistive devi"
},
{
"path": "docs/pages/frameworks/angular.md",
"chars": 3099,
"preview": "---\nmeta:\n title: Angular\n description: Tips for using Shoelace in your Angular app.\n---\n\n# Angular\n\nAngular [plays ni"
},
{
"path": "docs/pages/frameworks/react.md",
"chars": 7602,
"preview": "---\nmeta:\n title: React\n description: Tips for using Shoelace in your React app.\n---\n\n# React\n\nShoelace offers a React"
},
{
"path": "docs/pages/frameworks/svelte.md",
"chars": 2985,
"preview": "---\nmeta:\n title: Svelte\n description: Tips for using Shoelace in your Svelte app.\n---\n\n# Svelte\n\nSvelte [plays nice]("
},
{
"path": "docs/pages/frameworks/vue-2.md",
"chars": 3157,
"preview": "---\nmeta:\n title: Vue (version 2)\n description: Tips for using Shoelace in your Vue 2 app.\n---\n\n# Vue (version 2)\n\nVue"
},
{
"path": "docs/pages/frameworks/vue.md",
"chars": 4336,
"preview": "---\nmeta:\n title: Vue\n description: Tips for using Shoelace in your Vue 3 app.\n---\n\n# Vue\n\nVue [plays nice](https://cu"
},
{
"path": "docs/pages/getting-started/customizing.md",
"chars": 7633,
"preview": "---\nmeta:\n title: Customizing\n description: Learn how to customize Shoelace through parts and custom properties.\n---\n\n"
},
{
"path": "docs/pages/getting-started/form-controls.md",
"chars": 22693,
"preview": "---\nmeta:\n title: Form Controls\n description: Some things to note about Shoelace and forms.\n---\n\n# Form Controls\n\nEver"
},
{
"path": "docs/pages/getting-started/installation.md",
"chars": 10749,
"preview": "---\nmeta:\n title: Installation\n description: Choose the installation method that works best for you.\n---\n\n# Installati"
},
{
"path": "docs/pages/getting-started/localization.md",
"chars": 5509,
"preview": "---\nmeta:\n title: Localization\n description: Discover how to localize Shoelace with minimal effort.\n---\n\n# Localizatio"
},
{
"path": "docs/pages/getting-started/themes.md",
"chars": 7192,
"preview": "---\nmeta:\n title: Themes\n description: Everything you need to know about theming Shoelace.\n---\n\n# Themes\n\nShoelace is "
},
{
"path": "docs/pages/getting-started/usage.md",
"chars": 10824,
"preview": "---\nmeta:\n title: Usage\n description: Learn more about using custom elements.\n---\n\n# Usage\n\nShoelace components are ju"
},
{
"path": "docs/pages/index.md",
"chars": 7082,
"preview": "---\nmeta:\n title: 'Shoelace: A forward-thinking library of web components.'\n description: Hand-crafted custom elements"
},
{
"path": "docs/pages/resources/accessibility.md",
"chars": 2659,
"preview": "---\nmeta:\n title: Accessibility Commitment\n description: Shoelace recognizes the need for all users to have undeterred"
},
{
"path": "docs/pages/resources/changelog.md",
"chars": 134785,
"preview": "---\nmeta:\n title: Changelog\n description: Changes to each version of the project are documented here.\n---\n\n# Changelog"
},
{
"path": "docs/pages/resources/community.md",
"chars": 2942,
"preview": "---\nmeta:\n title: Community\n description: Shoelace has a growing community of designers and developers that are buildi"
},
{
"path": "docs/pages/resources/contributing.md",
"chars": 28275,
"preview": "---\nmeta:\n title: Contributing\n description: Shoelace is an open source project, meaning everyone can use it and contr"
},
{
"path": "docs/pages/tokens/border-radius.md",
"chars": 1620,
"preview": "---\nmeta:\n title: Border Radius\n description: Border radius tokens are used to give sharp edges a more subtle, rounded"
},
{
"path": "docs/pages/tokens/color.md",
"chars": 40901,
"preview": "---\nmeta:\n title: Color Tokens\n description: Color tokens help maintain consistent use of color throughout your app.\n-"
},
{
"path": "docs/pages/tokens/elevation.md",
"chars": 1112,
"preview": "---\nmeta:\n title: Elevation\n description: Elevation tokens are used to give elements the appearance of being raised of"
},
{
"path": "docs/pages/tokens/more.md",
"chars": 10562,
"preview": "---\nmeta:\n title: More Design Tokens\n description: Additional design tokens can be found here.\n---\n\n# More Design Toke"
},
{
"path": "docs/pages/tokens/spacing.md",
"chars": 2147,
"preview": "---\nmeta:\n title: Spacing Tokens\n description: Spacing tokens are used to provide consistent spacing between content i"
},
{
"path": "docs/pages/tokens/transition.md",
"chars": 1181,
"preview": "---\nmeta:\n title: Transition Tokens\n description: Transition tokens are used to provide consistent transitions through"
},
{
"path": "docs/pages/tokens/typography.md",
"chars": 6947,
"preview": "---\nmeta:\n title: Typography\n description: Typography tokens are used to maintain a consistent set of font styles thro"
},
{
"path": "docs/pages/tokens/z-index.md",
"chars": 445,
"preview": "---\nmeta:\n title: Z-Index Tokens\n description: Z-indexes are used to stack components in a logical manner.\n---\n\n# Z-In"
},
{
"path": "docs/pages/tutorials/integrating-with-astro.md",
"chars": 3859,
"preview": "---\nmeta:\n title: Integrating with Astro\n description: This page explains how to integrate Shoelace with an Astro app."
},
{
"path": "docs/pages/tutorials/integrating-with-laravel.md",
"chars": 4056,
"preview": "---\nmeta:\n title: Integrating with Laravel\n description: This page explains how to integrate Shoelace with a Laravel a"
},
{
"path": "docs/pages/tutorials/integrating-with-nextjs.md",
"chars": 9350,
"preview": "---\nmeta:\n title: Integrating with NextJS\n description: This page explains how to integrate Shoelace with a NextJS app"
},
{
"path": "docs/pages/tutorials/integrating-with-rails.md",
"chars": 3356,
"preview": "---\nmeta:\n title: Integrating with Rails\n description: This page explains how to integrate Shoelace with a Rails app.\n"
},
{
"path": "lint-staged.config.js",
"chars": 195,
"preview": "export default {\n '*.{js,ts,json,html,xml,css,scss,sass,md}': 'cspell --no-must-find-files',\n 'src/**/*.{js,ts}': 'esl"
},
{
"path": "package.json",
"chars": 4998,
"preview": "{\n \"name\": \"@shoelace-style/shoelace\",\n \"description\": \"A forward-thinking library of web components.\",\n \"version\": \""
},
{
"path": "prettier.config.js",
"chars": 430,
"preview": "/** @type {import(\"prettier\").Config} */\nconst config = {\n arrowParens: 'avoid',\n bracketSpacing: true,\n htmlWhitespa"
},
{
"path": "scripts/build.js",
"chars": 10157,
"preview": "import { deleteAsync } from 'del';\nimport { exec, spawn } from 'child_process';\nimport { globby } from 'globby';\nimport "
},
{
"path": "scripts/make-icons.js",
"chars": 1771,
"preview": "//\n// This script downloads and generates icons and icon metadata.\n//\nimport chalk from 'chalk';\nimport commandLineArgs "
},
{
"path": "scripts/make-metadata.js",
"chars": 342,
"preview": "//\n// This script runs the Custom Elements Manifest analyzer to generate custom-elements.json\n//\n\nimport { execSync } fr"
},
{
"path": "scripts/make-react.js",
"chars": 2598,
"preview": "import commandLineArgs from 'command-line-args';\nimport fs from 'fs';\nimport path from 'path';\nimport { deleteSync } fro"
},
{
"path": "scripts/make-themes.js",
"chars": 2016,
"preview": "//\n// This script bakes and copies themes, then generates a corresponding Lit stylesheet in dist/themes\n//\nimport chalk "
},
{
"path": "scripts/plop/plopfile.js",
"chars": 2141,
"preview": "export default function (plop) {\n plop.setHelper('tagWithoutPrefix', tag => tag.replace(/^sl-/, ''));\n\n plop.setHelper"
},
{
"path": "scripts/plop/templates/component/component.hbs",
"chars": 1253,
"preview": "import { property } from 'lit/decorators.js';\nimport { html } from 'lit';\nimport { LocalizeController } from '../../util"
},
{
"path": "scripts/plop/templates/component/define.hbs",
"chars": 317,
"preview": "import {{ properCase tag }} from './{{ tagWithoutPrefix tag }}.component.js';\n\nexport * from './{{ tagWithoutPrefix tag "
},
{
"path": "scripts/plop/templates/component/docs.hbs",
"chars": 218,
"preview": "---\nmeta:\n title: {{ tagToTitle tag }}\n description:\nlayout: component\n---\n\n```html:preview\n<{{ tag }}></{{ tag }}>\n``"
},
{
"path": "scripts/plop/templates/component/styles.hbs",
"chars": 85,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: block;\n }\n`;\n"
},
{
"path": "scripts/plop/templates/component/tests.hbs",
"chars": 274,
"preview": "import '../../../dist/shoelace.js';\nimport { expect, fixture, html } from '@open-wc/testing';\n\ndescribe('<{{ tag }}>', ("
},
{
"path": "scripts/shared.js",
"chars": 472,
"preview": "/** Gets an array of components from a CEM object. */\nexport function getAllComponents(metadata) {\n const allComponents"
},
{
"path": "src/components/alert/alert.component.ts",
"chars": 10262,
"preview": "import { animateTo, stopAnimations } from '../../internal/animate.js';\nimport { blurActiveElement } from '../../internal"
},
{
"path": "src/components/alert/alert.styles.ts",
"chars": 3128,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: contents;\n\n /* For better DX, we'll reset the "
},
{
"path": "src/components/alert/alert.test.ts",
"chars": 13430,
"preview": "import '../../../dist/shoelace.js';\nimport { aTimeout, expect, fixture, html, oneEvent } from '@open-wc/testing';\nimport"
},
{
"path": "src/components/alert/alert.ts",
"chars": 221,
"preview": "import SlAlert from './alert.component.js';\n\nexport * from './alert.component.js';\nexport default SlAlert;\n\nSlAlert.defi"
},
{
"path": "src/components/animated-image/animated-image.component.ts",
"chars": 3792,
"preview": "import { html } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { watch } from '../../int"
},
{
"path": "src/components/animated-image/animated-image.styles.ts",
"chars": 1195,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n --control-box-size: 3rem;\n --icon-size: calc(var(--cont"
},
{
"path": "src/components/animated-image/animated-image.test.ts",
"chars": 2419,
"preview": "import '../../../dist/shoelace.js';\nimport { clickOnElement } from '../../internal/test.js';\nimport { expect, fixture, h"
},
{
"path": "src/components/animated-image/animated-image.ts",
"chars": 289,
"preview": "import SlAnimatedImage from './animated-image.component.js';\n\nexport * from './animated-image.component.js';\nexport defa"
},
{
"path": "src/components/animation/animation.component.ts",
"chars": 6960,
"preview": "import { animations } from './animations.js';\nimport { html } from 'lit';\nimport { property, queryAsync } from 'lit/deco"
},
{
"path": "src/components/animation/animation.styles.ts",
"chars": 88,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: contents;\n }\n`;\n"
},
{
"path": "src/components/animation/animation.test.ts",
"chars": 2924,
"preview": "import '../../../dist/shoelace.js';\nimport { aTimeout, expect, fixture, oneEvent } from '@open-wc/testing';\nimport { htm"
},
{
"path": "src/components/animation/animation.ts",
"chars": 253,
"preview": "import SlAnimation from './animation.component.js';\n\nexport * from './animation.component.js';\nexport default SlAnimatio"
},
{
"path": "src/components/animation/animations.ts",
"chars": 448,
"preview": "import * as animations from '@shoelace-style/animations';\n\nexport { animations };\n\n/** Gets a list of all supported anim"
},
{
"path": "src/components/avatar/avatar.component.ts",
"chars": 3283,
"preview": "import { classMap } from 'lit/directives/class-map.js';\nimport { html } from 'lit';\nimport { property, state } from 'lit"
},
{
"path": "src/components/avatar/avatar.styles.ts",
"chars": 1257,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: inline-block;\n\n --size: 3rem;\n }\n\n .avatar {"
},
{
"path": "src/components/avatar/avatar.test.ts",
"chars": 6778,
"preview": "import '../../../dist/shoelace.js';\nimport { aTimeout, expect, fixture, html, waitUntil } from '@open-wc/testing';\nimpor"
},
{
"path": "src/components/avatar/avatar.ts",
"chars": 229,
"preview": "import SlAvatar from './avatar.component.js';\n\nexport * from './avatar.component.js';\nexport default SlAvatar;\n\nSlAvatar"
},
{
"path": "src/components/badge/badge.component.ts",
"chars": 1654,
"preview": "import { classMap } from 'lit/directives/class-map.js';\nimport { html } from 'lit';\nimport { property } from 'lit/decora"
},
{
"path": "src/components/badge/badge.styles.ts",
"chars": 1969,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: inline-flex;\n }\n\n .badge {\n display: inline-"
},
{
"path": "src/components/badge/badge.test.ts",
"chars": 2887,
"preview": "import '../../../dist/shoelace.js';\nimport { expect, fixture, html } from '@open-wc/testing';\nimport type SlBadge from '"
},
{
"path": "src/components/badge/badge.ts",
"chars": 221,
"preview": "import SlBadge from './badge.component.js';\n\nexport * from './badge.component.js';\nexport default SlBadge;\n\nSlBadge.defi"
},
{
"path": "src/components/breadcrumb/breadcrumb.component.ts",
"chars": 3873,
"preview": "import { html } from 'lit';\nimport { LocalizeController } from '../../utilities/localize.js';\nimport { property, query }"
},
{
"path": "src/components/breadcrumb/breadcrumb.styles.ts",
"chars": 136,
"preview": "import { css } from 'lit';\n\nexport default css`\n .breadcrumb {\n display: flex;\n align-items: center;\n flex-wra"
},
{
"path": "src/components/breadcrumb/breadcrumb.test.ts",
"chars": 4064,
"preview": "import '../../../dist/shoelace.js';\nimport { expect, fixture, html } from '@open-wc/testing';\nimport type SlBreadcrumb f"
},
{
"path": "src/components/breadcrumb/breadcrumb.ts",
"chars": 261,
"preview": "import SlBreadcrumb from './breadcrumb.component.js';\n\nexport * from './breadcrumb.component.js';\nexport default SlBread"
},
{
"path": "src/components/breadcrumb-item/breadcrumb-item.component.ts",
"chars": 4572,
"preview": "import { classMap } from 'lit/directives/class-map.js';\nimport { HasSlotController } from '../../internal/slot.js';\nimpo"
},
{
"path": "src/components/breadcrumb-item/breadcrumb-item.styles.ts",
"chars": 1999,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: inline-flex;\n }\n\n .breadcrumb-item {\n displa"
},
{
"path": "src/components/breadcrumb-item/breadcrumb-item.test.ts",
"chars": 6732,
"preview": "import '../../../dist/shoelace.js';\nimport { expect, fixture, html } from '@open-wc/testing';\nimport type SlBreadcrumbIt"
},
{
"path": "src/components/breadcrumb-item/breadcrumb-item.ts",
"chars": 297,
"preview": "import SlBreadcrumbItem from './breadcrumb-item.component.js';\n\nexport * from './breadcrumb-item.component.js';\nexport d"
},
{
"path": "src/components/button/button.component.ts",
"chars": 11887,
"preview": "import { classMap } from 'lit/directives/class-map.js';\nimport { FormControlController, validValidityState } from '../.."
},
{
"path": "src/components/button/button.styles.ts",
"chars": 15814,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: inline-block;\n position: relative;\n width: "
},
{
"path": "src/components/button/button.test.ts",
"chars": 12354,
"preview": "import '../../../dist/shoelace.js';\nimport { expect, fixture, html, waitUntil } from '@open-wc/testing';\nimport { runFor"
},
{
"path": "src/components/button/button.ts",
"chars": 229,
"preview": "import SlButton from './button.component.js';\n\nexport * from './button.component.js';\nexport default SlButton;\n\nSlButton"
},
{
"path": "src/components/button-group/button-group.component.ts",
"chars": 3370,
"preview": "import { html } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport componentStyles from '../"
},
{
"path": "src/components/button-group/button-group.styles.ts",
"chars": 157,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n display: inline-block;\n }\n\n .button-group {\n display:"
},
{
"path": "src/components/button-group/button-group.test.ts",
"chars": 3705,
"preview": "import '../../../dist/shoelace.js';\nimport { elementUpdated, expect, fixture, html } from '@open-wc/testing';\nimport typ"
},
{
"path": "src/components/button-group/button-group.ts",
"chars": 273,
"preview": "import SlButtonGroup from './button-group.component.js';\n\nexport * from './button-group.component.js';\nexport default Sl"
},
{
"path": "src/components/card/card.component.ts",
"chars": 2208,
"preview": "import { classMap } from 'lit/directives/class-map.js';\nimport { HasSlotController } from '../../internal/slot.js';\nimpo"
},
{
"path": "src/components/card/card.styles.ts",
"chars": 1550,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n --border-color: var(--sl-color-neutral-200);\n --border-"
},
{
"path": "src/components/card/card.test.ts",
"chars": 4795,
"preview": "import '../../../dist/shoelace.js';\nimport { expect, fixture, html } from '@open-wc/testing';\nimport type SlCard from '."
},
{
"path": "src/components/card/card.ts",
"chars": 213,
"preview": "import SlCard from './card.component.js';\n\nexport * from './card.component.js';\nexport default SlCard;\n\nSlCard.define('s"
},
{
"path": "src/components/carousel/autoplay-controller.ts",
"chars": 2017,
"preview": "import type { ReactiveController, ReactiveElement } from 'lit';\n\n/**\n * A controller that repeatedly calls the specified"
},
{
"path": "src/components/carousel/carousel.component.ts",
"chars": 24064,
"preview": "import '../../internal/scrollend-polyfill.js';\n\nimport { AutoplayController } from './autoplay-controller.js';\nimport { "
},
{
"path": "src/components/carousel/carousel.styles.ts",
"chars": 3584,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n --slide-gap: var(--sl-spacing-medium, 1rem);\n --aspect-"
},
{
"path": "src/components/carousel/carousel.test.ts",
"chars": 28870,
"preview": "import '../../../dist/shoelace.js';\nimport { aTimeout, expect, fixture, html, nextFrame, oneEvent, waitUntil } from '@op"
},
{
"path": "src/components/carousel/carousel.ts",
"chars": 245,
"preview": "import SlCarousel from './carousel.component.js';\n\nexport * from './carousel.component.js';\nexport default SlCarousel;\n\n"
},
{
"path": "src/components/carousel-item/carousel-item.component.ts",
"chars": 774,
"preview": "import { html } from 'lit';\nimport componentStyles from '../../styles/component.styles.js';\nimport ShoelaceElement from "
},
{
"path": "src/components/carousel-item/carousel-item.styles.ts",
"chars": 438,
"preview": "import { css } from 'lit';\n\nexport default css`\n :host {\n --aspect-ratio: inherit;\n\n display: flex;\n align-ite"
},
{
"path": "src/components/carousel-item/carousel-item.test.ts",
"chars": 504,
"preview": "import '../../../dist/shoelace.js';\nimport { expect, fixture, html } from '@open-wc/testing';\n\ndescribe('<sl-carousel-it"
},
{
"path": "src/components/carousel-item/carousel-item.ts",
"chars": 281,
"preview": "import SlCarouselItem from './carousel-item.component.js';\n\nexport * from './carousel-item.component.js';\nexport default"
}
]
// ... and 292 more files (download for full content)
About this extraction
This page contains the full source code of the shoelace-style/shoelace GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 492 files (2.2 MB), approximately 597.6k tokens, and a symbol index with 1000 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.