Page Not Found
+ +The page you were looking for couldn’t be found.
+Press / to search, or head back to the homepage.
+diff --git a/404.html b/404.html new file mode 100644 index 00000000..bcbafaaf --- /dev/null +++ b/404.html @@ -0,0 +1,451 @@ + + +
+ + + +The page you were looking for couldn’t be found.
+Press / to search, or head back to the homepage.
+<sl-alert> | SlAlert
+ + Alerts are used to display important messages inline or as toast notifications. +
+ ++<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> +
+import SlAlert from '@onsonr/nebula/dist/react/alert'; +import SlIcon from '@onsonr/nebula/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> +); +
Alerts will not be visible if the open
attribute is not present.
Set the variant
attribute to change the alert’s variant.
+<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> +
+import SlAlert from '@onsonr/nebula/dist/react/alert'; +import SlIcon from '@onsonr/nebula/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> + </> +); +
Add the closable
attribute to show a close button that will hide the alert.
+<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> +
+import { useState } from 'react'; +import SlAlert from '@onsonr/nebula/dist/react/alert'; +import SlIcon from '@onsonr/nebula/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> + ); +}; +
Icons are optional. Simply omit the icon
slot if you don’t want them.
+<sl-alert variant="primary" open> Nothing fancy here, just a simple alert. </sl-alert> +
+import SlAlert from '@onsonr/nebula/dist/react/alert'; + +const App = () => ( + <SlAlert variant="primary" open> + Nothing fancy here, just a simple alert. + </SlAlert> +); +
+ Set the duration
attribute to automatically hide an alert after a period of time. This is
+ useful for alerts that don’t require acknowledgement.
+
+<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> +
+import { useState } from 'react'; +import SlAlert from '@onsonr/nebula/dist/react/alert'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/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> + </> + ); +}; +
+ 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 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.
+
+<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> +
+import { useRef } from 'react'; +import SlAlert from '@onsonr/nebula/dist/react/alert'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/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> + </> + ); +}; +
+ 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.
+
+<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 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.
+
+.sl-toast-stack { + left: 0; + right: auto; +} +
+ 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. +
++ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/alert/alert.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/alert/alert.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/alert/alert.js';
To import this component as a React component:
++import SlAlert from '@onsonr/nebula/dist/react/alert';
Name | +Description | +
---|---|
(default) | +The alert’s main content. | +
+ icon
+ |
+ An icon to show in the alert. Works best with <sl-icon> . |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ open
+ |
+
+ Indicates whether or not the alert is open. You can toggle this attribute to show and hide the
+ alert, or you can use the show() and hide() methods and this attribute
+ will reflect the alert’s open state.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ closable
+ |
+ Enables a close button that allows the user to dismiss the alert. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ variant
+ |
+ The alert’s theme variant. | +
+ |
+
+ 'primary' | 'success' | 'neutral' | 'warning' | 'danger'
+ |
+
+ 'primary'
+ |
+
+ duration
+ |
+
+ The length of time, in milliseconds, the alert will show before closing itself. If the user
+ interacts with the alert before it closes (e.g. moves the mouse over it), the timer will restart.
+ Defaults to Infinity , meaning the alert will not close on its own.
+ |
+ + | - | +
+ Infinity
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-show |
+ onSlShow |
+ Emitted when the alert opens. | +- | +
sl-after-show |
+ onSlAfterShow |
+ Emitted after the alert opens and all animations are complete. | +- | +
sl-hide |
+ onSlHide |
+ Emitted when the alert closes. | +- | +
sl-after-hide |
+ onSlAfterHide |
+ Emitted after the alert closes and all animations are complete. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the alert. | +- | +
hide() |
+ Hides the alert | +- | +
toast() |
+ + Displays the alert as a toast notification. This will move the alert out of its position in the DOM + and, when dismissed, it will be removed from the DOM completely. By storing a reference to the + alert, you can reuse it by calling this method again. The returned promise will resolve after the + alert is hidden. + | +- | +
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
icon |
+ The container that wraps the optional icon. | +
message |
+ The container that wraps the alert’s main content. | +
close-button |
+ The close button, an <sl-icon-button> . |
+
close-button__base |
+ The close button’s exported base part. |
+
+ Learn more about customizing CSS parts. +
+ +Name | +Description | +
---|---|
alert.show |
+ The animation to use when showing the alert. | +
alert.hide |
+ The animation to use when hiding the alert. | +
+ Learn more about customizing animations. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon-button>
<sl-animated-image> | SlAnimatedImage
+ + A component for displaying animated GIFs and WEBPs that play and pause on interaction. +
+ ++<sl-animated-image + src="https://shoelace.style/assets/images/walk.gif" + alt="Animation of untied shoes walking on pavement" +></sl-animated-image> +
+import SlAnimatedImage from '@onsonr/nebula/dist/react/animated-image'; + +const App = () => ( + <SlAnimatedImage + src="https://shoelace.style/assets/images/walk.gif" + alt="Animation of untied shoes walking on pavement" + /> +); +
+ This component uses <canvas>
to draw freeze frames, so images are subject to
+ cross-origin restrictions.
+
Both GIF and WEBP images are supported.
+ ++<sl-animated-image + src="https://shoelace.style/assets/images/tie.webp" + alt="Animation of a shoe being tied" +></sl-animated-image> +
+import SlAnimatedImage from '@onsonr/nebula/dist/react/animated-image'; + +const App = () => ( + <SlAnimatedImage src="https://shoelace.style/assets/images/tie.webp" alt="Animation of a shoe being tied" /> +); +
To set a custom size, apply a width and/or height to the host element.
+ ++<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> +
+import SlAnimatedImage from '@onsonr/nebula/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' }} + /> +); +
+ You can change the appearance and location of the control box by targeting the control-box
part
+ in your styles.
+
+<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> +
+import SlAnimatedImage from '@onsonr/nebula/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> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/animated-image/animated-image.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/animated-image/animated-image.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/animated-image/animated-image.js';
To import this component as a React component:
++import SlAnimatedImage from '@onsonr/nebula/dist/react/animated-image';
Name | +Description | +
---|---|
+ play-icon
+ |
+
+ Optional play icon to use instead of the default. Works best with <sl-icon> .
+ |
+
+ pause-icon
+ |
+
+ Optional pause icon to use instead of the default. Works best with <sl-icon> .
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ src
+ |
+ The path to the image to load. | ++ |
+ string
+ |
+ - | +
+ alt
+ |
+ A description of the image used by assistive devices. | ++ |
+ string
+ |
+ - | +
+ play
+ |
+ Plays the animation. When this attribute is remove, the animation will pause. | +
+ |
+
+ boolean
+ |
+ - | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-load |
+ onSlLoad |
+ Emitted when the image loads successfully. | +- | +
sl-error |
+ onSlError |
+ Emitted when the image fails to load. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Default | +
---|---|---|
--control-box-size |
+ The size of the icon box. | ++ |
--icon-size |
+ The size of the play/pause icons. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
control-box |
+ The container that surrounds the pause/play icons and provides their background. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-animation> | SlAnimation
+ + Animate elements declaratively with nearly 100 baked-in presets, or roll your own with custom keyframes. + Powered by the + Web Animations API. +
+ +
+ 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 for a list of all animation options.
+
+<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> +
+import SlAnimation from '@onsonr/nebula/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> + </> +); +
+ The animation will only be applied to the first child element found in <sl-animation>
.
+
+ This example demonstrates all of the baked-in animations and easings. Animations are based on those found in + the popular + Animate.css + library. +
+ ++<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> +
+ Use an + Intersection Observer + 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. +
+ ++<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> +
+import { useEffect, useRef, useState } from 'react'; +import SlAnimation from '@onsonr/nebula/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> + </> + ); +}; +
+ Supply your own + keyframe formats + to build custom animations. +
+ ++<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> +
+import SlAnimation from '@onsonr/nebula/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> + </> +); +
+ 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.
+
+<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> +
+import { useState } from 'react'; +import SlAnimation from '@onsonr/nebula/dist/react/animation'; +import SlButton from '@onsonr/nebula/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> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/animation/animation.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/animation/animation.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/animation/animation.js';
To import this component as a React component:
++import SlAnimation from '@onsonr/nebula/dist/react/animation';
Name | +Description | +
---|---|
(default) | +
+ The element to animate. Avoid slotting in more than one element, as subsequent ones will be ignored.
+ To animate multiple elements, either wrap them in a single container or use multiple
+ <sl-animation> elements.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+
+ The name of the built-in animation to use. For custom animations, use the
+ keyframes prop.
+ |
+ + |
+ string
+ |
+
+ 'none'
+ |
+
+ play
+ |
+ + Plays the animation. When omitted, the animation will be paused. This attribute will be + automatically removed when the animation finishes or gets canceled. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ delay
+ |
+ The number of milliseconds to delay the start of the animation. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ direction
+ |
+ + Determines the direction of playback as well as the behavior when reaching the end of an iteration. + Learn more + | ++ |
+ PlaybackDirection
+ |
+
+ 'normal'
+ |
+
+ duration
+ |
+ The number of milliseconds each iteration of the animation takes to complete. | ++ |
+ number
+ |
+
+ 1000
+ |
+
+ easing
+ |
+
+ The easing function to use for the animation. This can be a Shoelace easing function or a custom
+ easing function such as cubic-bezier(0, 1, .76, 1.14) .
+ |
+ + |
+ string
+ |
+
+ 'linear'
+ |
+
+ endDelay
+
+ + end-delay
+
+ |
+ The number of milliseconds to delay after the active period of an animation sequence. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ fill
+ |
+ Sets how the animation applies styles to its target before and after its execution. | ++ |
+ FillMode
+ |
+
+ 'auto'
+ |
+
+ iterations
+ |
+
+ The number of iterations to run before the animation completes. Defaults to Infinity ,
+ which loops.
+ |
+ + | - | +
+ Infinity
+ |
+
+ iterationStart
+
+ + iteration-start
+
+ |
+ The offset at which to start the animation, usually between 0 (start) and 1 (end). | ++ |
+ number
+ |
+
+ 0
+ |
+
+ keyframes
+ |
+ The keyframes to use for the animation. If this is set, name will be ignored. |
+ + |
+ Keyframe[] | undefined
+ |
+ - | +
+ playbackRate
+
+ + playback-rate
+
+ |
+
+ Sets the animation’s playback rate. The default is 1 , which plays the animation at a
+ normal speed. Setting this to 2 , for example, will double the animation’s speed. A
+ negative value can be used to reverse the animation. This value can be changed without causing the
+ animation to restart.
+ |
+ + |
+ number
+ |
+
+ 1
+ |
+
+ currentTime
+ |
+ Gets and sets the current animation time. | ++ |
+ CSSNumberish
+ |
+ - | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-cancel |
+ onSlCancel |
+ Emitted when the animation is canceled. | +- | +
sl-finish |
+ onSlFinish |
+ Emitted when the animation finishes. | +- | +
sl-start |
+ onSlStart |
+ Emitted when the animation starts or restarts. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
cancel() |
+ Clears all keyframe effects caused by this animation and aborts its playback. | +- | +
finish() |
+ + Sets the playback time to the end of the animation corresponding to the current playback direction. + | +- | +
+ Learn more about methods. +
+<sl-avatar> | SlAvatar
+ Avatars are used to represent a person or object.
+ +
+ 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.
+
+<sl-avatar label="User avatar"></sl-avatar> +
+import SlAvatar from '@onsonr/nebula/dist/react/avatar'; + +const App = () => <SlAvatar label="User avatar" />; +
+ 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
.
+
+<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> +
+import SlAvatar from '@onsonr/nebula/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" + /> +); +
+ When you don’t have an image to use, you can set the initials
attribute to show something more
+ personalized than an icon.
+
+<sl-avatar initials="SL" label="Avatar with initials: SL"></sl-avatar> +
+import SlAvatar from '@onsonr/nebula/dist/react/avatar'; + +const App = () => <SlAvatar initials="SL" label="Avatar with initials: SL" />; +
+ 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.
+
+<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> +
+import SlAvatar from '@onsonr/nebula/dist/react/avatar'; +import SlIcon from '@onsonr/nebula/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> + </> +); +
Avatars can be shaped using the shape
attribute.
+<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> +
+import SlAvatar from '@onsonr/nebula/dist/react/avatar'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <> + <SlAvatar shape="square" label="Square avatar" /> + <SlAvatar shape="rounded" label="Rounded avatar" /> + <SlAvatar shape="circle" label="Circle avatar" /> + </> +); +
You can group avatars with a few lines of CSS.
+ ++<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> +
+import SlAvatar from '@onsonr/nebula/dist/react/avatar'; +import SlIcon from '@onsonr/nebula/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> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/avatar/avatar.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/avatar/avatar.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/avatar/avatar.js';
To import this component as a React component:
++import SlAvatar from '@onsonr/nebula/dist/react/avatar';
Name | +Description | +
---|---|
+ icon
+ |
+
+ The default icon to use when no image or initials are present. Works best with
+ <sl-icon> .
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ image
+ |
+ The image source to use for the avatar. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ label
+ |
+ A label to use to describe the avatar to assistive devices. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ initials
+ |
+ Initials to use as a fallback when no image is available (1–2 characters max recommended). | ++ |
+ string
+ |
+
+ ''
+ |
+
+ loading
+ |
+ Indicates how the browser should load the image. | ++ |
+ 'eager' | 'lazy'
+ |
+
+ 'eager'
+ |
+
+ shape
+ |
+ The shape of the avatar. | +
+ |
+
+ 'circle' | 'square' | 'rounded'
+ |
+
+ 'circle'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Default | +
---|---|---|
--size |
+ The size of the avatar. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
icon |
+ The container that wraps the avatar’s icon. | +
initials |
+ The container that wraps the avatar’s initials. | +
image |
+ The avatar image. Only shown when the image attribute is set. |
+
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-badge> | SlBadge
+ Badges are used to draw attention and display statuses or counts.
+ ++<sl-badge>Badge</sl-badge> +
+import SlBadge from '@onsonr/nebula/dist/react/badge'; + +const App = () => <SlBadge>Badge</SlBadge>; +
Set the variant
attribute to change the badge’s variant.
+<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> +
+import SlBadge from '@onsonr/nebula/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> + </> +); +
Use the pill
attribute to give badges rounded edges.
+<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> +
+import SlBadge from '@onsonr/nebula/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> + </> +); +
Use the pulse
attribute to draw attention to the badge with a subtle animation.
+<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> +
+import SlBadge from '@onsonr/nebula/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> + </> +); +
+ 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. +
+ ++<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> +
+import SlBadge from '@onsonr/nebula/dist/react/badge'; +import SlButton from '@onsonr/nebula/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> + </> +); +
+ When including badges in menu items, use the suffix
slot to make sure they’re aligned
+ correctly.
+
+<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> +
+import SlBadge from '@onsonr/nebula/dist/react/badge'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; +import SlMenuLabel from '@onsonr/nebula/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> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/badge/badge.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/badge/badge.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/badge/badge.js';
To import this component as a React component:
++import SlBadge from '@onsonr/nebula/dist/react/badge';
Name | +Description | +
---|---|
(default) | +The badge’s content. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ variant
+ |
+ The badge’s theme variant. | +
+ |
+
+ 'primary' | 'success' | 'neutral' | 'warning' | 'danger'
+ |
+
+ 'primary'
+ |
+
+ pill
+ |
+ Draws a pill-style badge with rounded edges. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ pulse
+ |
+ Makes the badge pulsate to draw attention. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-breadcrumb-item> | SlBreadcrumbItem
+ + Breadcrumb Items are used inside breadcrumbs to represent different + links. +
+ ++<sl-breadcrumb> + <sl-breadcrumb-item> + <sl-icon slot="prefix" name="house"></sl-icon> + Home + </sl-breadcrumb-item> + <sl-breadcrumb-item>Clothing</sl-breadcrumb-item> + <sl-breadcrumb-item>Shirts</sl-breadcrumb-item> +</sl-breadcrumb> +
+import SlBreadcrumb from '@onsonr/nebula/dist/react/breadcrumb'; +import SlBreadcrumbItem from '@onsonr/nebula/dist/react/breadcrumb-item'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <SlBreadcrumb> + <SlBreadcrumbItem> + <SlIcon slot="prefix" name="house"></SlIcon> + Home + </SlBreadcrumbItem> + <SlBreadcrumbItem>Clothing</SlBreadcrumbItem> + <SlBreadcrumbItem>Shirts</SlBreadcrumbItem> + </SlBreadcrumb> +); +
+ Additional demonstrations can be found in the breadcrumb examples. +
++ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/breadcrumb-item/breadcrumb-item.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/breadcrumb-item/breadcrumb-item.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/breadcrumb-item/breadcrumb-item.js';
To import this component as a React component:
++import SlBreadcrumbItem from '@onsonr/nebula/dist/react/breadcrumb-item';
Name | +Description | +
---|---|
(default) | +The breadcrumb item’s label. | +
+ prefix
+ |
+ An optional prefix, usually an icon or icon button. | +
+ suffix
+ |
+ An optional suffix, usually an icon or icon button. | +
+ separator
+ |
+
+ The separator to use for the breadcrumb item. This will only change the separator for this item. If
+ you want to change it for all items in the group, set the separator on
+ <sl-breadcrumb> instead.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ href
+ |
+ + Optional URL to direct the user to when the breadcrumb item is activated. When set, a link will be + rendered internally. When unset, a button will be rendered instead. + | ++ |
+ string | undefined
+ |
+ - | +
+ target
+ |
+ Tells the browser where to open the link. Only used when href is set. |
+ + |
+ '_blank' | '_parent' | '_self' | '_top' | undefined
+ |
+ - | +
+ rel
+ |
+ The rel attribute to use on the link. Only used when href is set. |
+ + |
+ string
+ |
+
+ 'noreferrer noopener'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
label |
+ The breadcrumb item’s label. | +
prefix |
+ The container that wraps the prefix. | +
suffix |
+ The container that wraps the suffix. | +
separator |
+ The container that wraps the separator. | +
+ Learn more about customizing CSS parts. +
+<sl-breadcrumb> | SlBreadcrumb
+ + Breadcrumbs provide a group of links so users can easily navigate a website’s hierarchy. +
+ ++ Breadcrumbs are usually placed before a page’s main content with the current page shown last to indicate the + user’s position in the navigation. +
+ ++<sl-breadcrumb> + <sl-breadcrumb-item>Catalog</sl-breadcrumb-item> + <sl-breadcrumb-item>Clothing</sl-breadcrumb-item> + <sl-breadcrumb-item>Women's</sl-breadcrumb-item> + <sl-breadcrumb-item>Shirts & Tops</sl-breadcrumb-item> +</sl-breadcrumb> +
+import SlBreadcrumb from '@onsonr/nebula/dist/react/breadcrumb'; +import SlBreadcrumbItem from '@onsonr/nebula/dist/react/breadcrumb-item'; + +const App = () => ( + <SlBreadcrumb> + <SlBreadcrumbItem>Catalog</SlBreadcrumbItem> + <SlBreadcrumbItem>Clothing</SlBreadcrumbItem> + <SlBreadcrumbItem>Women's</SlBreadcrumbItem> + <SlBreadcrumbItem>Shirts & Tops</SlBreadcrumbItem> + </SlBreadcrumb> +); +
+ By default, breadcrumb items are rendered as buttons so you can use them to navigate single-page + applications. In this case, you’ll need to add event listeners to handle clicks. +
+
+ For websites, you’ll probably want to use links instead. You can make any breadcrumb item a link by applying
+ an href
attribute to it. Now, when the user activates it, they’ll be taken to the corresponding
+ page — no event listeners required.
+
+<sl-breadcrumb> + <sl-breadcrumb-item href="https://example.com/home">Homepage</sl-breadcrumb-item> + + <sl-breadcrumb-item href="https://example.com/home/services">Our Services</sl-breadcrumb-item> + + <sl-breadcrumb-item href="https://example.com/home/services/digital">Digital Media</sl-breadcrumb-item> + + <sl-breadcrumb-item href="https://example.com/home/services/digital/web-design">Web Design</sl-breadcrumb-item> +</sl-breadcrumb> +
+import SlBreadcrumb from '@onsonr/nebula/dist/react/breadcrumb'; +import SlBreadcrumbItem from '@onsonr/nebula/dist/react/breadcrumb-item'; + +const App = () => ( + <SlBreadcrumb> + <SlBreadcrumbItem href="https://example.com/home">Homepage</SlBreadcrumbItem> + + <SlBreadcrumbItem href="https://example.com/home/services">Our Services</SlBreadcrumbItem> + + <SlBreadcrumbItem href="https://example.com/home/services/digital">Digital Media</SlBreadcrumbItem> + + <SlBreadcrumbItem href="https://example.com/home/services/digital/web-design">Web Design</SlBreadcrumbItem> + </SlBreadcrumb> +); +
+ Use the separator
slot to change the separator that goes between breadcrumb items. Icons work
+ well, but you can also use text or an image.
+
+<sl-breadcrumb> + <sl-icon name="dot" slot="separator"></sl-icon> + <sl-breadcrumb-item>First</sl-breadcrumb-item> + <sl-breadcrumb-item>Second</sl-breadcrumb-item> + <sl-breadcrumb-item>Third</sl-breadcrumb-item> +</sl-breadcrumb> + +<br /> + +<sl-breadcrumb> + <sl-icon name="arrow-right" slot="separator"></sl-icon> + <sl-breadcrumb-item>First</sl-breadcrumb-item> + <sl-breadcrumb-item>Second</sl-breadcrumb-item> + <sl-breadcrumb-item>Third</sl-breadcrumb-item> +</sl-breadcrumb> + +<br /> + +<sl-breadcrumb> + <span slot="separator">/</span> + <sl-breadcrumb-item>First</sl-breadcrumb-item> + <sl-breadcrumb-item>Second</sl-breadcrumb-item> + <sl-breadcrumb-item>Third</sl-breadcrumb-item> +</sl-breadcrumb> +
+import '@onsonr/nebula/dist/components/icon/icon.js'; +import SlBreadcrumb from '@onsonr/nebula/dist/react/breadcrumb'; +import SlBreadcrumbItem from '@onsonr/nebula/dist/react/breadcrumb-item'; + +const App = () => ( + <> + <SlBreadcrumb> + <sl-icon name="dot" slot="separator" /> + <SlBreadcrumbItem>First</SlBreadcrumbItem> + <SlBreadcrumbItem>Second</SlBreadcrumbItem> + <SlBreadcrumbItem>Third</SlBreadcrumbItem> + </SlBreadcrumb> + + <br /> + + <SlBreadcrumb> + <sl-icon name="arrow-right" slot="separator" /> + <SlBreadcrumbItem>First</SlBreadcrumbItem> + <SlBreadcrumbItem>Second</SlBreadcrumbItem> + <SlBreadcrumbItem>Third</SlBreadcrumbItem> + </SlBreadcrumb> + + <br /> + + <SlBreadcrumb> + <span slot="separator">/</span> + <SlBreadcrumbItem>First</SlBreadcrumbItem> + <SlBreadcrumbItem>Second</SlBreadcrumbItem> + <SlBreadcrumbItem>Third</SlBreadcrumbItem> + </SlBreadcrumb> + </> +); +
Use the prefix
slot to add content before any breadcrumb item.
+<sl-breadcrumb> + <sl-breadcrumb-item> + <sl-icon slot="prefix" name="house"></sl-icon> + Home + </sl-breadcrumb-item> + <sl-breadcrumb-item>Articles</sl-breadcrumb-item> + <sl-breadcrumb-item>Traveling</sl-breadcrumb-item> +</sl-breadcrumb> +
+import SlBreadcrumb from '@onsonr/nebula/dist/react/breadcrumb'; +import SlBreadcrumbItem from '@onsonr/nebula/dist/react/breadcrumb-item'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <SlBreadcrumb> + <SlBreadcrumbItem> + <SlIcon slot="prefix" name="house" /> + Home + </SlBreadcrumbItem> + <SlBreadcrumbItem>Articles</SlBreadcrumbItem> + <SlBreadcrumbItem>Traveling</SlBreadcrumbItem> + </SlBreadcrumb> +); +
Use the suffix
slot to add content after any breadcrumb item.
+<sl-breadcrumb> + <sl-breadcrumb-item>Documents</sl-breadcrumb-item> + <sl-breadcrumb-item>Policies</sl-breadcrumb-item> + <sl-breadcrumb-item> + Security + <sl-icon slot="suffix" name="shield-lock"></sl-icon> + </sl-breadcrumb-item> +</sl-breadcrumb> +
+import SlBreadcrumb from '@onsonr/nebula/dist/react/breadcrumb'; +import SlBreadcrumbItem from '@onsonr/nebula/dist/react/breadcrumb-item'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <SlBreadcrumb> + <SlBreadcrumbItem>Documents</SlBreadcrumbItem> + <SlBreadcrumbItem>Policies</SlBreadcrumbItem> + <SlBreadcrumbItem> + Security + <SlIcon slot="suffix" name="shield-lock"></SlIcon> + </SlBreadcrumbItem> + </SlBreadcrumb> +); +
Dropdown menus can be placed in a prefix or suffix slot to provide additional options.
+ ++<sl-breadcrumb> + <sl-breadcrumb-item>Homepage</sl-breadcrumb-item> + <sl-breadcrumb-item>Our Services</sl-breadcrumb-item> + <sl-breadcrumb-item>Digital Media</sl-breadcrumb-item> + <sl-breadcrumb-item> + Web Design + <sl-dropdown slot="suffix"> + <sl-button slot="trigger" size="small" circle> + <sl-icon label="More options" name="three-dots"></sl-icon> + </sl-button> + <sl-menu> + <sl-menu-item type="checkbox" checked>Web Design</sl-menu-item> + <sl-menu-item type="checkbox">Web Development</sl-menu-item> + <sl-menu-item type="checkbox">Marketing</sl-menu-item> + </sl-menu> + </sl-dropdown> + </sl-breadcrumb-item> +</sl-breadcrumb> +
+import { + SlBreadcrumb, + SlBreadcrumbItem, + SlButton, + SlDropdown, + SlIcon, + SlMenu, + SlMenuItem +} from '@onsonr/nebula/dist/react'; + +const App = () => ( + <SlBreadcrumb> + <SlBreadcrumbItem>Homepage</SlBreadcrumbItem> + <SlBreadcrumbItem>Our Services</SlBreadcrumbItem> + <SlBreadcrumbItem>Digital Media</SlBreadcrumbItem> + <SlBreadcrumbItem> + Web Design + <SlDropdown slot="suffix"> + <SlButton slot="trigger" size="small" circle> + <SlIcon label="More options" name="three-dots"></SlIcon> + </SlButton> + <SlMenu> + <SlMenuItem type="checkbox" checked> + Web Design + </SlMenuItem> + <SlMenuItem type="checkbox">Web Development</SlMenuItem> + <SlMenuItem type="checkbox">Marketing</SlMenuItem> + </SlMenu> + </SlDropdown> + </SlBreadcrumbItem> + </SlBreadcrumb> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/breadcrumb/breadcrumb.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/breadcrumb/breadcrumb.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/breadcrumb/breadcrumb.js';
To import this component as a React component:
++import SlBreadcrumb from '@onsonr/nebula/dist/react/breadcrumb';
Name | +Description | +
---|---|
(default) | +One or more breadcrumb items to display. | +
+ separator
+ |
+ The separator to use between breadcrumb items. Works best with <sl-icon> . |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ label
+ |
+ + The label to use for the breadcrumb control. This will not be shown on the screen, but it will be + announced by screen readers and other assistive devices to provide more context for users. + | ++ |
+ string
+ |
+
+ ''
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-button-group> | SlButtonGroup
+ Button groups can be used to group related buttons into sections.
+ ++<sl-button-group label="Alignment"> + <sl-button>Left</sl-button> + <sl-button>Center</sl-button> + <sl-button>Right</sl-button> +</sl-button-group> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; + +const App = () => ( + <SlButtonGroup label="Alignment"> + <SlButton>Left</SlButton> + <SlButton>Center</SlButton> + <SlButton>Right</SlButton> + </SlButtonGroup> +); +
All button sizes are supported, but avoid mixing sizes within the same button group.
+ ++<sl-button-group label="Alignment"> + <sl-button size="small">Left</sl-button> + <sl-button size="small">Center</sl-button> + <sl-button size="small">Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button size="medium">Left</sl-button> + <sl-button size="medium">Center</sl-button> + <sl-button size="medium">Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button size="large">Left</sl-button> + <sl-button size="large">Center</sl-button> + <sl-button size="large">Right</sl-button> +</sl-button-group> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; + +const App = () => ( + <> + <SlButtonGroup label="Alignment"> + <SlButton size="small">Left</SlButton> + <SlButton size="small">Center</SlButton> + <SlButton size="small">Right</SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton size="medium">Left</SlButton> + <SlButton size="medium">Center</SlButton> + <SlButton size="medium">Right</SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton size="large">Left</SlButton> + <SlButton size="large">Center</SlButton> + <SlButton size="large">Right</SlButton> + </SlButtonGroup> + </> +); +
Theme buttons are supported through the button’s variant
attribute.
+<sl-button-group label="Alignment"> + <sl-button variant="primary">Left</sl-button> + <sl-button variant="primary">Center</sl-button> + <sl-button variant="primary">Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button variant="success">Left</sl-button> + <sl-button variant="success">Center</sl-button> + <sl-button variant="success">Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button variant="neutral">Left</sl-button> + <sl-button variant="neutral">Center</sl-button> + <sl-button variant="neutral">Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button variant="warning">Left</sl-button> + <sl-button variant="warning">Center</sl-button> + <sl-button variant="warning">Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button variant="danger">Left</sl-button> + <sl-button variant="danger">Center</sl-button> + <sl-button variant="danger">Right</sl-button> +</sl-button-group> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; + +const App = () => ( + <> + <SlButtonGroup label="Alignment"> + <SlButton variant="primary">Left</SlButton> + <SlButton variant="primary">Center</SlButton> + <SlButton variant="primary">Right</SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton variant="success">Left</SlButton> + <SlButton variant="success">Center</SlButton> + <SlButton variant="success">Right</SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton variant="neutral">Left</SlButton> + <SlButton variant="neutral">Center</SlButton> + <SlButton variant="neutral">Right</SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton variant="warning">Left</SlButton> + <SlButton variant="warning">Center</SlButton> + <SlButton variant="warning">Right</SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton variant="danger">Left</SlButton> + <SlButton variant="danger">Center</SlButton> + <SlButton variant="danger">Right</SlButton> + </SlButtonGroup> + </> +); +
Pill buttons are supported through the button’s pill
attribute.
+<sl-button-group label="Alignment"> + <sl-button size="small" pill>Left</sl-button> + <sl-button size="small" pill>Center</sl-button> + <sl-button size="small" pill>Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button size="medium" pill>Left</sl-button> + <sl-button size="medium" pill>Center</sl-button> + <sl-button size="medium" pill>Right</sl-button> +</sl-button-group> + +<br /><br /> + +<sl-button-group label="Alignment"> + <sl-button size="large" pill>Left</sl-button> + <sl-button size="large" pill>Center</sl-button> + <sl-button size="large" pill>Right</sl-button> +</sl-button-group> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; + +const App = () => ( + <> + <SlButtonGroup label="Alignment"> + <SlButton size="small" pill> + Left + </SlButton> + <SlButton size="small" pill> + Center + </SlButton> + <SlButton size="small" pill> + Right + </SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton size="medium" pill> + Left + </SlButton> + <SlButton size="medium" pill> + Center + </SlButton> + <SlButton size="medium" pill> + Right + </SlButton> + </SlButtonGroup> + + <br /> + <br /> + + <SlButtonGroup label="Alignment"> + <SlButton size="large" pill> + Left + </SlButton> + <SlButton size="large" pill> + Center + </SlButton> + <SlButton size="large" pill> + Right + </SlButton> + </SlButtonGroup> + </> +); +
+ Dropdowns can be placed inside button groups as long as the trigger is an
+ <sl-button>
element.
+
+<sl-button-group label="Example Button Group"> + <sl-button>Button</sl-button> + <sl-button>Button</sl-button> + <sl-dropdown> + <sl-button slot="trigger" caret>Dropdown</sl-button> + <sl-menu> + <sl-menu-item>Item 1</sl-menu-item> + <sl-menu-item>Item 2</sl-menu-item> + <sl-menu-item>Item 3</sl-menu-item> + </sl-menu> + </sl-dropdown> +</sl-button-group> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlButtonGroup label="Example Button Group"> + <SlButton>Button</SlButton> + <SlButton>Button</SlButton> + <SlDropdown> + <SlButton slot="trigger" caret> + Dropdown + </SlButton> + <SlMenu> + <SlMenuItem>Item 1</SlMenuItem> + <SlMenuItem>Item 2</SlMenuItem> + <SlMenuItem>Item 3</SlMenuItem> + </SlMenu> + </SlDropdown> + </SlButtonGroup> +); +
+ Create a split button using a button and a dropdown. Use a + visually hidden label to ensure the dropdown is accessible to + users with assistive devices. +
+ ++<sl-button-group label="Example Button Group"> + <sl-button variant="primary">Save</sl-button> + <sl-dropdown placement="bottom-end"> + <sl-button slot="trigger" variant="primary" caret> + <sl-visually-hidden>More options</sl-visually-hidden> + </sl-button> + <sl-menu> + <sl-menu-item>Save</sl-menu-item> + <sl-menu-item>Save as…</sl-menu-item> + <sl-menu-item>Save all</sl-menu-item> + </sl-menu> + </sl-dropdown> +</sl-button-group> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlButtonGroup label="Example Button Group"> + <SlButton variant="primary">Save</SlButton> + <SlDropdown placement="bottom-end"> + <SlButton slot="trigger" variant="primary" caret></SlButton> + <SlMenu> + <SlMenuItem>Save</SlMenuItem> + <SlMenuItem>Save as…</SlMenuItem> + <SlMenuItem>Save all</SlMenuItem> + </SlMenu> + </SlDropdown> + </SlButtonGroup> +); +
Buttons can be wrapped in tooltips to provide more detail when the user interacts with them.
+ ++<sl-button-group label="Alignment"> + <sl-tooltip content="I'm on the left"> + <sl-button>Left</sl-button> + </sl-tooltip> + + <sl-tooltip content="I'm in the middle"> + <sl-button>Center</sl-button> + </sl-tooltip> + + <sl-tooltip content="I'm on the right"> + <sl-button>Right</sl-button> + </sl-tooltip> +</sl-button-group> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => ( + <> + <SlButtonGroup label="Alignment"> + <SlTooltip content="I'm on the left"> + <SlButton>Left</SlButton> + </SlTooltip> + + <SlTooltip content="I'm in the middle"> + <SlButton>Center</SlButton> + </SlTooltip> + + <SlTooltip content="I'm on the right"> + <SlButton>Right</SlButton> + </SlTooltip> + </SlButtonGroup> + </> +); +
Create interactive toolbars with button groups.
+ ++<div class="button-group-toolbar"> + <sl-button-group label="History"> + <sl-tooltip content="Undo"> + <sl-button><sl-icon name="arrow-counterclockwise" label="Undo"></sl-icon></sl-button> + </sl-tooltip> + <sl-tooltip content="Redo"> + <sl-button><sl-icon name="arrow-clockwise" label="Redo"></sl-icon></sl-button> + </sl-tooltip> + </sl-button-group> + + <sl-button-group label="Formatting"> + <sl-tooltip content="Bold"> + <sl-button><sl-icon name="type-bold" label="Bold"></sl-icon></sl-button> + </sl-tooltip> + <sl-tooltip content="Italic"> + <sl-button><sl-icon name="type-italic" label="Italic"></sl-icon></sl-button> + </sl-tooltip> + <sl-tooltip content="Underline"> + <sl-button><sl-icon name="type-underline" label="Underline"></sl-icon></sl-button> + </sl-tooltip> + </sl-button-group> + + <sl-button-group label="Alignment"> + <sl-tooltip content="Align Left"> + <sl-button><sl-icon name="justify-left" label="Align Left"></sl-icon></sl-button> + </sl-tooltip> + <sl-tooltip content="Align Center"> + <sl-button><sl-icon name="justify" label="Align Center"></sl-icon></sl-button> + </sl-tooltip> + <sl-tooltip content="Align Right"> + <sl-button><sl-icon name="justify-right" label="Align Right"></sl-icon></sl-button> + </sl-tooltip> + </sl-button-group> +</div> + +<style> + .button-group-toolbar sl-button-group:not(:last-of-type) { + margin-right: var(--sl-spacing-x-small); + } +</style> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlButtonGroup from '@onsonr/nebula/dist/react/button-group'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const css = ` + .button-group-toolbar sl-button-group:not(:last-of-type) { + margin-right: var(--sl-spacing-x-small); + } +`; + +const App = () => ( + <> + <div className="button-group-toolbar"> + <SlButtonGroup label="History"> + <SlTooltip content="Undo"> + <SlButton> + <SlIcon name="arrow-counterclockwise"></SlIcon> + </SlButton> + </SlTooltip> + <SlTooltip content="Redo"> + <SlButton> + <SlIcon name="arrow-clockwise"></SlIcon> + </SlButton> + </SlTooltip> + </SlButtonGroup> + + <SlButtonGroup label="Formatting"> + <SlTooltip content="Bold"> + <SlButton> + <SlIcon name="type-bold"></SlIcon> + </SlButton> + </SlTooltip> + <SlTooltip content="Italic"> + <SlButton> + <SlIcon name="type-italic"></SlIcon> + </SlButton> + </SlTooltip> + <SlTooltip content="Underline"> + <SlButton> + <SlIcon name="type-underline"></SlIcon> + </SlButton> + </SlTooltip> + </SlButtonGroup> + + <SlButtonGroup label="Alignment"> + <SlTooltip content="Align Left"> + <SlButton> + <SlIcon name="justify-left"></SlIcon> + </SlButton> + </SlTooltip> + <SlTooltip content="Align Center"> + <SlButton> + <SlIcon name="justify"></SlIcon> + </SlButton> + </SlTooltip> + <SlTooltip content="Align Right"> + <SlButton> + <SlIcon name="justify-right"></SlIcon> + </SlButton> + </SlTooltip> + </SlButtonGroup> + </div> + + <style>{css}</style> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/button-group/button-group.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/button-group/button-group.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/button-group/button-group.js';
To import this component as a React component:
++import SlButtonGroup from '@onsonr/nebula/dist/react/button-group';
Name | +Description | +
---|---|
(default) | +One or more <sl-button> elements to display in the button group. |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ label
+ |
+ + A label to use for the button group. This won’t be displayed on the screen, but it will be announced + by assistive devices when interacting with the control and is strongly recommended. + | ++ |
+ string
+ |
+
+ ''
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-button> | SlButton
+ Buttons represent actions that are available to the user.
+ ++<sl-button>Button</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => <SlButton>Button</SlButton>; +
Use the variant
attribute to set the button’s variant.
+<sl-button variant="default">Default</sl-button> +<sl-button variant="primary">Primary</sl-button> +<sl-button variant="success">Success</sl-button> +<sl-button variant="neutral">Neutral</sl-button> +<sl-button variant="warning">Warning</sl-button> +<sl-button variant="danger">Danger</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton variant="default">Default</SlButton> + <SlButton variant="primary">Primary</SlButton> + <SlButton variant="success">Success</SlButton> + <SlButton variant="neutral">Neutral</SlButton> + <SlButton variant="warning">Warning</SlButton> + <SlButton variant="danger">Danger</SlButton> + </> +); +
Use the size
attribute to change a button’s size.
+<sl-button size="small">Small</sl-button> +<sl-button size="medium">Medium</sl-button> +<sl-button size="large">Large</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton size="small">Small</SlButton> + <SlButton size="medium">Medium</SlButton> + <SlButton size="large">Large</SlButton> + </> +); +
Use the outline
attribute to draw outlined buttons with transparent backgrounds.
+<sl-button variant="default" outline>Default</sl-button> +<sl-button variant="primary" outline>Primary</sl-button> +<sl-button variant="success" outline>Success</sl-button> +<sl-button variant="neutral" outline>Neutral</sl-button> +<sl-button variant="warning" outline>Warning</sl-button> +<sl-button variant="danger" outline>Danger</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton variant="default" outline> + Default + </SlButton> + <SlButton variant="primary" outline> + Primary + </SlButton> + <SlButton variant="success" outline> + Success + </SlButton> + <SlButton variant="neutral" outline> + Neutral + </SlButton> + <SlButton variant="warning" outline> + Warning + </SlButton> + <SlButton variant="danger" outline> + Danger + </SlButton> + </> +); +
Use the pill
attribute to give buttons rounded edges.
+<sl-button size="small" pill>Small</sl-button> +<sl-button size="medium" pill>Medium</sl-button> +<sl-button size="large" pill>Large</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton size="small" pill> + Small + </SlButton> + <SlButton size="medium" pill> + Medium + </SlButton> + <SlButton size="large" pill> + Large + </SlButton> + </> +); +
+ Use the circle
attribute to create circular icon buttons. When this attribute is set, the
+ button expects a single <sl-icon>
in the default slot.
+
+<sl-button variant="default" size="small" circle> + <sl-icon name="gear" label="Settings"></sl-icon> +</sl-button> + +<sl-button variant="default" size="medium" circle> + <sl-icon name="gear" label="Settings"></sl-icon> +</sl-button> + +<sl-button variant="default" size="large" circle> + <sl-icon name="gear" label="Settings"></sl-icon> +</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <> + <SlButton variant="default" size="small" circle> + <SlIcon name="gear" /> + </SlButton> + <SlButton variant="default" size="medium" circle> + <SlIcon name="gear" /> + </SlButton> + <SlButton variant="default" size="large" circle> + <SlIcon name="gear" /> + </SlButton> + </> +); +
+ Use the text
variant to create text buttons that share the same size as regular buttons but
+ don’t have backgrounds or borders.
+
+<sl-button variant="text" size="small">Text</sl-button> +<sl-button variant="text" size="medium">Text</sl-button> +<sl-button variant="text" size="large">Text</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton variant="text" size="small"> + Text + </SlButton> + <SlButton variant="text" size="medium"> + Text + </SlButton> + <SlButton variant="text" size="large"> + Text + </SlButton> + </> +); +
+ It’s often helpful to have a button that works like a link. This is possible by setting the
+ href
attribute, which will make the component render an <a>
under the hood.
+ This gives you all the default link behavior the browser provides (e.g. CMD/CTRL/SHIFT +
+ CLICK) and exposes the target
and download
attributes.
+
+<sl-button href="https://example.com/">Link</sl-button> +<sl-button href="https://example.com/" target="_blank">New Window</sl-button> +<sl-button href="/assets/images/wordmark.svg" download="shoelace.svg">Download</sl-button> +<sl-button href="https://example.com/" disabled>Disabled</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton href="https://example.com/">Link</SlButton> + <SlButton href="https://example.com/" target="_blank"> + New Window + </SlButton> + <SlButton href="/assets/images/wordmark.svg" download="shoelace.svg"> + Download + </SlButton> + <SlButton href="https://example.com/" disabled> + Disabled + </SlButton> + </> +); +
+ When a target
is set, the link will receive rel="noreferrer noopener"
for
+ security reasons.
+
+ As expected, buttons can be given a custom width by passing inline styles to the component (or using a + class). This is useful for making buttons span the full width of their container on smaller screens. +
+ ++<sl-button variant="default" size="small" style="width: 100%; margin-bottom: 1rem;">Small</sl-button> +<sl-button variant="default" size="medium" style="width: 100%; margin-bottom: 1rem;">Medium</sl-button> +<sl-button variant="default" size="large" style="width: 100%;">Large</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton variant="default" size="small" style={{ width: '100%', marginBottom: '1rem' }}> + Small + </SlButton> + <SlButton variant="default" size="medium" style={{ width: '100%', marginBottom: '1rem' }}> + Medium + </SlButton> + <SlButton variant="default" size="large" style={{ width: '100%' }}> + Large + </SlButton> + </> +); +
Use the prefix
and suffix
slots to add icons.
+<sl-button variant="default" size="small"> + <sl-icon slot="prefix" name="gear"></sl-icon> + Settings +</sl-button> + +<sl-button variant="default" size="small"> + <sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon> + Refresh +</sl-button> + +<sl-button variant="default" size="small"> + <sl-icon slot="prefix" name="link-45deg"></sl-icon> + <sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon> + Open +</sl-button> + +<br /><br /> + +<sl-button variant="default"> + <sl-icon slot="prefix" name="gear"></sl-icon> + Settings +</sl-button> + +<sl-button variant="default"> + <sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon> + Refresh +</sl-button> + +<sl-button variant="default"> + <sl-icon slot="prefix" name="link-45deg"></sl-icon> + <sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon> + Open +</sl-button> + +<br /><br /> + +<sl-button variant="default" size="large"> + <sl-icon slot="prefix" name="gear"></sl-icon> + Settings +</sl-button> + +<sl-button variant="default" size="large"> + <sl-icon slot="suffix" name="arrow-counterclockwise"></sl-icon> + Refresh +</sl-button> + +<sl-button variant="default" size="large"> + <sl-icon slot="prefix" name="link-45deg"></sl-icon> + <sl-icon slot="suffix" name="box-arrow-up-right"></sl-icon> + Open +</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <> + <SlButton variant="default" size="small"> + <SlIcon slot="prefix" name="gear"></SlIcon> + Settings + </SlButton> + + <SlButton variant="default" size="small"> + <SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon> + Refresh + </SlButton> + + <SlButton variant="default" size="small"> + <SlIcon slot="prefix" name="link-45deg"></SlIcon> + <SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon> + Open + </SlButton> + + <br /> + <br /> + + <SlButton variant="default"> + <SlIcon slot="prefix" name="gear"></SlIcon> + Settings + </SlButton> + + <SlButton variant="default"> + <SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon> + Refresh + </SlButton> + + <SlButton variant="default"> + <SlIcon slot="prefix" name="link-45deg"></SlIcon> + <SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon> + Open + </SlButton> + + <br /> + <br /> + + <SlButton variant="default" size="large"> + <SlIcon slot="prefix" name="gear"></SlIcon> + Settings + </SlButton> + + <SlButton variant="default" size="large"> + <SlIcon slot="suffix" name="arrow-counterclockwise"></SlIcon> + Refresh + </SlButton> + + <SlButton variant="default" size="large"> + <SlIcon slot="prefix" name="link-45deg"></SlIcon> + <SlIcon slot="suffix" name="box-arrow-up-right"></SlIcon> + Open + </SlButton> + </> +); +
+ Use the caret
attribute to add a dropdown indicator when a button will trigger a dropdown,
+ menu, or popover.
+
+<sl-button size="small" caret>Small</sl-button> +<sl-button size="medium" caret>Medium</sl-button> +<sl-button size="large" caret>Large</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton size="small" caret> + Small + </SlButton> + <SlButton size="medium" caret> + Medium + </SlButton> + <SlButton size="large" caret> + Large + </SlButton> + </> +); +
+ Use the loading
attribute to make a button busy. The width will remain the same as before,
+ preventing adjacent elements from moving around.
+
+<sl-button variant="default" loading>Default</sl-button> +<sl-button variant="primary" loading>Primary</sl-button> +<sl-button variant="success" loading>Success</sl-button> +<sl-button variant="neutral" loading>Neutral</sl-button> +<sl-button variant="warning" loading>Warning</sl-button> +<sl-button variant="danger" loading>Danger</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton variant="default" loading> + Default + </SlButton> + <SlButton variant="primary" loading> + Primary + </SlButton> + <SlButton variant="success" loading> + Success + </SlButton> + <SlButton variant="neutral" loading> + Neutral + </SlButton> + <SlButton variant="warning" loading> + Warning + </SlButton> + <SlButton variant="danger" loading> + Danger + </SlButton> + </> +); +
Use the disabled
attribute to disable a button.
+<sl-button variant="default" disabled>Default</sl-button> +<sl-button variant="primary" disabled>Primary</sl-button> +<sl-button variant="success" disabled>Success</sl-button> +<sl-button variant="neutral" disabled>Neutral</sl-button> +<sl-button variant="warning" disabled>Warning</sl-button> +<sl-button variant="danger" disabled>Danger</sl-button> +
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const App = () => ( + <> + <SlButton variant="default" disabled> + Default + </SlButton> + + <SlButton variant="primary" disabled> + Primary + </SlButton> + + <SlButton variant="success" disabled> + Success + </SlButton> + + <SlButton variant="neutral" disabled> + Neutral + </SlButton> + + <SlButton variant="warning" disabled> + Warning + </SlButton> + + <SlButton variant="danger" disabled> + Danger + </SlButton> + </> +); +
+ This example demonstrates how to style buttons using a custom class. This is the recommended approach if you
+ need to add additional variations. To customize an existing variation, modify the selector to target the
+ button’s variant
attribute instead of a class (e.g. sl-button[variant="primary"]
).
+
+<sl-button class="pink">Pink Button</sl-button> + +<style> + sl-button.pink::part(base) { + /* Set design tokens for height and border width */ + --sl-input-height-medium: 48px; + --sl-input-border-width: 4px; + + border-radius: 0; + background-color: #ff1493; + border-top-color: #ff7ac1; + border-left-color: #ff7ac1; + border-bottom-color: #ad005c; + border-right-color: #ad005c; + color: white; + font-size: 1.125rem; + box-shadow: 0 2px 10px #0002; + transition: var(--sl-transition-medium) transform ease, var(--sl-transition-medium) border ease; + } + + sl-button.pink::part(base):hover { + transform: scale(1.05) rotate(-1deg); + } + + sl-button.pink::part(base):active { + border-top-color: #ad005c; + border-right-color: #ff7ac1; + border-bottom-color: #ff7ac1; + border-left-color: #ad005c; + transform: scale(1.05) rotate(-1deg) translateY(2px); + } + + sl-button.pink::part(base):focus-visible { + outline: dashed 2px deeppink; + outline-offset: 4px; + } +</style> +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/button/button.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/button/button.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/button/button.js';
To import this component as a React component:
++import SlButton from '@onsonr/nebula/dist/react/button';
Name | +Description | +
---|---|
(default) | +The button’s label. | +
+ prefix
+ |
+ A presentational prefix icon or similar element. | +
+ suffix
+ |
+ A presentational suffix icon or similar element. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ variant
+ |
+ The button’s theme variant. | +
+ |
+
+ 'default' | 'primary' | 'success' | 'neutral' | 'warning' | 'danger' | 'text'
+ |
+
+ 'default'
+ |
+
+ size
+ |
+ The button’s size. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ caret
+ |
+ + Draws the button with a caret. Used to indicate that the button triggers a dropdown menu or similar + behavior. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ Disables the button. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ loading
+ |
+ Draws the button in a loading state. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ outline
+ |
+ Draws an outlined button. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ pill
+ |
+ Draws a pill-style button with rounded edges. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ circle
+ |
+
+ Draws a circular icon button. When this attribute is present, the button expects a single
+ <sl-icon> in the default slot.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ type
+ |
+
+ The type of button. Note that the default value is button instead of
+ submit , which is opposite of how native <button> elements behave.
+ When the type is submit , the button will submit the surrounding form.
+ |
+ + |
+ 'button' | 'submit' | 'reset'
+ |
+
+ 'button'
+ |
+
+ name
+ |
+
+ The name of the button, submitted as a name/value pair with form data, but only when this button is
+ the submitter. This attribute is ignored when href is present.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+
+ The value of the button, submitted as a pair with the button’s name as part of the form data, but
+ only when this button is the submitter. This attribute is ignored when href is present.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ href
+ |
+
+ When set, the underlying button will be rendered as an <a> with this
+ href instead of a <button> .
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ target
+ |
+ Tells the browser where to open the link. Only used when href is present. |
+ + |
+ '_blank' | '_parent' | '_self' | '_top'
+ |
+ - | +
+ rel
+ |
+
+ When using href , this attribute will map to the underlying link’s
+ rel attribute. Unlike regular links, the default is noreferrer noopener to
+ prevent security exploits. However, if you’re using target to point to a specific
+ tab/window, this will prevent that from working correctly. You can remove or change the default
+ value by setting the attribute to an empty string or a value of your choice, respectively.
+ |
+ + |
+ string
+ |
+
+ 'noreferrer noopener'
+ |
+
+ download
+ |
+
+ Tells the browser to download the linked file as this filename. Only used when href is
+ present.
+ |
+ + |
+ string | undefined
+ |
+ - | +
+ form
+ |
+ + The “form owner” to associate the button with. If omitted, the closest containing form will be used + instead. The value of this attribute must be an id of a form in the same document or shadow root as + the button. + | ++ |
+ string
+ |
+ - | +
+ formAction
+
+ + formaction
+
+ |
+ Used to override the form owner’s action attribute. |
+ + |
+ string
+ |
+ - | +
+ formEnctype
+
+ + formenctype
+
+ |
+ Used to override the form owner’s enctype attribute. |
+ + |
+ 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain'
+ |
+ - | +
+ formMethod
+
+ + formmethod
+
+ |
+ Used to override the form owner’s method attribute. |
+ + |
+ 'post' | 'get'
+ |
+ - | +
+ formNoValidate
+
+ + formnovalidate
+
+ |
+ Used to override the form owner’s novalidate attribute. |
+ + |
+ boolean
+ |
+ - | +
+ formTarget
+
+ + formtarget
+
+ |
+ Used to override the form owner’s target attribute. |
+ + |
+ '_self' | '_blank' | '_parent' | '_top' | string
+ |
+ - | +
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the button loses focus. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the button gains focus. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
click() |
+ Simulates a click on the button. | +- | +
focus() |
+ Sets focus on the button. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the button. | +- | +
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
prefix |
+ The container that wraps the prefix. | +
label |
+ The button’s label. | +
suffix |
+ The container that wraps the suffix. | +
caret |
+ The button’s caret icon, an <sl-icon> element. |
+
spinner |
+ The spinner that shows when the button is in the loading state. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-spinner>
<sl-card> | SlCard
+ Cards can be used to group related subjects in a container.
+ ++<sl-card class="card-overview"> + <img + slot="image" + src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80" + alt="A kitten sits patiently between a terracotta pot and decorative grasses." + /> + + <strong>Mittens</strong><br /> + This kitten is as cute as he is playful. Bring him home today!<br /> + <small>6 weeks old</small> + + <div slot="footer"> + <sl-button variant="primary" pill>More Info</sl-button> + <sl-rating></sl-rating> + </div> +</sl-card> + +<style> + .card-overview { + max-width: 300px; + } + + .card-overview small { + color: var(--sl-color-neutral-500); + } + + .card-overview [slot='footer'] { + display: flex; + justify-content: space-between; + align-items: center; + } +</style> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlCard from '@onsonr/nebula/dist/react/card'; +import SlRating from '@onsonr/nebula/dist/react/rating'; + +const css = ` + .card-overview { + max-width: 300px; + } + + .card-overview small { + color: var(--sl-color-neutral-500); + } + + .card-overview [slot="footer"] { + display: flex; + justify-content: space-between; + align-items: center; + } +`; + +const App = () => ( + <> + <SlCard className="card-overview"> + <img + slot="image" + src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80" + alt="A kitten sits patiently between a terracotta pot and decorative grasses." + /> + <strong>Mittens</strong> + <br /> + This kitten is as cute as he is playful. Bring him home today! + <br /> + <small>6 weeks old</small> + <div slot="footer"> + <SlButton variant="primary" pill> + More Info + </SlButton> + <SlRating></SlRating> + </div> + </SlCard> + + <style>{css}</style> + </> +); +
Basic cards aren’t very exciting, but they can display any content you want them to.
+ ++<sl-card class="card-basic"> + This is just a basic card. No image, no header, and no footer. Just your content. +</sl-card> + +<style> + .card-basic { + max-width: 300px; + } +</style> +
+import SlCard from '@onsonr/nebula/dist/react/card'; + +const css = ` + .card-basic { + max-width: 300px; + } +`; + +const App = () => ( + <> + <SlCard className="card-basic"> + This is just a basic card. No image, no header, and no footer. Just your content. + </SlCard> + + <style>{css}</style> + </> +); +
Headers can be used to display titles and more.
+ ++<sl-card class="card-header"> + <div slot="header"> + Header Title + <sl-icon-button name="gear" label="Settings"></sl-icon-button> + </div> + + This card has a header. You can put all sorts of things in it! +</sl-card> + +<style> + .card-header { + max-width: 300px; + } + + .card-header [slot='header'] { + display: flex; + align-items: center; + justify-content: space-between; + } + + .card-header h3 { + margin: 0; + } + + .card-header sl-icon-button { + font-size: var(--sl-font-size-medium); + } +</style> +
+import SlCard from '@onsonr/nebula/dist/react/card'; +import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const css = ` + .card-header { + max-width: 300px; + } + + .card-header [slot="header"] { + display: flex; + align-items: center; + justify-content: space-between; + } + + .card-header h3 { + margin: 0; + } + + .card-header sl-icon-button { + font-size: var(--sl-font-size-medium); + } +`; + +const App = () => ( + <> + <SlCard className="card-header"> + <div slot="header"> + Header Title + <SlIconButton name="gear"></SlIconButton> + </div> + This card has a header. You can put all sorts of things in it! + </SlCard> + + <style>{css}</style> + </> +); +
Footers can be used to display actions, summaries, or other relevant content.
+ ++<sl-card class="card-footer"> + This card has a footer. You can put all sorts of things in it! + + <div slot="footer"> + <sl-rating></sl-rating> + <sl-button variant="primary">Preview</sl-button> + </div> +</sl-card> + +<style> + .card-footer { + max-width: 300px; + } + + .card-footer [slot='footer'] { + display: flex; + justify-content: space-between; + align-items: center; + } +</style> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlCard from '@onsonr/nebula/dist/react/card'; +import SlRating from '@onsonr/nebula/dist/react/rating'; + +const css = ` + .card-footer { + max-width: 300px; + } + + .card-footer [slot="footer"] { + display: flex; + justify-content: space-between; + align-items: center; + } +`; + +const App = () => ( + <> + <SlCard className="card-footer"> + This card has a footer. You can put all sorts of things in it! + <div slot="footer"> + <SlRating></SlRating> + <SlButton slot="footer" variant="primary"> + Preview + </SlButton> + </div> + </SlCard> + + <style>{css}</style> + </> +); +
Cards accept an image
slot. The image is displayed atop the card and stretches to fit.
+<sl-card class="card-image"> + <img + slot="image" + src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80" + alt="A kitten walks towards camera on top of pallet." + /> + This is a kitten, but not just any kitten. This kitten likes walking along pallets. +</sl-card> + +<style> + .card-image { + max-width: 300px; + } +</style> +
+import SlCard from '@onsonr/nebula/dist/react/card'; + +const css = ` + .card-image { + max-width: 300px; + } +`; + +const App = () => ( + <> + <SlCard className="card-image"> + <img + slot="image" + src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80" + alt="A kitten walks towards camera on top of pallet." + /> + This is a kitten, but not just any kitten. This kitten likes walking along pallets. + </SlCard> + + <style>{css}</style> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/card/card.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/card/card.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/card/card.js';
To import this component as a React component:
++import SlCard from '@onsonr/nebula/dist/react/card';
Name | +Description | +
---|---|
(default) | +The card’s main content. | +
+ header
+ |
+ An optional header for the card. | +
+ footer
+ |
+ An optional footer for the card. | +
+ image
+ |
+ An optional image to render at the start of the card. | +
+ Learn more about using slots. +
+ +Name | +Description | +Default | +
---|---|---|
--border-color |
+ The card’s border color, including borders that occur inside the card. | ++ |
--border-radius |
+ The border radius for the card’s edges. | ++ |
--border-width |
+ The width of the card’s borders. | ++ |
--padding |
+ The padding to use for the card’s sections. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
image |
+ The container that wraps the card’s image. | +
header |
+ The container that wraps the card’s header. | +
body |
+ The container that wraps the card’s main content. | +
footer |
+ The container that wraps the card’s footer. | +
+ Learn more about customizing CSS parts. +
+<sl-carousel-item> | SlCarouselItem
+ + A carousel item represent a slide within a carousel. +
+ ++<sl-carousel pagination> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const App = () => ( + <SlCarousel pagination> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> +); +
Additional demonstrations can be found in the carousel examples.
++ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/carousel-item/carousel-item.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/carousel-item/carousel-item.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/carousel-item/carousel-item.js';
To import this component as a React component:
++import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item';
Name | +Description | +
---|---|
(default) | +The carousel item’s content.. | +
+ Learn more about using slots. +
+ +Name | +Description | +Default | +
---|---|---|
--aspect-ratio |
+ The slide’s aspect ratio. Inherited from the carousel by default. | ++ |
+ Learn more about + customizing CSS custom properties. +
+<sl-carousel> | SlCarousel
+ + Carousels display an arbitrary number of content slides along a horizontal or vertical axis. +
+ ++<sl-carousel pagination navigation mouse-dragging loop> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const App = () => ( + <> + <SlCarousel pagination mouse-dragging> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> + </> +); +
+ Use the pagination
attribute to show the total number of slides and the current slide as a set
+ of interactive dots.
+
+<sl-carousel pagination> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const App = () => ( + <SlCarousel pagination> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> +); +
Use the navigation
attribute to show previous and next buttons.
+<sl-carousel navigation> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const App = () => ( + <SlCarousel navigation> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> +); +
+ By default, the carousel will not advanced beyond the first and last slides. You can change this behavior
+ and force the carousel to “wrap” with the loop
attribute.
+
+<sl-carousel loop navigation pagination> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const App = () => ( + <SlCarousel loop navigation pagination> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> +); +
+ The carousel will automatically advance when the autoplay
attribute is used. To change how long
+ a slide is shown before advancing, set autoplay-interval
to the desired number of milliseconds.
+ For best results, use the loop
attribute when autoplay is enabled. Note that autoplay will
+ pause while the user interacts with the carousel.
+
+<sl-carousel autoplay loop pagination> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const App = () => ( + <SlCarousel autoplay loop pagination> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> +); +
+ The carousel uses
+ scroll snap
+ to position slides at various snap positions. This allows users to scroll through the slides very naturally,
+ especially on touch devices. Unfortunately, desktop users won’t be able to click and drag with a mouse,
+ which can feel unnatural. Adding the mouse-dragging
attribute can help with this.
+
+ This example is best demonstrated using a mouse. Try clicking and dragging the slide to move it. Then toggle + the switch and try again. +
+ ++<div class="mouse-dragging"> + <sl-carousel pagination> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> + </sl-carousel> + + <sl-divider></sl-divider> + + <sl-switch>Enable mouse dragging</sl-switch> +</div> + +<script> + const container = document.querySelector('.mouse-dragging'); + const carousel = container.querySelector('sl-carousel'); + const toggle = container.querySelector('sl-switch'); + + toggle.addEventListener('sl-change', () => { + carousel.toggleAttribute('mouse-dragging', toggle.checked); + }); +</script> +
+import { useState } from 'react'; +import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const App = () => { + const [isEnabled, setIsEnabled] = useState(false); + + return ( + <> + <SlCarousel navigation mouseDragging={isEnabled}> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> + + <SlDivider></SlDivider> + + <SlSwitch checked={isEnabled} onSlInput={() => setIsEnabled(!isEnabled)}> + Enable mouse dragging + </SlSwitch> + </> + ); +}; +
+ The slides-per-page
attribute makes it possible to display multiple slides at a time. You can
+ also use the slides-per-move
attribute to advance more than once slide at a time, if desired.
+
+<sl-carousel navigation pagination slides-per-page="2" slides-per-move="2"> + <sl-carousel-item style="background: var(--sl-color-red-200);">Slide 1</sl-carousel-item> + <sl-carousel-item style="background: var(--sl-color-orange-200);">Slide 2</sl-carousel-item> + <sl-carousel-item style="background: var(--sl-color-yellow-200);">Slide 3</sl-carousel-item> + <sl-carousel-item style="background: var(--sl-color-green-200);">Slide 4</sl-carousel-item> + <sl-carousel-item style="background: var(--sl-color-blue-200);">Slide 5</sl-carousel-item> + <sl-carousel-item style="background: var(--sl-color-violet-200);">Slide 6</sl-carousel-item> +</sl-carousel> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const App = () => ( + <SlCarousel navigation pagination slidesPerPage={2} slidesPerMove={2}> + <SlCarouselItem style={{ background: 'var(--sl-color-red-200)' }}>Slide 1</SlCarouselItem> + <SlCarouselItem style={{ background: 'var(--sl-color-orange-200)' }}>Slide 2</SlCarouselItem> + <SlCarouselItem style={{ background: 'var(--sl-color-yellow-200)' }}>Slide 3</SlCarouselItem> + <SlCarouselItem style={{ background: 'var(--sl-color-green-200)' }}>Slide 4</SlCarouselItem> + <SlCarouselItem style={{ background: 'var(--sl-color-blue-200)' }}>Slide 5</SlCarouselItem> + <SlCarouselItem style={{ background: 'var(--sl-color-violet-200)' }}>Slide 6</SlCarouselItem> + </SlCarousel> +); +
+ The content of the carousel can be changed by adding or removing carousel items. The carousel will update + itself automatically. +
+ ++<sl-carousel class="dynamic-carousel" pagination navigation> + <sl-carousel-item style="background: var(--sl-color-red-200)">Slide 1</sl-carousel-item> + <sl-carousel-item style="background: var(--sl-color-orange-200)">Slide 2</sl-carousel-item> + <sl-carousel-item style="background: var(--sl-color-yellow-200)">Slide 3</sl-carousel-item> +</sl-carousel> + +<div class="carousel-options"> + <sl-button id="dynamic-add">Add slide</sl-button> + <sl-button id="dynamic-remove">Remove slide</sl-button> +</div> + +<style> + .dynamic-carousel { + --aspect-ratio: 3 / 2; + } + + .dynamic-carousel ~ .carousel-options { + display: flex; + justify-content: center; + gap: var(--sl-spacing-x-small); + margin-top: var(--sl-spacing-large); + } + + .dynamic-carousel sl-carousel-item { + flex: 0 0 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: var(--sl-font-size-2x-large); + } +</style> + +<script> + (() => { + const dynamicCarousel = document.querySelector('.dynamic-carousel'); + const dynamicAdd = document.querySelector('#dynamic-add'); + const dynamicRemove = document.querySelector('#dynamic-remove'); + const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet']; + let colorIndex = 2; + + const addSlide = () => { + const slide = document.createElement('sl-carousel-item'); + const color = colors[++colorIndex % colors.length]; + slide.innerText = `Slide ${dynamicCarousel.children.length + 1}`; + slide.style.setProperty('background', `var(--sl-color-${color}-200)`); + dynamicCarousel.appendChild(slide); + dynamicRemove.disabled = false; + }; + + const removeSlide = () => { + const slide = dynamicCarousel.children[dynamicCarousel.children.length - 1]; + const numSlides = dynamicCarousel.querySelectorAll('sl-carousel-item').length; + + if (numSlides > 1) { + slide.remove(); + colorIndex--; + } + + dynamicRemove.disabled = numSlides - 1 <= 1; + }; + + dynamicAdd.addEventListener('click', addSlide); + dynamicRemove.addEventListener('click', removeSlide); + })(); +</script> +
+import { useState } from 'react'; +import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const css = ` + .dynamic-carousel { + --aspect-ratio: 3 / 2; + } + + .dynamic-carousel ~ .carousel-options { + display: flex; + justify-content: center; + margin-top: var(--sl-spacing-large); + } + + .dynamic-carousel sl-carousel-item { + flex: 0 0 100%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: var(--sl-font-size-2x-large); + } +`; + +const App = () => { + const [slides, setSlides] = useState(['#204ed8', '#be133d', '#6e28d9']); + const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet']; + + const addSlide = () => { + setSlides([...slides, getRandomColor()]); + }; + + const removeSlide = () => { + setSlides(slides.slice(0, -1)); + }; + + return ( + <> + <SlCarousel className="dynamic-carousel" pagination navigation> + {slides.map((color, i) => ( + <SlCarouselItem style={{ background: colors[i % colors.length }}> + Slide {i} + </SlCarouselItem> + ))} + </SlCarousel> + + <div className="carousel-options"> + <SlButton onClick={addSlide}>Add slide</SlButton> + <SlButton onClick={removeSlide}>Remove slide</SlButton> + </div> + + <style>{css}</style> + </> + ); +}; +
+ Setting the orientation
attribute to vertical
will render the carousel in a
+ vertical layout. If the content of your slides vary in height, you will need to set amn explicit
+ height
or max-height
on the carousel using CSS.
+
+<sl-carousel class="vertical" pagination orientation="vertical"> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +<style> + .vertical { + max-height: 400px; + } + + .vertical::part(base) { + grid-template-areas: 'slides slides pagination'; + } + + .vertical::part(pagination) { + flex-direction: column; + } + + .vertical::part(navigation) { + transform: rotate(90deg); + display: flex; + } +</style> +
+import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; + +const css = ` + .vertical { + max-height: 400px; + } + + .vertical::part(base) { + grid-template-areas: 'slides slides pagination'; + } + + .vertical::part(pagination) { + flex-direction: column; + } + + .vertical::part(navigation) { + transform: rotate(90deg); + display: flex; + } +`; + +const App = () => ( + <> + <SlCarousel className="vertical" loop pagination orientation="vertical"> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> + <style>{css}</style> + </> +); +
+ Use the --aspect-ratio
custom property to customize the size of the carousel’s viewport from
+ the default value of 16/9.
+
+<sl-carousel class="aspect-ratio" navigation pagination style="--aspect-ratio: 3/2;"> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> + +<sl-divider></sl-divider> + +<sl-select label="Aspect ratio" name="aspect" value="3/2"> + <sl-option value="1/1">1/1</sl-option> + <sl-option value="3/2">3/2</sl-option> + <sl-option value="16/9">16/9</sl-option> +</sl-select> + +<script> + (() => { + const carousel = document.querySelector('sl-carousel.aspect-ratio'); + const aspectRatio = document.querySelector('sl-select[name="aspect"]'); + + aspectRatio.addEventListener('sl-change', () => { + carousel.style.setProperty('--aspect-ratio', aspectRatio.value); + }); + })(); +</script> +
+import { useState } from 'react'; +import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlSelect from '@onsonr/nebula/dist/react/select'; +import SlOption from '@onsonr/nebula/dist/react/option'; + +const App = () => { + const [aspectRatio, setAspectRatio] = useState('3/2'); + + return ( + <> + <SlCarousel className="aspect-ratio" navigation pagination style={{ '--aspect-ratio': aspectRatio }}> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> + + <SlDivider /> + + <SlSelect + label="Aspect ratio" + name="aspect" + value={aspectRatio} + onSlChange={event => setAspectRatio(event.target.value)} + > + <SlOption value="1 / 1">1 / 1</SlOption> + <SlOption value="3 / 2">3 / 2</SlOption> + <SlOption value="16 / 9">16 / 9</SlOption> + </SlSelect> + + <style>{css}</style> + </> + ); +}; +
+ Use the --scroll-hint
custom property to add inline padding in horizontal carousels and block
+ padding in vertical carousels. This will make the closest slides slightly visible, hinting that there are
+ more items in the carousel.
+
+<sl-carousel class="scroll-hint" pagination style="--scroll-hint: 10%;"> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> +
+import { useState } from 'react'; +import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => ( + <> + <SlCarousel className="scroll-hint" pagination style={{ '--scroll-hint': '10%' }}> + <SlCarouselItem> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </SlCarouselItem> + <SlCarouselItem> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </SlCarouselItem> + </SlCarousel> + </> +); +
+ The carousel has a robust API that makes it possible to extend and customize. This example syncs the active + slide with a set of thumbnails, effectively creating a gallery-style carousel. +
+ ++<sl-carousel class="carousel-thumbnails" navigation loop> + <sl-carousel-item> + <img + alt="The sun shines on the mountains and trees (by Adam Kool on Unsplash)" + src="/assets/examples/carousel/mountains.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A waterfall in the middle of a forest (by Thomas Kelly on Unsplash)" + src="/assets/examples/carousel/waterfall.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="The sun is setting over a lavender field (by Leonard Cotte on Unsplash)" + src="/assets/examples/carousel/sunset.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A field of grass with the sun setting in the background (by Sapan Patel on Unsplash)" + src="/assets/examples/carousel/field.jpg" + /> + </sl-carousel-item> + <sl-carousel-item> + <img + alt="A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash)" + src="/assets/examples/carousel/valley.jpg" + /> + </sl-carousel-item> +</sl-carousel> + +<div class="thumbnails"> + <div class="thumbnails__scroller"> + <img alt="Thumbnail by 1" class="thumbnails__image active" src="/assets/examples/carousel/mountains.jpg" /> + <img alt="Thumbnail by 2" class="thumbnails__image" src="/assets/examples/carousel/waterfall.jpg" /> + <img alt="Thumbnail by 3" class="thumbnails__image" src="/assets/examples/carousel/sunset.jpg" /> + <img alt="Thumbnail by 4" class="thumbnails__image" src="/assets/examples/carousel/field.jpg" /> + <img alt="Thumbnail by 5" class="thumbnails__image" src="/assets/examples/carousel/valley.jpg" /> + </div> +</div> + +<style> + .carousel-thumbnails { + --slide-aspect-ratio: 3 / 2; + } + + .thumbnails { + display: flex; + justify-content: center; + } + + .thumbnails__scroller { + display: flex; + gap: var(--sl-spacing-small); + overflow-x: auto; + scrollbar-width: none; + scroll-behavior: smooth; + scroll-padding: var(--sl-spacing-small); + } + + .thumbnails__scroller::-webkit-scrollbar { + display: none; + } + + .thumbnails__image { + width: 64px; + height: 64px; + object-fit: cover; + + opacity: 0.3; + will-change: opacity; + transition: 250ms opacity; + + cursor: pointer; + } + + .thumbnails__image.active { + opacity: 1; + } +</style> + +<script> + { + const carousel = document.querySelector('.carousel-thumbnails'); + const scroller = document.querySelector('.thumbnails__scroller'); + const thumbnails = document.querySelectorAll('.thumbnails__image'); + + scroller.addEventListener('click', e => { + const target = e.target; + + if (target.matches('.thumbnails__image')) { + const index = [...thumbnails].indexOf(target); + carousel.goToSlide(index); + } + }); + + carousel.addEventListener('sl-slide-change', e => { + const slideIndex = e.detail.index; + + [...thumbnails].forEach((thumb, i) => { + thumb.classList.toggle('active', i === slideIndex); + if (i === slideIndex) { + thumb.scrollIntoView({ + block: 'nearest' + }); + } + }); + }); + } +</script> +
+import { useRef } from 'react'; +import SlCarousel from '@onsonr/nebula/dist/react/carousel'; +import SlCarouselItem from '@onsonr/nebula/dist/react/carousel-item'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlRange from '@onsonr/nebula/dist/react/range'; + +const css = ` + .carousel-thumbnails { + --slide-aspect-ratio: 3 / 2; + } + + .thumbnails { + display: flex; + justify-content: center; + } + + .thumbnails__scroller { + display: flex; + gap: var(--sl-spacing-small); + overflow-x: auto; + scrollbar-width: none; + scroll-behavior: smooth; + scroll-padding: var(--sl-spacing-small); + } + + .thumbnails__scroller::-webkit-scrollbar { + display: none; + } + + .thumbnails__image { + width: 64px; + height: 64px; + object-fit: cover; + + opacity: 0.3; + will-change: opacity; + transition: 250ms opacity; + + cursor: pointer; + } + + .thumbnails__image.active { + opacity: 1; + } +`; + +const images = [ + { + src: '/assets/examples/carousel/mountains.jpg', + alt: 'The sun shines on the mountains and trees (by Adam Kool on Unsplash' + }, + { + src: '/assets/examples/carousel/waterfall.jpg', + alt: 'A waterfall in the middle of a forest (by Thomas Kelly on Unsplash' + }, + { + src: '/assets/examples/carousel/sunset.jpg', + alt: 'The sun is setting over a lavender field (by Leonard Cotte on Unsplash' + }, + { + src: '/assets/examples/carousel/field.jpg', + alt: 'A field of grass with the sun setting in the background (by Sapan Patel on Unsplash' + }, + { + src: '/assets/examples/carousel/valley.jpg', + alt: 'A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash' + } +]; + +const App = () => { + const carouselRef = useRef(); + const thumbnailsRef = useRef(); + const [currentSlide, setCurrentSlide] = useState(0); + + useEffect(() => { + const thumbnails = Array.from(thumbnailsRef.current.querySelectorAll('.thumbnails__image')); + + thumbnails[currentSlide]..scrollIntoView({ + block: 'nearest' + }); + }, [currentSlide]); + + const handleThumbnailClick = (index) => { + carouselRef.current.goToSlide(index); + } + + const handleSlideChange = (event) => { + const slideIndex = e.detail.index; + setCurrentSlide(slideIndex); + } + + return ( + <> + <SlCarousel className="carousel-thumbnails" navigation loop onSlSlideChange={handleSlideChange}> + {images.map({ src, alt }) => ( + <SlCarouselItem> + <img + alt={alt} + src={src} + /> + </SlCarouselItem> + )} + </SlCarousel> + + <div class="thumbnails"> + <div class="thumbnails__scroller"> + {images.map({ src, alt }, i) => ( + <img + alt={`Thumbnail by ${i + 1}`} + className={`thumbnails__image ${i === currentSlide ? 'active' : ''}`} + onClick={() => handleThumbnailClick(i)} + src={src} + /> + )} + </div> + </div> + <style>{css}</style> + </> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/carousel/carousel.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/carousel/carousel.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/carousel/carousel.js';
To import this component as a React component:
++import SlCarousel from '@onsonr/nebula/dist/react/carousel';
Name | +Description | +
---|---|
(default) | +The carousel’s main content, one or more <sl-carousel-item> elements. |
+
+ next-icon
+ |
+
+ Optional next icon to use instead of the default. Works best with <sl-icon> .
+ |
+
+ previous-icon
+ |
+
+ Optional previous icon to use instead of the default. Works best with <sl-icon> .
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ loop
+ |
+ When set, allows the user to navigate the carousel in the same direction indefinitely. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ navigation
+ |
+ When set, show the carousel’s navigation. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ pagination
+ |
+ When set, show the carousel’s pagination indicators. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ autoplay
+ |
+ When set, the slides will scroll automatically when the user is not interacting with them. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ autoplayInterval
+
+ + autoplay-interval
+
+ |
+ Specifies the amount of time, in milliseconds, between each automatic scroll. | ++ |
+ number
+ |
+
+ 3000
+ |
+
+ slidesPerPage
+
+ + slides-per-page
+
+ |
+ Specifies how many slides should be shown at a given time. | ++ |
+ number
+ |
+
+ 1
+ |
+
+ slidesPerMove
+
+ + slides-per-move
+
+ |
+
+ Specifies the number of slides the carousel will advance when scrolling, useful when specifying a
+ slides-per-page greater than one. It can’t be higher than slides-per-page .
+ |
+ + |
+ number
+ |
+
+ 1
+ |
+
+ orientation
+ |
+ Specifies the orientation in which the carousel will lay out. | ++ |
+ 'horizontal' | 'vertical'
+ |
+
+ 'horizontal'
+ |
+
+ mouseDragging
+
+ + mouse-dragging
+
+ |
+ When set, it is possible to scroll through the slides by dragging them with the mouse. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-slide-change |
+ onSlSlideChange |
+ Emitted when the active slide changes. | +
+ { index: number, slide: SlCarouselItem }
+ |
+
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
previous() |
+ Move the carousel backward by slides-per-move slides. |
+
+ behavior: ScrollBehavior
+ |
+
next() |
+ Move the carousel forward by slides-per-move slides. |
+
+ behavior: ScrollBehavior
+ |
+
goToSlide() |
+ Scrolls the carousel to the slide specified by index . |
+
+ index: number, behavior: ScrollBehavior
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--slide-gap |
+ The space between each slide. | ++ |
--aspect-ratio |
+ The aspect ratio of each slide. | +16/9 | +
--scroll-hint |
+ + The amount of padding to apply to the scroll area, allowing adjacent slides to become partially + visible as a scroll hint. + | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The carousel’s internal wrapper. | +
scroll-container |
+ The scroll container that wraps the slides. | +
pagination |
+ The pagination indicators wrapper. | +
pagination-item |
+ The pagination indicator. | +
pagination-item--active |
+ Applied when the item is active. | +
navigation |
+ The navigation wrapper. | +
navigation-button |
+ The navigation button. | +
navigation-button--previous |
+ Applied to the previous button. | +
navigation-button--next |
+ Applied to the next button. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-checkbox> | SlCheckbox
+ Checkboxes allow the user to toggle an option on or off.
+ ++<sl-checkbox>Checkbox</sl-checkbox> +
+import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => <SlCheckbox>Checkbox</SlCheckbox>; +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
Use the checked
attribute to activate the checkbox.
+<sl-checkbox checked>Checked</sl-checkbox> +
+import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => <SlCheckbox checked>Checked</SlCheckbox>; +
Use the indeterminate
attribute to make the checkbox indeterminate.
+<sl-checkbox indeterminate>Indeterminate</sl-checkbox> +
+import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => <SlCheckbox indeterminate>Indeterminate</SlCheckbox>; +
Use the disabled
attribute to disable the checkbox.
+<sl-checkbox disabled>Disabled</sl-checkbox> +
+import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => <SlCheckbox disabled>Disabled</SlCheckbox>; +
Use the size
attribute to change a checkbox’s size.
+<sl-checkbox size="small">Small</sl-checkbox> +<br /> +<sl-checkbox size="medium">Medium</sl-checkbox> +<br /> +<sl-checkbox size="large">Large</sl-checkbox> +
+import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => ( + <> + <SlCheckbox size="small">Small</SlCheckbox> + <br /> + <SlCheckbox size="medium">Medium</SlCheckbox> + <br /> + <SlCheckbox size="large">Large</SlCheckbox> + </> +); +
+ Add descriptive help text to a switch with the help-text
attribute. For help texts that contain
+ HTML, use the help-text
slot instead.
+
+<sl-checkbox help-text="What should the user know about the checkbox?">Label</sl-checkbox> +
+import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => <SlCheckbox help-text="What should the user know about the switch?">Label</SlCheckbox>; +
+ Use the setCustomValidity()
method to set a custom validation message. This will prevent the
+ form from submitting and make the browser display the error message you provide. To clear the error, call
+ this function with an empty string.
+
+<form class="custom-validity"> + <sl-checkbox>Check me</sl-checkbox> + <br /> + <sl-button type="submit" variant="primary" style="margin-top: 1rem;">Submit</sl-button> +</form> +<script> + const form = document.querySelector('.custom-validity'); + const checkbox = form.querySelector('sl-checkbox'); + const errorMessage = `Don't forget to check me!`; + + // Set initial validity as soon as the element is defined + customElements.whenDefined('sl-checkbox').then(async () => { + await checkbox.updateComplete; + checkbox.setCustomValidity(errorMessage); + }); + + // Update validity on change + checkbox.addEventListener('sl-change', () => { + checkbox.setCustomValidity(checkbox.checked ? '' : errorMessage); + }); + + // Handle submit + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); +</script> +
+import { useEffect, useRef } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => { + const checkbox = useRef(null); + const errorMessage = `Don't forget to check me!`; + + function handleChange() { + checkbox.current.setCustomValidity(checkbox.current.checked ? '' : errorMessage); + } + + function handleSubmit(event) { + event.preventDefault(); + alert('All fields are valid!'); + } + + useEffect(() => { + checkbox.current.setCustomValidity(errorMessage); + }, []); + + return ( + <form class="custom-validity" onSubmit={handleSubmit}> + <SlCheckbox ref={checkbox} onSlChange={handleChange}> + Check me + </SlCheckbox> + <br /> + <SlButton type="submit" variant="primary" style={{ marginTop: '1rem' }}> + Submit + </SlButton> + </form> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/checkbox/checkbox.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/checkbox/checkbox.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/checkbox/checkbox.js';
To import this component as a React component:
++import SlCheckbox from '@onsonr/nebula/dist/react/checkbox';
Name | +Description | +
---|---|
(default) | +The checkbox’s label. | +
+ help-text
+ |
+
+ Text that describes how to use the checkbox. Alternatively, you can use the
+ help-text attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The name of the checkbox, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+ The current value of the checkbox, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+ - | +
+ size
+ |
+ The checkbox’s size. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ disabled
+ |
+ Disables the checkbox. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ checked
+ |
+ Draws the checkbox in a checked state. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ indeterminate
+ |
+ + Draws the checkbox in an indeterminate state. This is usually applied to checkboxes that represents + a “select all/none” behavior when associated checkboxes have a mix of checked and unchecked states. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ defaultChecked
+ |
+ The default value of the form control. Primarily used for resetting the form control. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ required
+ |
+ Makes the checkbox a required field. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ helpText
+
+ + help-text
+
+ |
+
+ The checkbox’s help text. If you need to display HTML, use the help-text slot instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the checkbox loses focus. | +- | +
sl-change |
+ onSlChange |
+ Emitted when the checked state changes. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the checkbox gains focus. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the checkbox receives input. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
click() |
+ Simulates a click on the checkbox. | +- | +
focus() |
+ Sets focus on the checkbox. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the checkbox. | +- | +
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ + Sets a custom validation message. The value provided will be shown to the user when the form is + submitted. To clear the custom validation message, call this method with an empty string. + | +
+ message: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
control |
+ The square container that wraps the checkbox’s checked state. | +
control--checked |
+ Matches the control part when the checkbox is checked. | +
control--indeterminate |
+ Matches the control part when the checkbox is indeterminate. | +
checked-icon |
+ The checked icon, an <sl-icon> element. |
+
indeterminate-icon |
+ The indeterminate icon, an <sl-icon> element. |
+
label |
+ The container that wraps the checkbox’s label. | +
form-control-help-text |
+ The help text’s wrapper. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-color-picker> | SlColorPicker
+ Color pickers allow the user to select a color.
+ ++<sl-color-picker label="Select a color"></sl-color-picker> +
+import SlColorPicker from '@onsonr/nebula/dist/react/color-picker'; + +const App = () => <SlColorPicker label="Select a color" />; +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
Use the value
attribute to set an initial value for the color picker.
+<sl-color-picker value="#4a90e2" label="Select a color"></sl-color-picker> +
+import SlColorPicker from '@onsonr/nebula/dist/react/color-picker'; + +const App = () => <SlColorPicker value="#4a90e2" label="Select a color" />; +
+ Use the opacity
attribute to enable the opacity slider. When this is enabled, the value will be
+ displayed as HEXA, RGBA, HSLA, or HSVA based on format
.
+
+<sl-color-picker value="#f5a623ff" opacity label="Select a color"></sl-color-picker> +
+import SlColorPicker from '@onsonr/nebula/dist/react/color-picker'; + +const App = () => <SlColorPicker opacity label="Select a color" />; +
+ Set the color picker’s format with the format
attribute. Valid options include
+ hex
, rgb
, hsl
, and hsv
. Note that the color picker’s
+ input will accept any parsable format (including CSS color names) regardless of this option.
+
To prevent users from toggling the format themselves, add the no-format-toggle
attribute.
+<sl-color-picker format="hex" value="#4a90e2" label="Select a color"></sl-color-picker> +<sl-color-picker format="rgb" value="rgb(80, 227, 194)" label="Select a color"></sl-color-picker> +<sl-color-picker format="hsl" value="hsl(290, 87%, 47%)" label="Select a color"></sl-color-picker> +<sl-color-picker format="hsv" value="hsv(55, 89%, 97%)" label="Select a color"></sl-color-picker> +
+import SlColorPicker from '@onsonr/nebula/dist/react/color-picker'; + +const App = () => ( + <> + <SlColorPicker format="hex" value="#4a90e2" /> + <SlColorPicker format="rgb" value="rgb(80, 227, 194)" /> + <SlColorPicker format="hsl" value="hsl(290, 87%, 47%)" /> + <SlColorPicker format="hsv" value="hsv(55, 89%, 97%)" /> + </> +); +
+ Use the swatches
attribute to add convenient presets to the color picker. Any format the color
+ picker can parse is acceptable (including CSS color names), but each value must be separated by a semicolon
+ (;
). Alternatively, you can pass an array of color values to this property using JavaScript.
+
+<sl-color-picker + label="Select a color" + swatches=" + #d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe; + #4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff; + " +></sl-color-picker> +
+import SlColorPicker from '@onsonr/nebula/dist/react/color-picker'; + +const App = () => ( + <SlColorPicker + label="Select a color" + swatches=" + #d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe; + #4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff; + " + /> +); +
Use the size
attribute to change the color picker’s trigger size.
+<sl-color-picker size="small" label="Select a color"></sl-color-picker> +<sl-color-picker size="medium" label="Select a color"></sl-color-picker> +<sl-color-picker size="large" label="Select a color"></sl-color-picker> +
+import SlColorPicker from '@onsonr/nebula/dist/react/color-picker'; + +const App = () => ( + <> + <SlColorPicker size="small" label="Select a color" /> + <SlColorPicker size="medium" label="Select a color" /> + <SlColorPicker size="large" label="Select a color" /> + </> +); +
+ The color picker can be rendered inline instead of in a dropdown using the inline
attribute.
+
+<sl-color-picker inline label="Select a color"></sl-color-picker> +
+import SlColorPicker from '@onsonr/nebula/dist/react/color-picker'; + +const App = () => <SlColorPicker inline label="Select a color" />; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/color-picker/color-picker.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/color-picker/color-picker.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/color-picker/color-picker.js';
To import this component as a React component:
++import SlColorPicker from '@onsonr/nebula/dist/react/color-picker';
Name | +Description | +
---|---|
+ label
+ |
+ The color picker’s form label. Alternatively, you can use the label attribute. |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+
+ The current value of the color picker. The value’s format will vary based the
+ format attribute. To get the value in a specific format, use the
+ getFormattedValue() method. The value is submitted as a name/value pair with form data.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ defaultValue
+ |
+ The default value of the form control. Primarily used for resetting the form control. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ label
+ |
+
+ The color picker’s label. This will not be displayed, but it will be announced by assistive devices.
+ If you need to display HTML, you can use the label slot` instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ format
+ |
+ + The format to use. If opacity is enabled, these will translate to HEXA, RGBA, HSLA, and HSVA + respectively. The color picker will accept user input in any format (including CSS color names) and + convert it to the desired format. + | ++ |
+ 'hex' | 'rgb' | 'hsl' | 'hsv'
+ |
+
+ 'hex'
+ |
+
+ inline
+ |
+ Renders the color picker inline rather than in a dropdown. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ size
+ |
+ + Determines the size of the color picker’s trigger. This has no effect on inline color pickers. + | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ noFormatToggle
+
+ + no-format-toggle
+
+ |
+ Removes the button that lets users toggle between format. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ name
+ |
+ The name of the form control, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ disabled
+ |
+ Disables the color picker. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ hoist
+ |
+
+ Enable this option to prevent the panel from being clipped when the component is placed inside a
+ container with
+ overflow: auto|scroll . Hoisting uses a fixed positioning strategy that works in many,
+ but not all, scenarios.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
+ opacity
+ |
+ + Shows the opacity slider. Enabling this will cause the formatted value to be HEXA, RGBA, or HSLA. + | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ uppercase
+ |
+ By default, values are lowercase. With this attribute, values will be uppercase instead. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ swatches
+ |
+
+ One or more predefined color swatches to display as presets in the color picker. Can include any
+ format the color picker can parse, including HEX(A), RGB(A), HSL(A), HSV(A), and CSS color names.
+ Each color must be separated by a semicolon (; ). Alternatively, you can pass an array
+ of color values to this property using JavaScript.
+ |
+ + |
+ string | string[]
+ |
+
+ ''
+ |
+
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ required
+ |
+ Makes the color picker a required field. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the color picker loses focus. | +- | +
sl-change |
+ onSlChange |
+ Emitted when the color picker’s value changes. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the color picker receives focus. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the color picker receives input. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
focus() |
+ Sets focus on the color picker. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the color picker. | +- | +
getFormattedValue() |
+ Returns the current value as a string in the specified format. | +
+ format: 'hex' | 'hexa' | 'rgb' | 'rgba' | 'hsl' | 'hsla' | 'hsv' | 'hsva'
+ |
+
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--grid-width |
+ The width of the color grid. | ++ |
--grid-height |
+ The height of the color grid. | ++ |
--grid-handle-size |
+ The size of the color grid’s handle. | ++ |
--slider-height |
+ The height of the hue and alpha sliders. | ++ |
--slider-handle-size |
+ The diameter of the slider’s handle. | ++ |
--swatch-size |
+ The size of each predefined color swatch. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
trigger |
+ The color picker’s dropdown trigger. | +
swatches |
+ The container that holds the swatches. | +
swatch |
+ Each individual swatch. | +
grid |
+ The color grid. | +
grid-handle |
+ The color grid’s handle. | +
slider |
+ Hue and opacity sliders. | +
slider-handle |
+ Hue and opacity slider handles. | +
hue-slider |
+ The hue slider. | +
hue-slider-handle |
+ The hue slider’s handle. | +
opacity-slider |
+ The opacity slider. | +
opacity-slider-handle |
+ The opacity slider’s handle. | +
preview |
+ The preview color. | +
input |
+ The text input. | +
eye-dropper-button |
+ The eye dropper button. | +
eye-dropper-button__base |
+ The eye dropper button’s exported button part. |
+
eye-dropper-button__prefix |
+ The eye dropper button’s exported prefix part. |
+
eye-dropper-button__label |
+ The eye dropper button’s exported label part. |
+
eye-dropper-button__suffix |
+ The eye dropper button’s exported suffix part. |
+
eye-dropper-button__caret |
+ The eye dropper button’s exported caret part. |
+
format-button |
+ The format button. | +
format-button__base |
+ The format button’s exported button part. |
+
format-button__prefix |
+ The format button’s exported prefix part. |
+
format-button__label |
+ The format button’s exported label part. |
+
format-button__suffix |
+ The format button’s exported suffix part. |
+
format-button__caret |
+ The format button’s exported caret part. |
+
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-button>
<sl-button-group>
<sl-dropdown>
<sl-icon>
<sl-input>
<sl-popup>
<sl-spinner>
<sl-visually-hidden>
<sl-copy-button> | SlCopyButton
+ Copies text data to the clipboard when the user clicks the trigger.
+ ++<sl-copy-button value="Nebula rocks!"></sl-copy-button> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; + +const App = () => ( + <SlCopyButton value="Nebula rocks!" /> +); +
+ Copy Buttons display feedback in a tooltip. You can customize the labels using the copy-label
,
+ success-label
, and error-label
attributes.
+
+<sl-copy-button + value="Custom labels are easy" + copy-label="Click to copy" + success-label="You did it!" + error-label="Whoops, your browser doesn't support this!" +></sl-copy-button> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; + +const App = () => ( + <SlCopyButton + value="Custom labels are easy" + copy-label="Click to copy" + success-label="You did it!" + error-label="Whoops, your browser doesn't support this!" + /> +); +
+ Use the copy-icon
, success-icon
, and error-icon
slots to customize
+ the icons that get displayed for each state. You can use
+ <sl-icon>
or your own images.
+
+<sl-copy-button value="Copied from a custom button"> + <sl-icon slot="copy-icon" name="clipboard"></sl-icon> + <sl-icon slot="success-icon" name="clipboard-check"></sl-icon> + <sl-icon slot="error-icon" name="clipboard-x"></sl-icon> +</sl-copy-button> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; +import { SlIcon } from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <> + <SlCopyButton value="Copied from a custom button"> + <SlIcon slot="copy-icon" name="clipboard" /> + <SlIcon slot="success-icon" name="clipboard-check" /> + <SlIcon slot="error-icon" name="clipboard-x" /> + </SlCopyButton> + </> +); +
+ Normally, the data that gets copied will come from the component’s value
attribute, but you can
+ copy data from any element within the same document by providing its id
to the
+ from
attribute.
+
+ When using the from
attribute, the element’s
+ textContent
+ will be copied by default. Passing an attribute or property modifier will let you copy data from one of the
+ element’s attributes or properties instead.
+
+ To copy data from an attribute, use from="id[attr]"
where id
is the id of the
+ target element and attr
is the name of the attribute you’d like to copy. To copy data from a
+ property, use from="id.prop"
where id
is the id of the target element and
+ prop
is the name of the property you’d like to copy.
+
+<!-- Copies the span's textContent --> +<span id="my-phone">+1 (234) 456-7890</span> +<sl-copy-button from="my-phone"></sl-copy-button> + +<br><br> + +<!-- Copies the input's "value" property --> +<sl-input id="my-input" type="text" value="User input" style="display: inline-block; max-width: 300px;"></sl-input> +<sl-copy-button from="my-input.value"></sl-copy-button> + +<br><br> + +<!-- Copies the link's "href" attribute --> +<a id="my-link" href="https://shoelace.style/">Nebula Website</a> +<sl-copy-button from="my-link[href]"></sl-copy-button> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; +import { SlInput } from '@onsonr/nebula/dist/react/input'; + +const App = () => ( + <> + {/* Copies the span's textContent */} + <span id="my-phone">+1 (234) 456-7890</span> + <SlCopyButton from="my-phone" /> + + <br /><br /> + + {/* Copies the input's "value" property */} + <SlInput id="my-input" type="text" /> + <SlCopyButton from="my-input.value" /> + + <br /><br /> + + {/* Copies the link's "href" attribute */} + <a id="my-link" href="https://shoelace.style/">Nebula Website</a> + <SlCopyButton from="my-link[href]" /> + </> +); +
+ A copy error will occur if the value is an empty string, if the from
attribute points to an id
+ that doesn’t exist, or if the browser rejects the operation for any reason. When this happens, the
+ sl-error
event will be emitted.
+
+ This example demonstrates what happens when a copy error occurs. You can customize the error label and icon
+ using the error-label
attribute and the error-icon
slot, respectively.
+
+<sl-copy-button from="i-do-not-exist"></sl-copy-button> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; + +const App = () => ( + <SlCopyButton from="i-do-not-exist" /> +); +
Copy buttons can be disabled by adding the disabled
attribute.
+<sl-copy-button value="You can't copy me" disabled></sl-copy-button> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; + +const App = () => ( + <SlCopyButton value="You can't copy me" disabled /> +); +
+ A success indicator is briefly shown after copying. You can customize the length of time the indicator is
+ shown using the feedback-duration
attribute.
+
+<sl-copy-button value="Nebula rocks!" feedback-duration="250"></sl-copy-button> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; + +const App = () => ( + <SlCopyButton value="Nebula rocks!" feedback-duration={250} /> +); +
You can customize the button to your liking with CSS.
+ ++<sl-copy-button value="I'm so stylish" class="custom-styles"> + <sl-icon slot="copy-icon" name="asterisk"></sl-icon> + <sl-icon slot="success-icon" name="check-lg"></sl-icon> + <sl-icon slot="error-icon" name="x-lg"></sl-icon> +</sl-copy-button> + +<style> + .custom-styles { + --success-color: white; + --error-color: white; + color: white; + } + + .custom-styles::part(button) { + background-color: #ff1493; + border: solid 4px #ff7ac1; + border-right-color: #ad005c; + border-bottom-color: #ad005c; + border-radius: 0; + transition: 100ms scale ease-in-out, 100ms translate ease-in-out; + } + + .custom-styles::part(button):hover { + scale: 1.1; + } + + .custom-styles::part(button):active { + translate: 0 2px; + } + + .custom-styles::part(button):focus-visible { + outline: dashed 2px deeppink; + outline-offset: 4px; + } +</style> +
+import { SlCopyButton } from '@onsonr/nebula/dist/react/copy-button'; + +const css = ` + .custom-styles { + --success-color: white; + --error-color: white; + color: white; + } + + .custom-styles::part(button) { + background-color: #ff1493; + border: solid 4px #ff7ac1; + border-right-color: #ad005c; + border-bottom-color: #ad005c; + border-radius: 0; + transition: 100ms scale ease-in-out, 100ms translate ease-in-out; + } + + .custom-styles::part(button):hover { + scale: 1.1; + } + + .custom-styles::part(button):active { + translate: 0 2px; + } + + .custom-styles::part(button):focus-visible { + outline: dashed 2px deeppink; + outline-offset: 4px; + } +`; + +const App = () => ( + <> + <SlCopyButton value="I'm so stylish" className="custom-styles" /> + + <style>{css}</style> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/copy-button/copy-button.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/copy-button/copy-button.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/copy-button/copy-button.js';
To import this component as a React component:
++import SlCopyButton from '@onsonr/nebula/dist/react/copy-button';
Name | +Description | +
---|---|
+ copy-icon
+ |
+ The icon to show in the default copy state. Works best with <sl-icon> . |
+
+ success-icon
+ |
+ The icon to show when the content is copied. Works best with <sl-icon> . |
+
+ error-icon
+ |
+ The icon to show when a copy error occurs. Works best with <sl-icon> . |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The text value to copy. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ from
+ |
+
+ An id that references an element in the same document from which data will be copied. If both this
+ and value are present, this value will take precedence. By default, the target
+ element’s textContent will be copied. To copy an attribute, append the attribute name
+ wrapped in square brackets, e.g. from="el[value]" . To copy a property, append a dot and
+ the property name, e.g. from="el.value" .
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ disabled
+ |
+ Disables the copy button. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ copyLabel
+
+ + copy-label
+
+ |
+ A custom label to show in the tooltip. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ successLabel
+
+ + success-label
+
+ |
+ A custom label to show in the tooltip after copying. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ errorLabel
+
+ + error-label
+
+ |
+ A custom label to show in the tooltip when a copy error occurs. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ feedbackDuration
+
+ + feedback-duration
+
+ |
+ The length of time to show feedback before restoring the default trigger. | ++ |
+ number
+ |
+
+ 1000
+ |
+
+ tooltipPlacement
+
+ + tooltip-placement
+
+ |
+ The preferred placement of the tooltip. | ++ |
+ 'top' | 'right' | 'bottom' | 'left'
+ |
+
+ 'top'
+ |
+
+ hoist
+ |
+
+ Enable this option to prevent the tooltip from being clipped when the component is placed inside a
+ container with
+ overflow: auto|hidden|scroll . Hoisting uses a fixed positioning strategy that works in
+ many, but not all, scenarios.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-copy |
+ onSlCopy |
+ Emitted when the data has been copied. | +- | +
sl-error |
+ onSlError |
+ Emitted when the data could not be copied. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Default | +
---|---|---|
--success-color |
+ The color to use for success feedback. | ++ |
--error-color |
+ The color to use for error feedback. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
button |
+ The internal <button> element. |
+
copy-icon |
+ The container that holds the copy icon. | +
success-icon |
+ The container that holds the success icon. | +
error-icon |
+ The container that holds the error icon. | +
tooltip__base |
+ The tooltip’s exported base part. |
+
tooltip__base__popup |
+ The tooltip’s exported popup part. |
+
tooltip__base__arrow |
+ The tooltip’s exported arrow part. |
+
tooltip__body |
+ The tooltip’s exported body part. |
+
+ Learn more about customizing CSS parts. +
+ +Name | +Description | +
---|---|
copy.in |
+ The animation to use when feedback icons animate in. | +
copy.out |
+ The animation to use when feedback icons animate out. | +
+ Learn more about customizing animations. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-popup>
<sl-tooltip>
<sl-details> | SlDetails
+ Details show a brief summary and expand to show additional content.
+ + + ++<sl-details summary="Toggle Me"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +</sl-details> +
+import SlDetails from '@onsonr/nebula/dist/react/details'; + +const App = () => ( + <SlDetails summary="Toggle Me"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + </SlDetails> +); +
Use the disable
attribute to prevent the details from expanding.
+<sl-details summary="Disabled" disabled> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +</sl-details> +
+import SlDetails from '@onsonr/nebula/dist/react/details'; + +const App = () => ( + <SlDetails summary="Disabled" disabled> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + </SlDetails> +); +
+ Use the expand-icon
and collapse-icon
slots to change the expand and collapse
+ icons, respectively. To disable the animation, override the rotate
property on the
+ summary-icon
part as shown below.
+
+<sl-details summary="Toggle Me" class="custom-icons"> + <sl-icon name="plus-square" slot="expand-icon"></sl-icon> + <sl-icon name="dash-square" slot="collapse-icon"></sl-icon> + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +</sl-details> + +<style> + sl-details.custom-icons::part(summary-icon) { + /* Disable the expand/collapse animation */ + rotate: none; + } +</style> +
+import SlDetails from '@onsonr/nebula/dist/react/details'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const css = ` + sl-details.custom-icon::part(summary-icon) { + /* Disable the expand/collapse animation */ + rotate: none; + } +`; + +const App = () => ( + <> + <SlDetails summary="Toggle Me" class="custom-icon"> + <SlIcon name="plus-square" slot="expand-icon" /> + <SlIcon name="dash-square" slot="collapse-icon" /> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. + </SlDetails> + + <style>{css}</style> + </> +); +
+ Details are designed to function independently, but you can simulate a group or “accordion” where only one
+ is shown at a time by listening for the sl-show
event.
+
+<div class="details-group-example"> + <sl-details summary="First" open> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + </sl-details> + + <sl-details summary="Second"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + </sl-details> + + <sl-details summary="Third"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + </sl-details> +</div> + +<script> + const container = document.querySelector('.details-group-example'); + + // Close all other details when one is shown + container.addEventListener('sl-show', event => { + if (event.target.localName === 'sl-details') { + [...container.querySelectorAll('sl-details')].map(details => (details.open = event.target === details)); + } + }); +</script> + +<style> + .details-group-example sl-details:not(:last-of-type) { + margin-bottom: var(--sl-spacing-2x-small); + } +</style> +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/details/details.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/details/details.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/details/details.js';
To import this component as a React component:
++import SlDetails from '@onsonr/nebula/dist/react/details';
Name | +Description | +
---|---|
(default) | +The details’ main content. | +
+ summary
+ |
+ The details’ summary. Alternatively, you can use the summary attribute. |
+
+ expand-icon
+ |
+
+ Optional expand icon to use instead of the default. Works best with <sl-icon> .
+ |
+
+ collapse-icon
+ |
+
+ Optional collapse icon to use instead of the default. Works best with <sl-icon> .
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ open
+ |
+
+ Indicates whether or not the details is open. You can toggle this attribute to show and hide the
+ details, or you can use the show() and hide() methods and this attribute
+ will reflect the details’ open state.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ summary
+ |
+
+ The summary to show in the header. If you need to display HTML, use the summary slot
+ instead.
+ |
+ + |
+ string
+ |
+ - | +
+ disabled
+ |
+ Disables the details so it can’t be toggled. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-show |
+ onSlShow |
+ Emitted when the details opens. | +- | +
sl-after-show |
+ onSlAfterShow |
+ Emitted after the details opens and all animations are complete. | +- | +
sl-hide |
+ onSlHide |
+ Emitted when the details closes. | +- | +
sl-after-hide |
+ onSlAfterHide |
+ Emitted after the details closes and all animations are complete. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the details. | +- | +
hide() |
+ Hides the details | +- | +
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
header |
+ The header that wraps both the summary and the expand/collapse icon. | +
summary |
+ The container that wraps the summary. | +
summary-icon |
+ The container that wraps the expand/collapse icons. | +
content |
+ The details content. | +
+ Learn more about customizing CSS parts. +
+ +Name | +Description | +
---|---|
details.show |
+
+ The animation to use when showing details. You can use height: auto with this
+ animation.
+ |
+
details.hide |
+
+ The animation to use when hiding details. You can use height: auto with this animation.
+ |
+
+ Learn more about customizing animations. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-dialog> | SlDialog
+ + Dialogs, sometimes called “modals”, appear above the page and require the user’s immediate attention. +
+ + + ++<sl-dialog label="Dialog" class="dialog-overview"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-dialog> + +<sl-button>Open Dialog</sl-button> + +<script> + const dialog = document.querySelector('.dialog-overview'); + const openButton = dialog.nextElementSibling; + const closeButton = dialog.querySelector('sl-button[slot="footer"]'); + + openButton.addEventListener('click', () => dialog.show()); + closeButton.addEventListener('click', () => dialog.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDialog from '@onsonr/nebula/dist/react/dialog'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDialog> + + <SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton> + </> + ); +}; +
Use the --width
custom property to set the dialog’s width.
+<sl-dialog label="Dialog" class="dialog-width" style="--width: 50vw;"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-dialog> + +<sl-button>Open Dialog</sl-button> + +<script> + const dialog = document.querySelector('.dialog-width'); + const openButton = dialog.nextElementSibling; + const closeButton = dialog.querySelector('sl-button[slot="footer"]'); + + openButton.addEventListener('click', () => dialog.show()); + closeButton.addEventListener('click', () => dialog.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDialog from '@onsonr/nebula/dist/react/dialog'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDialog label="Dialog" open={open} style={{ '--width': '50vw' }} onSlAfterHide={() => setOpen(false)}> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDialog> + + <SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton> + </> + ); +}; +
+ By design, a dialog’s height will never exceed that of the viewport. As such, dialogs will not scroll with + the page ensuring the header and footer are always accessible to the user. +
+ +Scroll down and give it a try! 👇
++<sl-dialog label="Dialog" class="dialog-scrolling"> + <div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;"> + <p>Scroll down and give it a try! 👇</p> + </div> + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-dialog> + +<sl-button>Open Dialog</sl-button> + +<script> + const dialog = document.querySelector('.dialog-scrolling'); + const openButton = dialog.nextElementSibling; + const closeButton = dialog.querySelector('sl-button[slot="footer"]'); + + openButton.addEventListener('click', () => dialog.show()); + closeButton.addEventListener('click', () => dialog.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDialog from '@onsonr/nebula/dist/react/dialog'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}> + <div + style={{ + height: '150vh', + border: 'dashed 2px var(--sl-color-neutral-200)', + padding: '0 1rem' + }} + > + <p>Scroll down and give it a try! 👇</p> + </div> + + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDialog> + + <SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton> + </> + ); +}; +
+ The header shows a functional close button by default. You can use the header-actions
slot to
+ add additional icon buttons if needed.
+
+<sl-dialog label="Dialog" class="dialog-header-actions"> + <sl-icon-button class="new-window" slot="header-actions" name="box-arrow-up-right"></sl-icon-button> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-dialog> + +<sl-button>Open Dialog</sl-button> + +<script> + const dialog = document.querySelector('.dialog-header-actions'); + const openButton = dialog.nextElementSibling; + const closeButton = dialog.querySelector('sl-button[slot="footer"]'); + const newWindowButton = dialog.querySelector('.new-window'); + + openButton.addEventListener('click', () => dialog.show()); + closeButton.addEventListener('click', () => dialog.hide()); + newWindowButton.addEventListener('click', () => window.open(location.href)); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDialog from '@onsonr/nebula/dist/react/dialog'; +import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}> + <SlIconButton + class="new-window" + slot="header-actions" + name="box-arrow-up-right" + onClick={() => window.open(location.href)} + /> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDialog> + + <SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton> + </> + ); +}; +
+ By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the + Escape key. In most cases, the default behavior is the best behavior in terms of UX. However, + there are situations where this may be undesirable, such as when data loss will occur. +
+
+ To keep the dialog open in such cases, you can cancel the sl-request-close
event. When
+ canceled, the dialog will remain open and pulse briefly to draw the user’s attention to it.
+
+ You can use event.detail.source
to determine what triggered the request to close. This example
+ prevents the dialog from closing when the overlay is clicked, but allows the close button or
+ Escape to dismiss it.
+
+<sl-dialog label="Dialog" class="dialog-deny-close"> + This dialog will not close when you click on the overlay. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-dialog> + +<sl-button>Open Dialog</sl-button> + +<script> + const dialog = document.querySelector('.dialog-deny-close'); + const openButton = dialog.nextElementSibling; + const closeButton = dialog.querySelector('sl-button[slot="footer"]'); + + openButton.addEventListener('click', () => dialog.show()); + closeButton.addEventListener('click', () => dialog.hide()); + + // Prevent the dialog from closing when the user clicks on the overlay + dialog.addEventListener('sl-request-close', event => { + if (event.detail.source === 'overlay') { + event.preventDefault(); + } + }); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDialog from '@onsonr/nebula/dist/react/dialog'; + +const App = () => { + const [open, setOpen] = useState(false); + + // Prevent the dialog from closing when the user clicks on the overlay + function handleRequestClose(event) { + if (event.detail.source === 'overlay') { + event.preventDefault(); + } + } + + return ( + <> + <SlDialog label="Dialog" open={open} onSlRequestClose={handleRequestClose} onSlAfterHide={() => setOpen(false)}> + This dialog will not close when you click on the overlay. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDialog> + + <SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton> + </> + ); +}; +
+ By default, the dialog’s panel will gain focus when opened. This allows a subsequent tab press to focus on
+ the first tabbable element in the dialog. If you want a different element to have focus, add the
+ autofocus
attribute to it as shown below.
+
+<sl-dialog label="Dialog" class="dialog-focus"> + <sl-input autofocus placeholder="I will have focus when the dialog is opened"></sl-input> + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-dialog> + +<sl-button>Open Dialog</sl-button> + +<script> + const dialog = document.querySelector('.dialog-focus'); + const input = dialog.querySelector('sl-input'); + const openButton = dialog.nextElementSibling; + const closeButton = dialog.querySelector('sl-button[slot="footer"]'); + + openButton.addEventListener('click', () => dialog.show()); + closeButton.addEventListener('click', () => dialog.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDialog from '@onsonr/nebula/dist/react/dialog'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDialog label="Dialog" open={open} onSlAfterHide={() => setOpen(false)}> + <SlInput autofocus placeholder="I will have focus when the dialog is opened" /> + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDialog> + + <SlButton onClick={() => setOpen(true)}>Open Dialog</SlButton> + </> + ); +}; +
+ You can further customize initial focus behavior by canceling the sl-initial-focus
event and
+ setting focus yourself inside the event handler.
+
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/dialog/dialog.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/dialog/dialog.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/dialog/dialog.js';
To import this component as a React component:
++import SlDialog from '@onsonr/nebula/dist/react/dialog';
Name | +Description | +
---|---|
(default) | +The dialog’s main content. | +
+ label
+ |
+ The dialog’s label. Alternatively, you can use the label attribute. |
+
+ header-actions
+ |
+ Optional actions to add to the header. Works best with <sl-icon-button> . |
+
+ footer
+ |
+ The dialog’s footer, usually one or more buttons representing various options. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ modal
+ |
+
+ Exposes the internal modal utility that controls focus trapping. To temporarily disable focus
+ trapping and allow third-party modals spawned from an active Shoelace modal, call
+ modal.activateExternal() when the third-party modal opens. Upon closing, call
+ modal.deactivateExternal() to restore Shoelace’s focus trapping.
+ |
+ + | - | +
+ new Modal(this)
+ |
+
+ open
+ |
+
+ Indicates whether or not the dialog is open. You can toggle this attribute to show and hide the
+ dialog, or you can use the show() and hide() methods and this attribute
+ will reflect the dialog’s open state.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ label
+ |
+
+ The dialog’s label as displayed in the header. You should always include a relevant label even when
+ using
+ no-header , as it is required for proper accessibility. If you need to display HTML, use
+ the label slot instead.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ noHeader
+
+ + no-header
+
+ |
+ + Disables the header. This will also remove the default close button, so please ensure you provide an + easy, accessible way for users to dismiss the dialog. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-show |
+ onSlShow |
+ Emitted when the dialog opens. | +- | +
sl-after-show |
+ onSlAfterShow |
+ Emitted after the dialog opens and all animations are complete. | +- | +
sl-hide |
+ onSlHide |
+ Emitted when the dialog closes. | +- | +
sl-after-hide |
+ onSlAfterHide |
+ Emitted after the dialog closes and all animations are complete. | +- | +
sl-initial-focus |
+ onSlInitialFocus |
+
+ Emitted when the dialog opens and is ready to receive focus. Calling
+ event.preventDefault() will prevent focusing and allow you to set it on a different
+ element, such as an input.
+ |
+ - | +
sl-request-close |
+ onSlRequestClose |
+
+ Emitted when the user attempts to close the dialog by clicking the close button, clicking the
+ overlay, or pressing escape. Calling event.preventDefault() will keep the dialog open.
+ Avoid using this unless closing the dialog will result in destructive behavior such as data loss.
+ |
+
+ { source: 'close-button' | 'keyboard' | 'overlay' }
+ |
+
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the dialog. | +- | +
hide() |
+ Hides the dialog | +- | +
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--width |
+ + The preferred width of the dialog. Note that the dialog will shrink to accommodate smaller screens. + | ++ |
--header-spacing |
+ The amount of padding to use for the header. | ++ |
--body-spacing |
+ The amount of padding to use for the body. | ++ |
--footer-spacing |
+ The amount of padding to use for the footer. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
overlay |
+ The overlay that covers the screen behind the dialog. | +
panel |
+ The dialog’s panel (where the dialog and its content are rendered). | +
header |
+ The dialog’s header. This element wraps the title and header actions. | +
header-actions |
+ Optional actions to add to the header. Works best with <sl-icon-button> . |
+
title |
+ The dialog’s title. | +
close-button |
+ The close button, an <sl-icon-button> . |
+
close-button__base |
+ The close button’s exported base part. |
+
body |
+ The dialog’s body. | +
footer |
+ The dialog’s footer. | +
+ Learn more about customizing CSS parts. +
+ +Name | +Description | +
---|---|
dialog.show |
+ The animation to use when showing the dialog. | +
dialog.hide |
+ The animation to use when hiding the dialog. | +
dialog.denyClose |
+ The animation to use when a request to close the dialog is denied. | +
dialog.overlay.show |
+ The animation to use when showing the dialog’s overlay. | +
dialog.overlay.hide |
+ The animation to use when hiding the dialog’s overlay. | +
+ Learn more about customizing animations. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon-button>
<sl-divider> | SlDivider
+ Dividers are used to visually separate or group elements.
+ ++<sl-divider></sl-divider> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; + +const App = () => <SlDivider />; +
Use the --width
custom property to change the width of the divider.
+<sl-divider style="--width: 4px;"></sl-divider> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; + +const App = () => <SlDivider style={{ '--width': '4px' }} />; +
Use the --color
custom property to change the color of the divider.
+<sl-divider style="--color: tomato;"></sl-divider> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; + +const App = () => <SlDivider style={{ '--color': 'tomato' }} />; +
+ Use the --spacing
custom property to change the amount of space between the divider and it’s
+ neighboring elements.
+
+<div style="text-align: center;"> + Above + <sl-divider style="--spacing: 2rem;"></sl-divider> + Below +</div> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; + +const App = () => ( + <> + Above + <SlDivider style={{ '--spacing': '2rem' }} /> + Below + </> +); +
+ Add the vertical
attribute to draw the divider in a vertical orientation. The divider will span
+ the full height of its container. Vertical dividers work especially well inside of a flex container.
+
+<div style="display: flex; align-items: center; height: 2rem;"> + First + <sl-divider vertical></sl-divider> + Middle + <sl-divider vertical></sl-divider> + Last +</div> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; + +const App = () => ( + <div + style={{ + display: 'flex', + alignItems: 'center', + height: '2rem' + }} + > + First + <SlDivider vertical /> + Middle + <SlDivider vertical /> + Last + </div> +); +
Use dividers in menus to visually group menu items.
+ ++<sl-menu style="max-width: 200px;"> + <sl-menu-item value="1">Option 1</sl-menu-item> + <sl-menu-item value="2">Option 2</sl-menu-item> + <sl-menu-item value="3">Option 3</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item value="4">Option 4</sl-menu-item> + <sl-menu-item value="5">Option 5</sl-menu-item> + <sl-menu-item value="6">Option 6</sl-menu-item> +</sl-menu> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem value="1">Option 1</SlMenuItem> + <SlMenuItem value="2">Option 2</SlMenuItem> + <SlMenuItem value="3">Option 3</SlMenuItem> + <sl-divider /> + <SlMenuItem value="4">Option 4</SlMenuItem> + <SlMenuItem value="5">Option 5</SlMenuItem> + <SlMenuItem value="6">Option 6</SlMenuItem> + </SlMenu> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/divider/divider.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/divider/divider.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/divider/divider.js';
To import this component as a React component:
++import SlDivider from '@onsonr/nebula/dist/react/divider';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ vertical
+ |
+ Draws the divider in a vertical orientation. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Default | +
---|---|---|
--color |
+ The color of the divider. | ++ |
--width |
+ The width of the divider. | ++ |
--spacing |
+ The spacing of the divider. | ++ |
+ Learn more about + customizing CSS custom properties. +
+<sl-drawer> | SlDrawer
+ + Drawers slide in from a container to expose additional options and information. +
+ + + ++<sl-drawer label="Drawer" class="drawer-overview"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-overview'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ By default, drawers slide in from the end. To make the drawer slide in from the start, set the
+ placement
attribute to start
.
+
+<sl-drawer label="Drawer" placement="start" class="drawer-placement-start"> + This drawer slides in from the start. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-placement-start'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" placement="start" open={open} onSlAfterHide={() => setOpen(false)}> + This drawer slides in from the start. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
To make the drawer slide in from the top, set the placement
attribute to top
.
+<sl-drawer label="Drawer" placement="top" class="drawer-placement-top"> + This drawer slides in from the top. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-placement-top'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" placement="top" open={open} onSlAfterHide={() => setOpen(false)}> + This drawer slides in from the top. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ To make the drawer slide in from the bottom, set the placement
attribute to
+ bottom
.
+
+<sl-drawer label="Drawer" placement="bottom" class="drawer-placement-bottom"> + This drawer slides in from the bottom. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-placement-bottom'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" placement="bottom" open={open} onSlAfterHide={() => setOpen(false)}> + This drawer slides in from the bottom. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ By default, drawers slide out of their
+ containing block, which is usually the viewport. To make a drawer slide out of a parent element, add the
+ contained
attribute to the drawer and apply position: relative
to its parent.
+
+ Unlike normal drawers, contained drawers are not modal. This means they do not show an overlay, they do not + trap focus, and they are not dismissible with Escape. This is intentional to allow users to + interact with elements outside of the drawer. +
+ ++<div + style="position: relative; border: solid 2px var(--sl-panel-border-color); height: 300px; padding: 1rem; margin-bottom: 1rem;" +> + The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer opens. + + <sl-drawer label="Drawer" contained class="drawer-contained" style="--size: 50%;"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <sl-button slot="footer" variant="primary">Close</sl-button> + </sl-drawer> +</div> + +<sl-button>Toggle Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-contained'); + const openButton = drawer.parentElement.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => (drawer.open = !drawer.open)); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <div + style={{ + position: 'relative', + border: 'solid 2px var(--sl-panel-border-color)', + height: '300px', + padding: '1rem', + marginBottom: '1rem' + }} + > + The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer + opens. + <SlDrawer + label="Drawer" + contained + no-modal + open={open} + onSlAfterHide={() => setOpen(false)} + style={{ '--size': '50%' }} + > + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + </div> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ Use the --size
custom property to set the drawer’s size. This will be applied to the drawer’s
+ width or height depending on its placement
.
+
+<sl-drawer label="Drawer" class="drawer-custom-size" style="--size: 50vw;"> + This drawer is always 50% of the viewport. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-custom-size'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)} style={{ '--size': '50vw' }}> + This drawer is always 50% of the viewport. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ By design, a drawer’s height will never exceed 100% of its container. As such, drawers will not scroll with + the page to ensure the header and footer are always accessible to the user. +
+ +Scroll down and give it a try! 👇
++<sl-drawer label="Drawer" class="drawer-scrolling"> + <div style="height: 150vh; border: dashed 2px var(--sl-color-neutral-200); padding: 0 1rem;"> + <p>Scroll down and give it a try! 👇</p> + </div> + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-scrolling'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}> + <div + style={{ + height: '150vh', + border: 'dashed 2px var(--sl-color-neutral-200)', + padding: '0 1rem' + }} + > + <p>Scroll down and give it a try! 👇</p> + </div> + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ The header shows a functional close button by default. You can use the header-actions
slot to
+ add additional icon buttons if needed.
+
+<sl-drawer label="Drawer" class="drawer-header-actions"> + <sl-icon-button class="new-window" slot="header-actions" name="box-arrow-up-right"></sl-icon-button> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-header-actions'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + const newWindowButton = drawer.querySelector('.new-window'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); + newWindowButton.addEventListener('click', () => window.open(location.href)); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; +import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}> + <SlIconButton slot="header-actions" name="box-arrow-up-right" onClick={() => window.open(location.href)} /> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the + Escape key. In most cases, the default behavior is the best behavior in terms of UX. However, + there are situations where this may be undesirable, such as when data loss will occur. +
+
+ To keep the drawer open in such cases, you can cancel the sl-request-close
event. When
+ canceled, the drawer will remain open and pulse briefly to draw the user’s attention to it.
+
+ You can use event.detail.source
to determine what triggered the request to close. This example
+ prevents the drawer from closing when the overlay is clicked, but allows the close button or
+ Escape to dismiss it.
+
+<sl-drawer label="Drawer" class="drawer-deny-close"> + This drawer will not close when you click on the overlay. + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-deny-close'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); + + // Prevent the drawer from closing when the user clicks on the overlay + drawer.addEventListener('sl-request-close', event => { + if (event.detail.source === 'overlay') { + event.preventDefault(); + } + }); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; + +const App = () => { + const [open, setOpen] = useState(false); + + // Prevent the drawer from closing when the user clicks on the overlay + function handleRequestClose(event) { + if (event.detail.source === 'overlay') { + event.preventDefault(); + } + } + + return ( + <> + <SlDrawer label="Drawer" open={open} onSlRequestClose={handleRequestClose} onSlAfterHide={() => setOpen(false)}> + This drawer will not close when you click on the overlay. + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Save & Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ By default, the drawer’s panel will gain focus when opened. This allows a subsequent tab press to focus on
+ the first tabbable element in the drawer. If you want a different element to have focus, add the
+ autofocus
attribute to it as shown below.
+
+<sl-drawer label="Drawer" class="drawer-focus"> + <sl-input autofocus placeholder="I will have focus when the drawer is opened"></sl-input> + <sl-button slot="footer" variant="primary">Close</sl-button> +</sl-drawer> + +<sl-button>Open Drawer</sl-button> + +<script> + const drawer = document.querySelector('.drawer-focus'); + const input = drawer.querySelector('sl-input'); + const openButton = drawer.nextElementSibling; + const closeButton = drawer.querySelector('sl-button[variant="primary"]'); + + openButton.addEventListener('click', () => drawer.show()); + closeButton.addEventListener('click', () => drawer.hide()); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDrawer from '@onsonr/nebula/dist/react/drawer'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlDrawer label="Drawer" open={open} onSlAfterHide={() => setOpen(false)}> + <SlInput autofocus placeholder="I will have focus when the drawer is opened" /> + <SlButton slot="footer" variant="primary" onClick={() => setOpen(false)}> + Close + </SlButton> + </SlDrawer> + + <SlButton onClick={() => setOpen(true)}>Open Drawer</SlButton> + </> + ); +}; +
+ You can further customize initial focus behavior by canceling the sl-initial-focus
event and
+ setting focus yourself inside the event handler.
+
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/drawer/drawer.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/drawer/drawer.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/drawer/drawer.js';
To import this component as a React component:
++import SlDrawer from '@onsonr/nebula/dist/react/drawer';
Name | +Description | +
---|---|
(default) | +The drawer’s main content. | +
+ label
+ |
+ The drawer’s label. Alternatively, you can use the label attribute. |
+
+ header-actions
+ |
+ Optional actions to add to the header. Works best with <sl-icon-button> . |
+
+ footer
+ |
+ The drawer’s footer, usually one or more buttons representing various options. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ modal
+ |
+
+ Exposes the internal modal utility that controls focus trapping. To temporarily disable focus
+ trapping and allow third-party modals spawned from an active Shoelace modal, call
+ modal.activateExternal() when the third-party modal opens. Upon closing, call
+ modal.deactivateExternal() to restore Shoelace’s focus trapping.
+ |
+ + | - | +
+ new Modal(this)
+ |
+
+ open
+ |
+
+ Indicates whether or not the drawer is open. You can toggle this attribute to show and hide the
+ drawer, or you can use the show() and hide() methods and this attribute
+ will reflect the drawer’s open state.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ label
+ |
+
+ The drawer’s label as displayed in the header. You should always include a relevant label even when
+ using
+ no-header , as it is required for proper accessibility. If you need to display HTML, use
+ the label slot instead.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ placement
+ |
+ The direction from which the drawer will open. | +
+ |
+
+ 'top' | 'end' | 'bottom' | 'start'
+ |
+
+ 'end'
+ |
+
+ contained
+ |
+
+ By default, the drawer slides out of its containing block (usually the viewport). To make the drawer
+ slide out of its parent element, set this attribute and add position: relative to the
+ parent.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ noHeader
+
+ + no-header
+
+ |
+ + Removes the header. This will also remove the default close button, so please ensure you provide an + easy, accessible way for users to dismiss the drawer. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-show |
+ onSlShow |
+ Emitted when the drawer opens. | +- | +
sl-after-show |
+ onSlAfterShow |
+ Emitted after the drawer opens and all animations are complete. | +- | +
sl-hide |
+ onSlHide |
+ Emitted when the drawer closes. | +- | +
sl-after-hide |
+ onSlAfterHide |
+ Emitted after the drawer closes and all animations are complete. | +- | +
sl-initial-focus |
+ onSlInitialFocus |
+
+ Emitted when the drawer opens and is ready to receive focus. Calling
+ event.preventDefault() will prevent focusing and allow you to set it on a different
+ element, such as an input.
+ |
+ - | +
sl-request-close |
+ onSlRequestClose |
+
+ Emitted when the user attempts to close the drawer by clicking the close button, clicking the
+ overlay, or pressing escape. Calling event.preventDefault() will keep the drawer open.
+ Avoid using this unless closing the drawer will result in destructive behavior such as data loss.
+ |
+
+ { source: 'close-button' | 'keyboard' | 'overlay' }
+ |
+
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the drawer. | +- | +
hide() |
+ Hides the drawer | +- | +
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--size |
+
+ The preferred size of the drawer. This will be applied to the drawer’s width or height depending on
+ its placement . Note that the drawer will shrink to accommodate smaller screens.
+ |
+ + |
--header-spacing |
+ The amount of padding to use for the header. | ++ |
--body-spacing |
+ The amount of padding to use for the body. | ++ |
--footer-spacing |
+ The amount of padding to use for the footer. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
overlay |
+ The overlay that covers the screen behind the drawer. | +
panel |
+ The drawer’s panel (where the drawer and its content are rendered). | +
header |
+ The drawer’s header. This element wraps the title and header actions. | +
header-actions |
+ Optional actions to add to the header. Works best with <sl-icon-button> . |
+
title |
+ The drawer’s title. | +
close-button |
+ The close button, an <sl-icon-button> . |
+
close-button__base |
+ The close button’s exported base part. |
+
body |
+ The drawer’s body. | +
footer |
+ The drawer’s footer. | +
+ Learn more about customizing CSS parts. +
+ +Name | +Description | +
---|---|
drawer.showTop |
+ The animation to use when showing a drawer with top placement. |
+
drawer.showEnd |
+ The animation to use when showing a drawer with end placement. |
+
drawer.showBottom |
+ The animation to use when showing a drawer with bottom placement. |
+
drawer.showStart |
+ The animation to use when showing a drawer with start placement. |
+
drawer.hideTop |
+ The animation to use when hiding a drawer with top placement. |
+
drawer.hideEnd |
+ The animation to use when hiding a drawer with end placement. |
+
drawer.hideBottom |
+ The animation to use when hiding a drawer with bottom placement. |
+
drawer.hideStart |
+ The animation to use when hiding a drawer with start placement. |
+
drawer.denyClose |
+ The animation to use when a request to close the drawer is denied. | +
drawer.overlay.show |
+ The animation to use when showing the drawer’s overlay. | +
drawer.overlay.hide |
+ The animation to use when hiding the drawer’s overlay. | +
+ Learn more about customizing animations. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon-button>
<sl-dropdown> | SlDropdown
+ Dropdowns expose additional content that “drops down” in a panel.
+ ++ Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and + interacting outside of the panel will close it. +
++ Dropdowns are designed to work well with menus to provide a list of options + the user can select from. However, dropdowns can also be used in lower-level applications (e.g. + color picker). The API gives you complete control over showing, + hiding, and positioning the panel. +
+ ++<sl-dropdown> + <sl-button slot="trigger" caret>Dropdown</sl-button> + <sl-menu> + <sl-menu-item>Dropdown Item 1</sl-menu-item> + <sl-menu-item>Dropdown Item 2</sl-menu-item> + <sl-menu-item>Dropdown Item 3</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item> + <sl-menu-item disabled>Disabled</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item> + Prefix + <sl-icon slot="prefix" name="gift"></sl-icon> + </sl-menu-item> + <sl-menu-item> + Suffix Icon + <sl-icon slot="suffix" name="heart"></sl-icon> + </sl-menu-item> + </sl-menu> +</sl-dropdown> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlDropdown> + <SlButton slot="trigger" caret> + Dropdown + </SlButton> + <SlMenu> + <SlMenuItem>Dropdown Item 1</SlMenuItem> + <SlMenuItem>Dropdown Item 2</SlMenuItem> + <SlMenuItem>Dropdown Item 3</SlMenuItem> + <SlDivider /> + <SlMenuItem type="checkbox" checked> + Checkbox + </SlMenuItem> + <SlMenuItem disabled>Disabled</SlMenuItem> + <SlDivider /> + <SlMenuItem> + Prefix + <SlIcon slot="prefix" name="gift" /> + </SlMenuItem> + <SlMenuItem> + Suffix Icon + <SlIcon slot="suffix" name="heart" /> + </SlMenuItem> + </SlMenu> + </SlDropdown> +); +
+ When dropdowns are used with menus, you can listen for the
+ sl-select
event to determine which menu item was
+ selected. The menu item element will be exposed in event.detail.item
. You can set
+ value
props to make it easier to identify commands.
+
+<div class="dropdown-selection"> + <sl-dropdown> + <sl-button slot="trigger" caret>Edit</sl-button> + <sl-menu> + <sl-menu-item value="cut">Cut</sl-menu-item> + <sl-menu-item value="copy">Copy</sl-menu-item> + <sl-menu-item value="paste">Paste</sl-menu-item> + </sl-menu> + </sl-dropdown> +</div> + +<script> + const container = document.querySelector('.dropdown-selection'); + const dropdown = container.querySelector('sl-dropdown'); + + dropdown.addEventListener('sl-select', event => { + const selectedItem = event.detail.item; + console.log(selectedItem.value); + }); +</script> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => { + function handleSelect(event) { + const selectedItem = event.detail.item; + console.log(selectedItem.value); + } + + return ( + <SlDropdown> + <SlButton slot="trigger" caret> + Edit + </SlButton> + <SlMenu onSlSelect={handleSelect}> + <SlMenuItem value="cut">Cut</SlMenuItem> + <SlMenuItem value="copy">Copy</SlMenuItem> + <SlMenuItem value="paste">Paste</SlMenuItem> + </SlMenu> + </SlDropdown> + ); +}; +
+ Alternatively, you can listen for the click
event on individual menu items. Note that, using
+ this approach, disabled menu items will still emit a click
event.
+
+<div class="dropdown-selection-alt"> + <sl-dropdown> + <sl-button slot="trigger" caret>Edit</sl-button> + <sl-menu> + <sl-menu-item value="cut">Cut</sl-menu-item> + <sl-menu-item value="copy">Copy</sl-menu-item> + <sl-menu-item value="paste">Paste</sl-menu-item> + </sl-menu> + </sl-dropdown> +</div> + +<script> + const container = document.querySelector('.dropdown-selection-alt'); + const cut = container.querySelector('sl-menu-item[value="cut"]'); + const copy = container.querySelector('sl-menu-item[value="copy"]'); + const paste = container.querySelector('sl-menu-item[value="paste"]'); + + cut.addEventListener('click', () => console.log('cut')); + copy.addEventListener('click', () => console.log('copy')); + paste.addEventListener('click', () => console.log('paste')); +</script> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => { + function handleCut() { + console.log('cut'); + } + + function handleCopy() { + console.log('copy'); + } + + function handlePaste() { + console.log('paste'); + } + + return ( + <SlDropdown> + <SlButton slot="trigger" caret> + Edit + </SlButton> + <SlMenu> + <SlMenuItem onClick={handleCut}>Cut</SlMenuItem> + <SlMenuItem onClick={handleCopy}>Copy</SlMenuItem> + <SlMenuItem onClick={handlePaste}>Paste</SlMenuItem> + </SlMenu> + </SlDropdown> + ); +}; +
+ The preferred placement of the dropdown can be set with the placement
attribute. Note that the
+ actual position may vary to ensure the panel remains in the viewport.
+
+<sl-dropdown placement="top-start"> + <sl-button slot="trigger" caret>Edit</sl-button> + <sl-menu> + <sl-menu-item>Cut</sl-menu-item> + <sl-menu-item>Copy</sl-menu-item> + <sl-menu-item>Paste</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item>Find</sl-menu-item> + <sl-menu-item>Replace</sl-menu-item> + </sl-menu> +</sl-dropdown> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlDropdown placement="top-start"> + <SlButton slot="trigger" caret> + Edit + </SlButton> + <SlMenu> + <SlMenuItem>Cut</SlMenuItem> + <SlMenuItem>Copy</SlMenuItem> + <SlMenuItem>Paste</SlMenuItem> + <SlDivider /> + <SlMenuItem>Find</SlMenuItem> + <SlMenuItem>Replace</SlMenuItem> + </SlMenu> + </SlDropdown> +); +
+ The distance from the panel to the trigger can be customized using the distance
attribute. This
+ value is specified in pixels.
+
+<sl-dropdown distance="30"> + <sl-button slot="trigger" caret>Edit</sl-button> + <sl-menu> + <sl-menu-item>Cut</sl-menu-item> + <sl-menu-item>Copy</sl-menu-item> + <sl-menu-item>Paste</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item>Find</sl-menu-item> + <sl-menu-item>Replace</sl-menu-item> + </sl-menu> +</sl-dropdown> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlDropdown distance={30}> + <SlButton slot="trigger" caret> + Edit + </SlButton> + <SlMenu> + <SlMenuItem>Cut</SlMenuItem> + <SlMenuItem>Copy</SlMenuItem> + <SlMenuItem>Paste</SlMenuItem> + <SlDivider /> + <SlMenuItem>Find</SlMenuItem> + <SlMenuItem>Replace</SlMenuItem> + </SlMenu> + </SlDropdown> +); +
+ The offset of the panel along the trigger can be customized using the skidding
attribute. This
+ value is specified in pixels.
+
+<sl-dropdown skidding="30"> + <sl-button slot="trigger" caret>Edit</sl-button> + <sl-menu> + <sl-menu-item>Cut</sl-menu-item> + <sl-menu-item>Copy</sl-menu-item> + <sl-menu-item>Paste</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item>Find</sl-menu-item> + <sl-menu-item>Replace</sl-menu-item> + </sl-menu> +</sl-dropdown> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlDropdown skidding={30}> + <SlButton slot="trigger" caret> + Edit + </SlButton> + <SlMenu> + <SlMenuItem>Cut</SlMenuItem> + <SlMenuItem>Copy</SlMenuItem> + <SlMenuItem>Paste</SlMenuItem> + <SlDivider /> + <SlMenuItem>Find</SlMenuItem> + <SlMenuItem>Replace</SlMenuItem> + </SlMenu> + </SlDropdown> +); +
+ To create a submenu, nest an <sl-menu slot="submenu">
element in a
+ menu item.
+
+<sl-dropdown> + <sl-button slot="trigger" caret>Edit</sl-button> + + <sl-menu style="max-width: 200px;"> + <sl-menu-item value="undo">Undo</sl-menu-item> + <sl-menu-item value="redo">Redo</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item value="cut">Cut</sl-menu-item> + <sl-menu-item value="copy">Copy</sl-menu-item> + <sl-menu-item value="paste">Paste</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item> + Find + <sl-menu slot="submenu"> + <sl-menu-item value="find">Find…</sl-menu-item> + <sl-menu-item value="find-previous">Find Next</sl-menu-item> + <sl-menu-item value="find-next">Find Previous</sl-menu-item> + </sl-menu> + </sl-menu-item> + <sl-menu-item> + Transformations + <sl-menu slot="submenu"> + <sl-menu-item value="uppercase">Make uppercase</sl-menu-item> + <sl-menu-item value="lowercase">Make lowercase</sl-menu-item> + <sl-menu-item value="capitalize">Capitalize</sl-menu-item> + </sl-menu> + </sl-menu-item> + </sl-menu> +</sl-dropdown> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const css = ` + .dropdown-hoist { + border: solid 2px var(--sl-panel-border-color); + padding: var(--sl-spacing-medium); + overflow: hidden; + } +`; + +const App = () => ( + <> + <SlDropdown> + <SlButton slot="trigger" caret>Edit</SlButton> + + <SlMenu style="max-width: 200px;"> + <SlMenuItem value="undo">Undo</SlMenuItem> + <SlMenuItem value="redo">Redo</SlMenuItem> + <SlDivider /> + <SlMenuItem value="cut">Cut</SlMenuItem> + <SlMenuItem value="copy">Copy</SlMenuItem> + <SlMenuItem value="paste">Paste</SlMenuItem> + <SlDivider /> + <SlMenuItem> + Find + <SlMenu slot="submenu"> + <SlMenuItem value="find">Find…</SlMenuItem> + <SlMenuItem value="find-previous">Find Next</SlMenuItem> + <SlMenuItem value="find-next">Find Previous</SlMenuItem> + </SlMenu> + </SlMenuItem> + <SlMenuItem> + Transformations + <SlMenu slot="submenu"> + <SlMenuItem value="uppercase">Make uppercase</SlMenuItem> + <SlMenuItem value="lowercase">Make lowercase</SlMenuItem> + <SlMenuItem value="capitalize">Capitalize</SlMenuItem> + </SlMenu> + </SlMenuItem> + </SlMenu> + </SlDropdown> + </> +); +
As a UX best practice, avoid using more than one level of submenu when possible.
+
+ Dropdown panels will be clipped if they’re inside a container that has overflow: auto|hidden
.
+ The hoist
attribute forces the panel to use a fixed positioning strategy, allowing it to break
+ out of the container. In this case, the panel will be positioned relative to its
+ containing block, which is usually the viewport unless an ancestor uses a transform
, perspective
,
+ or filter
.
+ Refer to this page
+ for more details.
+
+<div class="dropdown-hoist"> + <sl-dropdown> + <sl-button slot="trigger" caret>No Hoist</sl-button> + <sl-menu> + <sl-menu-item>Item 1</sl-menu-item> + <sl-menu-item>Item 2</sl-menu-item> + <sl-menu-item>Item 3</sl-menu-item> + </sl-menu> + </sl-dropdown> + + <sl-dropdown hoist> + <sl-button slot="trigger" caret>Hoist</sl-button> + <sl-menu> + <sl-menu-item>Item 1</sl-menu-item> + <sl-menu-item>Item 2</sl-menu-item> + <sl-menu-item>Item 3</sl-menu-item> + </sl-menu> + </sl-dropdown> +</div> + +<style> + .dropdown-hoist { + position: relative; + border: solid 2px var(--sl-panel-border-color); + padding: var(--sl-spacing-medium); + overflow: hidden; + } +</style> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const css = ` + .dropdown-hoist { + border: solid 2px var(--sl-panel-border-color); + padding: var(--sl-spacing-medium); + overflow: hidden; + } +`; + +const App = () => ( + <> + <div className="dropdown-hoist"> + <SlDropdown> + <SlButton slot="trigger" caret> + No Hoist + </SlButton> + <SlMenu> + <SlMenuItem>Item 1</SlMenuItem> + <SlMenuItem>Item 2</SlMenuItem> + <SlMenuItem>Item 3</SlMenuItem> + </SlMenu> + </SlDropdown> + + <SlDropdown hoist> + <SlButton slot="trigger" caret> + Hoist + </SlButton> + <SlMenu> + <SlMenuItem>Item 1</SlMenuItem> + <SlMenuItem>Item 2</SlMenuItem> + <SlMenuItem>Item 3</SlMenuItem> + </SlMenu> + </SlDropdown> + </div> + + <style>{css}</style> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/dropdown/dropdown.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/dropdown/dropdown.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/dropdown/dropdown.js';
To import this component as a React component:
++import SlDropdown from '@onsonr/nebula/dist/react/dropdown';
Name | +Description | +
---|---|
(default) | +The dropdown’s main content. | +
+ trigger
+ |
+ The dropdown’s trigger, usually a <sl-button> element. |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ open
+ |
+
+ Indicates whether or not the dropdown is open. You can toggle this attribute to show and hide the
+ dropdown, or you can use the show() and hide() methods and this attribute
+ will reflect the dropdown’s open state.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ placement
+ |
+ + The preferred placement of the dropdown panel. Note that the actual placement may vary as needed to + keep the panel inside of the viewport. + | +
+ |
+
+
+ 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'right' |
+ 'right-start' | 'right-end' | 'left' | 'left-start' | 'left-end'
+ |
+
+ 'bottom-start'
+ |
+
+ disabled
+ |
+ Disables the dropdown so the panel will not open. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ stayOpenOnSelect
+
+ + stay-open-on-select
+
+ |
+ + By default, the dropdown is closed when an item is selected. This attribute will keep it open + instead. Useful for dropdowns that allow for multiple interactions. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ containingElement
+ |
+ + The dropdown will close when the user interacts outside of this element (e.g. clicking). Useful for + composing other components that use a dropdown internally. + | ++ |
+ HTMLElement | undefined
+ |
+ - | +
+ distance
+ |
+ The distance in pixels from which to offset the panel away from its trigger. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ skidding
+ |
+ The distance in pixels from which to offset the panel along its trigger. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ hoist
+ |
+
+ Enable this option to prevent the panel from being clipped when the component is placed inside a
+ container with
+ overflow: auto|scroll . Hoisting uses a fixed positioning strategy that works in many,
+ but not all, scenarios.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
+ sync
+ |
+ Syncs the popup width or height to that of the trigger element. | +
+ |
+
+ 'width' | 'height' | 'both' | undefined
+ |
+
+ undefined
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-show |
+ onSlShow |
+ Emitted when the dropdown opens. | +- | +
sl-after-show |
+ onSlAfterShow |
+ Emitted after the dropdown opens and all animations are complete. | +- | +
sl-hide |
+ onSlHide |
+ Emitted when the dropdown closes. | +- | +
sl-after-hide |
+ onSlAfterHide |
+ Emitted after the dropdown closes and all animations are complete. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the dropdown panel. | +- | +
hide() |
+ Hides the dropdown panel | +- | +
reposition() |
+ + Instructs the dropdown menu to reposition. Useful when the position or size of the trigger changes + when the menu is activated. + | +- | +
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper, an <sl-popup> element. |
+
base__popup |
+
+ The popup’s exported popup part. Use this to target the tooltip’s popup container.
+ |
+
trigger |
+ The container that wraps the trigger. | +
panel |
+ The panel that gets shown when the dropdown is open. | +
+ Learn more about customizing CSS parts. +
+ +Name | +Description | +
---|---|
dropdown.show |
+ The animation to use when showing the dropdown. | +
dropdown.hide |
+ The animation to use when hiding the dropdown. | +
+ Learn more about customizing animations. +
+ +This component automatically imports the following dependencies.
+ +<sl-popup>
<sl-format-bytes> | SlFormatBytes
+ Formats a number as a human readable bytes value.
+ ++<div class="format-bytes-overview"> + The file is <sl-format-bytes value="1000"></sl-format-bytes> in size. <br /><br /> + <sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input> +</div> + +<script> + const container = document.querySelector('.format-bytes-overview'); + const formatter = container.querySelector('sl-format-bytes'); + const input = container.querySelector('sl-input'); + + input.addEventListener('sl-input', () => (formatter.value = input.value || 0)); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlFormatBytes from '@onsonr/nebula/dist/react/format-bytes'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => { + const [value, setValue] = useState(1000); + + return ( + <> + The file is <SlFormatBytes value={value} /> in size. + <br /> + <br /> + <SlInput + type="number" + value={value} + label="Number to Format" + style={{ maxWidth: '180px' }} + onSlInput={event => setValue(event.target.value)} + /> + </> + ); +}; +
Set the value
attribute to a number to get the value in bytes.
+<sl-format-bytes value="12"></sl-format-bytes><br /> +<sl-format-bytes value="1200"></sl-format-bytes><br /> +<sl-format-bytes value="1200000"></sl-format-bytes><br /> +<sl-format-bytes value="1200000000"></sl-format-bytes> +
+import SlFormatBytes from '@onsonr/nebula/dist/react/format-bytes'; + +const App = () => ( + <> + <SlFormatBytes value="12" /> + <br /> + <SlFormatBytes value="1200" /> + <br /> + <SlFormatBytes value="1200000" /> + <br /> + <SlFormatBytes value="1200000000" /> + </> +); +
To get the value in bits, set the unit
attribute to bit
.
+<sl-format-bytes value="12" unit="bit"></sl-format-bytes><br /> +<sl-format-bytes value="1200" unit="bit"></sl-format-bytes><br /> +<sl-format-bytes value="1200000" unit="bit"></sl-format-bytes><br /> +<sl-format-bytes value="1200000000" unit="bit"></sl-format-bytes> +
+import SlFormatBytes from '@onsonr/nebula/dist/react/format-bytes'; + +const App = () => ( + <> + <SlFormatBytes value="12" unit="bit" /> + <br /> + <SlFormatBytes value="1200" unit="bit" /> + <br /> + <SlFormatBytes value="1200000" unit="bit" /> + <br /> + <SlFormatBytes value="1200000000" unit="bit" /> + </> +); +
Use the lang
attribute to set the number formatting locale.
+<sl-format-bytes value="12" lang="de"></sl-format-bytes><br /> +<sl-format-bytes value="1200" lang="de"></sl-format-bytes><br /> +<sl-format-bytes value="1200000" lang="de"></sl-format-bytes><br /> +<sl-format-bytes value="1200000000" lang="de"></sl-format-bytes> +
+import SlFormatBytes from '@onsonr/nebula/dist/react/format-bytes'; + +const App = () => ( + <> + <SlFormatBytes value="12" lang="de" /> + <br /> + <SlFormatBytes value="1200" lang="de" /> + <br /> + <SlFormatBytes value="1200000" lang="de" /> + <br /> + <SlFormatBytes value="1200000000" lang="de" /> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/format-bytes/format-bytes.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/format-bytes/format-bytes.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/format-bytes/format-bytes.js';
To import this component as a React component:
++import SlFormatBytes from '@onsonr/nebula/dist/react/format-bytes';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The number to format in bytes. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ unit
+ |
+ The type of unit to display. | ++ |
+ 'byte' | 'bit'
+ |
+
+ 'byte'
+ |
+
+ display
+ |
+ Determines how to display the result, e.g. “100 bytes”, “100 b”, or “100b”. | ++ |
+ 'long' | 'short' | 'narrow'
+ |
+
+ 'short'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+<sl-format-date> | SlFormatDate
+ Formats a date/time using the specified locale and options.
+ +
+ Localization is handled by the browser’s
+ Intl.DateTimeFormat
API. No language packs are required.
+
+<!-- Nebula 2 release date 🎉 --> +<sl-format-date date="2020-07-15T09:17:00-04:00"></sl-format-date> +
+import SlFormatDate from '@onsonr/nebula/dist/react/format-date'; + +const App = () => <SlFormatDate date="2020-07-15T09:17:00-04:00" />; +
+ The date
attribute determines the date/time to use when formatting. It must be a string that
+ Date.parse()
+ can interpret or a
+ Date
+ object set via JavaScript. If omitted, the current date/time will be assumed.
+
+ When using strings, avoid ambiguous dates such as 03/04/2020
which can be interpreted as
+ March 4 or April 3 depending on the user’s browser and locale. Instead, always use a valid
+ ISO 8601 date time string
+ to ensure the date will be parsed properly by all clients.
+
+ Formatting options are based on those found in the
+ Intl.DateTimeFormat
API. When formatting options are provided, the date/time will be formatted according to those values. When no
+ formatting options are provided, a localized, numeric date will be displayed instead.
+
+<!-- Human-readable date --> +<sl-format-date month="long" day="numeric" year="numeric"></sl-format-date><br /> + +<!-- Time --> +<sl-format-date hour="numeric" minute="numeric"></sl-format-date><br /> + +<!-- Weekday --> +<sl-format-date weekday="long"></sl-format-date><br /> + +<!-- Month --> +<sl-format-date month="long"></sl-format-date><br /> + +<!-- Year --> +<sl-format-date year="numeric"></sl-format-date><br /> + +<!-- No formatting options --> +<sl-format-date></sl-format-date> +
+import SlFormatDate from '@onsonr/nebula/dist/react/format-date'; + +const App = () => ( + <> + {/* Human-readable date */} + <SlFormatDate month="long" day="numeric" year="numeric" /> + <br /> + + {/* Time */} + <SlFormatDate hour="numeric" minute="numeric" /> + <br /> + + {/* Weekday */} + <SlFormatDate weekday="long" /> + <br /> + + {/* Month */} + <SlFormatDate month="long" /> + <br /> + + {/* Year */} + <SlFormatDate year="numeric" /> + <br /> + + {/* No formatting options */} + <SlFormatDate /> + </> +); +
+ By default, the browser will determine whether to use 12-hour or 24-hour time. To force one or the other,
+ set the hour-format
attribute to 12
or 24
.
+
+<sl-format-date hour="numeric" minute="numeric" hour-format="12"></sl-format-date><br /> +<sl-format-date hour="numeric" minute="numeric" hour-format="24"></sl-format-date> +
+import SlFormatDate from '@onsonr/nebula/dist/react/format-date'; + +const App = () => ( + <> + <SlFormatDate hour="numeric" minute="numeric" hour-format="12" /> + <br /> + <SlFormatDate hour="numeric" minute="numeric" hour-format="24" /> + </> +); +
Use the lang
attribute to set the date/time formatting locale.
+English: <sl-format-date lang="en"></sl-format-date><br /> +French: <sl-format-date lang="fr"></sl-format-date><br /> +Russian: <sl-format-date lang="ru"></sl-format-date> +
+import SlFormatDate from '@onsonr/nebula/dist/react/format-date'; + +const App = () => ( + <> + English: <SlFormatDate lang="en" /> + <br /> + French: <SlFormatDate lang="fr" /> + <br /> + Russian: <SlFormatDate lang="ru" /> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/format-date/format-date.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/format-date/format-date.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/format-date/format-date.js';
To import this component as a React component:
++import SlFormatDate from '@onsonr/nebula/dist/react/format-date';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ date
+ |
+
+ The date/time to format. If not set, the current date and time will be used. When passing a string,
+ it’s strongly recommended to use the ISO 8601 format to ensure timezones are handled correctly. To
+ convert a date to this format in JavaScript, use
+ date.toISOString() .
+ |
+ + |
+ Date | string
+ |
+
+ new Date()
+ |
+
+ weekday
+ |
+ The format for displaying the weekday. | ++ |
+ 'narrow' | 'short' | 'long'
+ |
+ - | +
+ era
+ |
+ The format for displaying the era. | ++ |
+ 'narrow' | 'short' | 'long'
+ |
+ - | +
+ year
+ |
+ The format for displaying the year. | ++ |
+ 'numeric' | '2-digit'
+ |
+ - | +
+ month
+ |
+ The format for displaying the month. | ++ |
+ 'numeric' | '2-digit' | 'narrow' | 'short' | 'long'
+ |
+ - | +
+ day
+ |
+ The format for displaying the day. | ++ |
+ 'numeric' | '2-digit'
+ |
+ - | +
+ hour
+ |
+ The format for displaying the hour. | ++ |
+ 'numeric' | '2-digit'
+ |
+ - | +
+ minute
+ |
+ The format for displaying the minute. | ++ |
+ 'numeric' | '2-digit'
+ |
+ - | +
+ second
+ |
+ The format for displaying the second. | ++ |
+ 'numeric' | '2-digit'
+ |
+ - | +
+ timeZoneName
+
+ + time-zone-name
+
+ |
+ The format for displaying the time. | ++ |
+ 'short' | 'long'
+ |
+ - | +
+ timeZone
+
+ + time-zone
+
+ |
+ The time zone to express the time in. | ++ |
+ string
+ |
+ - | +
+ hourFormat
+
+ + hour-format
+
+ |
+ The format for displaying the hour. | ++ |
+ 'auto' | '12' | '24'
+ |
+
+ 'auto'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+<sl-format-number> | SlFormatNumber
+ Formats a number using the specified locale and options.
+ +
+ Localization is handled by the browser’s
+ Intl.NumberFormat
API. No language packs are required.
+
+<div class="format-number-overview"> + <sl-format-number value="1000"></sl-format-number> + <br /><br /> + <sl-input type="number" value="1000" label="Number to Format" style="max-width: 180px;"></sl-input> +</div> + +<script> + const container = document.querySelector('.format-number-overview'); + const formatter = container.querySelector('sl-format-number'); + const input = container.querySelector('sl-input'); + + input.addEventListener('sl-input', () => (formatter.value = input.value || 0)); +</script> +
+import { useState } from 'react'; +import SlFormatNumber from '@onsonr/nebula/dist/react/format-number'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => { + const [value, setValue] = useState(1000); + + return ( + <> + <SlFormatNumber value={value} /> + <br /> + <br /> + <SlInput + type="number" + value={value} + label="Number to Format" + style={{ maxWidth: '180px' }} + onSlInput={event => setValue(event.target.value)} + /> + </> + ); +}; +
To get the value as a percent, set the type
attribute to percent
.
+<sl-format-number type="percent" value="0"></sl-format-number><br /> +<sl-format-number type="percent" value="0.25"></sl-format-number><br /> +<sl-format-number type="percent" value="0.50"></sl-format-number><br /> +<sl-format-number type="percent" value="0.75"></sl-format-number><br /> +<sl-format-number type="percent" value="1"></sl-format-number> +
+import SlFormatNumber from '@onsonr/nebula/dist/react/format-number'; + +const App = () => ( + <> + <SlFormatNumber type="percent" value={0} /> + <br /> + <SlFormatNumber type="percent" value={0.25} /> + <br /> + <SlFormatNumber type="percent" value={0.5} /> + <br /> + <SlFormatNumber type="percent" value={0.75} /> + <br /> + <SlFormatNumber type="percent" value={1} /> + </> +); +
Use the lang
attribute to set the number formatting locale.
+English: <sl-format-number value="2000" lang="en" minimum-fraction-digits="2"></sl-format-number><br /> +German: <sl-format-number value="2000" lang="de" minimum-fraction-digits="2"></sl-format-number><br /> +Russian: <sl-format-number value="2000" lang="ru" minimum-fraction-digits="2"></sl-format-number> +
+import SlFormatNumber from '@onsonr/nebula/dist/react/format-number'; + +const App = () => ( + <> + English: <SlFormatNumber value="2000" lang="en" minimum-fraction-digits="2" /> + <br /> + German: <SlFormatNumber value="2000" lang="de" minimum-fraction-digits="2" /> + <br /> + Russian: <SlFormatNumber value="2000" lang="ru" minimum-fraction-digits="2" /> + </> +); +
+ To format a number as a monetary value, set the type
attribute to currency
and set
+ the currency
attribute to the desired ISO 4217 currency code. You should also specify
+ lang
to ensure the the number is formatted correctly for the target locale.
+
+<sl-format-number type="currency" currency="USD" value="2000" lang="en-US"></sl-format-number><br /> +<sl-format-number type="currency" currency="GBP" value="2000" lang="en-GB"></sl-format-number><br /> +<sl-format-number type="currency" currency="EUR" value="2000" lang="de"></sl-format-number><br /> +<sl-format-number type="currency" currency="RUB" value="2000" lang="ru"></sl-format-number><br /> +<sl-format-number type="currency" currency="CNY" value="2000" lang="zh-cn"></sl-format-number> +
+import SlFormatNumber from '@onsonr/nebula/dist/react/format-number'; + +const App = () => ( + <> + <SlFormatNumber type="currency" currency="USD" value="2000" lang="en-US" /> + <br /> + <SlFormatNumber type="currency" currency="GBP" value="2000" lang="en-GB" /> + <br /> + <SlFormatNumber type="currency" currency="EUR" value="2000" lang="de" /> + <br /> + <SlFormatNumber type="currency" currency="RUB" value="2000" lang="ru" /> + <br /> + <SlFormatNumber type="currency" currency="CNY" value="2000" lang="zh-cn" /> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/format-number/format-number.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/format-number/format-number.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/format-number/format-number.js';
To import this component as a React component:
++import SlFormatNumber from '@onsonr/nebula/dist/react/format-number';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The number to format. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ type
+ |
+ The formatting style to use. | ++ |
+ 'currency' | 'decimal' | 'percent'
+ |
+
+ 'decimal'
+ |
+
+ noGrouping
+
+ + no-grouping
+
+ |
+ Turns off grouping separators. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ currency
+ |
+ + The + ISO 4217 + currency code to use when formatting. + | ++ |
+ string
+ |
+
+ 'USD'
+ |
+
+ currencyDisplay
+
+ + currency-display
+
+ |
+ How to display the currency. | ++ |
+ 'symbol' | 'narrowSymbol' | 'code' | 'name'
+ |
+
+ 'symbol'
+ |
+
+ minimumIntegerDigits
+
+ + minimum-integer-digits
+
+ |
+ The minimum number of integer digits to use. Possible values are 1–21. | ++ |
+ number
+ |
+ - | +
+ minimumFractionDigits
+
+ + minimum-fraction-digits
+
+ |
+ The minimum number of fraction digits to use. Possible values are 0–20. | ++ |
+ number
+ |
+ - | +
+ maximumFractionDigits
+
+ + maximum-fraction-digits
+
+ |
+ The maximum number of fraction digits to use. Possible values are 0–0. | ++ |
+ number
+ |
+ - | +
+ minimumSignificantDigits
+
+ + minimum-significant-digits
+
+ |
+ The minimum number of significant digits to use. Possible values are 1–21. | ++ |
+ number
+ |
+ - | +
+ maximumSignificantDigits
+
+ + maximum-significant-digits
+
+ |
+ The maximum number of significant digits to use,. Possible values are 1–21. | ++ |
+ number
+ |
+ - | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+<sl-icon-button> | SlIconButton
+ + Icons buttons are simple, icon-only buttons that can be used for actions and in toolbars. +
+ ++ For a full list of icons that come bundled with Nebula, refer to the + icon component. +
+ ++<sl-icon-button name="gear" label="Settings"></sl-icon-button> +
+import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const App = () => <SlIconButton name="gear" label="Settings" />; +
Icon buttons inherit their parent element’s font-size
.
+<sl-icon-button name="pencil" label="Edit" style="font-size: 1.5rem;"></sl-icon-button> +<sl-icon-button name="pencil" label="Edit" style="font-size: 2rem;"></sl-icon-button> +<sl-icon-button name="pencil" label="Edit" style="font-size: 2.5rem;"></sl-icon-button> +
+import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const App = () => ( + <> + <SlIconButton name="pencil" label="Edit" style={{ fontSize: '1.5rem' }} /> + <SlIconButton name="pencil" label="Edit" style={{ fontSize: '2rem' }} /> + <SlIconButton name="pencil" label="Edit" style={{ fontSize: '2.5rem' }} /> + </> +); +
+ Icon buttons are designed to have a uniform appearance, so their color is not inherited. However, you can
+ still customize them by styling the base
part.
+
+<div class="icon-button-color"> + <sl-icon-button name="type-bold" label="Bold"></sl-icon-button> + <sl-icon-button name="type-italic" label="Italic"></sl-icon-button> + <sl-icon-button name="type-underline" label="Underline"></sl-icon-button> +</div> + +<style> + .icon-button-color sl-icon-button::part(base) { + color: #b00091; + } + + .icon-button-color sl-icon-button::part(base):hover, + .icon-button-color sl-icon-button::part(base):focus { + color: #c913aa; + } + + .icon-button-color sl-icon-button::part(base):active { + color: #960077; + } +</style> +
+import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const css = ` + .icon-button-color sl-icon-button::part(base) { + color: #b00091; + } + + .icon-button-color sl-icon-button::part(base):hover, + .icon-button-color sl-icon-button::part(base):focus { + color: #c913aa; + } + + .icon-button-color sl-icon-button::part(base):active { + color: #960077; + } +`; + +const App = () => ( + <> + <div className="icon-button-color"> + <SlIconButton name="type-bold" label="Bold" /> + <SlIconButton name="type-italic" label="Italic" /> + <SlIconButton name="type-underline" label="Underline" /> + </div> + + <style>{css}</style> + </> +); +
Use the href
attribute to convert the button to a link.
+<sl-icon-button name="gear" label="Settings" href="https://example.com" target="_blank"></sl-icon-button> +
+import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const App = () => <SlIconButton name="gear" label="Settings" href="https://example.com" target="_blank" />; +
Wrap a tooltip around an icon button to provide contextual information to the user.
+ ++<sl-tooltip content="Settings"> + <sl-icon-button name="gear" label="Settings"></sl-icon-button> +</sl-tooltip> +
+import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => ( + <SlTooltip content="Settings"> + <SlIconButton name="gear" label="Settings" /> + </SlTooltip> +); +
Use the disabled
attribute to disable the icon button.
+<sl-icon-button name="gear" label="Settings" disabled></sl-icon-button> +
+import SlIconButton from '@onsonr/nebula/dist/react/icon-button'; + +const App = () => <SlIconButton name="gear" label="Settings" disabled />; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/icon-button/icon-button.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/icon-button/icon-button.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/icon-button/icon-button.js';
To import this component as a React component:
++import SlIconButton from '@onsonr/nebula/dist/react/icon-button';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The name of the icon to draw. Available names depend on the icon library being used. | ++ |
+ string | undefined
+ |
+ - | +
+ library
+ |
+ The name of a registered custom icon library. | ++ |
+ string | undefined
+ |
+ - | +
+ src
+ |
+ + An external URL of an SVG file. Be sure you trust the content you are including, as it will be + executed as code and can result in XSS attacks. + | ++ |
+ string | undefined
+ |
+ - | +
+ href
+ |
+
+ When set, the underlying button will be rendered as an <a> with this
+ href instead of a <button> .
+ |
+ + |
+ string | undefined
+ |
+ - | +
+ target
+ |
+ Tells the browser where to open the link. Only used when href is set. |
+ + |
+ '_blank' | '_parent' | '_self' | '_top' | undefined
+ |
+ - | +
+ download
+ |
+
+ Tells the browser to download the linked file as this filename. Only used when href is
+ set.
+ |
+ + |
+ string | undefined
+ |
+ - | +
+ label
+ |
+ + A description that gets read by assistive devices. For optimal accessibility, you should always + include a label that describes what the icon button does. + | ++ |
+ string
+ |
+
+ ''
+ |
+
+ disabled
+ |
+ Disables the button. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the icon button loses focus. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the icon button gains focus. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
click() |
+ Simulates a click on the icon button. | +- | +
focus() |
+ Sets focus on the icon button. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the icon button. | +- | +
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon> | SlIcon
+ + Icons are symbols that can be used to represent various options within an application. +
+ +
+ Nebula comes bundled with over 1,500 icons courtesy of the
+ Bootstrap Icons
+ project. These icons are part of the default
icon library. If you prefer, you can register
+ custom icon libraries as well.
+
+ Depending on how you’re loading Nebula, you may need to copy icon assets and/or + set the base path so Nebula knows where + to load them from. Otherwise, icons may not appear and you’ll see 404 Not Found errors in the dev console. +
+
+ All available icons in the default
icon library are shown below. Click or tap on any icon to
+ copy its name, then you can use it in your HTML like this.
+
+<sl-icon name="icon-name-here"></sl-icon> +
+ Icons inherit their color from the current text color. Thus, you can set the color
property on
+ the <sl-icon>
element or an ancestor to change the color.
+
+<div style="color: #4a90e2;"> + <sl-icon name="exclamation-triangle"></sl-icon> + <sl-icon name="archive"></sl-icon> + <sl-icon name="battery-charging"></sl-icon> + <sl-icon name="bell"></sl-icon> +</div> +<div style="color: #9013fe;"> + <sl-icon name="clock"></sl-icon> + <sl-icon name="cloud"></sl-icon> + <sl-icon name="download"></sl-icon> + <sl-icon name="file-earmark"></sl-icon> +</div> +<div style="color: #417505;"> + <sl-icon name="flag"></sl-icon> + <sl-icon name="heart"></sl-icon> + <sl-icon name="image"></sl-icon> + <sl-icon name="lightning"></sl-icon> +</div> +<div style="color: #f5a623;"> + <sl-icon name="mic"></sl-icon> + <sl-icon name="search"></sl-icon> + <sl-icon name="star"></sl-icon> + <sl-icon name="trash"></sl-icon> +</div> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <> + <div style={{ color: '#4a90e2' }}> + <SlIcon name="exclamation-triangle"></SlIcon> + <SlIcon name="archive"></SlIcon> + <SlIcon name="battery-charging"></SlIcon> + <SlIcon name="bell"></SlIcon> + </div> + <div style={{ color: '#9013fe' }}> + <SlIcon name="clock"></SlIcon> + <SlIcon name="cloud"></SlIcon> + <SlIcon name="download"></SlIcon> + <SlIcon name="file-earmark"></SlIcon> + </div> + <div style={{ color: '#417505' }}> + <SlIcon name="flag"></SlIcon> + <SlIcon name="heart"></SlIcon> + <SlIcon name="image"></SlIcon> + <SlIcon name="lightning"></SlIcon> + </div> + <div style={{ color: '#f5a623' }}> + <SlIcon name="mic"></SlIcon> + <SlIcon name="search"></SlIcon> + <SlIcon name="star"></SlIcon> + <SlIcon name="trash"></SlIcon> + </div> + </> +); +
+ Icons are sized relative to the current font size. To change their size, set the
+ font-size
property on the icon itself or on a parent element as shown below.
+
+<div style="font-size: 32px;"> + <sl-icon name="exclamation-triangle"></sl-icon> + <sl-icon name="archive"></sl-icon> + <sl-icon name="battery-charging"></sl-icon> + <sl-icon name="bell"></sl-icon> + <sl-icon name="clock"></sl-icon> + <sl-icon name="cloud"></sl-icon> + <sl-icon name="download"></sl-icon> + <sl-icon name="file-earmark"></sl-icon> + <sl-icon name="flag"></sl-icon> + <sl-icon name="heart"></sl-icon> + <sl-icon name="image"></sl-icon> + <sl-icon name="lightning"></sl-icon> + <sl-icon name="mic"></sl-icon> + <sl-icon name="search"></sl-icon> + <sl-icon name="star"></sl-icon> + <sl-icon name="trash"></sl-icon> +</div> +
For non-decorative icons, use the label
attribute to announce it to assistive devices.
+<sl-icon name="star-fill" label="Add to favorites"></sl-icon> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => <SlIcon name="star-fill" label="Add to favorites" />; +
+ You can register additional icons to use with the <sl-icon>
component through icon
+ libraries. Icon files can exist locally or on a CORS-enabled endpoint (e.g. a CDN). There is no limit to how
+ many icon libraries you can register and there is no cost associated with registering them, as individual
+ icons are only requested when they’re used.
+
+ Nebula ships with two built-in icon libraries, default
and system
. The
+ default icon library contains all of the icons in the
+ Bootstrap Icons project. The system icon library contains only
+ a small subset of icons that are used internally by Nebula components.
+
+ To register an additional icon library, use the registerIconLibrary()
function that’s exported
+ from utilities/icon-library.js
. At a minimum, you must provide a name and a resolver function.
+ The resolver function translates an icon name to a URL where the corresponding SVG file exists. Refer to the
+ examples below to better understand how it works.
+
+ If necessary, a mutator function can be used to mutate the SVG element before rendering. This is necessary
+ for some libraries due to the many possible ways SVGs are crafted. For example, icons should ideally inherit
+ the current text color via currentColor
, so you may need to apply
+ fill="currentColor
or stroke="currentColor"
to the SVG element using this
+ function.
+
+ The following examples demonstrate how to register a number of popular, open source icon libraries via CDN. + Feel free to adapt the code as you see fit to use your own origin or naming conventions. +
+Icons sourced from our internal style guide.
+++ +These are pre-packaged with Nebula and are not available for custom registration.
+
+<div style="font-size: 24px;"> + <sl-icon library="sonr" name="passkey"></sl-icon> + <sl-icon library="sonr" name="docs"></sl-icon> + <sl-icon library="sonr" name="social-github"></sl-icon> + <sl-icon library="sonr" name="social-twitter"></sl-icon> + <sl-icon library="sonr" name="social-youtube"></sl-icon> + <sl-icon library="sonr" name="error-blockchain"></sl-icon> + <sl-icon library="sonr" name="error-network"></sl-icon> + <sl-icon library="sonr" name="swap-coins"></sl-icon> + <sl-icon library="sonr" name="scanner-qr"></sl-icon> + <sl-icon library="sonr" name="external-link"></sl-icon> + <sl-icon library="sonr" name="face-id"></sl-icon> + <sl-icon library="sonr" name="lock-access"></sl-icon> + <sl-icon library="sonr" name="check-shield"></sl-icon> + <sl-icon library="sonr" name="vault"></sl-icon> + <sl-icon library="sonr" name="coin-assets"></sl-icon> + <sl-icon library="sonr" name="coins-hand"></sl-icon> + <sl-icon library="sonr" name="coins-stack"></sl-icon> + <sl-icon library="sonr" name="currency-dollar"></sl-icon> + <sl-icon library="sonr" name="currency-euro"></sl-icon> + <sl-icon library="sonr" name="currency-pound"></sl-icon> + <sl-icon library="sonr" name="currency-rupee"></sl-icon> + <sl-icon library="sonr" name="currency-yen"></sl-icon> + <sl-icon library="sonr" name="sonr"></sl-icon> + <sl-icon library="sonr" name="sonr-fill"></sl-icon> + <sl-icon library="sonr" name="osmo"></sl-icon> + <sl-icon library="sonr" name="btc"></sl-icon> + <sl-icon library="sonr" name="sol"></sl-icon> + <sl-icon library="sonr" name="usdc"></sl-icon> + </div> +
+ A collection of icons for cryptocurrencies which are tradeable on the Sonr network. Provide the All Caps + ticker symbol for the icon. +
+++ +These are pre-packaged with Nebula and are not available for custom registration.
+
+<div style="font-size: 24px;"> + <sl-icon library="crypto" name="SNR"></sl-icon> + <sl-icon library="crypto" name="BTC"></sl-icon> + <sl-icon library="crypto" name="ETH"></sl-icon> + <sl-icon library="crypto" name="LTC"></sl-icon> + <sl-icon library="crypto" name="XRP"></sl-icon> + <sl-icon library="crypto" name="BCH"></sl-icon> + <sl-icon library="crypto" name="BNB"></sl-icon> + <sl-icon library="crypto" name="LINK"></sl-icon> + <sl-icon library="crypto" name="DOT"></sl-icon> + <sl-icon library="crypto" name="SOL"></sl-icon> + <sl-icon library="crypto" name="USDT"></sl-icon> + <sl-icon library="crypto" name="USDC"></sl-icon> + <sl-icon library="crypto" name="EVMOS"></sl-icon> + <sl-icon library="crypto" name="FIL"></sl-icon> + <sl-icon library="crypto" name="ATOM"></sl-icon> + <sl-icon library="crypto" name="AXL"></sl-icon> + <sl-icon library="crypto" name="HNS"></sl-icon> + <sl-icon library="crypto" name="AKT"></sl-icon> + <sl-icon library="crypto" name="DOGE"></sl-icon> + <sl-icon library="crypto" name="SOL"></sl-icon> + <sl-icon library="crypto" name="STARZ"></sl-icon> +</div> +
+ This will register the
+ Font Awesome Free
+ library using the jsDelivr CDN. This library has three variations: regular (far-*
), solid
+ (fas-*
), and brands (fab-*
). A mutator function is required to set the SVG’s
+ fill
to currentColor
.
+
+ Icons in this library are licensed under the + Font Awesome Free License. Some of the icons that appear on the Font Awesome website require a license and are therefore not + available in the CDN. +
+ ++<script type="module"> + import { registerIconLibrary } from '/dist/utilities/icon-library.js'; + + registerIconLibrary('fa', { + resolver: name => { + const filename = name.replace(/^fa[rbs]-/, ''); + let folder = 'regular'; + if (name.substring(0, 4) === 'fas-') folder = 'solid'; + if (name.substring(0, 4) === 'fab-') folder = 'brands'; + return `https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/svgs/${folder}/${filename}.svg`; + }, + mutator: svg => svg.setAttribute('fill', 'currentColor') + }); +</script> + +<div style="font-size: 24px;"> + <sl-icon library="fa" name="far-bell"></sl-icon> + <sl-icon library="fa" name="far-comment"></sl-icon> + <sl-icon library="fa" name="far-hand-point-right"></sl-icon> + <sl-icon library="fa" name="far-hdd"></sl-icon> + <sl-icon library="fa" name="far-heart"></sl-icon> + <sl-icon library="fa" name="far-star"></sl-icon> + <br /> + <sl-icon library="fa" name="fas-archive"></sl-icon> + <sl-icon library="fa" name="fas-book"></sl-icon> + <sl-icon library="fa" name="fas-chess-knight"></sl-icon> + <sl-icon library="fa" name="fas-dice"></sl-icon> + <sl-icon library="fa" name="fas-pizza-slice"></sl-icon> + <sl-icon library="fa" name="fas-scroll"></sl-icon> + <br /> + <sl-icon library="fa" name="fab-apple"></sl-icon> + <sl-icon library="fa" name="fab-chrome"></sl-icon> + <sl-icon library="fa" name="fab-edge"></sl-icon> + <sl-icon library="fa" name="fab-firefox"></sl-icon> + <sl-icon library="fa" name="fab-opera"></sl-icon> + <sl-icon library="fa" name="fab-microsoft"></sl-icon> +</div> +
+ The system library contains only the icons used internally by Nebula components. Unlike the default icon + library, the system library does not rely on physical assets. Instead, its icons are hard-coded as data URIs + into the resolver to ensure their availability. +
+
+ If you want to change the icons Nebula uses internally, you can register an icon library using the
+ system
name and a custom resolver. If you choose to do this, it’s your responsibility to
+ provide all of the icons that are required by components. You can reference
+ src/components/library.system.ts
for a complete list of system icons used by Nebula.
+
+ + + +<script type="module"> + import { registerIconLibrary } from '/dist/utilities/icon-library.js'; + + registerIconLibrary('system', { + resolver: name => `/path/to/custom/icons/${name}.svg` + }); +</script> +
+ ++
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/icon/icon.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/icon/icon.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/icon/icon.js';
To import this component as a React component:
++import SlIcon from '@onsonr/nebula/dist/react/icon';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The name of the icon to draw. Available names depend on the icon library being used. | +
+ |
+
+ string | undefined
+ |
+ - | +
+ src
+ |
+ + An external URL of an SVG file. Be sure you trust the content you are including, as it will be + executed as code and can result in XSS attacks. + | ++ |
+ string | undefined
+ |
+ - | +
+ label
+ |
+ + An alternate description to use for assistive devices. If omitted, the icon will be considered + presentational and ignored by assistive devices. + | ++ |
+ string
+ |
+
+ ''
+ |
+
+ library
+ |
+ The name of a registered custom icon library. | +
+ |
+
+ string
+ |
+
+ 'default'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-load |
+ onSlLoad |
+
+ Emitted when the icon has loaded. When using spriteSheet: true this will not emit.
+ |
+ - | +
sl-error |
+ onSlError |
+
+ Emitted when the icon fails to load due to an error. When using spriteSheet: true this
+ will not emit.
+ |
+ - | +
+ Learn more about events. +
+ +Name | +Description | +
---|---|
svg |
+ The internal SVG element. | +
use |
+ + The + | +
+ Learn more about customizing CSS parts. +
+<sl-image-comparer> | SlImageComparer
+ Compare visual differences between similar photos with a sliding panel.
+ ++ For best results, use images that share the same dimensions. The slider can be controlled by dragging or + pressing the left and right arrow keys. (Tip: press shift + arrows to move the slider in larger intervals, + or home + end to jump to the beginning or end.) +
+ ++<sl-image-comparer> + <img + slot="before" + src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&sat=-100&bri=-5" + alt="Grayscale version of kittens in a basket looking around." + /> + <img + slot="after" + src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80" + alt="Color version of kittens in a basket looking around." + /> +</sl-image-comparer> +
+import SlImageComparer from '@onsonr/nebula/dist/react/image-comparer'; + +const App = () => ( + <SlImageComparer> + <img + slot="before" + src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80&sat=-100&bri=-5" + alt="Grayscale version of kittens in a basket looking around." + /> + <img + slot="after" + src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=80" + alt="Color version of kittens in a basket looking around." + /> + </SlImageComparer> +); +
+ Use the position
attribute to set the initial position of the slider. This is a percentage from
+ 0
to 100
.
+
+<sl-image-comparer position="25"> + <img + slot="before" + src="https://images.unsplash.com/photo-1520903074185-8eca362b3dce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80" + alt="A person sitting on bricks wearing untied boots." + /> + <img + slot="after" + src="https://images.unsplash.com/photo-1520640023173-50a135e35804?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80" + alt="A person sitting on a yellow curb tying shoelaces on a boot." + /> +</sl-image-comparer> +
+import SlImageComparer from '@onsonr/nebula/dist/react/image-comparer'; + +const App = () => ( + <SlImageComparer position={25}> + <img + slot="before" + src="https://images.unsplash.com/photo-1520903074185-8eca362b3dce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80" + alt="A person sitting on bricks wearing untied boots." + /> + <img + slot="after" + src="https://images.unsplash.com/photo-1520640023173-50a135e35804?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80" + alt="A person sitting on a yellow curb tying shoelaces on a boot." + /> + </SlImageComparer> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/image-comparer/image-comparer.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/image-comparer/image-comparer.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/image-comparer/image-comparer.js';
To import this component as a React component:
++import SlImageComparer from '@onsonr/nebula/dist/react/image-comparer';
Name | +Description | +
---|---|
+ before
+ |
+ The before image, an <img> or <svg> element. |
+
+ after
+ |
+ The after image, an <img> or <svg> element. |
+
+ handle
+ |
+ The icon used inside the handle. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ position
+ |
+ The position of the divider as a percentage. | +
+ |
+
+ number
+ |
+
+ 50
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-change |
+ onSlChange |
+ Emitted when the position changes. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Default | +
---|---|---|
--divider-width |
+ The width of the dividing line. | ++ |
--handle-size |
+ The size of the compare handle. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
before |
+ The container that wraps the before image. | +
after |
+ The container that wraps the after image. | +
divider |
+ The divider that separates the images. | +
handle |
+ The handle that the user drags to expose the after image. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-include> | SlInclude
+ Includes give you the power to embed external HTML files into the page.
+ +
+ Included files are asynchronously requested using window.fetch()
. Requests are cached, so the
+ same file can be included multiple times, but only one request will be made.
+
+ The included content will be inserted into the <sl-include>
element’s default slot so it
+ can be easily accessed and styled through the light DOM.
+
+<sl-include src="https://shoelace.style/assets/examples/include.html"></sl-include> +
+import SlInclude from '@onsonr/nebula/dist/react/include'; + +const App = () => <SlInclude src="https://shoelace.style/assets/examples/include.html" />; +
+ When an include file loads successfully, the sl-load
event will be emitted. You can listen for
+ this event to add custom loading logic to your includes.
+
+ If the request fails, the sl-error
event will be emitted. In this case,
+ event.detail.status
will contain the resulting HTTP status code of the request, e.g. 404 (not
+ found).
+
+ +<sl-include src="https://shoelace.style/assets/examples/include.html"></sl-include> + +<script> + const include = document.querySelector('sl-include'); + + include.addEventListener('sl-load', event => { + if (event.eventPhase === Event.AT_TARGET) { + console.log('Success'); + } + }); + + include.addEventListener('sl-error', event => { + if (event.eventPhase === Event.AT_TARGET) { + console.log('Error', event.detail.status); + } + }); +</script> +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/include/include.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/include/include.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/include/include.js';
To import this component as a React component:
++import SlInclude from '@onsonr/nebula/dist/react/include';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ src
+ |
+ + The location of the HTML file to include. Be sure you trust the content you are including as it will + be executed as code and can result in XSS attacks. + | ++ |
+ string
+ |
+ - | +
+ mode
+ |
+ The fetch mode to use. | ++ |
+ 'cors' | 'no-cors' | 'same-origin'
+ |
+
+ 'cors'
+ |
+
+ allowScripts
+
+ + allow-scripts
+
+ |
+ + Allows included scripts to be executed. Be sure you trust the content you are including as it will + be executed as code and can result in XSS attacks. + | ++ |
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-load |
+ onSlLoad |
+ Emitted when the included file is loaded. | +- | +
sl-error |
+ onSlError |
+ Emitted when the included file fails to load due to an error. | +
+ { status: number }
+ |
+
+ Learn more about events. +
+<sl-input> | SlInput
+ Inputs collect data from the user.
+ ++<sl-input></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput />; +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
+ Use the label
attribute to give the input an accessible label. For labels that contain HTML,
+ use the label
slot instead.
+
+<sl-input label="What is your name?"></sl-input> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput label="What is your name?" />; +
+ Add descriptive help text to an input with the help-text
attribute. For help texts that contain
+ HTML, use the help-text
slot instead.
+
+<sl-input label="Nickname" help-text="What would you like people to call you?"></sl-input> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput label="Nickname" help-text="What would you like people to call you?" />; +
Use the placeholder
attribute to add a placeholder.
+<sl-input placeholder="Type something"></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput placeholder="Type something" />; +
Add the clearable
attribute to add a clear button when the input has content.
+<sl-input placeholder="Clearable" clearable></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput placeholder="Clearable" clearable />; +
+ Add the password-toggle
attribute to add a toggle button that will show the password when
+ activated.
+
+<sl-input type="password" placeholder="Password Toggle" password-toggle></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput type="password" placeholder="Password Toggle" size="medium" password-toggle />; +
Add the filled
attribute to draw a filled input.
+<sl-input placeholder="Type something" filled></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput placeholder="Type something" filled />; +
Use the disabled
attribute to disable an input.
+<sl-input placeholder="Disabled" disabled></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => <SlInput placeholder="Disabled" disabled />; +
Use the size
attribute to change an input’s size.
+<sl-input placeholder="Small" size="small"></sl-input> +<br /> +<sl-input placeholder="Medium" size="medium"></sl-input> +<br /> +<sl-input placeholder="Large" size="large"></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => ( + <> + <SlInput placeholder="Small" size="small" /> + <br /> + <SlInput placeholder="Medium" size="medium" /> + <br /> + <SlInput placeholder="Large" size="large" /> + </> +); +
Use the pill
attribute to give inputs rounded edges.
+<sl-input placeholder="Small" size="small" pill></sl-input> +<br /> +<sl-input placeholder="Medium" size="medium" pill></sl-input> +<br /> +<sl-input placeholder="Large" size="large" pill></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => ( + <> + <SlInput placeholder="Small" size="small" pill /> + <br /> + <SlInput placeholder="Medium" size="medium" pill /> + <br /> + <SlInput placeholder="Large" size="large" pill /> + </> +); +
The type
attribute controls the type of input the browser renders.
+<sl-input type="email" placeholder="Email"></sl-input> +<br /> +<sl-input type="number" placeholder="Number"></sl-input> +<br /> +<sl-input type="date" placeholder="Date"></sl-input> +
+import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => ( + <> + <SlInput type="email" placeholder="Email" /> + <br /> + <SlInput type="number" placeholder="Number" /> + <br /> + <SlInput type="date" placeholder="Date" /> + </> +); +
Use the prefix
and suffix
slots to add icons.
+<sl-input placeholder="Small" size="small"> + <sl-icon name="house" slot="prefix"></sl-icon> + <sl-icon name="chat" slot="suffix"></sl-icon> +</sl-input> +<br /> +<sl-input placeholder="Medium" size="medium"> + <sl-icon name="house" slot="prefix"></sl-icon> + <sl-icon name="chat" slot="suffix"></sl-icon> +</sl-input> +<br /> +<sl-input placeholder="Large" size="large"> + <sl-icon name="house" slot="prefix"></sl-icon> + <sl-icon name="chat" slot="suffix"></sl-icon> +</sl-input> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => ( + <> + <SlInput placeholder="Small" size="small"> + <SlIcon name="house" slot="prefix"></SlIcon> + <SlIcon name="chat" slot="suffix"></SlIcon> + </SlInput> + <br /> + <SlInput placeholder="Medium" size="medium"> + <SlIcon name="house" slot="prefix"></SlIcon> + <SlIcon name="chat" slot="suffix"></SlIcon> + </SlInput> + <br /> + <SlInput placeholder="Large" size="large"> + <SlIcon name="house" slot="prefix"></SlIcon> + <SlIcon name="chat" slot="suffix"></SlIcon> + </SlInput> + </> +); +
+ Use CSS parts to customize the way form controls are drawn. This example uses CSS + grid to position the label to the left of the control, but the possible orientations are nearly endless. The + same technique works for inputs, textareas, radio groups, and similar form controls. +
+ ++<sl-input class="label-on-left" label="Name" help-text="Enter your name"></sl-input> +<sl-input class="label-on-left" label="Email" type="email" help-text="Enter your email"></sl-input> +<sl-textarea class="label-on-left" label="Bio" help-text="Tell us something about yourself"></sl-textarea> + +<style> + .label-on-left { + --label-width: 3.75rem; + --gap-width: 1rem; + } + + .label-on-left + .label-on-left { + margin-top: var(--sl-spacing-medium); + } + + .label-on-left::part(form-control) { + display: grid; + grid: auto / var(--label-width) 1fr; + gap: var(--sl-spacing-3x-small) var(--gap-width); + align-items: center; + } + + .label-on-left::part(form-control-label) { + text-align: right; + } + + .label-on-left::part(form-control-help-text) { + grid-column-start: 2; + } +</style> +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/input/input.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/input/input.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/input/input.js';
To import this component as a React component:
++import SlInput from '@onsonr/nebula/dist/react/input';
Name | +Description | +
---|---|
+ label
+ |
+ The input’s label. Alternatively, you can use the label attribute. |
+
+ prefix
+ |
+ Used to prepend a presentational icon or similar element to the input. | +
+ suffix
+ |
+ Used to append a presentational icon or similar element to the input. | +
+ clear-icon
+ |
+ An icon to use in lieu of the default clear icon. | +
+ show-password-icon
+ |
+ An icon to use in lieu of the default show password icon. | +
+ hide-password-icon
+ |
+ An icon to use in lieu of the default hide password icon. | +
+ help-text
+ |
+
+ Text that describes how to use the input. Alternatively, you can use the
+ help-text attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ type
+ |
+
+ The type of input. Works the same as a native <input> element, but only a subset
+ of types are supported. Defaults to text .
+ |
+
+ |
+
+
+ 'date' | 'datetime-local' | 'email' | 'number' | 'password' | 'search' | 'tel' | 'text' | 'time' |
+ 'url'
+ |
+
+ 'text'
+ |
+
+ name
+ |
+ The name of the input, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+ The current value of the input, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ defaultValue
+ |
+ The default value of the form control. Primarily used for resetting the form control. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ size
+ |
+ The input’s size. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ filled
+ |
+ Draws a filled input. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ pill
+ |
+ Draws a pill-style input with rounded edges. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ label
+ |
+ The input’s label. If you need to display HTML, use the label slot instead. |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ helpText
+
+ + help-text
+
+ |
+
+ The input’s help text. If you need to display HTML, use the help-text slot instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ clearable
+ |
+ Adds a clear button when the input is not empty. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ Disables the input. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ placeholder
+ |
+ Placeholder text to show as a hint when the input is empty. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ readonly
+ |
+ Makes the input readonly. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ passwordToggle
+
+ + password-toggle
+
+ |
+ Adds a button to toggle the password’s visibility. Only applies to password types. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ passwordVisible
+
+ + password-visible
+
+ |
+ + Determines whether or not the password is currently visible. Only applies to password input types. + | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ noSpinButtons
+
+ + no-spin-buttons
+
+ |
+ Hides the browser’s built-in increment/decrement spin buttons for number inputs. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ required
+ |
+ Makes the input a required field. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ pattern
+ |
+ A regular expression pattern to validate input against. | ++ |
+ string
+ |
+ - | +
+ minlength
+ |
+ The minimum length of input that will be considered valid. | ++ |
+ number
+ |
+ - | +
+ maxlength
+ |
+ The maximum length of input that will be considered valid. | ++ |
+ number
+ |
+ - | +
+ min
+ |
+ The input’s minimum value. Only applies to date and number input types. | ++ |
+ number | string
+ |
+ - | +
+ max
+ |
+ The input’s maximum value. Only applies to date and number input types. | ++ |
+ number | string
+ |
+ - | +
+ step
+ |
+
+ Specifies the granularity that the value must adhere to, or the special value any which
+ means no stepping is implied, allowing any numeric value. Only applies to date and number input
+ types.
+ |
+ + |
+ number | 'any'
+ |
+ - | +
+ autocapitalize
+ |
+ + Controls whether and how text input is automatically capitalized as it is entered by the user. + | ++ |
+ 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters'
+ |
+ - | +
+ autocorrect
+ |
+ Indicates whether the browser’s autocorrect feature is on or off. | ++ |
+ 'off' | 'on'
+ |
+ - | +
+ autocomplete
+ |
+ + Specifies what permission the browser has to provide assistance in filling out form field values. + Refer to + this page on MDN + for available values. + | ++ |
+ string
+ |
+ - | +
+ autofocus
+ |
+ Indicates that the input should receive focus on page load. | ++ |
+ boolean
+ |
+ - | +
+ enterkeyhint
+ |
+ Used to customize the label or icon of the Enter key on virtual keyboards. | ++ |
+ 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
+ |
+ - | +
+ spellcheck
+ |
+ Enables spell checking on the input. | ++ |
+ boolean
+ |
+
+ true
+ |
+
+ inputmode
+ |
+ + Tells the browser what type of data will be entered by the user, allowing it to display the + appropriate virtual keyboard on supportive devices. + | ++ |
+ 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url'
+ |
+ - | +
+ valueAsDate
+ |
+
+ Gets or sets the current value as a Date object. Returns null if the value
+ can’t be converted. This will use the native
+ <input type="{{type}}"> implementation and may result in an error.
+ |
+ + | - | +- | +
+ valueAsNumber
+ |
+
+ Gets or sets the current value as a number. Returns NaN if the value can’t be
+ converted.
+ |
+ + | - | +- | +
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the control loses focus. | +- | +
sl-change |
+ onSlChange |
+ Emitted when an alteration to the control’s value is committed by the user. | +- | +
sl-clear |
+ onSlClear |
+ Emitted when the clear button is activated. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the control gains focus. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the control receives input. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
focus() |
+ Sets focus on the input. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the input. | +- | +
select() |
+ Selects all the text in the input. | +- | +
setSelectionRange() |
+ Sets the start and end positions of the text selection (0-based). | +
+
+ selectionStart: number, selectionEnd: number, selectionDirection: 'forward' | 'backward' | 'none'
+
+ |
+
setRangeText() |
+ Replaces a range of text with a new string. | +
+
+ replacement: string, start: number, end: number, selectMode: 'select' | 'start' | 'end' |
+ 'preserve'
+
+ |
+
showPicker() |
+ + Displays the browser picker for an input element (only works if the browser supports it for the + input type). + | +- | +
stepUp() |
+ Increments the value of a numeric input type by the value of the step attribute. | +- | +
stepDown() |
+ Decrements the value of a numeric input type by the value of the step attribute. | +- | +
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
form-control |
+ The form control that wraps the label, input, and help text. | +
form-control-label |
+ The label’s wrapper. | +
form-control-input |
+ The input’s wrapper. | +
form-control-help-text |
+ The help text’s wrapper. | +
base |
+ The component’s base wrapper. | +
input |
+ The internal <input> control. |
+
prefix |
+ The container that wraps the prefix. | +
clear-button |
+ The clear button. | +
password-toggle-button |
+ The password toggle button. | +
suffix |
+ The container that wraps the suffix. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-menu-item> | SlMenuItem
+ Menu items provide options for the user to pick from in a menu.
+ ++<sl-menu style="max-width: 200px;"> + <sl-menu-item>Option 1</sl-menu-item> + <sl-menu-item>Option 2</sl-menu-item> + <sl-menu-item>Option 3</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item> + <sl-menu-item disabled>Disabled</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item> + Prefix Icon + <sl-icon slot="prefix" name="gift"></sl-icon> + </sl-menu-item> + <sl-menu-item> + Suffix Icon + <sl-icon slot="suffix" name="heart"></sl-icon> + </sl-menu-item> +</sl-menu> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem>Option 1</SlMenuItem> + <SlMenuItem>Option 2</SlMenuItem> + <SlMenuItem>Option 3</SlMenuItem> + <SlDivider /> + <SlMenuItem type="checkbox" checked> + Checkbox + </SlMenuItem> + <SlMenuItem disabled>Disabled</SlMenuItem> + <SlDivider /> + <SlMenuItem> + Prefix Icon + <SlIcon slot="prefix" name="gift" /> + </SlMenuItem> + <SlMenuItem> + Suffix Icon + <SlIcon slot="suffix" name="heart" /> + </SlMenuItem> + </SlMenu> +); +
+ Add content to the start and end of menu items using the prefix
and suffix
slots.
+
+<sl-menu style="max-width: 200px;"> + <sl-menu-item> + <sl-icon slot="prefix" name="house"></sl-icon> + Home + </sl-menu-item> + + <sl-menu-item> + <sl-icon slot="prefix" name="envelope"></sl-icon> + Messages + <sl-badge slot="suffix" variant="primary" pill>12</sl-badge> + </sl-menu-item> + + <sl-divider></sl-divider> + + <sl-menu-item> + <sl-icon slot="prefix" name="gear"></sl-icon> + Settings + </sl-menu-item> +</sl-menu> +
+import SlBadge from '@onsonr/nebula/dist/react/badge'; +import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem> + <SlIcon slot="prefix" name="house" /> + Home + </SlMenuItem> + + <SlMenuItem> + <SlIcon slot="prefix" name="envelope" /> + Messages + <SlBadge slot="suffix" variant="primary" pill> + 12 + </SlBadge> + </SlMenuItem> + + <SlDivider /> + + <SlMenuItem> + <SlIcon slot="prefix" name="gear" /> + Settings + </SlMenuItem> + </SlMenu> +); +
Add the disabled
attribute to disable the menu item so it cannot be selected.
+<sl-menu style="max-width: 200px;"> + <sl-menu-item>Option 1</sl-menu-item> + <sl-menu-item disabled>Option 2</sl-menu-item> + <sl-menu-item>Option 3</sl-menu-item> +</sl-menu> +
+import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem>Option 1</SlMenuItem> + <SlMenuItem disabled>Option 2</SlMenuItem> + <SlMenuItem>Option 3</SlMenuItem> + </SlMenu> +); +
+ Use the loading
attribute to indicate that a menu item is busy. Like a disabled menu item,
+ clicks will be suppressed until the loading state is removed.
+
+<sl-menu style="max-width: 200px;"> + <sl-menu-item>Option 1</sl-menu-item> + <sl-menu-item loading>Option 2</sl-menu-item> + <sl-menu-item>Option 3</sl-menu-item> +</sl-menu> +
+import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem>Option 1</SlMenuItem> + <SlMenuItem loading>Option 2</SlMenuItem> + <SlMenuItem>Option 3</SlMenuItem> + </SlMenu> +); +
+ Set the type
attribute to checkbox
to create a menu item that will toggle on and
+ off when selected. You can use the checked
attribute to set the initial state.
+
+ Checkbox menu items are visually indistinguishable from regular menu items. Their ability to be toggled is + primarily inferred from context, much like you’d find in the menu of a native app. +
+ ++<sl-menu style="max-width: 200px;"> + <sl-menu-item type="checkbox">Autosave</sl-menu-item> + <sl-menu-item type="checkbox" checked>Check Spelling</sl-menu-item> + <sl-menu-item type="checkbox">Word Wrap</sl-menu-item> +</sl-menu> +
+import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem type="checkbox">Autosave</SlMenuItem> + <SlMenuItem type="checkbox" checked> + Check Spelling + </SlMenuItem> + <SlMenuItem type="checkbox">Word Wrap</SlMenuItem> + </SlMenu> +); +
+ The value
attribute can be used to assign a hidden value, such as a unique identifier, to a
+ menu item. When an item is selected, the sl-select
event will be emitted and a reference to the
+ item will be available at event.detail.item
. You can use this reference to access the selected
+ item’s value, its checked state, and more.
+
+<sl-menu class="menu-value" style="max-width: 200px;"> + <sl-menu-item value="opt-1">Option 1</sl-menu-item> + <sl-menu-item value="opt-2">Option 2</sl-menu-item> + <sl-menu-item value="opt-3">Option 3</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item type="checkbox" value="opt-4">Checkbox 4</sl-menu-item> + <sl-menu-item type="checkbox" value="opt-5">Checkbox 5</sl-menu-item> + <sl-menu-item type="checkbox" value="opt-6">Checkbox 6</sl-menu-item> +</sl-menu> + +<script> + const menu = document.querySelector('.menu-value'); + + menu.addEventListener('sl-select', event => { + const item = event.detail.item; + + // Log value + if (item.type === 'checkbox') { + console.log(`Selected value: ${item.value} (${item.checked ? 'checked' : 'unchecked'})`); + } else { + console.log(`Selected value: ${item.value}`); + } + }); +</script> +
+import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => { + function handleSelect(event) { + const item = event.detail.item; + + // Toggle checked state + item.checked = !item.checked; + + // Log value + console.log(`Selected value: ${item.value}`); + } + + return ( + <SlMenu style={{ maxWidth: '200px' }} onSlSelect={handleSelect}> + <SlMenuItem value="opt-1">Option 1</SlMenuItem> + <SlMenuItem value="opt-2">Option 2</SlMenuItem> + <SlMenuItem value="opt-3">Option 3</SlMenuItem> + </SlMenu> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/menu-item/menu-item.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/menu-item/menu-item.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/menu-item/menu-item.js';
To import this component as a React component:
++import SlMenuItem from '@onsonr/nebula/dist/react/menu-item';
Name | +Description | +
---|---|
(default) | +The menu item’s label. | +
+ prefix
+ |
+ Used to prepend an icon or similar element to the menu item. | +
+ suffix
+ |
+ Used to append an icon or similar element to the menu item. | +
+ submenu
+ |
+ Used to denote a nested menu. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ type
+ |
+
+ The type of menu item to render. To use checked , this value must be set to
+ checkbox .
+ |
+ + |
+ 'normal' | 'checkbox'
+ |
+
+ 'normal'
+ |
+
+ checked
+ |
+ Draws the item in a checked state. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ value
+ |
+ + A unique value to store in the menu item. This can be used as a way to identify menu items when + selected. + | ++ |
+ string
+ |
+
+ ''
+ |
+
+ loading
+ |
+ Draws the menu item in a loading state. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ Draws the menu item in a disabled state, preventing selection. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Arguments | +
---|---|---|
getTextLabel() |
+ Returns a text label based on the contents of the menu item’s default slot. | +- | +
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--submenu-offset |
+ The distance submenus shift to overlap the parent menu. | +-2px | +
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
checked-icon |
+ The checked icon, which is only visible when the menu item is checked. | +
prefix |
+ The prefix container. | +
label |
+ The menu item label. | +
suffix |
+ The suffix container. | +
spinner |
+ The spinner that shows when the menu item is in the loading state. | +
spinner__base |
+ The spinner’s base part. | +
submenu-icon |
+ The submenu icon, visible only when the menu item has a submenu (not yet implemented). | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-popup>
<sl-spinner>
<sl-menu-label> | SlMenuLabel
+ Menu labels are used to describe a group of menu items.
+ ++<sl-menu style="max-width: 200px;"> + <sl-menu-label>Fruits</sl-menu-label> + <sl-menu-item value="apple">Apple</sl-menu-item> + <sl-menu-item value="banana">Banana</sl-menu-item> + <sl-menu-item value="orange">Orange</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-label>Vegetables</sl-menu-label> + <sl-menu-item value="broccoli">Broccoli</sl-menu-item> + <sl-menu-item value="carrot">Carrot</sl-menu-item> + <sl-menu-item value="zucchini">Zucchini</sl-menu-item> +</sl-menu> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuLabel from '@onsonr/nebula/dist/react/menu-label'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuLabel>Fruits</SlMenuLabel> + <SlMenuItem value="apple">Apple</SlMenuItem> + <SlMenuItem value="banana">Banana</SlMenuItem> + <SlMenuItem value="orange">Orange</SlMenuItem> + <SlDivider /> + <SlMenuLabel>Vegetables</SlMenuLabel> + <SlMenuItem value="broccoli">Broccoli</SlMenuItem> + <SlMenuItem value="carrot">Carrot</SlMenuItem> + <SlMenuItem value="zucchini">Zucchini</SlMenuItem> + </SlMenu> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/menu-label/menu-label.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/menu-label/menu-label.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/menu-label/menu-label.js';
To import this component as a React component:
++import SlMenuLabel from '@onsonr/nebula/dist/react/menu-label';
Name | +Description | +
---|---|
(default) | +The menu label’s content. | +
+ Learn more about using slots. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-menu> | SlMenu
+ Menus provide a list of options for the user to choose from.
+ ++ You can use menu items, + menu labels, and dividers to compose + a menu. Menus support keyboard interactions, including type-to-select an option. +
+ ++<sl-menu style="max-width: 200px;"> + <sl-menu-item value="undo">Undo</sl-menu-item> + <sl-menu-item value="redo">Redo</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item value="cut">Cut</sl-menu-item> + <sl-menu-item value="copy">Copy</sl-menu-item> + <sl-menu-item value="paste">Paste</sl-menu-item> + <sl-menu-item value="delete">Delete</sl-menu-item> +</sl-menu> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem value="undo">Undo</SlMenuItem> + <SlMenuItem value="redo">Redo</SlMenuItem> + <SlDivider /> + <SlMenuItem value="cut">Cut</SlMenuItem> + <SlMenuItem value="copy">Copy</SlMenuItem> + <SlMenuItem value="paste">Paste</SlMenuItem> + <SlMenuItem value="delete">Delete</SlMenuItem> + </SlMenu> +); +
+ Menus are intended for system menus (dropdown menus, select menus, context menus, etc.). They should not
+ be mistaken for navigation menus which serve a different purpose and have a different semantic meaning. If
+ you’re building navigation, use <nav>
and <a>
elements instead.
+
Menus work really well when used inside dropdowns.
+ ++<sl-dropdown> + <sl-button slot="trigger" caret>Edit</sl-button> + <sl-menu> + <sl-menu-item value="cut">Cut</sl-menu-item> + <sl-menu-item value="copy">Copy</sl-menu-item> + <sl-menu-item value="paste">Paste</sl-menu-item> + </sl-menu> +</sl-dropdown> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlDropdown from '@onsonr/nebula/dist/react/dropdown'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlDropdown> + <SlButton slot="trigger" caret>Edit</SlButton> + <SlMenu> + <SlMenuItem value="cut">Cut</SlMenuItem> + <SlMenuItem value="copy">Copy</SlMenuItem> + <SlMenuItem value="paste">Paste</SlMenuItem> + </SlMenu> + </SlDropdown> +); +
+ To create a submenu, nest an <sl-menu slot="submenu">
in any
+ menu item.
+
+<sl-menu style="max-width: 200px;"> + <sl-menu-item value="undo">Undo</sl-menu-item> + <sl-menu-item value="redo">Redo</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item value="cut">Cut</sl-menu-item> + <sl-menu-item value="copy">Copy</sl-menu-item> + <sl-menu-item value="paste">Paste</sl-menu-item> + <sl-divider></sl-divider> + <sl-menu-item> + Find + <sl-menu slot="submenu"> + <sl-menu-item value="find">Find…</sl-menu-item> + <sl-menu-item value="find-previous">Find Next</sl-menu-item> + <sl-menu-item value="find-next">Find Previous</sl-menu-item> + </sl-menu> + </sl-menu-item> + <sl-menu-item> + Transformations + <sl-menu slot="submenu"> + <sl-menu-item value="uppercase">Make uppercase</sl-menu-item> + <sl-menu-item value="lowercase">Make lowercase</sl-menu-item> + <sl-menu-item value="capitalize">Capitalize</sl-menu-item> + </sl-menu> + </sl-menu-item> +</sl-menu> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlMenu from '@onsonr/nebula/dist/react/menu'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => ( + <SlMenu style={{ maxWidth: '200px' }}> + <SlMenuItem value="undo">Undo</SlMenuItem> + <SlMenuItem value="redo">Redo</SlMenuItem> + <SlDivider /> + <SlMenuItem value="cut">Cut</SlMenuItem> + <SlMenuItem value="copy">Copy</SlMenuItem> + <SlMenuItem value="paste">Paste</SlMenuItem> + <SlDivider /> + <SlMenuItem> + Find + <SlMenu slot="submenu"> + <SlMenuItem value="find">Find…</SlMenuItem> + <SlMenuItem value="find-previous">Find Next</SlMenuItem> + <SlMenuItem value="find-next">Find Previous</SlMenuItem> + </SlMenu> + </SlMenuItem> + <SlMenuItem> + Transformations + <SlMenu slot="submenu"> + <SlMenuItem value="uppercase">Make uppercase</SlMenuItem> + <SlMenuItem value="lowercase">Make lowercase</SlMenuItem> + <SlMenuItem value="capitalize">Capitalize</SlMenuItem> + </SlMenu> + </SlMenuItem> + </SlMenu> +); +
As a UX best practice, avoid using more than one level of submenus when possible.
++ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/menu/menu.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/menu/menu.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/menu/menu.js';
To import this component as a React component:
++import SlMenu from '@onsonr/nebula/dist/react/menu';
Name | +Description | +
---|---|
(default) | +The menu’s content, including menu items, menu labels, and dividers. | +
+ Learn more about using slots. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-select |
+ onSlSelect |
+ Emitted when a menu item is selected. | +
+ { item: SlMenuItem }
+ |
+
+ Learn more about events. +
+<sl-mutation-observer> | SlMutationObserver
+
+ The Mutation Observer component offers a thin, declarative interface to the
+ MutationObserver API
.
+
+ The mutation observer will report changes to the content it wraps through the
+ sl-mutation
event. When emitted, a collection of
+ MutationRecord
+ objects will be attached to event.detail
that contains information about how it changed.
+
+<div class="mutation-overview"> + <sl-mutation-observer attr="variant"> + <sl-button variant="primary">Click to mutate</sl-button> + </sl-mutation-observer> + + <br /> + 👆 Click the button and watch the console + + <script> + const container = document.querySelector('.mutation-overview'); + const mutationObserver = container.querySelector('sl-mutation-observer'); + const button = container.querySelector('sl-button'); + const variants = ['primary', 'success', 'neutral', 'warning', 'danger']; + let clicks = 0; + + // Change the button's variant attribute + button.addEventListener('click', () => { + clicks++; + button.setAttribute('variant', variants[clicks % variants.length]); + }); + + // Log mutations + mutationObserver.addEventListener('sl-mutation', event => { + console.log(event.detail); + }); + </script> + + <style> + .mutation-overview sl-button { + margin-bottom: 1rem; + } + </style> +</div> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlMutationObserver from '@onsonr/nebula/dist/react/mutation-observer'; + +const css = ` + .resize-observer-overview div { + display: flex; + border: solid 2px var(--sl-input-border-color); + align-items: center; + justify-content: center; + text-align: center; + padding: 4rem 2rem; + } +`; + +const variants = ['primary', 'success', 'neutral', 'warning', 'danger']; +let clicks = 0; + +const App = () => { + const [variant, setVariant] = useState('primary'); + + function handleClick() { + clicks++; + setVariant(variants[clicks % variants.length]); + } + + return ( + <> + <SlMutationObserver attr="*" onSlMutation={event => console.log(event.detail)}> + <SlButton variant={variant} onClick={handleClick}> + Click to mutate + </SlButton> + </SlMutationObserver> + + <style>{css}</style> + </> + ); +}; +
+ When you create a mutation observer, you must indicate what changes it should respond to by including at
+ least one of attr
, child-list
, or char-data
. If you don’t specify
+ at least one of these attributes, no mutation events will be emitted.
+
Use the child-list
attribute to watch for new child elements that are added or removed.
+<div class="mutation-child-list"> + <sl-mutation-observer child-list> + <div class="buttons"> + <sl-button variant="primary">Add button</sl-button> + </div> + </sl-mutation-observer> + + 👆 Add and remove buttons and watch the console + + <script> + const container = document.querySelector('.mutation-child-list'); + const mutationObserver = container.querySelector('sl-mutation-observer'); + const buttons = container.querySelector('.buttons'); + const button = container.querySelector('sl-button[variant="primary"]'); + let i = 0; + + // Add a button + button.addEventListener('click', () => { + const button = document.createElement('sl-button'); + button.textContent = ++i; + buttons.append(button); + }); + + // Remove a button + buttons.addEventListener('click', event => { + const target = event.target.closest('sl-button:not([variant="primary"])'); + event.stopPropagation(); + + if (target) { + target.remove(); + } + }); + + // Log mutations + mutationObserver.addEventListener('sl-mutation', event => { + console.log(event.detail); + }); + </script> + + <style> + .mutation-child-list .buttons { + display: flex; + gap: 0.25rem; + flex-wrap: wrap; + margin-bottom: 1rem; + } + </style> +</div> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlMutationObserver from '@onsonr/nebula/dist/react/mutation-observer'; + +const css = ` + .mutation-child-list .buttons { + display: flex; + gap: .25rem; + flex-wrap: wrap; + margin-bottom: 1rem; + } +`; + +let buttonCount = 0; + +const App = () => { + const [buttonIds, setButtonIds] = useState([]); + + function addButton() { + setButtonIds([...buttonIds, ++buttonCount]); + } + + function removeButton(id) { + setButtonIds(buttonIds.filter(i => i !== id)); + } + + return ( + <> + <div className="mutation-child-list"> + <SlMutationObserver child-list onSlMutation={event => console.log(event.detail)}> + <div className="buttons"> + <SlButton variant="primary" onClick={addButton}> + Add button + </SlButton> + {buttonIds.map(id => ( + <SlButton key={id} variant="default" onClick={() => removeButton(id)}> + {id} + </SlButton> + ))} + </div> + </SlMutationObserver> + </div> + 👆 Add and remove buttons and watch the console + <style>{css}</style> + </> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/mutation-observer/mutation-observer.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/mutation-observer/mutation-observer.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/mutation-observer/mutation-observer.js';
To import this component as a React component:
++import SlMutationObserver from '@onsonr/nebula/dist/react/mutation-observer';
Name | +Description | +
---|---|
(default) | +The content to watch for mutations. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ attr
+ |
+
+ Watches for changes to attributes. To watch only specific attributes, separate them by a space, e.g.
+ attr="class id title" . To watch all attributes, use * .
+ |
+
+ |
+
+ string
+ |
+ - | +
+ attrOldValue
+
+ + attr-old-value
+
+ |
+ + Indicates whether or not the attribute’s previous value should be recorded when monitoring changes. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ charData
+
+ + char-data
+
+ |
+ Watches for changes to the character data contained within the node. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ charDataOldValue
+
+ + char-data-old-value
+
+ |
+ Indicates whether or not the previous value of the node’s text should be recorded. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ childList
+
+ + child-list
+
+ |
+ Watches for the addition or removal of new child nodes. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ Disables the observer. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-mutation |
+ onSlMutation |
+ Emitted when a mutation occurs. | +
+ { mutationList: MutationRecord[] }
+ |
+
+ Learn more about events. +
+<sl-option> | SlOption
+ + Options define the selectable items within various form controls such as + select. +
+ ++<sl-select label="Select one"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
Use the disabled
attribute to disable an option and prevent it from being selected.
+<sl-select label="Select one"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2" disabled>Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2" disabled> + Option 2 + </SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
+ Add icons to the start and end of menu items using the prefix
and suffix
slots.
+
+<sl-select label="Select one"> + <sl-option value="option-1"> + <sl-icon slot="prefix" name="envelope"></sl-icon> + Email + <sl-icon slot="suffix" name="patch-check"></sl-icon> + </sl-option> + + <sl-option value="option-2"> + <sl-icon slot="prefix" name="telephone"></sl-icon> + Phone + <sl-icon slot="suffix" name="patch-check"></sl-icon> + </sl-option> + + <sl-option value="option-3"> + <sl-icon slot="prefix" name="chat-dots"></sl-icon> + Chat + <sl-icon slot="suffix" name="patch-check"></sl-icon> + </sl-option> +</sl-select> +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/option/option.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/option/option.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/option/option.js';
To import this component as a React component:
++import SlOption from '@onsonr/nebula/dist/react/option';
Name | +Description | +
---|---|
(default) | +The option’s label. | +
+ prefix
+ |
+ Used to prepend an icon or similar element to the menu item. | +
+ suffix
+ |
+ Used to append an icon or similar element to the menu item. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ + The option’s value. When selected, the containing form control will receive this value. The value + must be unique from other options in the same group. Values may not contain spaces, as spaces are + used as delimiters when listing multiple values. + | +
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ disabled
+ |
+ Draws the option in a disabled state, preventing selection. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Arguments | +
---|---|---|
getTextLabel() |
+ Returns a plain text label based on the option’s content. | +- | +
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
checked-icon |
+ The checked icon, an <sl-icon> element. |
+
base |
+ The component’s base wrapper. | +
label |
+ The option’s label. | +
prefix |
+ The container that wraps the prefix. | +
suffix |
+ The container that wraps the suffix. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-popup> | SlPopup
+ + Popup is a utility that lets you declaratively anchor “popup” containers to another element. +
+ +
+ This component’s name is inspired by
+ <popup>
. It uses
+ Floating UI
+ under the hood to provide a well-tested, lightweight, and fully declarative positioning utility for
+ tooltips, dropdowns, and more.
+
+ Popup doesn’t provide any styles — just positioning! The popup’s preferred placement, distance, and skidding + (offset) can be configured using attributes. An arrow that points to the anchor can be shown and customized + to your liking. Additional positioning options are available and described in more detail below. +
+
+ Popup is a low-level utility built specifically for positioning elements. Do not mistake it for a
+ tooltip or similar because
+ it does not facilitate an accessible experience! Almost every correct usage of
+ <sl-popup>
will involve building other components. It should rarely, if ever, occur
+ directly in your HTML.
+
+<div class="popup-overview"> + <sl-popup placement="top" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <div class="popup-overview-options"> + <sl-select label="Placement" name="placement" value="top" class="popup-overview-select"> + <sl-option value="top">top</sl-option> + <sl-option value="top-start">top-start</sl-option> + <sl-option value="top-end">top-end</sl-option> + <sl-option value="bottom">bottom</sl-option> + <sl-option value="bottom-start">bottom-start</sl-option> + <sl-option value="bottom-end">bottom-end</sl-option> + <sl-option value="right">right</sl-option> + <sl-option value="right-start">right-start</sl-option> + <sl-option value="right-end">right-end</sl-option> + <sl-option value="left">left</sl-option> + <sl-option value="left-start">left-start</sl-option> + <sl-option value="left-end">left-end</sl-option> + </sl-select> + <sl-input type="number" name="distance" label="distance" value="0"></sl-input> + <sl-input type="number" name="skidding" label="Skidding" value="0"></sl-input> + </div> + + <div class="popup-overview-options"> + <sl-switch name="active" checked>Active</sl-switch> + <sl-switch name="arrow">Arrow</sl-switch> + </div> +</div> + +<script> + const container = document.querySelector('.popup-overview'); + const popup = container.querySelector('sl-popup'); + const select = container.querySelector('sl-select[name="placement"]'); + const distance = container.querySelector('sl-input[name="distance"]'); + const skidding = container.querySelector('sl-input[name="skidding"]'); + const active = container.querySelector('sl-switch[name="active"]'); + const arrow = container.querySelector('sl-switch[name="arrow"]'); + + select.addEventListener('sl-change', () => (popup.placement = select.value)); + distance.addEventListener('sl-input', () => (popup.distance = distance.value)); + skidding.addEventListener('sl-input', () => (popup.skidding = skidding.value)); + active.addEventListener('sl-change', () => (popup.active = active.checked)); + arrow.addEventListener('sl-change', () => (popup.arrow = arrow.checked)); +</script> + +<style> + .popup-overview sl-popup { + --arrow-color: var(--sl-color-primary-600); + } + + .popup-overview span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-overview .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-overview-options { + display: flex; + flex-wrap: wrap; + align-items: end; + gap: 1rem; + } + + .popup-overview-options sl-select { + width: 160px; + } + + .popup-overview-options sl-input { + width: 100px; + } + + .popup-overview-options + .popup-overview-options { + margin-top: 1rem; + } +</style> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSelect from '@onsonr/nebula/dist/react/select'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; +import SlInput from '@onsonr/nebula/dist/react/input'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-overview sl-popup { + --arrow-color: var(--sl-color-primary-600); + } + + .popup-overview span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-overview .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-overview-options { + display: flex; + flex-wrap: wrap; + align-items: end; + gap: 1rem; + } + + .popup-overview-options sl-select { + width: 160px; + } + + .popup-overview-options sl-input { + width: 100px; + } + + .popup-overview-options + .popup-overview-options { + margin-top: 1rem; + } +`; + +const App = () => { + const [placement, setPlacement] = useState('top'); + const [distance, setDistance] = useState(0); + const [skidding, setSkidding] = useState(0); + const [active, setActive] = useState(true); + const [arrow, setArrow] = useState(false); + + return ( + <> + <div className="popup-overview"> + <SlPopup + placement={placement} + active={active || null} + distance={distance} + skidding={skidding} + arrow={arrow || null} + > + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + + <div className="popup-overview-options"> + <SlSelect + label="Placement" + name="placement" + value={placement} + className="popup-overview-select" + onSlChange={event => setPlacement(event.target.value)} + > + <SlMenuItem value="top">top</SlMenuItem> + <SlMenuItem value="top-start">top-start</SlMenuItem> + <SlMenuItem value="top-end">top-end</SlMenuItem> + <SlMenuItem value="bottom">bottom</SlMenuItem> + <SlMenuItem value="bottom-start">bottom-start</SlMenuItem> + <SlMenuItem value="bottom-end">bottom-end</SlMenuItem> + <SlMenuItem value="right">right</SlMenuItem> + <SlMenuItem value="right-start">right-start</SlMenuItem> + <SlMenuItem value="right-end">right-end</SlMenuItem> + <SlMenuItem value="left">left</SlMenuItem> + <SlMenuItem value="left-start">left-start</SlMenuItem> + <SlMenuItem value="left-end">left-end</SlMenuItem> + </SlSelect> + <SlInput + type="number" + name="distance" + label="distance" + value={distance} + onSlInput={event => setDistance(event.target.value)} + /> + <SlInput + type="number" + name="skidding" + label="Skidding" + value={skidding} + onSlInput={event => setSkidding(event.target.value)} + /> + </div> + + <div className="popup-overview-options"> + <SlSwitch checked={active} onSlChange={event => setActive(event.target.checked)}> + Active + </SlSwitch> + <SlSwitch checked={arrow} onSlChange={event => setArrow(event.target.checked)}> + Arrow + </SlSwitch> + </div> + </div> + + <style>{css}</style> + </> + ); +}; +
+ A popup’s anchor should not be styled with display: contents
since the coordinates will not
+ be eligible for calculation. However, if the anchor is a <slot>
element, popup will use
+ the first assigned element as the anchor. This behavior allows other components to pass anchors through
+ more easily via composition.
+
+ Popups are inactive and hidden until the active
attribute is applied. Removing the attribute
+ will tear down all positioning logic and listeners, meaning you can have many idle popups on the page
+ without affecting performance.
+
+<div class="popup-active"> + <sl-popup placement="top" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <br /> + <sl-switch checked>Active</sl-switch> +</div> + +<style> + .popup-active span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-active .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +</style> + +<script> + const container = document.querySelector('.popup-active'); + const popup = container.querySelector('sl-popup'); + const active = container.querySelector('sl-switch'); + + active.addEventListener('sl-change', () => (popup.active = active.checked)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-active span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-active .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +`; + +const App = () => { + const [active, setActive] = useState(true); + + return ( + <> + <div className="popup-active"> + <SlPopup placement="top" active={active}> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + + <br /> + <SlSwitch checked={active} onSlChange={event => setActive(event.target.checked)}> + Active + </SlSwitch> + </div> + + <style>{css}</style> + </> + ); +}; +
+ By default, anchors are slotted into the popup using the anchor
slot. If your anchor needs to
+ live outside of the popup, you can pass the anchor’s id
to the anchor
attribute.
+ Alternatively, you can pass an element reference to the anchor
property to achieve the same
+ effect without using an id
.
+
+<span id="external-anchor"></span> + +<sl-popup anchor="external-anchor" placement="top" active> + <div class="box"></div> +</sl-popup> + +<style> + #external-anchor { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px 0 0 50px; + } + + #external-anchor ~ sl-popup .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +</style> +
+import SlPopup from '@onsonr/nebula/dist/react/popup'; + +const css = ` + #external-anchor { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px 0 0 50px; + } + + #external-anchor ~ sl-popup .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +`; + +const App = () => { + return ( + <> + <span id="external-anchor" /> + + <SlPopup anchor="external-anchor" placement="top" active> + <div class="box" /> + </SlPopup> + + <style>{css}</style> + </> + ); +}; +
+ Use the placement
attribute to tell the popup the preferred placement of the popup. Note that
+ the actual position will vary to ensure the panel remains in the viewport if you’re using positioning
+ features such as flip
and shift
.
+
+ Since placement is preferred when using flip
, you can observe the popup’s current placement
+ when it’s active by looking at the data-current-placement
attribute. This attribute will update
+ as the popup flips to find available space and it will be removed when the popup is deactivated.
+
+<div class="popup-placement"> + <sl-popup placement="top" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <sl-select label="Placement" value="top"> + <sl-option value="top">top</sl-option> + <sl-option value="top-start">top-start</sl-option> + <sl-option value="top-end">top-end</sl-option> + <sl-option value="bottom">bottom</sl-option> + <sl-option value="bottom-start">bottom-start</sl-option> + <sl-option value="bottom-end">bottom-end</sl-option> + <sl-option value="right">right</sl-option> + <sl-option value="right-start">right-start</sl-option> + <sl-option value="right-end">right-end</sl-option> + <sl-option value="left">left</sl-option> + <sl-option value="left-start">left-start</sl-option> + <sl-option value="left-end">left-end</sl-option> + </sl-select> +</div> + +<style> + .popup-placement span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-placement .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-placement sl-select { + max-width: 280px; + } +</style> + +<script> + const container = document.querySelector('.popup-placement'); + const popup = container.querySelector('sl-popup'); + const select = container.querySelector('sl-select'); + + select.addEventListener('sl-change', () => (popup.placement = select.value)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSelect from '@onsonr/nebula/dist/react/select'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const css = ` + .popup-placement span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-placement .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-placement sl-select { + max-width: 280px; + } +`; + +const App = () => { + const [placement, setPlacement] = useState('top'); + + return ( + <div className="popup-active"> + <div className="popup-placement"> + <SlPopup placement={placement} active> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + + <SlSelect label="Placement" value={placement} onSlChange={event => setPlacement(event.target.value)}> + <SlMenuItem value="top">top</SlMenuItem> + <SlMenuItem value="top-start">top-start</SlMenuItem> + <SlMenuItem value="top-end">top-end</SlMenuItem> + <SlMenuItem value="bottom">bottom</SlMenuItem> + <SlMenuItem value="bottom-start">bottom-start</SlMenuItem> + <SlMenuItem value="bottom-end">bottom-end</SlMenuItem> + <SlMenuItem value="right">right</SlMenuItem> + <SlMenuItem value="right-start">right-start</SlMenuItem> + <SlMenuItem value="right-end">right-end</SlMenuItem> + <SlMenuItem value="left">left</SlMenuItem> + <SlMenuItem value="left-start">left-start</SlMenuItem> + <SlMenuItem value="left-end">left-end</SlMenuItem> + </SlSelect> + </div> + + <style>{css}</style> + </div> + ); +}; +
+ Use the distance
attribute to change the distance between the popup and its anchor. A positive
+ value will move the popup further away and a negative value will move it closer.
+
+<div class="popup-distance"> + <sl-popup placement="top" distance="0" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <sl-range min="-50" max="50" step="1" value="0" label="Distance"></sl-range> +</div> + +<style> + .popup-distance span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-distance .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-distance sl-range { + max-width: 260px; + } +</style> + +<script> + const container = document.querySelector('.popup-distance'); + const popup = container.querySelector('sl-popup'); + const distance = container.querySelector('sl-range'); + + distance.addEventListener('sl-input', () => (popup.distance = distance.value)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlRange from '@onsonr/nebula/dist/react/range'; + +const css = ` + .popup-distance span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-distance .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-distance sl-range { + max-width: 260px; + } +`; + +const App = () => { + const [distance, setDistance] = useState(0); + + return ( + <> + <div className="popup-distance"> + <SlPopup placement="top" distance={distance} active> + <span slot="anchor" /> + <div class="box" /> + </SlPopup> + + <SlRange + label="Distance" + min="-50" + max="50" + step="1" + value={distance} + onSlChange={event => setDistance(event.target.value)} + /> + </div> + + <style>{css}</style> + </> + ); +}; +
+ The skidding
attribute is similar to distance
, but instead allows you to offset
+ the popup along the anchor’s axis. Both positive and negative values are allowed.
+
+<div class="popup-skidding"> + <sl-popup placement="top" skidding="0" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <sl-range min="-50" max="50" step="1" value="0" label="Skidding"></sl-range> +</div> + +<style> + .popup-skidding span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-skidding .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-skidding sl-range { + max-width: 260px; + } +</style> + +<script> + const container = document.querySelector('.popup-skidding'); + const popup = container.querySelector('sl-popup'); + const skidding = container.querySelector('sl-range'); + + skidding.addEventListener('sl-input', () => (popup.skidding = skidding.value)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlRange from '@onsonr/nebula/dist/react/range'; + +const css = ` + .popup-skidding span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-skidding .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-skidding sl-range { + max-width: 260px; + } +`; + +const App = () => { + const [skidding, setSkidding] = useState(0); + + return ( + <> + <div className="popup-skidding"> + <SlPopup placement="top" skidding={skidding} active> + <span slot="anchor"></span> + <div className="box"></div> + </SlPopup> + + <SlRange + label="Skidding" + min="-50" + max="50" + step="1" + value={skidding} + onSlChange={event => setSkidding(event.target.value)} + /> + </div> + + <style>{css}</style> + </> + ); +}; +
+ Add an arrow to your popup with the arrow
attribute. It’s usually a good idea to set a
+ distance
to make room for the arrow. To adjust the arrow’s color and size, use the
+ --arrow-color
and --arrow-size
custom properties, respectively. You can also
+ target the arrow
part to add additional styles such as shadows and borders.
+
+ By default, the arrow will be aligned as close to the center of the anchor as possible, considering
+ available space and arrow-padding
. You can use the arrow-placement
attribute to
+ force the arrow to align to the start, end, or center of the popup instead.
+
+<div class="popup-arrow"> + <sl-popup placement="top" arrow arrow-placement="anchor" distance="8" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <div class="popup-arrow-options"> + <sl-select label="Placement" name="placement" value="top" class="popup-overview-select"> + <sl-option value="top">top</sl-option> + <sl-option value="top-start">top-start</sl-option> + <sl-option value="top-end">top-end</sl-option> + <sl-option value="bottom">bottom</sl-option> + <sl-option value="bottom-start">bottom-start</sl-option> + <sl-option value="bottom-end">bottom-end</sl-option> + <sl-option value="right">right</sl-option> + <sl-option value="right-start">right-start</sl-option> + <sl-option value="right-end">right-end</sl-option> + <sl-option value="left">left</sl-option> + <sl-option value="left-start">left-start</sl-option> + <sl-option value="left-end">left-end</sl-option> + </sl-select> + + <sl-select label="Arrow Placement" name="arrow-placement" value="anchor"> + <sl-option value="anchor">anchor</sl-option> + <sl-option value="start">start</sl-option> + <sl-option value="end">end</sl-option> + <sl-option value="center">center</sl-option> + </sl-select> + </div> + + <div class="popup-arrow-options"> + <sl-switch name="arrow" checked>Arrow</sl-switch> + </div> + + <style> + .popup-arrow sl-popup { + --arrow-color: var(--sl-color-primary-600); + } + + .popup-arrow span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-arrow .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-arrow-options { + display: flex; + flex-wrap: wrap; + align-items: end; + gap: 1rem; + } + + .popup-arrow-options sl-select { + width: 160px; + } + + .popup-arrow-options + .popup-arrow-options { + margin-top: 1rem; + } + </style> + + <script> + const container = document.querySelector('.popup-arrow'); + const popup = container.querySelector('sl-popup'); + const placement = container.querySelector('[name="placement"]'); + const arrowPlacement = container.querySelector('[name="arrow-placement"]'); + const arrow = container.querySelector('[name="arrow"]'); + + placement.addEventListener('sl-change', () => (popup.placement = placement.value)); + arrowPlacement.addEventListener('sl-change', () => (popup.arrowPlacement = arrowPlacement.value)); + arrow.addEventListener('sl-change', () => (popup.arrow = arrow.checked)); + </script> +</div> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSelect from '@onsonr/nebula/dist/react/select'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-arrow sl-popup { + --arrow-color: var(--sl-color-primary-600); + } + + .popup-arrow span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-arrow .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-arrow-options { + display: flex; + flex-wrap: wrap; + align-items: end; + gap: 1rem; + } + + .popup-arrow-options sl-select { + width: 160px; + } + + .popup-arrow-options + .popup-arrow-options { + margin-top: 1rem; + } +`; + +const App = () => { + const [placement, setPlacement] = useState('top'); + const [arrowPlacement, setArrowPlacement] = useState('anchor'); + const [arrow, setArrow] = useState(true); + + return ( + <> + <div className="popup-arrow"> + <SlPopup placement={placement} arrow={arrow || null} arrow-placement={arrowPlacement} distance="8" active> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + + <div className="popup-arrow-options"> + <SlSelect + label="Placement" + name="placement" + value={placement} + className="popup-overview-select" + onSlChange={event => setPlacement(event.target.value)} + > + <SlMenuItem value="top">top</SlMenuItem> + <SlMenuItem value="top-start">top-start</SlMenuItem> + <SlMenuItem value="top-end">top-end</SlMenuItem> + <SlMenuItem value="bottom">bottom</SlMenuItem> + <SlMenuItem value="bottom-start">bottom-start</SlMenuItem> + <SlMenuItem value="bottom-end">bottom-end</SlMenuItem> + <SlMenuItem value="right">right</SlMenuItem> + <SlMenuItem value="right-start">right-start</SlMenuItem> + <SlMenuItem value="right-end">right-end</SlMenuItem> + <SlMenuItem value="left">left</SlMenuItem> + <SlMenuItem value="left-start">left-start</SlMenuItem> + <SlMenuItem value="left-end">left-end</SlMenuItem> + </SlSelect> + + <SlSelect + label="Arrow Placement" + name="arrow-placement" + value={arrowPlacement} + onSlChange={event => setArrowPlacement(event.target.value)} + > + <SlMenuItem value="anchor">anchor</SlMenuItem> + <SlMenuItem value="start">start</SlMenuItem> + <SlMenuItem value="end">end</SlMenuItem> + <SlMenuItem value="center">center</SlMenuItem> + </SlSelect> + </div> + + <div className="popup-arrow-options"> + <SlSwitch name="arrow" checked={arrow} onSlChange={event => setArrow(event.target.checked)}> + Arrow + </SlSwitch> + </div> + </div> + + <style>{css}</style> + </> + ); +}; +
+ Use the sync
attribute to make the popup the same width or height as the anchor element. This
+ is useful for controls that need the popup to stay the same width or height as the trigger.
+
+<div class="popup-sync"> + <sl-popup placement="top" sync="width" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <sl-select value="width" label="Sync"> + <sl-option value="width">Width</sl-option> + <sl-option value="height">Height</sl-option> + <sl-option value="both">Both</sl-option> + <sl-option value="">None</sl-option> + </sl-select> +</div> + +<style> + .popup-sync span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-sync .box { + width: 100%; + height: 100%; + min-width: 50px; + min-height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-sync sl-select { + width: 160px; + } +</style> + +<script> + const container = document.querySelector('.popup-sync'); + const popup = container.querySelector('sl-popup'); + const fixed = container.querySelector('sl-switch'); + const sync = container.querySelector('sl-select'); + + sync.addEventListener('sl-change', () => (popup.sync = sync.value)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSelect from '@onsonr/nebula/dist/react/select'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const css = ` + .popup-sync span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-sync .box { + width: 100%; + height: 100%; + min-width: 50px; + min-height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-sync sl-switch { + margin-top: 1rem; + } +`; + +const App = () => { + const [sync, setSync] = useState('width'); + + return ( + <> + <div class="popup-sync"> + <SlPopup placement="top" sync={sync} active> + <span slot="anchor" /> + <div class="box" /> + </SlPopup> + + <SlSelect value={sync} label="Sync" onSlChange={event => setSync(event.target.value)}> + <SlMenuItem value="width">Width</SlMenuItem> + <SlMenuItem value="height">Height</SlMenuItem> + <SlMenuItem value="both">Both</SlMenuItem> + <SlMenuItem value="">None</SlMenuItem> + </SlSelect> + </div> + + <style>{css}</style> + </> + ); +}; +
+ By default, the popup is positioned using an absolute positioning strategy. However, if your anchor is fixed
+ or exists within a container that has overflow: auto|hidden
, the popup risks being clipped. To
+ work around this, you can use a fixed positioning strategy by setting the strategy
attribute to
+ fixed
.
+
+ The fixed positioning strategy reduces jumpiness when the anchor is fixed and allows the popup to break out
+ containers that clip. When using this strategy, it’s important to note that the content will be positioned
+ relative to its
+ containing block, which is usually the viewport unless an ancestor uses a transform
, perspective
,
+ or filter
.
+ Refer to this page
+ for more details.
+
+ In this example, you can see how the popup breaks out of the overflow container when it’s fixed. The fixed + positioning strategy tends to be less performant than absolute, so avoid using it unnecessarily. +
+Toggle the switch and scroll the container to see the difference.
+ ++<div class="popup-strategy"> + <div class="overflow"> + <sl-popup placement="top" strategy="fixed" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + </div> + + <sl-switch checked>Fixed</sl-switch> +</div> + +<style> + .popup-strategy .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-strategy span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 150px 50px; + } + + .popup-strategy .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-strategy sl-switch { + margin-top: 1rem; + } +</style> + +<script> + const container = document.querySelector('.popup-strategy'); + const popup = container.querySelector('sl-popup'); + const fixed = container.querySelector('sl-switch'); + + fixed.addEventListener('sl-change', () => (popup.strategy = fixed.checked ? 'fixed' : 'absolute')); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-strategy .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-strategy span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 150px 50px; + } + + .popup-strategy .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-strategy sl-switch { + margin-top: 1rem; + } +`; + +const App = () => { + const [fixed, setFixed] = useState(true); + + return ( + <> + <div className="popup-strategy"> + <div className="overflow"> + <SlPopup placement="top" strategy={fixed ? 'fixed' : 'absolute'} active> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + </div> + + <SlSwitch checked={fixed} onSlChange={event => setFixed(event.target.checked)}> + Fixed + </SlSwitch> + </div> + + <style>{css}</style> + </> + ); +}; +
+ When the popup doesn’t have enough room in its preferred placement, it can automatically flip to keep it in
+ view. To enable this, use the flip
attribute. By default, the popup will flip to the opposite
+ placement, but you can configure preferred fallback placements using
+ flip-fallback-placement
and flip-fallback-strategy
. Additional options are
+ available to control the flip behavior’s boundary and padding.
+
Scroll the container to see how the popup flips to prevent clipping.
+ ++<div class="popup-flip"> + <div class="overflow"> + <sl-popup placement="top" flip active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + </div> + + <br /> + <sl-switch checked>Flip</sl-switch> +</div> + +<style> + .popup-flip .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-flip span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 150px 50px; + } + + .popup-flip .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +</style> + +<script> + const container = document.querySelector('.popup-flip'); + const popup = container.querySelector('sl-popup'); + const flip = container.querySelector('sl-switch'); + + flip.addEventListener('sl-change', () => (popup.flip = flip.checked)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-flip .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-flip span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 150px 50px; + } + + .popup-flip .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +`; + +const App = () => { + const [flip, setFlip] = useState(true); + + return ( + <> + <div className="popup-flip"> + <div className="overflow"> + <SlPopup placement="top" flip={flip} active> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + </div> + + <br /> + <SlSwitch checked={flip} onSlChange={event => setFlip(event.target.checked)}> + Flip + </SlSwitch> + </div> + + <style>{css}</style> + </> + ); +}; +
+ While using the flip
attribute, you can customize the placement of the popup when the preferred
+ placement doesn’t have room. For this, use flip-fallback-placements
and
+ flip-fallback-strategy
.
+
+ If the preferred placement doesn’t have room, the first suitable placement found in
+ flip-fallback-placement
will be used. The value of this attribute must be a string including
+ any number of placements separated by a space, e.g. "right bottom"
.
+
+ If no fallback placement works, the final placement will be determined by
+ flip-fallback-strategy
. This value can be either initial
(default), where the
+ placement reverts to the position in placement
, or best-fit
, where the placement
+ is chosen based on available space.
+
Scroll the container to see how the popup changes it’s fallback placement to prevent clipping.
+ ++<div class="popup-flip-fallbacks"> + <div class="overflow"> + <sl-popup placement="top" flip flip-fallback-placements="right bottom" flip-fallback-strategy="initial" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + </div> +</div> + +<style> + .popup-flip-fallbacks .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-flip-fallbacks span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 250px 50px; + } + + .popup-flip-fallbacks .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +</style> +
+import SlPopup from '@onsonr/nebula/dist/react/popup'; + +const css = ` + .popup-flip-fallbacks .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-flip-fallbacks span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 250px 50px; + } + + .popup-flip-fallbacks .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +`; + +const App = () => { + return ( + <> + <div className="popup-flip-fallbacks"> + <div className="overflow"> + <SlPopup placement="top" flip flip-fallback-placements="right bottom" flip-fallback-strategy="initial" active> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + </div> + </div> + + <style>{css}</style> + </> + ); +}; +
+ When a popup is longer than its anchor, it risks being clipped by an overflowing container. In this case,
+ use the shift
attribute to shift the popup along its axis and back into view. You can customize
+ the shift behavior using shiftBoundary
and shift-padding
.
+
Toggle the switch to see the difference.
+ ++<div class="popup-shift"> + <div class="overflow"> + <sl-popup placement="top" shift shift-padding="10" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + </div> + + <sl-switch checked>Shift</sl-switch> +</div> + +<style> + .popup-shift .overflow { + position: relative; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-shift span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 60px 0 0 10px; + } + + .popup-shift .box { + width: 300px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +</style> + +<script> + const container = document.querySelector('.popup-shift'); + const popup = container.querySelector('sl-popup'); + const shift = container.querySelector('sl-switch'); + + shift.addEventListener('sl-change', () => (popup.shift = shift.checked)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-shift .overflow { + position: relative; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-shift span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 60px 0 0 10px; + } + + .popup-shift .box { + width: 300px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } +`; + +const App = () => { + const [shift, setShift] = useState(true); + + return ( + <> + <div className="popup-shift"> + <div className="overflow"> + <SlPopup placement="top" shift={shift} shift-padding="10" active> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + </div> + + <SlSwitch checked={shift} onSlChange={event => setShift(event.target.checked)}> + Shift + </SlSwitch> + </div> + + <style>{css}</style> + </> + ); +}; +
+ Use the auto-size
attribute to tell the popup to resize when necessary to prevent it from
+ getting clipped. Possible values are horizontal
, vertical
, and both
.
+ You can use autoSizeBoundary
and auto-size-padding
to customize the behavior of
+ this option. Auto-size works well with flip
, but if you’re using
+ auto-size-padding
make sure flip-padding
is the same value.
+
+ When using auto-size
, one or both of --auto-size-available-width
and
+ --auto-size-available-height
will be applied to the host element. These values determine the
+ available space the popover has before clipping will occur. Since they cascade, you can use them to set a
+ max-width/height on your popup’s content and easily control its overflow.
+
Scroll the container to see the popup resize as its available space changes.
+ ++<div class="popup-auto-size"> + <div class="overflow"> + <sl-popup placement="top" auto-size="both" auto-size-padding="10" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + </div> + + <br /> + <sl-switch checked>Auto-size</sl-switch> +</div> + +<style> + .popup-auto-size .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-auto-size span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 250px 50px 100px 50px; + } + + .popup-auto-size .box { + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + + /* This sets the preferred size of the popup's content */ + width: 100px; + height: 200px; + + /* This sets the maximum dimensions and allows scrolling when auto-size kicks in */ + max-width: var(--auto-size-available-width); + max-height: var(--auto-size-available-height); + overflow: auto; + } +</style> + +<script> + const container = document.querySelector('.popup-auto-size'); + const popup = container.querySelector('sl-popup'); + const autoSize = container.querySelector('sl-switch'); + + autoSize.addEventListener('sl-change', () => (popup.autoSize = autoSize.checked ? 'both' : '')); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-auto-size .overflow { + position: relative; + height: 300px; + border: solid 2px var(--sl-color-neutral-200); + overflow: auto; + } + + .popup-auto-size span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 250px 50px 100px 50px; + } + + .popup-auto-size .box { + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + + /* This sets the preferred size of the popup's content */ + width: 100px; + height: 200px; + + /* This sets the maximum dimensions and allows scrolling when auto-size kicks in */ + max-width: var(--auto-size-available-width); + max-height: var(--auto-size-available-height); + overflow: auto; + } +`; + +const App = () => { + const [autoSize, setAutoSize] = useState(true); + + return ( + <> + <div className="popup-auto-size"> + <div className="overflow"> + <SlPopup placement="top" auto-size={autoSize ? 'both' || null} auto-size-padding="10" active> + <span slot="anchor" /> + <div className="box" /> + </SlPopup> + </div> + + <br /> + <SlSwitch checked={autoSize} onSlChange={event => setAutoSize(event.target.checked)}> + Auto-size + </SlSwitch> + </div> + + <style>{css}</style> + </> + ); +}; +
+ When a gap exists between the anchor and the popup element, this option will add a “hover bridge” that fills
+ the gap using an invisible element. This makes listening for events such as mouseover
and
+ mouseout
more sane because the pointer never technically leaves the element. The hover bridge
+ will only be drawn when the popover is active. For demonstration purposes, the bridge in this example is
+ shown in orange.
+
+<div class="popup-hover-bridge"> + <sl-popup placement="top" hover-bridge distance="10" skidding="0" active> + <span slot="anchor"></span> + <div class="box"></div> + </sl-popup> + + <br> + <sl-switch checked>Hover Bridge</sl-switch><br> + <sl-range min="0" max="50" step="1" value="10" label="Distance"></sl-range> + <sl-range min="-50" max="50" step="1" value="0" label="Skidding"></sl-range> +</div> + +<style> + .popup-hover-bridge span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-hover-bridge .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-hover-bridge sl-range { + max-width: 260px; + margin-top: .5rem; + } + + .popup-hover-bridge sl-popup::part(hover-bridge) { + background: tomato; + opacity: .5; + } +</style> + +<script> + const container = document.querySelector('.popup-hover-bridge'); + const popup = container.querySelector('sl-popup'); + const hoverBridge = container.querySelector('sl-switch'); + const distance = container.querySelector('sl-range[label="Distance"]'); + const skidding = container.querySelector('sl-range[label="Skidding"]'); + + distance.addEventListener('sl-input', () => (popup.distance = distance.value)); + skidding.addEventListener('sl-input', () => (popup.skidding = skidding.value)); + hoverBridge.addEventListener('sl-change', () => (popup.hoverBridge = hoverBridge.checked)); +</script> +
+import { useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlRange from '@onsonr/nebula/dist/react/range'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + .popup-hover-bridge span[slot='anchor'] { + display: inline-block; + width: 150px; + height: 150px; + border: dashed 2px var(--sl-color-neutral-600); + margin: 50px; + } + + .popup-hover-bridge .box { + width: 100px; + height: 50px; + background: var(--sl-color-primary-600); + border-radius: var(--sl-border-radius-medium); + } + + .popup-hover-bridge sl-range { + max-width: 260px; + margin-top: .5rem; + } + + .popup-hover-bridge sl-popup::part(hover-bridge) { + background: tomato; + opacity: .5; + } +`; + +const App = () => { + const [hoverBridge, setHoverBridge] = useState(true); + const [distance, setDistance] = useState(10); + const [skidding, setSkidding] = useState(0); + + return ( + <> + <div class="popup-hover-bridge"> + <SlPopup placement="top" hover-bridge={hoverBridge} distance={distance} skidding={skidding} active> + <span slot="anchor" /> + <div class="box" /> + </SlPopup> + + <br /> + <SlSwitch + checked={hoverBridge} + onSlChange={event => setHoverBridge(event.target.checked)} + > + Hover Bridge + </SlSwitch><br /> + <SlRange + min="0" + max="50" + step="1" + value={distance} + label="Distance" + onSlInput={event => setDistance(event.target.value)} + /> + <SlRange + min="-50" + max="50" + step="1" + value={skidding} + label="Skidding" + onSlInput={event => setSkidding(event.target.value)} + /> + </div> + + <style>{css}</style> + </> + ); +}; +
+ In most cases, popups are anchored to an actual element. Sometimes, it can be useful to anchor them to a
+ non-element. To do this, you can pass a VirtualElement
to the anchor property. A virtual
+ element must contain a function called getBoundingClientRect()
that returns a
+ DOMRect
+ object as shown below.
+
+const virtualElement = { + getBoundingClientRect() { + // ... + return { width, height, x, y, top, left, right, bottom }; + } +}; +
+ This example anchors a popup to the mouse cursor using a virtual element. As such, a mouse is required to + properly view it. +
+ ++<div class="popup-virtual-element"> + <sl-popup placement="right-start"> + <div class="circle"></div> + </sl-popup> + + <sl-switch>Highlight mouse cursor</sl-switch> +</div> + +<script> + const container = document.querySelector('.popup-virtual-element'); + const popup = container.querySelector('sl-popup'); + const circle = container.querySelector('.circle'); + const enabled = container.querySelector('sl-switch'); + let clientX = 0; + let clientY = 0; + + // Set the virtual element as a property + popup.anchor = { + getBoundingClientRect() { + return { + width: 0, + height: 0, + x: clientX, + y: clientY, + top: clientY, + left: clientX, + right: clientX, + bottom: clientY + }; + } + }; + + // Only activate the popup when the switch is checked + enabled.addEventListener('sl-change', () => { + popup.active = enabled.checked; + }); + + // Listen for the mouse to move + document.addEventListener('mousemove', handleMouseMove); + + // Update the virtual element as the mouse moves + function handleMouseMove(event) { + clientX = event.clientX; + clientY = event.clientY; + + // Reposition the popup when the virtual anchor moves + if (popup.active) { + popup.reposition(); + } + } +</script> + +<style> + /* If you need to set a z-index, set it on the popup part like this */ + .popup-virtual-element sl-popup::part(popup) { + z-index: 1000; + pointer-events: none; + } + + .popup-virtual-element .circle { + width: 100px; + height: 100px; + border: solid 4px var(--sl-color-primary-600); + border-radius: 50%; + translate: -50px -50px; + animation: 1s virtual-cursor infinite; + } + + @keyframes virtual-cursor { + 0% { scale: 1; } + 50% { scale: 1.1; } + } +</style> +
+import { useRef, useState } from 'react'; +import SlPopup from '@onsonr/nebula/dist/react/popup'; +import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const css = ` + /* If you need to set a z-index, set it on the popup part like this */ + .popup-virtual-element sl-popup::part(popup) { + z-index: 1000; + pointer-events: none; + } + + .popup-virtual-element .circle { + width: 100px; + height: 100px; + border: solid 4px var(--sl-color-primary-600); + border-radius: 50%; + translate: -50px -50px; + animation: 1s virtual-cursor infinite; + } + + @keyframes virtual-cursor { + 0% { scale: 1; } + 50% { scale: 1.1; } + } +`; + +const App = () => { + const [enabled, setEnabled] = useState(false); + const [clientX, setClientX] = useState(0); + const [clientY, setClientY] = useState(0); + const popup = useRef(null); + const circle = useRef(null); + const virtualElement = { + getBoundingClientRect() { + return { + width: 0, + height: 0, + x: clientX, + y: clientY, + top: clientY, + left: clientX, + right: clientX, + bottom: clientY + }; + } + }; + + // Listen for the mouse to move + document.addEventListener('mousemove', handleMouseMove); + + // Update the virtual element as the mouse moves + function handleMouseMove(event) { + setClientX(event.clientX); + setClientY(event.clientY); + + // Reposition the popup when the virtual anchor moves + if (popup.active) { + popup.current.reposition(); + } + } + + return ( + <> + <div className="popup-virtual-element"> + <SlPopup + ref={popup} + placement="right-start" + active={enabled} + anchor={virtualElement} + > + <div ref={circle} className="circle" /> + </SlPopup> + + <SlSwitch checked={enabled} onSlChange={event => setEnabled(event.target.checked)}> + Highlight mouse cursor + </SlSwitch> + </div> + + <style>{css}</style> + </> + ); +}; +
+ Sometimes the getBoundingClientRects
might be derived from a real element. In this case provide
+ the anchor element as context to ensure clipping and position updates for the popup work well.
+
+ +const virtualElement = { + getBoundingClientRect() { + // ... + return { width, height, x, y, top, left, right, bottom }; + }, + contextElement: anchorElement +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/popup/popup.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/popup/popup.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/popup/popup.js';
To import this component as a React component:
++import SlPopup from '@onsonr/nebula/dist/react/popup';
Name | +Description | +
---|---|
(default) | +The popup’s content. | +
+ anchor
+ |
+
+ The element the popup will be anchored to. If the anchor lives outside of the popup, you can use the
+ anchor attribute or property instead.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ popup
+ |
+ + A reference to the internal popup container. Useful for animating and styling the popup with + JavaScript. + | ++ |
+ HTMLElement
+ |
+ - | +
+ anchor
+ |
+
+ The element the popup will be anchored to. If the anchor lives outside of the popup, you can provide
+ the anchor element id , a DOM element reference, or a VirtualElement . If
+ the anchor lives inside the popup, use the anchor slot instead.
+ |
+ + |
+ Element | string | VirtualElement
+ |
+ - | +
+ active
+ |
+ + Activates the positioning logic and shows the popup. When this attribute is removed, the positioning + logic is torn down and the popup will be hidden. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ placement
+ |
+ + The preferred placement of the popup. Note that the actual placement will vary as configured to keep + the panel inside of the viewport. + | +
+ |
+
+
+ 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'right' |
+ 'right-start' | 'right-end' | 'left' | 'left-start' | 'left-end'
+ |
+
+ 'top'
+ |
+
+ strategy
+ |
+
+ Determines how the popup is positioned. The absolute strategy works well in most cases,
+ but if overflow is clipped, using a fixed position strategy can often workaround it.
+ |
+
+ |
+
+ 'absolute' | 'fixed'
+ |
+
+ 'absolute'
+ |
+
+ distance
+ |
+ The distance in pixels from which to offset the panel away from its anchor. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ skidding
+ |
+ The distance in pixels from which to offset the panel along its anchor. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ arrow
+ |
+
+ Attaches an arrow to the popup. The arrow’s size and color can be customized using the
+ --arrow-size and --arrow-color custom properties. For additional
+ customizations, you can also target the arrow using ::part(arrow) in your stylesheet.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
+ arrowPlacement
+
+ + arrow-placement
+
+ |
+
+ The placement of the arrow. The default is anchor , which will align the arrow as close
+ to the center of the anchor as possible, considering available space and arrow-padding .
+ A value of start , end , or center will align the arrow to the
+ start, end, or center of the popover instead.
+ |
+ + |
+ 'start' | 'end' | 'center' | 'anchor'
+ |
+
+ 'anchor'
+ |
+
+ arrowPadding
+
+ + arrow-padding
+
+ |
+ + The amount of padding between the arrow and the edges of the popup. If the popup has a + border-radius, for example, this will prevent it from overflowing the corners. + | ++ |
+ number
+ |
+
+ 10
+ |
+
+ flip
+ |
+
+ When set, placement of the popup will flip to the opposite site to keep it in view. You can use
+ flipFallbackPlacements to further configure how the fallback placement is determined.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
+ flipFallbackPlacements
+
+ + flip-fallback-placements
+
+ |
+ + If the preferred placement doesn’t fit, popup will be tested in these fallback placements until one + fits. Must be a string of any number of placements separated by a space, e.g. “top bottom left”. If + no placement fits, the flip fallback strategy will be used instead. + | ++ |
+ string
+ |
+
+ ''
+ |
+
+ flipFallbackStrategy
+
+ + flip-fallback-strategy
+
+ |
+ + When neither the preferred placement nor the fallback placements fit, this value will be used to + determine whether the popup should be positioned using the best available fit based on available + space or as it was initially preferred. + | ++ |
+ 'best-fit' | 'initial'
+ |
+
+ 'best-fit'
+ |
+
+ flipBoundary
+ |
+ + The flip boundary describes clipping element(s) that overflow will be checked relative to when + flipping. By default, the boundary includes overflow ancestors that will cause the element to be + clipped. If needed, you can change the boundary by passing a reference to one or more elements to + this property. + | ++ |
+ Element | Element[]
+ |
+ - | +
+ flipPadding
+
+ + flip-padding
+
+ |
+ The amount of padding, in pixels, to exceed before the flip behavior will occur. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ shift
+ |
+ Moves the popup along the axis to keep it in view when clipped. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ shiftBoundary
+ |
+ + The shift boundary describes clipping element(s) that overflow will be checked relative to when + shifting. By default, the boundary includes overflow ancestors that will cause the element to be + clipped. If needed, you can change the boundary by passing a reference to one or more elements to + this property. + | ++ |
+ Element | Element[]
+ |
+ - | +
+ shiftPadding
+
+ + shift-padding
+
+ |
+ The amount of padding, in pixels, to exceed before the shift behavior will occur. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ autoSize
+
+ + auto-size
+
+ |
+ + When set, this will cause the popup to automatically resize itself to prevent it from overflowing. + | ++ |
+ 'horizontal' | 'vertical' | 'both'
+ |
+ - | +
+ sync
+ |
+ Syncs the popup’s width or height to that of the anchor element. | ++ |
+ 'width' | 'height' | 'both'
+ |
+ - | +
+ autoSizeBoundary
+ |
+ + The auto-size boundary describes clipping element(s) that overflow will be checked relative to when + resizing. By default, the boundary includes overflow ancestors that will cause the element to be + clipped. If needed, you can change the boundary by passing a reference to one or more elements to + this property. + | ++ |
+ Element | Element[]
+ |
+ - | +
+ autoSizePadding
+
+ + auto-size-padding
+
+ |
+ The amount of padding, in pixels, to exceed before the auto-size behavior will occur. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ hoverBridge
+
+ + hover-bridge
+
+ |
+
+ When a gap exists between the anchor and the popup element, this option will add a “hover bridge”
+ that fills the gap using an invisible element. This makes listening for events such as
+ mouseenter and mouseleave more sane because the pointer never technically
+ leaves the element. The hover bridge will only be drawn when the popover is active.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-reposition |
+ onSlReposition |
+ + Emitted when the popup is repositioned. This event can fire a lot, so avoid putting expensive + operations in your listener or consider debouncing it. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
reposition() |
+ Forces the popup to recalculate and reposition itself. | +- | +
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--arrow-size |
+
+ The size of the arrow. Note that an arrow won’t be shown unless the arrow attribute is
+ used.
+ |
+ 6px | +
--arrow-color |
+ The color of the arrow. | +var(–sl-color-neutral-0) | +
--auto-size-available-width |
+
+ A read-only custom property that determines the amount of width the popup can be before overflowing.
+ Useful for positioning child elements that need to overflow. This property is only available when
+ using auto-size .
+ |
+ + |
--auto-size-available-height |
+
+ A read-only custom property that determines the amount of height the popup can be before
+ overflowing. Useful for positioning child elements that need to overflow. This property is only
+ available when using auto-size .
+ |
+ + |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
arrow |
+
+ The arrow’s container. Avoid setting top|bottom|left|right properties, as these values
+ are assigned dynamically as the popup moves. This is most useful for applying a background color to
+ match the popup, and maybe a border or box shadow.
+ |
+
popup |
+ The popup’s container. Useful for setting a background color, box shadow, etc. | +
hover-bridge |
+
+ The hover bridge element. Only available when the hover-bridge option is enabled.
+ |
+
+ Learn more about customizing CSS parts. +
+<sl-progress-bar> | SlProgressBar
+ Progress bars are used to show the status of an ongoing operation.
+ ++<sl-progress-bar value="50"></sl-progress-bar> +
+import SlProgressBar from '@onsonr/nebula/dist/react/progress-bar'; + +const App = () => <SlProgressBar value={50} />; +
+ Use the label
attribute to label the progress bar and tell assistive devices how to announce
+ it.
+
+<sl-progress-bar value="50" label="Upload progress"></sl-progress-bar> +
+import SlProgressBar from '@onsonr/nebula/dist/react/progress-bar'; + +const App = () => <SlProgressBar value="50" label="Upload progress" />; +
Use the --height
custom property to set the progress bar’s height.
+<sl-progress-bar value="50" style="--height: 6px;"></sl-progress-bar> +
+import SlProgressBar from '@onsonr/nebula/dist/react/progress-bar'; + +const App = () => <SlProgressBar value={50} style={{ '--height': '6px' }} />; +
Use the default slot to show a value.
+ ++<sl-progress-bar value="50" class="progress-bar-values">50%</sl-progress-bar> + +<br /> + +<sl-button circle><sl-icon name="dash" label="Decrease"></sl-icon></sl-button> +<sl-button circle><sl-icon name="plus" label="Increase"></sl-icon></sl-button> + +<script> + const progressBar = document.querySelector('.progress-bar-values'); + const subtractButton = progressBar.nextElementSibling.nextElementSibling; + const addButton = subtractButton.nextElementSibling; + + addButton.addEventListener('click', () => { + const value = Math.min(100, progressBar.value + 10); + progressBar.value = value; + progressBar.textContent = `${value}%`; + }); + + subtractButton.addEventListener('click', () => { + const value = Math.max(0, progressBar.value - 10); + progressBar.value = value; + progressBar.textContent = `${value}%`; + }); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlProgressBar from '@onsonr/nebula/dist/react/progress-bar'; + +const App = () => { + const [value, setValue] = useState(50); + + function adjustValue(amount) { + let newValue = value + amount; + if (newValue < 0) newValue = 0; + if (newValue > 100) newValue = 100; + setValue(newValue); + } + + return ( + <> + <SlProgressBar value={value}>{value}%</SlProgressBar> + + <br /> + + <SlButton circle onClick={() => adjustValue(-10)}> + <SlIcon name="dash" label="Decrease" /> + </SlButton> + + <SlButton circle onClick={() => adjustValue(10)}> + <SlIcon name="plus" label="Increase" /> + </SlButton> + </> + ); +}; +
+ The indeterminate
attribute can be used to inform the user that the operation is pending, but
+ its status cannot currently be determined. In this state, value
is ignored and the label, if
+ present, will not be shown.
+
+<sl-progress-bar indeterminate></sl-progress-bar> +
+import SlProgressBar from '@onsonr/nebula/dist/react/progress-bar'; + +const App = () => <SlProgressBar indeterminate />; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/progress-bar/progress-bar.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/progress-bar/progress-bar.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/progress-bar/progress-bar.js';
To import this component as a React component:
++import SlProgressBar from '@onsonr/nebula/dist/react/progress-bar';
Name | +Description | +
---|---|
(default) | +A label to show inside the progress indicator. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The current progress as a percentage, 0 to 100. | +
+ |
+
+ number
+ |
+
+ 0
+ |
+
+ indeterminate
+ |
+ + When true, percentage is ignored, the label is hidden, and the progress bar is drawn in an + indeterminate state. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ label
+ |
+ A custom label for assistive devices. | ++ |
+ string
+ |
+
+ ''
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Default | +
---|---|---|
--height |
+ The progress bar’s height. | ++ |
--track-color |
+ The color of the track. | ++ |
--indicator-color |
+ The color of the indicator. | ++ |
--label-color |
+ The color of the label. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
indicator |
+ The progress bar’s indicator. | +
label |
+ The progress bar’s label. | +
+ Learn more about customizing CSS parts. +
+<sl-progress-ring> | SlProgressRing
+ + Progress rings are used to show the progress of a determinate operation in a circular fashion. +
+ ++<sl-progress-ring value="25"></sl-progress-ring> +
+import SlProgressRing from '@onsonr/nebula/dist/react/progress-ring'; + +const App = () => <SlProgressRing value="25" />; +
Use the --size
custom property to set the diameter of the progress ring.
+<sl-progress-ring value="50" style="--size: 200px;"></sl-progress-ring> +
+import SlProgressRing from '@onsonr/nebula/dist/react/progress-ring'; + +const App = () => <SlProgressRing value="50" style={{ '--size': '200px' }} />; +
+ Use the --track-width
and --indicator-width
custom properties to set the width of
+ the progress ring’s track and indicator.
+
+<sl-progress-ring value="50" style="--track-width: 6px; --indicator-width: 12px;"></sl-progress-ring> +
+import SlProgressRing from '@onsonr/nebula/dist/react/progress-ring'; + +const App = () => <SlProgressRing value="50" style={{ '--track-width': '6px', '--indicator-width': '12px' }} />; +
+ To change the color, use the --track-color
and --indicator-color
custom
+ properties.
+
+<sl-progress-ring + value="50" + style=" + --track-color: pink; + --indicator-color: deeppink; + " +></sl-progress-ring> +
+import SlProgressRing from '@onsonr/nebula/dist/react/progress-ring'; + +const App = () => ( + <SlProgressRing + value="50" + style={{ + '--track-color': 'pink', + '--indicator-color': 'deeppink' + }} + /> +); +
+ Use the label
attribute to label the progress ring and tell assistive devices how to announce
+ it.
+
+<sl-progress-ring value="50" label="Upload progress"></sl-progress-ring> +
+import SlProgressRing from '@onsonr/nebula/dist/react/progress-ring'; + +const App = () => <SlProgressRing value="50" label="Upload progress" />; +
Use the default slot to show a label inside the progress ring.
+ ++<sl-progress-ring value="50" class="progress-ring-values" style="margin-bottom: .5rem;">50%</sl-progress-ring> + +<br /> + +<sl-button circle><sl-icon name="dash" label="Decrease"></sl-icon></sl-button> +<sl-button circle><sl-icon name="plus" label="Increase"></sl-icon></sl-button> + +<script> + const progressRing = document.querySelector('.progress-ring-values'); + const subtractButton = progressRing.nextElementSibling.nextElementSibling; + const addButton = subtractButton.nextElementSibling; + + addButton.addEventListener('click', () => { + const value = Math.min(100, progressRing.value + 10); + progressRing.value = value; + progressRing.textContent = `${value}%`; + }); + + subtractButton.addEventListener('click', () => { + const value = Math.max(0, progressRing.value - 10); + progressRing.value = value; + progressRing.textContent = `${value}%`; + }); +</script> +
+import { useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlProgressRing from '@onsonr/nebula/dist/react/progress-ring'; + +const App = () => { + const [value, setValue] = useState(50); + + function adjustValue(amount) { + let newValue = value + amount; + if (newValue < 0) newValue = 0; + if (newValue > 100) newValue = 100; + setValue(newValue); + } + + return ( + <> + <SlProgressRing value={value} style={{ marginBottom: '.5rem' }}> + {value}% + </SlProgressRing> + + <br /> + + <SlButton circle onClick={() => adjustValue(-10)}> + <SlIcon name="dash" label="Decrease" /> + </SlButton> + + <SlButton circle onClick={() => adjustValue(10)}> + <SlIcon name="plus" label="Increase" /> + </SlButton> + </> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/progress-ring/progress-ring.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/progress-ring/progress-ring.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/progress-ring/progress-ring.js';
To import this component as a React component:
++import SlProgressRing from '@onsonr/nebula/dist/react/progress-ring';
Name | +Description | +
---|---|
(default) | +A label to show inside the ring. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The current progress as a percentage, 0 to 100. | +
+ |
+
+ number
+ |
+
+ 0
+ |
+
+ label
+ |
+ A custom label for assistive devices. | ++ |
+ string
+ |
+
+ ''
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Default | +
---|---|---|
--size |
+ The diameter of the progress ring (cannot be a percentage). | ++ |
--track-width |
+ The width of the track. | ++ |
--track-color |
+ The color of the track. | ++ |
--indicator-width |
+ The width of the indicator. Defaults to the track width. | ++ |
--indicator-color |
+ The color of the indicator. | ++ |
--indicator-transition-duration |
+ The duration of the indicator’s transition when the value changes. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
label |
+ The progress ring label. | +
+ Learn more about customizing CSS parts. +
+<sl-qr-code> | SlQrCode
+ + Generates a + QR code + and renders it using the + Canvas API. +
+ ++ QR codes are useful for providing small pieces of information to users who can quickly scan them with a + smartphone. Most smartphones have built-in QR code scanners, so simply pointing the camera at a QR code will + decode it and allow the user to visit a website, dial a phone number, read a message, etc. +
+ ++<div class="qr-overview"> + <sl-qr-code value="https://shoelace.style/" label="Scan this code to visit Nebula on the web!"></sl-qr-code> + <br /> + + <sl-input maxlength="255" clearable label="Value"></sl-input> +</div> + +<script> + const container = document.querySelector('.qr-overview'); + const qrCode = container.querySelector('sl-qr-code'); + const input = container.querySelector('sl-input'); + + customElements.whenDefined('sl-qr-code').then(() => { + input.value = qrCode.value; + input.addEventListener('sl-input', () => (qrCode.value = input.value)); + }); +</script> + +<style> + .qr-overview { + max-width: 256px; + } + + .qr-overview sl-input { + margin-top: 1rem; + } +</style> +
+import { useState } from 'react'; +import SlQrCode from '@onsonr/nebula/dist/react/qr-code'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const css = ` + .qr-overview { + max-width: 256px; + } + + .qr-overview sl-input { + margin-top: 1rem; + } +`; + +const App = () => { + const [value, setValue] = useState('https://shoelace.style/'); + + return ( + <> + <div className="qr-overview"> + <SlQrCode value={value} label="Scan this code to visit Nebula on the web!" /> + <br /> + + <SlInput maxlength="255" clearable onInput={event => setValue(event.target.value)} /> + </div> + + <style>{css}</style> + </> + ); +}; +
+ Use the fill
and background
attributes to modify the QR code’s colors. You should
+ always ensure good contrast for optimal compatibility with QR code scanners.
+
+<sl-qr-code value="https://shoelace.style/" fill="deeppink" background="white"></sl-qr-code> +
+import SlQrCode from '@onsonr/nebula/dist/react/qr-code'; + +const App = () => <SlQrCode value="https://shoelace.style/" fill="deeppink" background="white" />; +
Use the size
attribute to change the size of the QR code.
+<sl-qr-code value="https://shoelace.style/" size="64"></sl-qr-code> +
+import SlQrCode from '@onsonr/nebula/dist/react/qr-code'; + +const App = () => <SlQrCode value="https://shoelace.style/" size="64" />; +
Create a rounded effect with the radius
attribute.
+<sl-qr-code value="https://shoelace.style/" radius="0.5"></sl-qr-code> +
+import SlQrCode from '@onsonr/nebula/dist/react/qr-code'; + +const App = () => <SlQrCode value="https://shoelace.style/" radius="0.5" />; +
+ QR codes can be rendered with various levels of
+ error correction
+ that can be set using the error-correction
attribute. This example generates four codes with
+ the same value using different error correction levels.
+
+<div class="qr-error-correction"> + <sl-qr-code value="https://shoelace.style/" error-correction="L"></sl-qr-code> + <sl-qr-code value="https://shoelace.style/" error-correction="M"></sl-qr-code> + <sl-qr-code value="https://shoelace.style/" error-correction="Q"></sl-qr-code> + <sl-qr-code value="https://shoelace.style/" error-correction="H"></sl-qr-code> +</div> + +<style> + .qr-error-correction { + display: flex; + flex-wrap: wrap; + gap: 1rem; + } +</style> +
+import SlQrCode from '@onsonr/nebula/dist/react/qr-code'; + +const css = ` + .qr-error-correction { + display: flex; + flex-wrap: wrap; + gap: 1rem; + } +`; + +const App = () => { + return ( + <> + <div className="qr-error-correction"> + <SlQrCode value="https://shoelace.style/" error-correction="L" /> + <SlQrCode value="https://shoelace.style/" error-correction="M" /> + <SlQrCode value="https://shoelace.style/" error-correction="Q" /> + <SlQrCode value="https://shoelace.style/" error-correction="H" /> + </div> + + <style>{css}</style> + </> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/qr-code/qr-code.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/qr-code/qr-code.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/qr-code/qr-code.js';
To import this component as a React component:
++import SlQrCode from '@onsonr/nebula/dist/react/qr-code';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The QR code’s value. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ label
+ |
+ The label for assistive devices to announce. If unspecified, the value will be used instead. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ size
+ |
+ The size of the QR code, in pixels. | ++ |
+ number
+ |
+
+ 128
+ |
+
+ fill
+ |
+ The fill color. This can be any valid CSS color, but not a CSS custom property. | ++ |
+ string
+ |
+
+ 'black'
+ |
+
+ background
+ |
+
+ The background color. This can be any valid CSS color or transparent . It cannot be a
+ CSS custom property.
+ |
+ + |
+ string
+ |
+
+ 'white'
+ |
+
+ radius
+ |
+ The edge radius of each module. Must be between 0 and 0.5. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ errorCorrection
+
+ + error-correction
+
+ |
+ + The level of error correction to use. + Learn more + | ++ |
+ 'L' | 'M' | 'Q' | 'H'
+ |
+
+ 'H'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-radio-button> | SlRadioButton
+ + Radios buttons allow the user to select a single option from a group using a button-like control. +
+ ++ Radio buttons are designed to be used with radio groups. When a radio + button has focus, the arrow keys can be used to change the selected option just like standard radio + controls. +
+ ++<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio-button value="1">Option 1</sl-radio-button> + <sl-radio-button value="2">Option 2</sl-radio-button> + <sl-radio-button value="3">Option 3</sl-radio-button> +</sl-radio-group> +
+import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadioButton value="1">Option 1</SlRadioButton> + <SlRadioButton value="2">Option 2</SlRadioButton> + <SlRadioButton value="3">Option 3</SlRadioButton> + </SlRadioGroup> +); +
+ To set the initial value and checked state, use the value
attribute on the containing radio
+ group.
+
+<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio-button value="1">Option 1</sl-radio-button> + <sl-radio-button value="2">Option 2</sl-radio-button> + <sl-radio-button value="3">Option 3</sl-radio-button> +</sl-radio-group> +
+import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadioButton value="1">Option 1</SlRadioButton> + <SlRadioButton value="2">Option 2</SlRadioButton> + <SlRadioButton value="3">Option 3</SlRadioButton> + </SlRadioGroup> +); +
Use the disabled
attribute to disable a radio button.
+<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio-button value="1">Option 1</sl-radio-button> + <sl-radio-button value="2" disabled>Option 2</sl-radio-button> + <sl-radio-button value="3">Option 3</sl-radio-button> +</sl-radio-group> +
+import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadioButton value="1">Option 1</SlRadioButton> + <SlRadioButton value="2" disabled> + Option 2 + </SlRadioButton> + <SlRadioButton value="3">Option 3</SlRadioButton> + </SlRadioGroup> +); +
Use the size
attribute to change a radio button’s size.
+<sl-radio-group size="small" label="Select an option" name="a" value="1"> + <sl-radio-button value="1">Option 1</sl-radio-button> + <sl-radio-button value="2">Option 2</sl-radio-button> + <sl-radio-button value="3">Option 3</sl-radio-button> +</sl-radio-group> + +<br /> + +<sl-radio-group size="medium" label="Select an option" name="a" value="1"> + <sl-radio-button value="1">Option 1</sl-radio-button> + <sl-radio-button value="2">Option 2</sl-radio-button> + <sl-radio-button value="3">Option 3</sl-radio-button> +</sl-radio-group> + +<br /> + +<sl-radio-group size="large" label="Select an option" name="a" value="1"> + <sl-radio-button value="1">Option 1</sl-radio-button> + <sl-radio-button value="2">Option 2</sl-radio-button> + <sl-radio-button value="3">Option 3</sl-radio-button> +</sl-radio-group> +
+import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup size="small" label="Select an option" name="a" value="1"> + <SlRadioButton value="1">Option 1</SlRadioButton> + <SlRadioButton value="2">Option 2</SlRadioButton> + <SlRadioButton value="3">Option 3</SlRadioButton> + </SlRadioGroup> + + <br /> + + <SlRadioGroup size="medium" label="Select an option" name="a" value="1"> + <SlRadioButton value="1">Option 1</SlRadioButton> + <SlRadioButton value="2">Option 2</SlRadioButton> + <SlRadioButton value="3">Option 3</SlRadioButton> + </SlRadioGroup> + + <br /> + + <SlRadioGroup size="large" label="Select an option" name="a" value="1"> + <SlRadioButton value="1">Option 1</SlRadioButton> + <SlRadioButton value="2">Option 2</SlRadioButton> + <SlRadioButton value="3">Option 3</SlRadioButton> + </SlRadioGroup> +); +
Use the pill
attribute to give radio buttons rounded edges.
+<sl-radio-group size="small" label="Select an option" name="a" value="1"> + <sl-radio-button pill value="1">Option 1</sl-radio-button> + <sl-radio-button pill value="2">Option 2</sl-radio-button> + <sl-radio-button pill value="3">Option 3</sl-radio-button> +</sl-radio-group> + +<br /> + +<sl-radio-group size="medium" label="Select an option" name="a" value="1"> + <sl-radio-button pill value="1">Option 1</sl-radio-button> + <sl-radio-button pill value="2">Option 2</sl-radio-button> + <sl-radio-button pill value="3">Option 3</sl-radio-button> +</sl-radio-group> + +<br /> + +<sl-radio-group size="large" label="Select an option" name="a" value="1"> + <sl-radio-button pill value="1">Option 1</sl-radio-button> + <sl-radio-button pill value="2">Option 2</sl-radio-button> + <sl-radio-button pill value="3">Option 3</sl-radio-button> +</sl-radio-group> +
+import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup size="small" label="Select an option" name="a" value="1"> + <SlRadioButton pill value="1">Option 1</SlRadioButton> + <SlRadioButton pill value="2">Option 2</SlRadioButton> + <SlRadioButton pill value="3">Option 3</SlRadioButton> + </SlRadioGroup> + + <br /> + + <SlRadioGroup size="medium" label="Select an option" name="a" value="1"> + <SlRadioButton pill value="1">Option 1</SlRadioButton> + <SlRadioButton pill value="2">Option 2</SlRadioButton> + <SlRadioButton pill value="3">Option 3</SlRadioButton> + </SlRadioGroup> + + <br /> + + <SlRadioGroup size="large" label="Select an option" name="a" value="1"> + <SlRadioButton pill value="1">Option 1</SlRadioButton> + <SlRadioButton pill value="2">Option 2</SlRadioButton> + <SlRadioButton pill value="3">Option 3</SlRadioButton> + </SlRadioGroup> +); +
Use the prefix
and suffix
slots to add icons.
+<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio-button value="1"> + <sl-icon slot="prefix" name="archive"></sl-icon> + Option 1 + </sl-radio-button> + + <sl-radio-button value="2"> + <sl-icon slot="suffix" name="bag"></sl-icon> + Option 2 + </sl-radio-button> + + <sl-radio-button value="3"> + <sl-icon slot="prefix" name="gift"></sl-icon> + <sl-icon slot="suffix" name="cart"></sl-icon> + Option 3 + </sl-radio-button> +</sl-radio-group> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadioButton value="1"> + <SlIcon slot="prefix" name="archive" /> + Option 1 + </SlRadioButton> + + <SlRadioButton value="2"> + <SlIcon slot="suffix" name="bag" /> + Option 2 + </SlRadioButton> + + <SlRadioButton value="3"> + <SlIcon slot="prefix" name="gift" /> + <SlIcon slot="suffix" name="cart" /> + Option 3 + </SlRadioButton> + </SlRadioGroup> +); +
+ You can omit button labels and use icons instead. Make sure to set a label
attribute on each
+ icon so screen readers will announce each option correctly.
+
+<sl-radio-group label="Select an option" name="a" value="neutral"> + <sl-radio-button value="angry"> + <sl-icon name="emoji-angry" label="Angry"></sl-icon> + </sl-radio-button> + + <sl-radio-button value="sad"> + <sl-icon name="emoji-frown" label="Sad"></sl-icon> + </sl-radio-button> + + <sl-radio-button value="neutral"> + <sl-icon name="emoji-neutral" label="Neutral"></sl-icon> + </sl-radio-button> + + <sl-radio-button value="happy"> + <sl-icon name="emoji-smile" label="Happy"></sl-icon> + </sl-radio-button> + + <sl-radio-button value="laughing"> + <sl-icon name="emoji-laughing" label="Laughing"></sl-icon> + </sl-radio-button> +</sl-radio-group> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="neutral"> + <SlRadioButton value="angry"> + <SlIcon name="emoji-angry" label="Angry" /> + </SlRadioButton> + + <SlRadioButton value="sad"> + <SlIcon name="emoji-frown" label="Sad" /> + </SlRadioButton> + + <SlRadioButton value="neutral"> + <SlIcon name="emoji-neutral" label="Neutral" /> + </SlRadioButton> + + <SlRadioButton value="happy"> + <SlIcon name="emoji-smile" label="Happy" /> + </SlRadioButton> + + <SlRadioButton value="laughing"> + <SlIcon name="emoji-laughing" label="Laughing" /> + </SlRadioButton> + </SlRadioGroup> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/radio-button/radio-button.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/radio-button/radio-button.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/radio-button/radio-button.js';
To import this component as a React component:
++import SlRadioButton from '@onsonr/nebula/dist/react/radio-button';
Name | +Description | +
---|---|
(default) | +The radio button’s label. | +
+ prefix
+ |
+ A presentational prefix icon or similar element. | +
+ suffix
+ |
+ A presentational suffix icon or similar element. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The radio’s value. When selected, the radio group will receive this value. | ++ |
+ string
+ |
+ - | +
+ disabled
+ |
+ Disables the radio button. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ size
+ |
+ + The radio button’s size. When used inside a radio group, the size will be determined by the radio + group’s size so this attribute can typically be omitted. + | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ pill
+ |
+ Draws a pill-style radio button with rounded edges. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the button loses focus. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the button gains focus. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
focus() |
+ Sets focus on the radio button. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the radio button. | +- | +
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
button |
+ The internal <button> element. |
+
button--checked |
+ The internal button element when the radio button is checked. | +
prefix |
+ The container that wraps the prefix. | +
label |
+ The container that wraps the radio button’s label. | +
suffix |
+ The container that wraps the suffix. | +
+ Learn more about customizing CSS parts. +
+<sl-radio-group> | SlRadioGroup
+ + Radio groups are used to group multiple radios or + radio buttons so they function as a single form control. +
+ ++<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio value="1">Option 1</sl-radio> + <sl-radio value="2">Option 2</sl-radio> + <sl-radio value="3">Option 3</sl-radio> +</sl-radio-group> +
+import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadio value="1">Option 1</SlRadio> + <SlRadio value="2">Option 2</SlRadio> + <SlRadio value="3">Option 3</SlRadio> + </SlRadioGroup> +); +
+ Add descriptive help text to a radio group with the help-text
attribute. For help texts that
+ contain HTML, use the help-text
slot instead.
+
+<sl-radio-group label="Select an option" help-text="Choose the most appropriate option." name="a" value="1"> + <sl-radio value="1">Option 1</sl-radio> + <sl-radio value="2">Option 2</sl-radio> + <sl-radio value="3">Option 3</sl-radio> +</sl-radio-group> +
+import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" help-text="Choose the most appropriate option." name="a" value="1"> + <SlRadio value="1">Option 1</SlRadio> + <SlRadio value="2">Option 2</SlRadio> + <SlRadio value="3">Option 3</SlRadio> + </SlRadioGroup> +); +
+ Radio buttons offer an alternate way to display radio controls. In + this case, an internal button group is used to group the buttons into + a single, cohesive control. +
+ ++<sl-radio-group label="Select an option" help-text="Select an option that makes you proud." name="a" value="1"> + <sl-radio-button value="1">Option 1</sl-radio-button> + <sl-radio-button value="2">Option 2</sl-radio-button> + <sl-radio-button value="3">Option 3</sl-radio-button> +</sl-radio-group> +
+import SlRadioButton from '@onsonr/nebula/dist/react/radio-button'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadioButton value="1">Option 1</SlRadioButton> + <SlRadioButton value="2">Option 2</SlRadioButton> + <SlRadioButton value="3">Option 3</SlRadioButton> + </SlRadioGroup> +); +
+ Radios and radio buttons can be disabled by adding the disabled
attribute to the respective
+ options inside the radio group.
+
+<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio value="1">Option 1</sl-radio> + <sl-radio value="2" disabled>Option 2</sl-radio> + <sl-radio value="3">Option 3</sl-radio> +</sl-radio-group> +
+import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadio value="1">Option 1</SlRadio> + <SlRadio value="2" disabled> + Option 2 + </SlRadio> + <SlRadio value="3">Option 3</SlRadio> + </SlRadioGroup> +); +
+ The size of Radios and
+ Radio Buttons will be determined by the Radio Group’s
+ size
attribute.
+
+<sl-radio-group label="Select an option" size="medium" value="medium" class="radio-group-size"> + <sl-radio value="small">Small</sl-radio> + <sl-radio value="medium">Medium</sl-radio> + <sl-radio value="large">Large</sl-radio> +</sl-radio-group> + +<script> + const radioGroup = document.querySelector('.radio-group-size'); + + radioGroup.addEventListener('sl-change', () => { + radioGroup.size = radioGroup.value; + }); +</script> +
+import { useState } from 'react'; +import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => { + const [size, setSize] = useState('medium'); + + return ( + <> + <SlRadioGroup + label="Select an option" + size={size} + value={size} + class="radio-group-size" + onSlChange={event => setSize(event.target.value)} + > + <SlRadio value="small">Small</SlRadio> + <SlRadio value="medium">Medium</SlRadio> + <SlRadio value="large">Large</SlRadio> + </SlRadioGroup> + </> + ); +}; +
+ Radios and Radio Buttons also have
+ a size
attribute. This can be useful in certain compositions, but it will be ignored when
+ used inside of a Radio Group.
+
+ Setting the required
attribute to make selecting an option mandatory. If a value has not been
+ selected, it will prevent the form from submitting and display an error message.
+
+<form class="validation"> + <sl-radio-group label="Select an option" name="a" required> + <sl-radio value="1">Option 1</sl-radio> + <sl-radio value="2">Option 2</sl-radio> + <sl-radio value="3">Option 3</sl-radio> + </sl-radio-group> + <br /> + <sl-button type="submit" variant="primary">Submit</sl-button> +</form> + +<script> + const form = document.querySelector('.validation'); + + // Handle form submit + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); +</script> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; +const App = () => { + function handleSubmit(event) { + event.preventDefault(); + alert('All fields are valid!'); + } + + return ( + <form class="custom-validity" onSubmit={handleSubmit}> + <SlRadioGroup label="Select an option" name="a" required onSlChange={handleChange}> + <SlRadio value="1"> + Option 1 + </SlRadio> + <SlRadiovalue="2"> + Option 2 + </SlRadio> + <SlRadio value="3"> + Option 3 + </SlRadio> + </SlRadioGroup> + <br /> + <SlButton type="submit" variant="primary"> + Submit + </SlButton> + </form> + ); +}; +
+ Use the setCustomValidity()
method to set a custom validation message. This will prevent the
+ form from submitting and make the browser display the error message you provide. To clear the error, call
+ this function with an empty string.
+
+<form class="custom-validity"> + <sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio value="1">Not me</sl-radio> + <sl-radio value="2">Me neither</sl-radio> + <sl-radio value="3">Choose me</sl-radio> + </sl-radio-group> + <br /> + <sl-button type="submit" variant="primary">Submit</sl-button> +</form> + +<script> + const form = document.querySelector('.custom-validity'); + const radioGroup = form.querySelector('sl-radio-group'); + const errorMessage = 'You must choose the last option'; + + // Set initial validity as soon as the element is defined + customElements.whenDefined('sl-radio').then(() => { + radioGroup.setCustomValidity(errorMessage); + }); + + // Update validity when a selection is made + form.addEventListener('sl-change', () => { + const isValid = radioGroup.value === '3'; + radioGroup.setCustomValidity(isValid ? '' : errorMessage); + }); + + // Handle form submit + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); +</script> +
+import { useEffect, useRef } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; +const App = () => { + const radioGroup = useRef(null); + const errorMessage = 'You must choose this option'; + + function handleChange() { + radioGroup.current.setCustomValidity(radioGroup.current.value === '3' ? '' : errorMessage); + } + + function handleSubmit(event) { + event.preventDefault(); + alert('All fields are valid!'); + } + + useEffect(() => { + radio.current.setCustomValidity(errorMessage); + }, []); + + return ( + <form class="custom-validity" onSubmit={handleSubmit}> + <SlRadioGroup ref={radioGroup} label="Select an option" name="a" value="1" onSlChange={handleChange}> + <SlRadio value="1">Not me</SlRadio> + <SlRadio value="2">Me neither</SlRadio> + <SlRadio value="3">Choose me</SlRadio> + </SlRadioGroup> + <br /> + <SlButton type="submit" variant="primary"> + Submit + </SlButton> + </form> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/radio-group/radio-group.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/radio-group/radio-group.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/radio-group/radio-group.js';
To import this component as a React component:
++import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group';
Name | +Description | +
---|---|
(default) | +
+ The default slot where <sl-radio> or
+ <sl-radio-button> elements are placed.
+ |
+
+ label
+ |
+
+ The radio group’s label. Required for proper accessibility. Alternatively, you can use the
+ label attribute.
+ |
+
+ help-text
+ |
+
+ Text that describes how to use the radio group. Alternatively, you can use the
+ help-text attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ label
+ |
+
+ The radio group’s label. Required for proper accessibility. If you need to display HTML, use the
+ label slot instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ helpText
+
+ + help-text
+
+ |
+
+ The radio groups’s help text. If you need to display HTML, use the help-text slot
+ instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ name
+ |
+ The name of the radio group, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ 'option'
+ |
+
+ value
+ |
+ The current value of the radio group, submitted as a name/value pair with form data. | +
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ size
+ |
+ The radio group’s size. This size will be applied to all child radios and radio buttons. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ required
+ |
+ Ensures a child radio is checked before allowing the containing form to submit. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-change |
+ onSlChange |
+ Emitted when the radio group’s selected value changes. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the radio group receives user input. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message:
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
form-control |
+ The form control that wraps the label, input, and help text. | +
form-control-label |
+ The label’s wrapper. | +
form-control-input |
+ The input’s wrapper. | +
form-control-help-text |
+ The help text’s wrapper. | +
button-group |
+ The button group that wraps radio buttons. | +
button-group__base |
+ The button group’s base part. |
+
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-button-group>
<sl-radio> | SlRadio
+ Radios allow the user to select a single option from a group.
+ +Radios are designed to be used with radio groups.
+ ++<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio value="1">Option 1</sl-radio> + <sl-radio value="2">Option 2</sl-radio> + <sl-radio value="3">Option 3</sl-radio> +</sl-radio-group> +
+import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadio value="1">Option 1</SlRadio> + <SlRadio value="2">Option 2</SlRadio> + <SlRadio value="3">Option 3</SlRadio> + </SlRadioGroup> +); +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
+ To set the initial value and checked state, use the value
attribute on the containing radio
+ group.
+
+<sl-radio-group label="Select an option" name="a" value="3"> + <sl-radio value="1">Option 1</sl-radio> + <sl-radio value="2">Option 2</sl-radio> + <sl-radio value="3">Option 3</sl-radio> +</sl-radio-group> +
+import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="3"> + <SlRadio value="1">Option 1</SlRadio> + <SlRadio value="2">Option 2</SlRadio> + <SlRadio value="3">Option 3</SlRadio> + </SlRadioGroup> +); +
Use the disabled
attribute to disable a radio.
+<sl-radio-group label="Select an option" name="a" value="1"> + <sl-radio value="1">Option 1</sl-radio> + <sl-radio value="2" disabled>Option 2</sl-radio> + <sl-radio value="3">Option 3</sl-radio> +</sl-radio-group> +
+import SlRadio from '@onsonr/nebula/dist/react/radio'; +import SlRadioGroup from '@onsonr/nebula/dist/react/radio-group'; + +const App = () => ( + <SlRadioGroup label="Select an option" name="a" value="1"> + <SlRadio value="1">Option 1</SlRadio> + <SlRadio value="2" disabled> + Option 2 + </SlRadio> + <SlRadio value="3">Option 3</SlRadio> + </SlRadioGroup> +); +
+ Add the size
attribute to the Radio Group to change the
+ radios’ size.
+
+<sl-radio-group size="small" value="1"> + <sl-radio value="1">Small 1</sl-radio> + <sl-radio value="2">Small 2</sl-radio> + <sl-radio value="3">Small 3</sl-radio> +</sl-radio-group> + +<br /> + +<sl-radio-group size="medium" value="1"> + <sl-radio value="1">Medium 1</sl-radio> + <sl-radio value="2">Medium 2</sl-radio> + <sl-radio value="3">Medium 3</sl-radio> +</sl-radio-group> + +<br /> + +<sl-radio-group size="large" value="1"> + <sl-radio value="1">Large 1</sl-radio> + <sl-radio value="2">Large 2</sl-radio> + <sl-radio value="3">Large 3</sl-radio> +</sl-radio-group> +
+ +import SlRadio from '@onsonr/nebula/dist/react/radio'; + +const App = () => ( + <> + <SlRadioGroup size="small" value="1"> + <SlRadio value="1">Small 1</SlRadio> + <SlRadio value="2">Small 2</SlRadio> + <SlRadio value="3">Small 3</SlRadio> + </SlRadioGroup> + + <br /> + + <SlRadioGroup size="medium" value="1"> + <SlRadio value="1">Medium 1</SlRadio> + <SlRadio value="2">Medium 2</SlRadio> + <SlRadio value="3">Medium 3</SlRadio> + </SlRadioGroup> + + <br /> + + <SlRadioGroup size="large" value="1"> + <SlRadio value="1">Large 1</SlRadio> + <SlRadio value="2">Large 2</SlRadio> + <SlRadio value="3">Large 3</SlRadio> + </SlRadioGroup> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/radio/radio.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/radio/radio.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/radio/radio.js';
To import this component as a React component:
++import SlRadio from '@onsonr/nebula/dist/react/radio';
Name | +Description | +
---|---|
(default) | +The radio’s label. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ value
+ |
+ The radio’s value. When selected, the radio group will receive this value. | ++ |
+ string
+ |
+ - | +
+ size
+ |
+ + The radio’s size. When used inside a radio group, the size will be determined by the radio group’s + size so this attribute can typically be omitted. + | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ disabled
+ |
+ Disables the radio. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the control loses focus. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the control gains focus. | +- | +
+ Learn more about events. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
control |
+ The circular container that wraps the radio’s checked state. | +
control--checked |
+ The radio control when the radio is checked. | +
checked-icon |
+ The checked icon, an <sl-icon> element. |
+
label |
+ The container that wraps the radio’s label. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-range> | SlRange
+ + Ranges allow the user to select a single value within a given range using a slider. +
+ ++<sl-range></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange />; +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
+ Use the label
attribute to give the range an accessible label. For labels that contain HTML,
+ use the label
slot instead.
+
+<sl-range label="Volume" min="0" max="100"></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange label="Volume" min={0} max={100} />; +
+ Add descriptive help text to a range with the help-text
attribute. For help texts that contain
+ HTML, use the help-text
slot instead.
+
+<sl-range label="Volume" help-text="Controls the volume of the current song." min="0" max="100"></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange label="Volume" help-text="Controls the volume of the current song." min={0} max={100} />; +
+ Use the min
and max
attributes to set the range’s minimum and maximum values,
+ respectively. The step
attribute determines the value’s interval when increasing and
+ decreasing.
+
+<sl-range min="0" max="10" step="1"></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange min={0} max={10} step={1} />; +
Use the disabled
attribute to disable a slider.
+<sl-range disabled></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange disabled />; +
+ By default, the tooltip is shown on top. Set tooltip
to bottom
to show it below
+ the slider.
+
+<sl-range tooltip="bottom"></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange tooltip="bottom" />; +
To disable the tooltip, set tooltip
to none
.
+<sl-range tooltip="none"></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange tooltip="none" />; +
+ You can customize the active and inactive portions of the track using the
+ --track-color-active
and --track-color-inactive
custom properties.
+
+<sl-range + style=" + --track-color-active: var(--sl-color-primary-600); + --track-color-inactive: var(--sl-color-primary-100); +" +></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => ( + <SlRange + style={{ + '--track-color-active': 'var(--sl-color-primary-600)', + '--track-color-inactive': 'var(--sl-color-primary-200)' + }} + /> +); +
+ You can customize the initial offset of the active track using the --track-active-offset
custom
+ property.
+
+<sl-range + min="-100" + max="100" + style=" + --track-color-active: var(--sl-color-primary-600); + --track-color-inactive: var(--sl-color-primary-100); + --track-active-offset: 50%; +" +></sl-range> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => ( + <SlRange + min={-100} + max={100} + style={{ + '--track-color-active': 'var(--sl-color-primary-600)', + '--track-color-inactive': 'var(--sl-color-primary-200)', + '--track-active-offset': '50%' + }} + /> +); +
+ You can change the tooltip’s content by setting the tooltipFormatter
property to a function
+ that accepts the range’s value as an argument.
+
+<sl-range min="0" max="100" step="1" class="range-with-custom-formatter"></sl-range> + +<script> + const range = document.querySelector('.range-with-custom-formatter'); + range.tooltipFormatter = value => `Total - ${value}%`; +</script> +
+import SlRange from '@onsonr/nebula/dist/react/range'; + +const App = () => <SlRange min={0} max={100} step={1} tooltipFormatter={value => `Total - ${value}%`} />; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/range/range.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/range/range.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/range/range.js';
To import this component as a React component:
++import SlRange from '@onsonr/nebula/dist/react/range';
Name | +Description | +
---|---|
+ label
+ |
+ The range’s label. Alternatively, you can use the label attribute. |
+
+ help-text
+ |
+
+ Text that describes how to use the input. Alternatively, you can use the
+ help-text attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The name of the range, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+ The current value of the range, submitted as a name/value pair with form data. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ label
+ |
+ The range’s label. If you need to display HTML, use the label slot instead. |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ helpText
+
+ + help-text
+
+ |
+ The range’s help text. If you need to display HTML, use the help-text slot instead. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ disabled
+ |
+ Disables the range. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ min
+ |
+ The minimum acceptable value of the range. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ max
+ |
+ The maximum acceptable value of the range. | ++ |
+ number
+ |
+
+ 100
+ |
+
+ step
+ |
+ The interval at which the range will increase and decrease. | ++ |
+ number
+ |
+
+ 1
+ |
+
+ tooltip
+ |
+ The preferred placement of the range’s tooltip. | ++ |
+ 'top' | 'bottom' | 'none'
+ |
+
+ 'top'
+ |
+
+ tooltipFormatter
+ |
+ + A function used to format the tooltip’s value. The range’s value is passed as the first and only + argument. The function should return a string to display in the tooltip. + | ++ |
+ (value: number) => string
+ |
+ - | +
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ defaultValue
+ |
+ The default value of the form control. Primarily used for resetting the form control. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the control loses focus. | +- | +
sl-change |
+ onSlChange |
+ Emitted when an alteration to the control’s value is committed by the user. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the control gains focus. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the control receives input. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
focus() |
+ Sets focus on the range. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the range. | +- | +
stepUp() |
+ Increments the value of the range by the value of the step attribute. | +- | +
stepDown() |
+ Decrements the value of the range by the value of the step attribute. | +- | +
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--thumb-size |
+ The size of the thumb. | ++ |
--tooltip-offset |
+ The vertical distance the tooltip is offset from the track. | ++ |
--track-color-active |
+ The color of the portion of the track that represents the current value. | ++ |
--track-color-inactive |
+ The of the portion of the track that represents the remaining value. | ++ |
--track-height |
+ The height of the track. | ++ |
--track-active-offset |
+ The point of origin of the active track. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
form-control |
+ The form control that wraps the label, input, and help text. | +
form-control-label |
+ The label’s wrapper. | +
form-control-input |
+ The range’s wrapper. | +
form-control-help-text |
+ The help text’s wrapper. | +
base |
+ The component’s base wrapper. | +
input |
+ The internal <input> element. |
+
tooltip |
+ The range’s tooltip. | +
+ Learn more about customizing CSS parts. +
+<sl-rating> | SlRating
+ Ratings give users a way to quickly view and provide feedback.
+ ++<sl-rating label="Rating"></sl-rating> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => <SlRating label="Rating" />; +
+ Ratings are commonly identified contextually, so labels aren’t displayed. However, you should always provide
+ one for assistive devices using the label
attribute.
+
+<sl-rating label="Rate this component"></sl-rating> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => <SlRating label="Rate this component" />; +
Ratings are 0–5 by default. To change the maximum possible value, use the max
attribute.
+<sl-rating label="Rating" max="3"></sl-rating> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => <SlRating label="Rating" max={3} />; +
Use the precision
attribute to let users select fractional ratings.
+<sl-rating label="Rating" precision="0.5" value="2.5"></sl-rating> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => <SlRating label="Rating" precision={0.5} value={2.5} />; +
Set the --symbol-size
custom property to adjust the size.
+<sl-rating label="Rating" style="--symbol-size: 2rem;"></sl-rating> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => <SlRating label="Rating" style={{ '--symbol-size': '2rem' }} />; +
Use the readonly
attribute to display a rating that users can’t change.
+<sl-rating label="Rating" readonly value="3"></sl-rating> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => <SlRating label="Rating" readonly value={3} />; +
Use the disable
attribute to disable the rating.
+<sl-rating label="Rating" disabled value="3"></sl-rating> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => <SlRating label="Rating" disabled value={3} />; +
+ Use the sl-hover
event to detect when the user hovers over (or touch and drag) the rating. This
+ lets you hook into values as the user interacts with the rating, but before they select a value.
+
+ The event has a payload with phase
and value
properties. The
+ phase
property tells when hovering starts, moves to a new value, and ends. The
+ value
property tells what the rating’s value would be if the user were to commit to the hovered
+ value.
+
+<div class="detect-hover"> + <sl-rating label="Rating"></sl-rating> + <span></span> +</div> + +<script> + const rating = document.querySelector('.detect-hover > sl-rating'); + const span = rating.nextElementSibling; + const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent']; + + rating.addEventListener('sl-hover', event => { + span.textContent = terms[event.detail.value]; + + // Clear feedback when hovering stops + if (event.detail.phase === 'end') { + span.textContent = ''; + } + }); +</script> + +<style> + .detect-hover span { + position: relative; + top: -4px; + left: 8px; + border-radius: var(--sl-border-radius-small); + background: var(--sl-color-neutral-900); + color: var(--sl-color-neutral-0); + text-align: center; + padding: 4px 6px; + } + + .detect-hover span:empty { + display: none; + } +</style> +
+import { useState } from 'react'; +import SlRating from '@onsonr/nebula/dist/react/rating'; + +const terms = ['No rating', 'Terrible', 'Bad', 'OK', 'Good', 'Excellent']; +const css = ` + .detect-hover span { + position: relative; + top: -4px; + left: 8px; + border-radius: var(--sl-border-radius-small); + background: var(--sl-color-neutral-900); + color: var(--sl-color-neutral-0); + text-align: center; + padding: 4px 6px; + } + + .detect-hover span:empty { + display: none; + } +`; + +function handleHover(event) { + rating.addEventListener('sl-hover', event => { + setFeedback(terms[event.detail.value]); + + // Clear feedback when hovering stops + if (event.detail.phase === 'end') { + setFeedback(''); + } + }); +} + +const App = () => { + const [feedback, setFeedback] = useState(true); + + return ( + <> + <div class="detect-hover"> + <SlRating label="Rating" onSlHover={handleHover} /> + <span>{feedback}</span> + </div> + <style>{css}</style> + </> + ); +}; +
You can provide custom icons by passing a function to the getSymbol
property.
+<sl-rating label="Rating" class="rating-hearts" style="--symbol-color-active: #ff4136;"></sl-rating> + +<script> + const rating = document.querySelector('.rating-hearts'); + rating.getSymbol = () => '<sl-icon name="heart-fill"></sl-icon>'; +</script> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +const App = () => ( + <SlRating + label="Rating" + getSymbol={() => '<sl-icon name="heart-fill"></sl-icon>'} + style={{ '--symbol-color-active': '#ff4136' }} + /> +); +
You can also use the getSymbol
property to render different icons based on value.
+<sl-rating label="Rating" class="rating-emojis"></sl-rating> + +<script> + const rating = document.querySelector('.rating-emojis'); + + rating.getSymbol = value => { + const icons = ['emoji-angry', 'emoji-frown', 'emoji-expressionless', 'emoji-smile', 'emoji-laughing']; + return `<sl-icon name="${icons[value - 1]}"></sl-icon>`; + }; +</script> +
+import SlRating from '@onsonr/nebula/dist/react/rating'; + +function getSymbol(value) { + const icons = ['emoji-angry', 'emoji-frown', 'emoji-expressionless', 'emoji-smile', 'emoji-laughing']; + return `<sl-icon name="${icons[value - 1]}"></sl-icon>`; +} + +const App = () => <SlRating label="Rating" getSymbol={getSymbol} />; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/rating/rating.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/rating/rating.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/rating/rating.js';
To import this component as a React component:
++import SlRating from '@onsonr/nebula/dist/react/rating';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ label
+ |
+ A label that describes the rating to assistive devices. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+ The current rating. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ max
+ |
+ The highest rating to show. | ++ |
+ number
+ |
+
+ 5
+ |
+
+ precision
+ |
+
+ The precision at which the rating will increase and decrease. For example, to allow half-star
+ ratings, set this attribute to 0.5 .
+ |
+ + |
+ number
+ |
+
+ 1
+ |
+
+ readonly
+ |
+ Makes the rating readonly. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ Disables the rating. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ getSymbol
+ |
+
+ A function that customizes the symbol to be rendered. The first and only argument is the rating’s
+ current value. The function should return a string containing trusted HTML of the symbol to render
+ at the specified value. Works well with <sl-icon> elements.
+ |
+ + |
+ (value: number) => string
+ |
+ - | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-change |
+ onSlChange |
+ Emitted when the rating’s value changes. | +- | +
sl-hover |
+ onSlHover |
+
+ Emitted when the user hovers over a value. The phase property indicates when hovering
+ starts, moves to a new value, or ends. The value property tells what the rating’s value
+ would be if the user were to commit to the hovered value.
+ |
+
+ { phase: 'start' | 'move' | 'end', value: number }
+ |
+
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
focus() |
+ Sets focus on the rating. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the rating. | +- | +
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--symbol-color |
+ The inactive color for symbols. | ++ |
--symbol-color-active |
+ The active color for symbols. | ++ |
--symbol-size |
+ The size of symbols. | ++ |
--symbol-spacing |
+ The spacing to use around symbols. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-relative-time> | SlRelativeTime
+ Outputs a localized time phrase relative to the current date and time.
+ +
+ Localization is handled by the browser’s
+ Intl.RelativeTimeFormat
API. No language packs are required.
+
+<!-- Nebula 2 release date 🎉 --> +<sl-relative-time date="2020-07-15T09:17:00-04:00"></sl-relative-time> +
+import SlRelativeTime from '@onsonr/nebula/dist/react/relative-time'; + +const App = () => <SlRelativeTime date="2020-07-15T09:17:00-04:00" />; +
+ The date
attribute determines when the date/time is calculated from. It must be a string that
+ Date.parse()
+ can interpret or a
+ Date
+ object set via JavaScript.
+
+ When using strings, avoid ambiguous dates such as 03/04/2020
which can be interpreted as
+ March 4 or April 3 depending on the user’s browser and locale. Instead, always use a valid
+ ISO 8601 date time string
+ to ensure the date will be parsed properly by all clients.
+
Use the sync
attribute to update the displayed value automatically as time passes.
+<div class="relative-time-sync"> + <sl-relative-time sync></sl-relative-time> +</div> + +<script> + const container = document.querySelector('.relative-time-sync'); + const relativeTime = container.querySelector('sl-relative-time'); + + relativeTime.date = new Date(new Date().getTime() - 60000); +</script> +
+import SlRelativeTime from '@onsonr/nebula/dist/react/relative-time'; + +const date = new Date(new Date().getTime() - 60000); + +const App = () => <SlRelativeTime date={date} sync />; +
+ You can change how the time is displayed using the format
attribute. Note that some locales may
+ display the same values for narrow
and short
formats.
+
+<sl-relative-time date="2020-07-15T09:17:00-04:00" format="narrow"></sl-relative-time><br /> +<sl-relative-time date="2020-07-15T09:17:00-04:00" format="short"></sl-relative-time><br /> +<sl-relative-time date="2020-07-15T09:17:00-04:00" format="long"></sl-relative-time> +
+import SlRelativeTime from '@onsonr/nebula/dist/react/relative-time'; + +const App = () => ( + <> + <SlRelativeTime date="2020-07-15T09:17:00-04:00" format="narrow" /> + <br /> + <SlRelativeTime date="2020-07-15T09:17:00-04:00" format="short" /> + <br /> + <SlRelativeTime date="2020-07-15T09:17:00-04:00" format="long" /> + </> +); +
Use the lang
attribute to set the desired locale.
+English: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="en-US"></sl-relative-time><br /> +Chinese: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="zh-CN"></sl-relative-time><br /> +German: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="de"></sl-relative-time><br /> +Greek: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="el"></sl-relative-time><br /> +Russian: <sl-relative-time date="2020-07-15T09:17:00-04:00" lang="ru"></sl-relative-time> +
+import SlRelativeTime from '@onsonr/nebula/dist/react/relative-time'; + +const App = () => ( + <> + English: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="en-US" /> + <br /> + Chinese: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="zh-CN" /> + <br /> + German: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="de" /> + <br /> + Greek: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="el" /> + <br /> + Russian: <SlRelativeTime date="2020-07-15T09:17:00-04:00" lang="ru" /> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/relative-time/relative-time.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/relative-time/relative-time.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/relative-time/relative-time.js';
To import this component as a React component:
++import SlRelativeTime from '@onsonr/nebula/dist/react/relative-time';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ date
+ |
+
+ The date from which to calculate time from. If not set, the current date and time will be used. When
+ passing a string, it’s strongly recommended to use the ISO 8601 format to ensure timezones are
+ handled correctly. To convert a date to this format in JavaScript, use
+ date.toISOString() .
+ |
+ + |
+ Date | string
+ |
+
+ new Date()
+ |
+
+ format
+ |
+ The formatting style to use. | ++ |
+ 'long' | 'short' | 'narrow'
+ |
+
+ 'long'
+ |
+
+ numeric
+ |
+
+ When auto , values such as “yesterday” and “tomorrow” will be shown when possible. When
+ always , values such as “1 day ago” and “in 1 day” will be shown.
+ |
+ + |
+ 'always' | 'auto'
+ |
+
+ 'auto'
+ |
+
+ sync
+ |
+ Keep the displayed value up to date as time passes. | ++ |
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+<sl-resize-observer> | SlResizeObserver
+
+ The Resize Observer component offers a thin, declarative interface to the
+ ResizeObserver API
.
+
+ The resize observer will report changes to the dimensions of the elements it wraps through the
+ sl-resize
event. When emitted, a collection of
+ ResizeObserverEntry
+ objects will be attached to event.detail
that contains the target element and information about
+ its dimensions.
+
+<div class="resize-observer-overview"> + <sl-resize-observer> + <div>Resize this box and watch the console 👉</div> + </sl-resize-observer> +</div> + +<script> + const container = document.querySelector('.resize-observer-overview'); + const resizeObserver = container.querySelector('sl-resize-observer'); + + resizeObserver.addEventListener('sl-resize', event => { + console.log(event.detail); + }); +</script> + +<style> + .resize-observer-overview div { + display: flex; + border: solid 2px var(--sl-input-border-color); + align-items: center; + justify-content: center; + text-align: center; + padding: 4rem 2rem; + } +</style> +
+import SlResizeObserver from '@onsonr/nebula/dist/react/resize-observer'; + +const css = ` + .resize-observer-overview div { + display: flex; + border: solid 2px var(--sl-input-border-color); + align-items: center; + justify-content: center; + text-align: center; + padding: 4rem 2rem; + } +`; + +const App = () => ( + <> + <div className="resize-observer-overview"> + <SlResizeObserver onSlResize={event => console.log(event.detail)}> + <div>Resize this box and watch the console 👉</div> + </SlResizeObserver> + </div> + + <style>{css}</style> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/resize-observer/resize-observer.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/resize-observer/resize-observer.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/resize-observer/resize-observer.js';
To import this component as a React component:
++import SlResizeObserver from '@onsonr/nebula/dist/react/resize-observer';
Name | +Description | +
---|---|
(default) | +One or more elements to watch for resizing. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ disabled
+ |
+ Disables the observer. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-resize |
+ onSlResize |
+ Emitted when the element is resized. | +
+ { entries: ResizeObserverEntry[] }
+ |
+
+ Learn more about events. +
+<sl-select> | SlSelect
+ Selects allow you to choose items from a menu of predefined options.
+ ++<sl-select> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> + <sl-option value="option-4">Option 4</sl-option> + <sl-option value="option-5">Option 5</sl-option> + <sl-option value="option-6">Option 6</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + <SlOption value="option-4">Option 4</SlOption> + <SlOption value="option-5">Option 5</SlOption> + <SlOption value="option-6">Option 6</SlOption> + </SlSelect> +); +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
+ Use the label
attribute to give the select an accessible label. For labels that contain HTML,
+ use the label
slot instead.
+
+<sl-select label="Select one"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect label="Select one"> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
+ Add descriptive help text to a select with the help-text
attribute. For help texts that contain
+ HTML, use the help-text
slot instead.
+
+<sl-select label="Experience" help-text="Please tell us your skill level."> + <sl-option value="1">Novice</sl-option> + <sl-option value="2">Intermediate</sl-option> + <sl-option value="3">Advanced</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect label="Experience" help-text="Please tell us your skill level."> + <SlOption value="1">Novice</SlOption> + <SlOption value="2">Intermediate</SlOption> + <SlOption value="3">Advanced</SlOption> + </SlSelect> +); +
Use the placeholder
attribute to add a placeholder.
+<sl-select placeholder="Select one"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect placeholder="Select one"> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
+ Use the clearable
attribute to make the control clearable. The clear button only appears when
+ an option is selected.
+
+<sl-select clearable value="option-1"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect placeholder="Clearable" clearable> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
Add the filled
attribute to draw a filled select.
+<sl-select filled> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect filled> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
Use the pill
attribute to give selects rounded edges.
+<sl-select pill> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect pill> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
Use the disabled
attribute to disable a select.
+<sl-select placeholder="Disabled" disabled> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect placeholder="Disabled" disabled> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
+ To allow multiple options to be selected, use the multiple
attribute. It’s a good practice to
+ use clearable
when this option is enabled. To set multiple values at once, set
+ value
to a space-delimited list of values.
+
+<sl-select label="Select a Few" value="option-1 option-2 option-3" multiple clearable> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> + <sl-option value="option-4">Option 4</sl-option> + <sl-option value="option-5">Option 5</sl-option> + <sl-option value="option-6">Option 6</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect label="Select a Few" value={["option-1", "option-2", "option-3"]} multiple clearable> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + <SlOption value="option-4">Option 4</SlOption> + <SlOption value="option-5">Option 5</SlOption> + <SlOption value="option-6">Option 6</SlOption> + </SlSelect> +); +
+ Note that multi-select options may wrap, causing the control to expand vertically. You can use the
+ max-options-visible
attribute to control the maximum number of selected options to show at
+ once.
+
Use the value
attribute to set the initial selection.
+ When using multiple
, the value
attribute uses space-delimited values to
+ select more than one option. Because of this, <sl-option>
values cannot contain spaces.
+ If you’re accessing the value
property through Javascript, it will be an array.
+
+<sl-select value="option-1 option-2" multiple clearable> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> + <sl-option value="option-4">Option 4</sl-option> +</sl-select> +
+import SlDivider from '@onsonr/nebula/dist/react/divider'; +import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect value={["option-1", "option-2"]} multiple clearable> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> +); +
+ Use <sl-divider>
to group listbox items visually. You can also use
+ <small>
to provide labels, but they won’t be announced by most assistive devices.
+
+<sl-select> + <small>Section 1</small> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> + <sl-divider></sl-divider> + <small>Section 2</small> + <sl-option value="option-4">Option 4</sl-option> + <sl-option value="option-5">Option 5</sl-option> + <sl-option value="option-6">Option 6</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + <SlOption value="option-4">Option 4</SlOption> + <SlOption value="option-5">Option 5</SlOption> + <SlOption value="option-6">Option 6</SlOption> + </SlSelect> +); +
+ Use the size
attribute to change a select’s size. Note that size does not apply to listbox
+ options.
+
+<sl-select placeholder="Small" size="small"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> + +<br /> + +<sl-select placeholder="Medium" size="medium"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> + +<br /> + +<sl-select placeholder="Large" size="large"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <> + <SlSelect placeholder="Small" size="small"> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> + + <br /> + + <SlSelect placeholder="Medium" size="medium"> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> + + <br /> + + <SlSelect placeholder="Large" size="large"> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> + </> +); +
+ The preferred placement of the select’s listbox can be set with the placement
attribute. Note
+ that the actual position may vary to ensure the panel remains in the viewport. Valid placements are
+ top
and bottom
.
+
+<sl-select placement="top"> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <SlSelect placement="top"> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlDropdown> +); +
Use the prefix
slot to prepend an icon to the control.
+<sl-select placeholder="Small" size="small" clearable> + <sl-icon name="house" slot="prefix"></sl-icon> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +<br /> +<sl-select placeholder="Medium" size="medium" clearable> + <sl-icon name="house" slot="prefix"></sl-icon> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +<br /> +<sl-select placeholder="Large" size="large" clearable> + <sl-icon name="house" slot="prefix"></sl-icon> + <sl-option value="option-1">Option 1</sl-option> + <sl-option value="option-2">Option 2</sl-option> + <sl-option value="option-3">Option 3</sl-option> +</sl-select> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlOption from '@onsonr/nebula/dist/react/option'; +import SlSelect from '@onsonr/nebula/dist/react/select'; + +const App = () => ( + <> + <SlSelect placeholder="Small" size="small"> + <SlIcon name="house" slot="prefix"></SlIcon> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> + <br /> + <SlSelect placeholder="Medium" size="medium"> + <SlIcon name="house" slot="prefix"></SlIcon> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> + <br /> + <SlSelect placeholder="Large" size="large"> + <SlIcon name="house" slot="prefix"></SlIcon> + <SlOption value="option-1">Option 1</SlOption> + <SlOption value="option-2">Option 2</SlOption> + <SlOption value="option-3">Option 3</SlOption> + </SlSelect> + </> +); +
+ When multiple options can be selected, you can provide custom tags by passing a function to the
+ getTag
property. Your function can return a string of HTML, a
+ Lit Template, or an
+ HTMLElement
. The getTag()
function will be called for each option. The first argument is an
+ <sl-option>
element and the second argument is the tag’s index (its position in the tag
+ list).
+
+ Remember that custom tags are rendered in a shadow root. To style them, you can use the
+ style
attribute in your template or you can add your own
+ parts and target them with the
+ ::part()
+ selector.
+
+<sl-select + placeholder="Select one" + value="email phone" + multiple + clearable + class="custom-tag" +> + <sl-option value="email"> + <sl-icon slot="prefix" name="envelope"></sl-icon> + Email + </sl-option> + <sl-option value="phone"> + <sl-icon slot="prefix" name="telephone"></sl-icon> + Phone + </sl-option> + <sl-option value="chat"> + <sl-icon slot="prefix" name="chat-dots"></sl-icon> + Chat + </sl-option> +</sl-select> + +<script type="module"> + const select = document.querySelector('.custom-tag'); + + select.getTag = (option, index) => { + // Use the same icon used in the <sl-option> + const name = option.querySelector('sl-icon[slot="prefix"]').name; + + // You can return a string, a Lit Template, or an HTMLElement here + return ` + <sl-tag removable> + <sl-icon name="${name}" style="padding-inline-end: .5rem;"></sl-icon> + ${option.getTextLabel()} + </sl-tag> + `; + }; +</script> +
+ Be sure you trust the content you are outputting! Passing unsanitized user input to
+ getTag()
can result in XSS vulnerabilities.
+
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/select/select.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/select/select.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/select/select.js';
To import this component as a React component:
++import SlSelect from '@onsonr/nebula/dist/react/select';
Name | +Description | +
---|---|
(default) | +
+ The listbox options. Must be <sl-option> elements. You can use
+ <sl-divider> to group items visually.
+ |
+
+ label
+ |
+ The input’s label. Alternatively, you can use the label attribute. |
+
+ prefix
+ |
+ Used to prepend a presentational icon or similar element to the combobox. | +
+ clear-icon
+ |
+ An icon to use in lieu of the default clear icon. | +
+ expand-icon
+ |
+ The icon to show when the control is expanded and collapsed. Rotates on open and close. | +
+ help-text
+ |
+
+ Text that describes how to use the input. Alternatively, you can use the
+ help-text attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The name of the select, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+
+ The current value of the select, submitted as a name/value pair with form data. When
+ multiple is enabled, the value attribute will be a space-delimited list of values based
+ on the options selected, and the value property will be an array.
+ For this reason, values must not contain spaces.
+ |
+ + |
+ string | string[]
+ |
+
+ ''
+ |
+
+ defaultValue
+ |
+ The default value of the form control. Primarily used for resetting the form control. | ++ |
+ string | string[]
+ |
+
+ ''
+ |
+
+ size
+ |
+ The select’s size. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ placeholder
+ |
+ Placeholder text to show as a hint when the select is empty. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ multiple
+ |
+ Allows more than one option to be selected. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ maxOptionsVisible
+
+ + max-options-visible
+
+ |
+
+ The maximum number of selected options to show when multiple is true. After the
+ maximum, ”+n” will be shown to indicate the number of additional items that are selected. Set to 0
+ to remove the limit.
+ |
+ + |
+ number
+ |
+
+ 3
+ |
+
+ disabled
+ |
+ Disables the select control. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ clearable
+ |
+ Adds a clear button when the select is not empty. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ open
+ |
+
+ Indicates whether or not the select is open. You can toggle this attribute to show and hide the
+ menu, or you can use the show() and hide() methods and this attribute will
+ reflect the select’s open state.
+ |
+
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ hoist
+ |
+
+ Enable this option to prevent the listbox from being clipped when the component is placed inside a
+ container with
+ overflow: auto|scroll . Hoisting uses a fixed positioning strategy that works in many,
+ but not all, scenarios.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
+ filled
+ |
+ Draws a filled select. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ pill
+ |
+ Draws a pill-style select with rounded edges. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ label
+ |
+ The select’s label. If you need to display HTML, use the label slot instead. |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ placement
+ |
+ + The preferred placement of the select’s menu. Note that the actual placement may vary as needed to + keep the listbox inside of the viewport. + | +
+ |
+
+ 'top' | 'bottom'
+ |
+
+ 'bottom'
+ |
+
+ helpText
+
+ + help-text
+
+ |
+
+ The select’s help text. If you need to display HTML, use the help-text slot instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ required
+ |
+ The select’s required attribute. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ getTag
+ |
+ + A function that customizes the tags to be rendered when multiple=true. The first argument is the + option, the second is the current tag’s index. The function should return either a Lit + TemplateResult or a string containing trusted HTML of the symbol to render at the specified value. + | ++ |
+ (option: SlOption, index: number) => TemplateResult | string | HTMLElement
+ |
+ - | +
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-change |
+ onSlChange |
+ Emitted when the control’s value changes. | +- | +
sl-clear |
+ onSlClear |
+ Emitted when the control’s value is cleared. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the control receives input. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the control gains focus. | +- | +
sl-blur |
+ onSlBlur |
+ Emitted when the control loses focus. | +- | +
sl-show |
+ onSlShow |
+ Emitted when the select’s menu opens. | +- | +
sl-after-show |
+ onSlAfterShow |
+ Emitted after the select’s menu opens and all animations are complete. | +- | +
sl-hide |
+ onSlHide |
+ Emitted when the select’s menu closes. | +- | +
sl-after-hide |
+ onSlAfterHide |
+ Emitted after the select’s menu closes and all animations are complete. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the listbox. | +- | +
hide() |
+ Hides the listbox. | +- | +
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message: string
+ |
+
focus() |
+ Sets focus on the control. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the control. | +- | +
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
form-control |
+ The form control that wraps the label, input, and help text. | +
form-control-label |
+ The label’s wrapper. | +
form-control-input |
+ The select’s wrapper. | +
form-control-help-text |
+ The help text’s wrapper. | +
combobox |
+ The container the wraps the prefix, combobox, clear icon, and expand button. | +
prefix |
+ The container that wraps the prefix slot. | +
display-input |
+ The element that displays the selected option’s label, an <input> element. |
+
listbox |
+ The listbox container where options are slotted. | +
tags |
+ The container that houses option tags when multiselect is used. |
+
tag |
+ The individual tags that represent each multiselect option. | +
tag__base |
+ The tag’s base part. | +
tag__content |
+ The tag’s content part. | +
tag__remove-button |
+ The tag’s remove button. | +
tag__remove-button__base |
+ The tag’s remove button base part. | +
clear-button |
+ The clear button. | +
expand-icon |
+ The container that wraps the expand icon. | +
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon-button>
<sl-popup>
<sl-tag>
<sl-skeleton> | SlSkeleton
+ + Skeletons are used to provide a visual representation of where content will eventually be drawn. +
+ ++ These are simple containers for scaffolding layouts that mimic what users will see when content has finished + loading. This prevents large areas of empty space during asynchronous operations. +
++ Skeletons try not to be opinionated, as there are endless possibilities for designing layouts. Therefore, + you’ll likely use more than one skeleton to create the effect you want. If you find yourself using them + frequently, consider creating a template that renders them with the desired arrangement and styles. +
+ ++<div class="skeleton-overview"> + <header> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> + </header> + + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> +</div> + +<style> + .skeleton-overview header { + display: flex; + align-items: center; + margin-bottom: 1rem; + } + + .skeleton-overview header sl-skeleton:last-child { + flex: 0 0 auto; + width: 30%; + } + + .skeleton-overview sl-skeleton { + margin-bottom: 1rem; + } + + .skeleton-overview sl-skeleton:nth-child(1) { + float: left; + width: 3rem; + height: 3rem; + margin-right: 1rem; + vertical-align: middle; + } + + .skeleton-overview sl-skeleton:nth-child(3) { + width: 95%; + } + + .skeleton-overview sl-skeleton:nth-child(4) { + width: 80%; + } +</style> +
+import SlSkeleton from '@onsonr/nebula/dist/react/skeleton'; + +const css = ` + .skeleton-overview header { + display: flex; + align-items: center; + margin-bottom: 1rem; + } + + .skeleton-overview header sl-skeleton:last-child { + flex: 0 0 auto; + width: 30%; + } + + .skeleton-overview sl-skeleton { + margin-bottom: 1rem; + } + + .skeleton-overview sl-skeleton:nth-child(1) { + float: left; + width: 3rem; + height: 3rem; + margin-right: 1rem; + vertical-align: middle; + } + + .skeleton-overview sl-skeleton:nth-child(3) { + width: 95%; + } + + .skeleton-overview sl-skeleton:nth-child(4) { + width: 80%; + } +`; + +const App = () => ( + <> + <div className="skeleton-overview"> + <header> + <SlSkeleton /> + <SlSkeleton /> + </header> + + <SlSkeleton /> + <SlSkeleton /> + <SlSkeleton /> + </div> + + <style>{css}</style> + </> +); +
+ There are two built-in effects, sheen
and pulse
. Effects are intentionally subtle,
+ as they can be distracting when used extensively. The default is none
, which displays a static,
+ non-animated skeleton.
+
+<div class="skeleton-effects"> + <sl-skeleton effect="none"></sl-skeleton> + None + + <sl-skeleton effect="sheen"></sl-skeleton> + Sheen + + <sl-skeleton effect="pulse"></sl-skeleton> + Pulse +</div> + +<style> + .skeleton-effects { + font-size: var(--sl-font-size-small); + } + + .skeleton-effects sl-skeleton:not(:first-child) { + margin-top: 1rem; + } +</style> +
+import SlSkeleton from '@onsonr/nebula/dist/react/skeleton'; + +const css = ` + .skeleton-effects { + font-size: var(--sl-font-size-small); + } + + .skeleton-effects sl-skeleton:not(:first-child) { + margin-top: 1rem; + } +`; + +const App = () => ( + <> + <div className="skeleton-effects"> + <SlSkeleton effect="none" /> + None + <SlSkeleton effect="sheen" /> + Sheen + <SlSkeleton effect="pulse" /> + Pulse + </div> + + <style>{css}</style> + </> +); +
Use multiple skeletons and some clever styles to simulate paragraphs.
+ ++<div class="skeleton-paragraphs"> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> +</div> + +<style> + .skeleton-paragraphs sl-skeleton { + margin-bottom: 1rem; + } + + .skeleton-paragraphs sl-skeleton:nth-child(2) { + width: 95%; + } + + .skeleton-paragraphs sl-skeleton:nth-child(4) { + width: 90%; + } + + .skeleton-paragraphs sl-skeleton:last-child { + width: 50%; + } +</style> +
+import SlSkeleton from '@onsonr/nebula/dist/react/skeleton'; + +const css = ` + .skeleton-paragraphs sl-skeleton { + margin-bottom: 1rem; + } + + .skeleton-paragraphs sl-skeleton:nth-child(2) { + width: 95%; + } + + .skeleton-paragraphs sl-skeleton:nth-child(4) { + width: 90%; + } + + .skeleton-paragraphs sl-skeleton:last-child { + width: 50%; + } +`; + +const App = () => ( + <> + <div className="skeleton-paragraphs"> + <SlSkeleton /> + <SlSkeleton /> + <SlSkeleton /> + <SlSkeleton /> + <SlSkeleton /> + </div> + + <style>{css}</style> + </> +); +
Set a matching width and height to make a circle, square, or rounded avatar skeleton.
+ ++<div class="skeleton-avatars"> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> + <sl-skeleton></sl-skeleton> +</div> + +<style> + .skeleton-avatars sl-skeleton { + display: inline-block; + width: 3rem; + height: 3rem; + margin-right: 0.5rem; + } + + .skeleton-avatars sl-skeleton:nth-child(1) { + --border-radius: 0; + } + + .skeleton-avatars sl-skeleton:nth-child(2) { + --border-radius: var(--sl-border-radius-medium); + } +</style> +
+import SlSkeleton from '@onsonr/nebula/dist/react/skeleton'; + +const css = ` + .skeleton-avatars sl-skeleton { + display: inline-block; + width: 3rem; + height: 3rem; + margin-right: .5rem; + } + + .skeleton-avatars sl-skeleton:nth-child(1) { + --border-radius: 0; + } + + .skeleton-avatars sl-skeleton:nth-child(2) { + --border-radius: var(--sl-border-radius-medium); + } +`; + +const App = () => ( + <> + <div className="skeleton-avatars"> + <SlSkeleton /> + <SlSkeleton /> + <SlSkeleton /> + </div> + + <style>{css}</style> + </> +); +
+ Use the --border-radius
custom property to make circles, squares, and rectangles. For more
+ complex shapes, you can apply clip-path
to the indicator
part.
+ Try Clippy
+ if you need help generating custom shapes.
+
+<div class="skeleton-shapes"> + <sl-skeleton class="square"></sl-skeleton> + <sl-skeleton class="circle"></sl-skeleton> + <sl-skeleton class="triangle"></sl-skeleton> + <sl-skeleton class="cross"></sl-skeleton> + <sl-skeleton class="comment"></sl-skeleton> +</div> + +<style> + .skeleton-shapes sl-skeleton { + display: inline-flex; + width: 50px; + height: 50px; + } + + .skeleton-shapes .square::part(indicator) { + --border-radius: var(--sl-border-radius-medium); + } + + .skeleton-shapes .circle::part(indicator) { + --border-radius: var(--sl-border-radius-circle); + } + + .skeleton-shapes .triangle::part(indicator) { + --border-radius: 0; + clip-path: polygon(50% 0, 0 100%, 100% 100%); + } + + .skeleton-shapes .cross::part(indicator) { + --border-radius: 0; + clip-path: polygon( + 20% 0%, + 0% 20%, + 30% 50%, + 0% 80%, + 20% 100%, + 50% 70%, + 80% 100%, + 100% 80%, + 70% 50%, + 100% 20%, + 80% 0%, + 50% 30% + ); + } + + .skeleton-shapes .comment::part(indicator) { + --border-radius: 0; + clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%); + } + + .skeleton-shapes sl-skeleton:not(:last-child) { + margin-right: 0.5rem; + } +</style> +
+import SlSkeleton from '@onsonr/nebula/dist/react/skeleton'; + +const css = ` + .skeleton-shapes sl-skeleton { + display: inline-flex; + width: 50px; + height: 50px; + } + + .skeleton-shapes .square::part(indicator) { + --border-radius: var(--sl-border-radius-medium); + } + + .skeleton-shapes .circle::part(indicator) { + --border-radius: var(--sl-border-radius-circle); + } + + .skeleton-shapes .triangle::part(indicator) { + --border-radius: 0; + clip-path: polygon(50% 0, 0 100%, 100% 100%); + } + + .skeleton-shapes .cross::part(indicator) { + --border-radius: 0; + clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%); + } + + .skeleton-shapes .comment::part(indicator) { + --border-radius: 0; + clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%); + } + + .skeleton-shapes sl-skeleton:not(:last-child) { + margin-right: .5rem; + } +`; + +const App = () => ( + <> + <div className="skeleton-shapes"> + <SlSkeleton className="square" /> + <SlSkeleton className="circle" /> + <SlSkeleton className="triangle" /> + <SlSkeleton className="cross" /> + <SlSkeleton className="comment" /> + </div> + + <style>{css}</style> + </> +); +
+ Set the --color
and --sheen-color
custom properties to adjust the skeleton’s
+ color.
+
+<sl-skeleton effect="sheen" style="--color: tomato; --sheen-color: #ffb094;"></sl-skeleton> +
+import SlSkeleton from '@onsonr/nebula/dist/react/skeleton'; + +const css = ` + .skeleton-avatars sl-skeleton { + display: inline-block; + width: 3rem; + height: 3rem; + margin-right: .5rem; + } + + .skeleton-avatars sl-skeleton:nth-child(1) { + --border-radius: 0; + } + + .skeleton-avatars sl-skeleton:nth-child(2) { + --border-radius: var(--sl-border-radius-medium); + } +`; + +const App = () => <SlSkeleton effect="sheen" style={{ '--color': 'tomato', '--sheen-color': '#ffb094' }} />; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/skeleton/skeleton.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/skeleton/skeleton.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/skeleton/skeleton.js';
To import this component as a React component:
++import SlSkeleton from '@onsonr/nebula/dist/react/skeleton';
Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ effect
+ |
+ Determines which effect the skeleton will use. | ++ |
+ 'pulse' | 'sheen' | 'none'
+ |
+
+ 'none'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Default | +
---|---|---|
--border-radius |
+ The skeleton’s border radius. | ++ |
--color |
+ The color of the skeleton. | ++ |
--sheen-color |
+ The sheen color when the skeleton is in its loading state. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
indicator |
+ The skeleton’s indicator which is responsible for its color and animation. | +
+ Learn more about customizing CSS parts. +
+<sl-spinner> | SlSpinner
+ Spinners are used to show the progress of an indeterminate operation.
+ ++<sl-spinner></sl-spinner> +
+import SlSpinner from '@onsonr/nebula/dist/react/spinner'; + +const App = () => <SlSpinner />; +
+ Spinners are sized based on the current font size. To change their size, set the
+ font-size
property on the spinner itself or on a parent element as shown below.
+
+<sl-spinner></sl-spinner> +<sl-spinner style="font-size: 2rem;"></sl-spinner> +<sl-spinner style="font-size: 3rem;"></sl-spinner> +
+import SlSpinner from '@onsonr/nebula/dist/react/spinner'; + +const App = () => ( + <> + <SlSpinner /> + <SlSpinner style={{ fontSize: '2rem' }} /> + <SlSpinner style={{ fontSize: '3rem' }} /> + </> +); +
+ The width of the spinner’s track can be changed by setting the --track-width
custom property.
+
+<sl-spinner style="font-size: 50px; --track-width: 10px;"></sl-spinner> +
+import SlSpinner from '@onsonr/nebula/dist/react/spinner'; + +const App = () => ( + <SlSpinner + style={{ + fontSize: '3rem', + '--track-width': '6px' + }} + /> +); +
+ The spinner’s colors can be changed by setting the --indicator-color
and
+ --track-color
custom properties.
+
+<sl-spinner style="font-size: 3rem; --indicator-color: deeppink; --track-color: pink;"></sl-spinner> +
+import SlSpinner from '@onsonr/nebula/dist/react/spinner'; + +const App = () => ( + <SlSpinner + style={{ + fontSize: '3rem', + '--indicator-color': 'deeppink', + '--track-color': 'pink' + }} + /> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/spinner/spinner.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/spinner/spinner.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/spinner/spinner.js';
To import this component as a React component:
++import SlSpinner from '@onsonr/nebula/dist/react/spinner';
Name | +Description | +Default | +
---|---|---|
--track-width |
+ The width of the track. | ++ |
--track-color |
+ The color of the track. | ++ |
--indicator-color |
+ The color of the spinner’s indicator. | ++ |
--speed |
+ The time it takes for the spinner to complete one animation cycle. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-split-panel> | SlSplitPanel
+ + Split panels display two adjacent panels, allowing the user to reposition them. +
+ ++<sl-split-panel> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> +</sl-split-panel> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; + +const App = () => ( + <SlSplitPanel> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> +); +
+ To set the initial position, use the position
attribute. If no position is provided, it will
+ default to 50% of the available space.
+
+<sl-split-panel position="75"> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> +</sl-split-panel> +
+ To set the initial position in pixels instead of a percentage, use the
+ position-in-pixels
attribute.
+
+<sl-split-panel position-in-pixels="150"> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> +</sl-split-panel> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; + +const App = () => ( + <SlSplitPanel position="200"> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> +); +
+ Add the vertical
attribute to render the split panel in a vertical orientation where the start
+ and end panels are stacked. You also need to set a height when using the vertical orientation.
+
+<sl-split-panel vertical style="height: 400px;"> + <div + slot="start" + style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> +</sl-split-panel> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; + +const App = () => ( + <SlSplitPanel vertical style={{ height: '400px' }}> + <div + slot="start" + style={{ + height: '100%', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '100%', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> +); +
+ To snap panels at specific positions while dragging, add the snap
attribute with one or more
+ space-separated values. Values must be in pixels or percentages. For example, to snap the panel at
+ 100px
and 50%
, use snap="100px 50%"
. You can also customize how close
+ the divider must be before snapping with the snap-threshold
attribute.
+
+<div class="split-panel-snapping"> + <sl-split-panel snap="100px 50%"> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> + </sl-split-panel> + + <div class="split-panel-snapping-dots"></div> +</div> + +<style> + .split-panel-snapping { + position: relative; + } + + .split-panel-snapping-dots::before, + .split-panel-snapping-dots::after { + content: ''; + position: absolute; + bottom: -12px; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--sl-color-neutral-400); + transform: translateX(-3px); + } + + .split-panel-snapping-dots::before { + left: 100px; + } + + .split-panel-snapping-dots::after { + left: 50%; + } +</style> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; + +const css = ` + .split-panel-snapping { + position: relative; + } + + .split-panel-snapping-dots::before, + .split-panel-snapping-dots::after { + content: ''; + position: absolute; + bottom: -12px; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--sl-color-neutral-400); + transform: translateX(-3px); + } + + .split-panel-snapping-dots::before { + left: 100px; + } + + .split-panel-snapping-dots::after { + left: 50%; + } +`; + +const App = () => ( + <> + <div className="split-panel-snapping"> + <SlSplitPanel snap="100px 50%"> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> + + <div className="split-panel-snapping-dots" /> + </div> + + <style>{css}</style> + </> +); +
Add the disabled
attribute to prevent the divider from being repositioned.
+<sl-split-panel disabled> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> +</sl-split-panel> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; + +const App = () => ( + <SlSplitPanel disabled> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> +); +
+ By default, both panels will grow or shrink proportionally when the host element is resized. If a primary
+ panel is designated, it will maintain its size and the secondary panel will grow or shrink to fit the
+ remaining space. You can set the primary panel to start
or end
using the
+ primary
attribute.
+
Try resizing the example below with each option and notice how the panels respond.
+ ++<div class="split-panel-primary"> + <sl-split-panel> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> + </sl-split-panel> + + <sl-select label="Primary Panel" value="" style="max-width: 200px; margin-top: 1rem;"> + <sl-option value="">None</sl-option> + <sl-option value="start">Start</sl-option> + <sl-option value="end">End</sl-option> + </sl-select> +</div> + +<script> + const container = document.querySelector('.split-panel-primary'); + const splitPanel = container.querySelector('sl-split-panel'); + const select = container.querySelector('sl-select'); + + select.addEventListener('sl-change', () => (splitPanel.primary = select.value)); +</script> +
+import { useState } from 'react'; +import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; +import SlSelect from '@onsonr/nebula/dist/react/select'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; + +const App = () => { + const [primary, setPrimary] = useState(''); + + return ( + <> + <SlSplitPanel primary={primary}> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> + + <SlSelect + label="Primary Panel" + value={primary} + style={{ maxWidth: '200px', marginTop: '1rem' }} + onSlChange={event => setPrimary(event.target.value)} + > + <SlMenuItem value="">None</SlMenuItem> + <SlMenuItem value="start">Start</SlMenuItem> + <SlMenuItem value="end">End</SlMenuItem> + </SlSelect> + </> + ); +}; +
+ To set a minimum or maximum size of the primary panel, use the --min
and
+ --max
custom properties. Since the secondary panel is flexible, size constraints can only be
+ applied to the primary panel. If no primary panel is designated, these constraints will be applied to the
+ start
panel.
+
+ This examples demonstrates how you can ensure both panels are at least 150px using --min
,
+ --max
, and the calc()
function.
+
+<sl-split-panel style="--min: 150px; --max: calc(100% - 150px);"> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> +</sl-split-panel> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; + +const App = () => ( + <SlSplitPanel style={{ '--min': '150px', '--max': 'calc(100% - 150px)' }}> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> +); +
Create complex layouts that can be repositioned independently by nesting split panels.
+ ++<sl-split-panel> + <div + slot="start" + style="height: 400px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden" + > + Start + </div> + <div slot="end"> + <sl-split-panel vertical style="height: 400px;"> + <div + slot="start" + style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden" + > + Top + </div> + <div + slot="end" + style="height: 100%; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden" + > + Bottom + </div> + </sl-split-panel> + </div> +</sl-split-panel> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; + +const App = () => ( + <SlSplitPanel> + <div + slot="start" + style={{ + height: '400px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div slot="end"> + <SlSplitPanel vertical style={{ height: '400px' }}> + <div + slot="start" + style={{ + height: '100%', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '100%', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> + </div> + </SlSplitPanel> +); +
+ You can target the divider
part to apply CSS properties to the divider. To add a custom handle,
+ slot an icon into the divider
slot. When customizing the divider, make sure to think about
+ focus styles for keyboard users.
+
+<sl-split-panel style="--divider-width: 20px;"> + <sl-icon slot="divider" name="grip-vertical"></sl-icon> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> +</sl-split-panel> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const App = () => ( + <SlSplitPanel style={{ '--divider-width': '20px' }}> + <SlIcon slot="divider" name="grip-vertical" /> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> +); +
Here’s a more elaborate example that changes the divider’s color and width and adds a styled handle.
+ ++<div class="split-panel-divider"> + <sl-split-panel> + <sl-icon slot="divider" name="grip-vertical"></sl-icon> + <div + slot="start" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + Start + </div> + <div + slot="end" + style="height: 200px; background: var(--sl-color-neutral-50); display: flex; align-items: center; justify-content: center; overflow: hidden;" + > + End + </div> + </sl-split-panel> +</div> + +<style> + .split-panel-divider sl-split-panel { + --divider-width: 2px; + } + + .split-panel-divider sl-split-panel::part(divider) { + background-color: var(--sl-color-pink-600); + } + + .split-panel-divider sl-icon { + position: absolute; + border-radius: var(--sl-border-radius-small); + background: var(--sl-color-pink-600); + color: var(--sl-color-neutral-0); + padding: 0.5rem 0.125rem; + } + + .split-panel-divider sl-split-panel::part(divider):focus-visible { + background-color: var(--sl-color-primary-600); + } + + .split-panel-divider sl-split-panel:focus-within sl-icon { + background-color: var(--sl-color-primary-600); + color: var(--sl-color-neutral-0); + } +</style> +
+import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel'; +import SlIcon from '@onsonr/nebula/dist/react/icon'; + +const css = ` + .split-panel-divider sl-split-panel { + --divider-width: 2px; + } + + .split-panel-divider sl-split-panel::part(divider) { + background-color: var(--sl-color-pink-600); + } + + .split-panel-divider sl-icon { + position: absolute; + border-radius: var(--sl-border-radius-small); + background: var(--sl-color-pink-600); + color: var(--sl-color-neutral-0); + padding: .5rem .125rem; + } + + .split-panel-divider sl-split-panel::part(divider):focus-visible { + background-color: var(--sl-color-primary-600); + } + + .split-panel-divider sl-split-panel:focus-within sl-icon { + background-color: var(--sl-color-primary-600); + color: var(--sl-color-neutral-0); + } +`; + +const App = () => ( + <> + <div className="split-panel-divider"> + <SlSplitPanel> + <SlIcon slot="divider" name="grip-vertical" /> + <div + slot="start" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + Start + </div> + <div + slot="end" + style={{ + height: '200px', + background: 'var(--sl-color-neutral-50)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }} + > + End + </div> + </SlSplitPanel> + </div> + + <style>{css}</style> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/split-panel/split-panel.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/split-panel/split-panel.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/split-panel/split-panel.js';
To import this component as a React component:
++import SlSplitPanel from '@onsonr/nebula/dist/react/split-panel';
Name | +Description | +
---|---|
+ start
+ |
+ Content to place in the start panel. | +
+ end
+ |
+ Content to place in the end panel. | +
+ divider
+ |
+ The divider. Useful for slotting in a custom icon that renders as a handle. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ position
+ |
+ + The current position of the divider from the primary panel’s edge as a percentage 0–100. Defaults to + 50% of the container’s initial size. + | +
+ |
+
+ number
+ |
+
+ 50
+ |
+
+ positionInPixels
+
+ + position-in-pixels
+
+ |
+ The current position of the divider from the primary panel’s edge in pixels. | ++ |
+ number
+ |
+ - | +
+ vertical
+ |
+ Draws the split panel in a vertical orientation with the start and end panels stacked. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ + Disables resizing. Note that the position may still change as a result of resizing the host element. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ primary
+ |
+ + If no primary panel is designated, both panels will resize proportionally when the host element is + resized. If a primary panel is designated, it will maintain its size and the other panel will grow + or shrink as needed when the host element is resized. + | ++ |
+ 'start' | 'end' | undefined
+ |
+ - | +
+ snap
+ |
+
+ One or more space-separated values at which the divider should snap. Values can be in pixels or
+ percentages, e.g.
+ "100px 50%" .
+ |
+ + |
+ string | undefined
+ |
+ - | +
+ snapThreshold
+
+ + snap-threshold
+
+ |
+ How close the divider must be to a snap point until snapping occurs. | ++ |
+ number
+ |
+
+ 12
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-reposition |
+ onSlReposition |
+ Emitted when the divider’s position changes. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Default | +
---|---|---|
--divider-width |
+ The width of the visible divider. | +4px | +
--divider-hit-area |
+ + The invisible region around the divider where dragging can occur. This is usually wider than the + divider to facilitate easier dragging. + | +12px | +
--min |
+ The minimum allowed size of the primary panel. | +0 | +
--max |
+ The maximum allowed size of the primary panel. | +100% | +
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
start |
+ The start panel. | +
end |
+ The end panel. | +
panel |
+ Targets both the start and end panels. | +
divider |
+ The divider that separates the start and end panels. | +
+ Learn more about customizing CSS parts. +
+<sl-switch> | SlSwitch
+ Switches allow the user to toggle an option on or off.
+ ++<sl-switch>Switch</sl-switch> +
+import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const App = () => <SlSwitch>Switch</SlSwitch>; +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
Use the checked
attribute to activate the switch.
+<sl-switch checked>Checked</sl-switch> +
+import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const App = () => <SlSwitch checked>Checked</SlSwitch>; +
Use the disabled
attribute to disable the switch.
+<sl-switch disabled>Disabled</sl-switch> +
+import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const App = () => <SlSwitch disabled>Disabled</SlSwitch>; +
Use the size
attribute to change a switch’s size.
+<sl-switch size="small">Small</sl-switch> +<br /> +<sl-switch size="medium">Medium</sl-switch> +<br /> +<sl-switch size="large">Large</sl-switch> +
+import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const App = () => ( + <> + <SlSwitch size="small">Small</SlSwitch> + <br /> + <SlSwitch size="medium">Medium</SlSwitch> + <br /> + <SlSwitch size="large">Large</SlSwitch> + </> +); +
+ Add descriptive help text to a switch with the help-text
attribute. For help texts that contain
+ HTML, use the help-text
slot instead.
+
+<sl-switch help-text="What should the user know about the switch?">Label</sl-switch> +
+import SlSwitch from '@onsonr/nebula/dist/react/checkbox'; + +const App = () => <SlSwitch help-text="What should the user know about the switch?">Label</SlSwitch>; +
Use the available custom properties to change how the switch is styled.
+ ++<sl-switch style="--width: 80px; --height: 40px; --thumb-size: 36px;">Really big</sl-switch> +
+import SlSwitch from '@onsonr/nebula/dist/react/switch'; + +const App = () => ( + <SlSwitch + style={{ + '--width': '80px', + '--height': '32px', + '--thumb-size': '26px' + }} + /> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/switch/switch.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/switch/switch.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/switch/switch.js';
To import this component as a React component:
++import SlSwitch from '@onsonr/nebula/dist/react/switch';
Name | +Description | +
---|---|
(default) | +The switch’s label. | +
+ help-text
+ |
+
+ Text that describes how to use the switch. Alternatively, you can use the
+ help-text attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The name of the switch, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+ The current value of the switch, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+ - | +
+ size
+ |
+ The switch’s size. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ disabled
+ |
+ Disables the switch. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ checked
+ |
+ Draws the switch in a checked state. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ defaultChecked
+ |
+ The default value of the form control. Primarily used for resetting the form control. | ++ |
+ boolean
+ |
+
+ false
+ |
+
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ required
+ |
+ Makes the switch a required field. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ helpText
+
+ + help-text
+
+ |
+
+ The switch’s help text. If you need to display HTML, use the help-text slot instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the control loses focus. | +- | +
sl-change |
+ onSlChange |
+ Emitted when the control’s checked state changes. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the control receives input. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the control gains focus. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
click() |
+ Simulates a click on the switch. | +- | +
focus() |
+ Sets focus on the switch. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the switch. | +- | +
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--width |
+ The width of the switch. | ++ |
--height |
+ The height of the switch. | ++ |
--thumb-size |
+ The size of the thumb. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
control |
+ The control that houses the switch’s thumb. | +
thumb |
+ The switch’s thumb. | +
label |
+ The switch’s label. | +
form-control-help-text |
+ The help text’s wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-tab-group> | SlTabGroup
+ + Tab groups organize content into a container that shows one section at a time. +
+ +
+ Tab groups make use of tabs and
+ tab panels. Each tab must be slotted into the nav
slot and
+ its panel
must refer to a tab panel of the same name.
+
+<sl-tab-group> + <sl-tab slot="nav" panel="general">General</sl-tab> + <sl-tab slot="nav" panel="custom">Custom</sl-tab> + <sl-tab slot="nav" panel="advanced">Advanced</sl-tab> + <sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab> + + <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> + <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> + <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> + <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> +</sl-tab-group> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => ( + <SlTabGroup> + <SlTab slot="nav" panel="general"> + General + </SlTab> + <SlTab slot="nav" panel="custom"> + Custom + </SlTab> + <SlTab slot="nav" panel="advanced"> + Advanced + </SlTab> + <SlTab slot="nav" panel="disabled" disabled> + Disabled + </SlTab> + + <SlTabPanel name="general">This is the general tab panel.</SlTabPanel> + <SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel> + <SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel> + <SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel> + </SlTabGroup> +); +
Tabs can be shown on the bottom by setting placement
to bottom
.
+<sl-tab-group placement="bottom"> + <sl-tab slot="nav" panel="general">General</sl-tab> + <sl-tab slot="nav" panel="custom">Custom</sl-tab> + <sl-tab slot="nav" panel="advanced">Advanced</sl-tab> + <sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab> + + <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> + <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> + <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> + <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> +</sl-tab-group> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => ( + <SlTabGroup placement="bottom"> + <SlTab slot="nav" panel="general"> + General + </SlTab> + <SlTab slot="nav" panel="custom"> + Custom + </SlTab> + <SlTab slot="nav" panel="advanced"> + Advanced + </SlTab> + <SlTab slot="nav" panel="disabled" disabled> + Disabled + </SlTab> + + <SlTabPanel name="general">This is the general tab panel.</SlTabPanel> + <SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel> + <SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel> + <SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel> + </SlTabGroup> +); +
Tabs can be shown on the starting side by setting placement
to start
.
+<sl-tab-group placement="start"> + <sl-tab slot="nav" panel="general">General</sl-tab> + <sl-tab slot="nav" panel="custom">Custom</sl-tab> + <sl-tab slot="nav" panel="advanced">Advanced</sl-tab> + <sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab> + + <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> + <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> + <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> + <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> +</sl-tab-group> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => ( + <SlTabGroup placement="start"> + <SlTab slot="nav" panel="general"> + General + </SlTab> + <SlTab slot="nav" panel="custom"> + Custom + </SlTab> + <SlTab slot="nav" panel="advanced"> + Advanced + </SlTab> + <SlTab slot="nav" panel="disabled" disabled> + Disabled + </SlTab> + + <SlTabPanel name="general">This is the general tab panel.</SlTabPanel> + <SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel> + <SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel> + <SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel> + </SlTabGroup> +); +
Tabs can be shown on the ending side by setting placement
to end
.
+<sl-tab-group placement="end"> + <sl-tab slot="nav" panel="general">General</sl-tab> + <sl-tab slot="nav" panel="custom">Custom</sl-tab> + <sl-tab slot="nav" panel="advanced">Advanced</sl-tab> + <sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab> + + <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> + <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> + <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> + <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> +</sl-tab-group> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => ( + <SlTabGroup placement="end"> + <SlTab slot="nav" panel="general"> + General + </SlTab> + <SlTab slot="nav" panel="custom"> + Custom + </SlTab> + <SlTab slot="nav" panel="advanced"> + Advanced + </SlTab> + <SlTab slot="nav" panel="disabled" disabled> + Disabled + </SlTab> + + <SlTabPanel name="general">This is the general tab panel.</SlTabPanel> + <SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel> + <SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel> + <SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel> + </SlTabGroup> +); +
+ Add the closable
attribute to a tab to show a close button. This example shows how you can
+ dynamically remove tabs from the DOM when the close button is activated.
+
+<sl-tab-group class="tabs-closable"> + <sl-tab slot="nav" panel="general">General</sl-tab> + <sl-tab slot="nav" panel="closable-1" closable>Closable 1</sl-tab> + <sl-tab slot="nav" panel="closable-2" closable>Closable 2</sl-tab> + <sl-tab slot="nav" panel="closable-3" closable>Closable 3</sl-tab> + + <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> + <sl-tab-panel name="closable-1">This is the first closable tab panel.</sl-tab-panel> + <sl-tab-panel name="closable-2">This is the second closable tab panel.</sl-tab-panel> + <sl-tab-panel name="closable-3">This is the third closable tab panel.</sl-tab-panel> +</sl-tab-group> + +<script> + const tabGroup = document.querySelector('.tabs-closable'); + + tabGroup.addEventListener('sl-close', async event => { + const tab = event.target; + const panel = tabGroup.querySelector(`sl-tab-panel[name="${tab.panel}"]`); + + // Show the previous tab if the tab is currently active + if (tab.active) { + tabGroup.show(tab.previousElementSibling.panel); + } + + // Remove the tab + panel + tab.remove(); + panel.remove(); + }); +</script> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => { + function handleClose(event) { + // + // This is a crude example that removes the tab and its panel from the DOM. + // There are better ways to manage tab creation/removal in React, but that + // would significantly complicate the example. + // + const tab = event.target; + const tabGroup = tab.closest('sl-tab-group'); + const tabPanel = tabGroup.querySelector(`[aria-labelledby="${tab.id}"]`); + + tab.remove(); + tabPanel.remove(); + } + + return ( + <SlTabGroup className="tabs-closable" onSlClose={handleClose}> + <SlTab slot="nav" panel="general"> + General + </SlTab> + <SlTab slot="nav" panel="closable-1" closable onSlClose={handleClose}> + Closable 1 + </SlTab> + <SlTab slot="nav" panel="closable-2" closable onSlClose={handleClose}> + Closable 2 + </SlTab> + <SlTab slot="nav" panel="closable-3" closable onSlClose={handleClose}> + Closable 3 + </SlTab> + + <SlTabPanel name="general">This is the general tab panel.</SlTabPanel> + <SlTabPanel name="closable-1">This is the first closable tab panel.</SlTabPanel> + <SlTabPanel name="closable-2">This is the second closable tab panel.</SlTabPanel> + <SlTabPanel name="closable-3">This is the third closable tab panel.</SlTabPanel> + </SlTabGroup> + ); +}; +
When there are more tabs than horizontal space allows, the nav will be scrollable.
+ ++<sl-tab-group> + <sl-tab slot="nav" panel="tab-1">Tab 1</sl-tab> + <sl-tab slot="nav" panel="tab-2">Tab 2</sl-tab> + <sl-tab slot="nav" panel="tab-3">Tab 3</sl-tab> + <sl-tab slot="nav" panel="tab-4">Tab 4</sl-tab> + <sl-tab slot="nav" panel="tab-5">Tab 5</sl-tab> + <sl-tab slot="nav" panel="tab-6">Tab 6</sl-tab> + <sl-tab slot="nav" panel="tab-7">Tab 7</sl-tab> + <sl-tab slot="nav" panel="tab-8">Tab 8</sl-tab> + <sl-tab slot="nav" panel="tab-9">Tab 9</sl-tab> + <sl-tab slot="nav" panel="tab-10">Tab 10</sl-tab> + <sl-tab slot="nav" panel="tab-11">Tab 11</sl-tab> + <sl-tab slot="nav" panel="tab-12">Tab 12</sl-tab> + <sl-tab slot="nav" panel="tab-13">Tab 13</sl-tab> + <sl-tab slot="nav" panel="tab-14">Tab 14</sl-tab> + <sl-tab slot="nav" panel="tab-15">Tab 15</sl-tab> + <sl-tab slot="nav" panel="tab-16">Tab 16</sl-tab> + <sl-tab slot="nav" panel="tab-17">Tab 17</sl-tab> + <sl-tab slot="nav" panel="tab-18">Tab 18</sl-tab> + <sl-tab slot="nav" panel="tab-19">Tab 19</sl-tab> + <sl-tab slot="nav" panel="tab-20">Tab 20</sl-tab> + + <sl-tab-panel name="tab-1">Tab panel 1</sl-tab-panel> + <sl-tab-panel name="tab-2">Tab panel 2</sl-tab-panel> + <sl-tab-panel name="tab-3">Tab panel 3</sl-tab-panel> + <sl-tab-panel name="tab-4">Tab panel 4</sl-tab-panel> + <sl-tab-panel name="tab-5">Tab panel 5</sl-tab-panel> + <sl-tab-panel name="tab-6">Tab panel 6</sl-tab-panel> + <sl-tab-panel name="tab-7">Tab panel 7</sl-tab-panel> + <sl-tab-panel name="tab-8">Tab panel 8</sl-tab-panel> + <sl-tab-panel name="tab-9">Tab panel 9</sl-tab-panel> + <sl-tab-panel name="tab-10">Tab panel 10</sl-tab-panel> + <sl-tab-panel name="tab-11">Tab panel 11</sl-tab-panel> + <sl-tab-panel name="tab-12">Tab panel 12</sl-tab-panel> + <sl-tab-panel name="tab-13">Tab panel 13</sl-tab-panel> + <sl-tab-panel name="tab-14">Tab panel 14</sl-tab-panel> + <sl-tab-panel name="tab-15">Tab panel 15</sl-tab-panel> + <sl-tab-panel name="tab-16">Tab panel 16</sl-tab-panel> + <sl-tab-panel name="tab-17">Tab panel 17</sl-tab-panel> + <sl-tab-panel name="tab-18">Tab panel 18</sl-tab-panel> + <sl-tab-panel name="tab-19">Tab panel 19</sl-tab-panel> + <sl-tab-panel name="tab-20">Tab panel 20</sl-tab-panel> +</sl-tab-group> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => ( + <SlTabGroup> + <SlTab slot="nav" panel="tab-1"> + Tab 1 + </SlTab> + <SlTab slot="nav" panel="tab-2"> + Tab 2 + </SlTab> + <SlTab slot="nav" panel="tab-3"> + Tab 3 + </SlTab> + <SlTab slot="nav" panel="tab-4"> + Tab 4 + </SlTab> + <SlTab slot="nav" panel="tab-5"> + Tab 5 + </SlTab> + <SlTab slot="nav" panel="tab-6"> + Tab 6 + </SlTab> + <SlTab slot="nav" panel="tab-7"> + Tab 7 + </SlTab> + <SlTab slot="nav" panel="tab-8"> + Tab 8 + </SlTab> + <SlTab slot="nav" panel="tab-9"> + Tab 9 + </SlTab> + <SlTab slot="nav" panel="tab-10"> + Tab 10 + </SlTab> + <SlTab slot="nav" panel="tab-11"> + Tab 11 + </SlTab> + <SlTab slot="nav" panel="tab-12"> + Tab 12 + </SlTab> + <SlTab slot="nav" panel="tab-13"> + Tab 13 + </SlTab> + <SlTab slot="nav" panel="tab-14"> + Tab 14 + </SlTab> + <SlTab slot="nav" panel="tab-15"> + Tab 15 + </SlTab> + <SlTab slot="nav" panel="tab-16"> + Tab 16 + </SlTab> + <SlTab slot="nav" panel="tab-17"> + Tab 17 + </SlTab> + <SlTab slot="nav" panel="tab-18"> + Tab 18 + </SlTab> + <SlTab slot="nav" panel="tab-19"> + Tab 19 + </SlTab> + <SlTab slot="nav" panel="tab-20"> + Tab 20 + </SlTab> + + <SlTabPanel name="tab-1">Tab panel 1</SlTabPanel> + <SlTabPanel name="tab-2">Tab panel 2</SlTabPanel> + <SlTabPanel name="tab-3">Tab panel 3</SlTabPanel> + <SlTabPanel name="tab-4">Tab panel 4</SlTabPanel> + <SlTabPanel name="tab-5">Tab panel 5</SlTabPanel> + <SlTabPanel name="tab-6">Tab panel 6</SlTabPanel> + <SlTabPanel name="tab-7">Tab panel 7</SlTabPanel> + <SlTabPanel name="tab-8">Tab panel 8</SlTabPanel> + <SlTabPanel name="tab-9">Tab panel 9</SlTabPanel> + <SlTabPanel name="tab-10">Tab panel 10</SlTabPanel> + <SlTabPanel name="tab-11">Tab panel 11</SlTabPanel> + <SlTabPanel name="tab-12">Tab panel 12</SlTabPanel> + <SlTabPanel name="tab-13">Tab panel 13</SlTabPanel> + <SlTabPanel name="tab-14">Tab panel 14</SlTabPanel> + <SlTabPanel name="tab-15">Tab panel 15</SlTabPanel> + <SlTabPanel name="tab-16">Tab panel 16</SlTabPanel> + <SlTabPanel name="tab-17">Tab panel 17</SlTabPanel> + <SlTabPanel name="tab-18">Tab panel 18</SlTabPanel> + <SlTabPanel name="tab-19">Tab panel 19</SlTabPanel> + <SlTabPanel name="tab-20">Tab panel 20</SlTabPanel> + </SlTabGroup> +); +
+ When focused, keyboard users can press Left or Right to select the desired tab. By
+ default, the corresponding tab panel will be shown immediately (automatic activation). You can change this
+ behavior by setting activation="manual"
which will require the user to press
+ Space or Enter before showing the tab panel (manual activation).
+
+<sl-tab-group activation="manual"> + <sl-tab slot="nav" panel="general">General</sl-tab> + <sl-tab slot="nav" panel="custom">Custom</sl-tab> + <sl-tab slot="nav" panel="advanced">Advanced</sl-tab> + <sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab> + + <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> + <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> + <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> + <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> +</sl-tab-group> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => ( + <SlTabGroup activation="manual"> + <SlTab slot="nav" panel="general"> + General + </SlTab> + <SlTab slot="nav" panel="custom"> + Custom + </SlTab> + <SlTab slot="nav" panel="advanced"> + Advanced + </SlTab> + <SlTab slot="nav" panel="disabled" disabled> + Disabled + </SlTab> + + <SlTabPanel name="general">This is the general tab panel.</SlTabPanel> + <SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel> + <SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel> + <SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel> + </SlTabGroup> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tab-group/tab-group.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tab-group/tab-group.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/tab-group/tab-group.js';
To import this component as a React component:
++import SlTabGroup from '@onsonr/nebula/dist/react/tab-group';
Name | +Description | +
---|---|
(default) | +
+ Used for grouping tab panels in the tab group. Must be <sl-tab-panel> elements.
+ |
+
+ nav
+ |
+ Used for grouping tabs in the tab group. Must be <sl-tab> elements. |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ placement
+ |
+ The placement of the tabs. | ++ |
+ 'top' | 'bottom' | 'start' | 'end'
+ |
+
+ 'top'
+ |
+
+ activation
+ |
+ + When set to auto, navigating tabs with the arrow keys will instantly show the corresponding tab + panel. When set to manual, the tab will receive focus but will not show until the user presses + spacebar or enter. + | ++ |
+ 'auto' | 'manual'
+ |
+
+ 'auto'
+ |
+
+ noScrollControls
+
+ + no-scroll-controls
+
+ |
+ Disables the scroll arrows that appear when tabs overflow. | ++ |
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-tab-show |
+ onSlTabShow |
+ Emitted when a tab is shown. | +
+ { name: String }
+ |
+
sl-tab-hide |
+ onSlTabHide |
+ Emitted when a tab is hidden. | +
+ { name: String }
+ |
+
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the specified tab panel. | +
+ panel: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--indicator-color |
+ The color of the active tab indicator. | ++ |
--track-color |
+ The color of the indicator’s track (the line that separates tabs from panels). | ++ |
--track-width |
+ The width of the indicator’s track (the line that separates tabs from panels). | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
nav |
+ The tab group’s navigation container where tabs are slotted in. | +
tabs |
+ The container that wraps the tabs. | +
active-tab-indicator |
+ The line that highlights the currently selected tab. | +
body |
+ The tab group’s body where tab panels are slotted in. | +
scroll-button |
+
+ The previous/next scroll buttons that show when tabs are scrollable, an
+ <sl-icon-button> .
+ |
+
scroll-button--start |
+ The starting scroll button. | +
scroll-button--end |
+ The ending scroll button. | +
scroll-button__base |
+ The scroll button’s exported base part. |
+
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon-button>
<sl-tab-panel> | SlTabPanel
+ + Tab panels are used inside tab groups to display tabbed content. +
+ ++<sl-tab-group> + <sl-tab slot="nav" panel="general">General</sl-tab> + <sl-tab slot="nav" panel="custom">Custom</sl-tab> + <sl-tab slot="nav" panel="advanced">Advanced</sl-tab> + <sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab> + + <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> + <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> + <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> + <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> +</sl-tab-group> +
+import SlTab from '@onsonr/nebula/dist/react/tab'; +import SlTabGroup from '@onsonr/nebula/dist/react/tab-group'; +import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel'; + +const App = () => ( + <SlTabGroup> + <SlTab slot="nav" panel="general"> + General + </SlTab> + <SlTab slot="nav" panel="custom"> + Custom + </SlTab> + <SlTab slot="nav" panel="advanced"> + Advanced + </SlTab> + <SlTab slot="nav" panel="disabled" disabled> + Disabled + </SlTab> + + <SlTabPanel name="general">This is the general tab panel.</SlTabPanel> + <SlTabPanel name="custom">This is the custom tab panel.</SlTabPanel> + <SlTabPanel name="advanced">This is the advanced tab panel.</SlTabPanel> + <SlTabPanel name="disabled">This is a disabled tab panel.</SlTabPanel> + </SlTabGroup> +); +
Additional demonstrations can be found in the tab group examples.
++ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tab-panel/tab-panel.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tab-panel/tab-panel.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/tab-panel/tab-panel.js';
To import this component as a React component:
++import SlTabPanel from '@onsonr/nebula/dist/react/tab-panel';
Name | +Description | +
---|---|
(default) | +The tab panel’s content. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The tab panel’s name. | +
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ active
+ |
+ When true, the tab panel will be shown. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +Description | +Default | +
---|---|---|
--padding |
+ The tab panel’s padding. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-tab> | SlTab
+ + Tabs are used inside tab groups to represent and activate + tab panels. +
+ +Additional demonstrations can be found in the tab group examples.
++ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tab/tab.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tab/tab.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/tab/tab.js';
To import this component as a React component:
++import SlTab from '@onsonr/nebula/dist/react/tab';
Name | +Description | +
---|---|
(default) | +The tab’s label. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ panel
+ |
+ + The name of the tab panel this tab is associated with. The panel must be located in the same tab + group. + | +
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ active
+ |
+ Draws the tab in an active state. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ closable
+ |
+ Makes the tab closable and shows a close button. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ Disables the tab and prevents selection. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-close |
+ onSlClose |
+ Emitted when the tab is closable and the close button is activated. | +- | +
+ Learn more about events. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
close-button |
+ The close button, an <sl-icon-button> . |
+
close-button__base |
+ The close button’s exported base part. |
+
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon-button>
<sl-tag> | SlTag
+ Tags are used as labels to organize things or to indicate a selection.
+ ++<sl-tag variant="primary">Primary</sl-tag> +<sl-tag variant="success">Success</sl-tag> +<sl-tag variant="neutral">Neutral</sl-tag> +<sl-tag variant="warning">Warning</sl-tag> +<sl-tag variant="danger">Danger</sl-tag> +
+import SlTag from '@onsonr/nebula/dist/react/tag'; + +const App = () => ( + <> + <SlTag variant="primary">Primary</SlTag> + <SlTag variant="success">Success</SlTag> + <SlTag variant="neutral">Neutral</SlTag> + <SlTag variant="warning">Warning</SlTag> + <SlTag variant="danger">Danger</SlTag> + </> +); +
Use the size
attribute to change a tab’s size.
+<sl-tag size="small">Small</sl-tag> +<sl-tag size="medium">Medium</sl-tag> +<sl-tag size="large">Large</sl-tag> +
+import SlTag from '@onsonr/nebula/dist/react/tag'; + +const App = () => ( + <> + <SlTag size="small">Small</SlTag> + <SlTag size="medium">Medium</SlTag> + <SlTag size="large">Large</SlTag> + </> +); +
Use the pill
attribute to give tabs rounded edges.
+<sl-tag size="small" pill>Small</sl-tag> +<sl-tag size="medium" pill>Medium</sl-tag> +<sl-tag size="large" pill>Large</sl-tag> +
+import SlTag from '@onsonr/nebula/dist/react/tag'; + +const App = () => ( + <> + <SlTag size="small" pill> + Small + </SlTag> + <SlTag size="medium" pill> + Medium + </SlTag> + <SlTag size="large" pill> + Large + </SlTag> + </> +); +
Use the removable
attribute to add a remove button to the tag.
+<div class="tags-removable"> + <sl-tag size="small" removable>Small</sl-tag> + <sl-tag size="medium" removable>Medium</sl-tag> + <sl-tag size="large" removable>Large</sl-tag> +</div> + +<script> + const div = document.querySelector('.tags-removable'); + + div.addEventListener('sl-remove', event => { + const tag = event.target; + tag.style.opacity = '0'; + setTimeout(() => (tag.style.opacity = '1'), 2000); + }); +</script> + +<style> + .tags-removable sl-tag { + transition: var(--sl-transition-medium) opacity; + } +</style> +
+import SlTag from '@onsonr/nebula/dist/react/tag'; + +const css = ` + .tags-removable sl-tag { + transition: var(--sl-transition-medium) opacity; + } +`; + +const App = () => { + function handleRemove(event) { + const tag = event.target; + tag.style.opacity = '0'; + setTimeout(() => (tag.style.opacity = '1'), 2000); + } + + return ( + <> + <div className="tags-removable"> + <SlTag size="small" removable onSlRemove={handleRemove}> + Small + </SlTag> + + <SlTag size="medium" removable onSlRemove={handleRemove}> + Medium + </SlTag> + + <SlTag size="large" removable onSlRemove={handleRemove}> + Large + </SlTag> + </div> + + <style>{css}</style> + </> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tag/tag.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tag/tag.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/tag/tag.js';
To import this component as a React component:
++import SlTag from '@onsonr/nebula/dist/react/tag';
Name | +Description | +
---|---|
(default) | +The tag’s content. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ variant
+ |
+ The tag’s theme variant. | +
+ |
+
+ 'primary' | 'success' | 'neutral' | 'warning' | 'danger' | 'text'
+ |
+
+ 'neutral'
+ |
+
+ size
+ |
+ The tag’s size. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ pill
+ |
+ Draws a pill-style tag with rounded edges. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ removable
+ |
+ Makes the tag removable and shows a remove button. | ++ |
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-remove |
+ onSlRemove |
+ Emitted when the remove button is activated. | +- | +
+ Learn more about events. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
content |
+ The tag’s content. | +
remove-button |
+ The tag’s remove button, an <sl-icon-button> . |
+
remove-button__base |
+ The remove button’s exported base part. |
+
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-icon>
<sl-icon-button>
<sl-textarea> | SlTextarea
+ Textareas collect data from the user and allow multiple lines of text.
+ ++<sl-textarea></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea />; +
+ This component works with standard <form>
elements. Please refer to the section on
+ form controls to learn more about form submission and
+ client-side validation.
+
+ Use the label
attribute to give the textarea an accessible label. For labels that contain HTML,
+ use the label
slot instead.
+
+<sl-textarea label="Comments"></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea label="Comments" />; +
+ Add descriptive help text to a textarea with the help-text
attribute. For help texts that
+ contain HTML, use the help-text
slot instead.
+
+<sl-textarea label="Feedback" help-text="Please tell us what you think."> </sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea label="Feedback" help-text="Please tell us what you think." />; +
Use the rows
attribute to change the number of text rows that get shown.
+<sl-textarea rows="2"></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea rows={2} />; +
Use the placeholder
attribute to add a placeholder.
+<sl-textarea placeholder="Type something"></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea placeholder="Type something" />; +
Add the filled
attribute to draw a filled textarea.
+<sl-textarea placeholder="Type something" filled></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea placeholder="Type something" filled />; +
Use the disabled
attribute to disable a textarea.
+<sl-textarea placeholder="Textarea" disabled></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea placeholder="Textarea" disabled />; +
Use the size
attribute to change a textarea’s size.
+<sl-textarea placeholder="Small" size="small"></sl-textarea> +<br /> +<sl-textarea placeholder="Medium" size="medium"></sl-textarea> +<br /> +<sl-textarea placeholder="Large" size="large"></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => ( + <> + <SlTextarea placeholder="Small" size="small"></SlTextarea> + <br /> + <SlTextarea placeholder="Medium" size="medium"></SlTextarea> + <br /> + <SlTextarea placeholder="Large" size="large"></SlTextarea> + </> +); +
+ By default, textareas can be resized vertically by the user. To prevent resizing, set the
+ resize
attribute to none
.
+
+<sl-textarea resize="none"></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea resize="none" />; +
+ Textareas will automatically resize to expand to fit their content when resize
is set to
+ auto
.
+
+<sl-textarea resize="auto"></sl-textarea> +
+import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => <SlTextarea resize="auto" />; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/textarea/textarea.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/textarea/textarea.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/textarea/textarea.js';
To import this component as a React component:
++import SlTextarea from '@onsonr/nebula/dist/react/textarea';
Name | +Description | +
---|---|
+ label
+ |
+ The textarea’s label. Alternatively, you can use the label attribute. |
+
+ help-text
+ |
+
+ Text that describes how to use the input. Alternatively, you can use the
+ help-text attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ name
+ |
+ The name of the textarea, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ value
+ |
+ The current value of the textarea, submitted as a name/value pair with form data. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ size
+ |
+ The textarea’s size. | +
+ |
+
+ 'small' | 'medium' | 'large'
+ |
+
+ 'medium'
+ |
+
+ filled
+ |
+ Draws a filled textarea. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ label
+ |
+ The textarea’s label. If you need to display HTML, use the label slot instead. |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ helpText
+
+ + help-text
+
+ |
+
+ The textarea’s help text. If you need to display HTML, use the help-text slot instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ placeholder
+ |
+ Placeholder text to show as a hint when the input is empty. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ rows
+ |
+ The number of rows to display by default. | ++ |
+ number
+ |
+
+ 4
+ |
+
+ resize
+ |
+ Controls how the textarea can be resized. | ++ |
+ 'none' | 'vertical' | 'auto'
+ |
+
+ 'vertical'
+ |
+
+ disabled
+ |
+ Disables the textarea. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ readonly
+ |
+ Makes the textarea readonly. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ form
+ |
+
+ By default, form controls are associated with the nearest containing
+ <form> element. This attribute allows you to place the form control outside of a
+ form and associate it with the form that has this id . The form must be in the same
+ document or shadow root for this to work.
+ |
+
+ |
+
+ string
+ |
+
+ ''
+ |
+
+ required
+ |
+ Makes the textarea a required field. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ minlength
+ |
+ The minimum length of input that will be considered valid. | ++ |
+ number
+ |
+ - | +
+ maxlength
+ |
+ The maximum length of input that will be considered valid. | ++ |
+ number
+ |
+ - | +
+ autocapitalize
+ |
+ + Controls whether and how text input is automatically capitalized as it is entered by the user. + | ++ |
+ 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters'
+ |
+ - | +
+ autocorrect
+ |
+ Indicates whether the browser’s autocorrect feature is on or off. | ++ |
+ string
+ |
+ - | +
+ autocomplete
+ |
+ + Specifies what permission the browser has to provide assistance in filling out form field values. + Refer to + this page on MDN + for available values. + | ++ |
+ string
+ |
+ - | +
+ autofocus
+ |
+ Indicates that the input should receive focus on page load. | ++ |
+ boolean
+ |
+ - | +
+ enterkeyhint
+ |
+ Used to customize the label or icon of the Enter key on virtual keyboards. | ++ |
+ 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
+ |
+ - | +
+ spellcheck
+ |
+ Enables spell checking on the textarea. | ++ |
+ boolean
+ |
+
+ true
+ |
+
+ inputmode
+ |
+ + Tells the browser what type of data will be entered by the user, allowing it to display the + appropriate virtual keyboard on supportive devices. + | ++ |
+ 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url'
+ |
+ - | +
+ defaultValue
+ |
+ The default value of the form control. Primarily used for resetting the form control. | ++ |
+ string
+ |
+
+ ''
+ |
+
+ validity
+ |
+ Gets the validity state object | ++ | - | +- | +
+ validationMessage
+ |
+ Gets the validation message | ++ | - | +- | +
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-blur |
+ onSlBlur |
+ Emitted when the control loses focus. | +- | +
sl-change |
+ onSlChange |
+ Emitted when an alteration to the control’s value is committed by the user. | +- | +
sl-focus |
+ onSlFocus |
+ Emitted when the control gains focus. | +- | +
sl-input |
+ onSlInput |
+ Emitted when the control receives input. | +- | +
sl-invalid |
+ onSlInvalid |
+ + Emitted when the form control has been checked for validity and its constraints aren’t satisfied. + | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
focus() |
+ Sets focus on the textarea. | +
+ options: FocusOptions
+ |
+
blur() |
+ Removes focus from the textarea. | +- | +
select() |
+ Selects all the text in the textarea. | +- | +
scrollPosition() |
+ Gets or sets the textarea’s scroll position. | +
+ position: { top?: number; left?: number }
+ |
+
setSelectionRange() |
+ Sets the start and end positions of the text selection (0-based). | +
+
+ selectionStart: number, selectionEnd: number, selectionDirection: 'forward' | 'backward' | 'none'
+
+ |
+
setRangeText() |
+ Replaces a range of text with a new string. | +
+
+ replacement: string, start: number, end: number, selectMode: 'select' | 'start' | 'end' |
+ 'preserve'
+
+ |
+
checkValidity() |
+
+ Checks for validity but does not show a validation message. Returns true when valid and
+ false when invalid.
+ |
+ - | +
getForm() |
+ Gets the associated form, if one exists. | +- | +
reportValidity() |
+ Checks for validity and shows the browser’s validation message if the control is invalid. | +- | +
setCustomValidity() |
+ Sets a custom validation message. Pass an empty string to restore validity. | +
+ message: string
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
form-control |
+ The form control that wraps the label, input, and help text. | +
form-control-label |
+ The label’s wrapper. | +
form-control-input |
+ The input’s wrapper. | +
form-control-help-text |
+ The help text’s wrapper. | +
base |
+ The component’s base wrapper. | +
textarea |
+ The internal <textarea> control. |
+
+ Learn more about customizing CSS parts. +
+<sl-tooltip> | SlTooltip
+ Tooltips display additional information based on a specific action.
+ ++ A tooltip’s target is its first child element, so you should only wrap one element inside of the + tooltip. If you need the tooltip to show up for multiple elements, nest them inside a container first. +
+
+ Tooltips use display: contents
so they won’t interfere with how elements are positioned in a
+ flex or grid layout.
+
+<sl-tooltip content="This is a tooltip"> + <sl-button>Hover Me</sl-button> +</sl-tooltip> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => ( + <SlTooltip content="This is a tooltip"> + <SlButton>Hover Me</SlButton> + </SlTooltip> +); +
Use the placement
attribute to set the preferred placement of the tooltip.
+<div class="tooltip-placement-example"> + <div class="tooltip-placement-example-row"> + <sl-tooltip content="top-start" placement="top-start"> + <sl-button></sl-button> + </sl-tooltip> + + <sl-tooltip content="top" placement="top"> + <sl-button></sl-button> + </sl-tooltip> + + <sl-tooltip content="top-end" placement="top-end"> + <sl-button></sl-button> + </sl-tooltip> + </div> + + <div class="tooltip-placement-example-row"> + <sl-tooltip content="left-start" placement="left-start"> + <sl-button></sl-button> + </sl-tooltip> + + <sl-tooltip content="right-start" placement="right-start"> + <sl-button></sl-button> + </sl-tooltip> + </div> + + <div class="tooltip-placement-example-row"> + <sl-tooltip content="left" placement="left"> + <sl-button></sl-button> + </sl-tooltip> + + <sl-tooltip content="right" placement="right"> + <sl-button></sl-button> + </sl-tooltip> + </div> + + <div class="tooltip-placement-example-row"> + <sl-tooltip content="left-end" placement="left-end"> + <sl-button></sl-button> + </sl-tooltip> + + <sl-tooltip content="right-end" placement="right-end"> + <sl-button></sl-button> + </sl-tooltip> + </div> + + <div class="tooltip-placement-example-row"> + <sl-tooltip content="bottom-start" placement="bottom-start"> + <sl-button></sl-button> + </sl-tooltip> + + <sl-tooltip content="bottom" placement="bottom"> + <sl-button></sl-button> + </sl-tooltip> + + <sl-tooltip content="bottom-end" placement="bottom-end"> + <sl-button></sl-button> + </sl-tooltip> + </div> +</div> + +<style> + .tooltip-placement-example { + width: 250px; + margin: 1rem; + } + + .tooltip-placement-example-row:after { + content: ''; + display: table; + clear: both; + } + + .tooltip-placement-example sl-button { + float: left; + width: 2.5rem; + margin-right: 0.25rem; + margin-bottom: 0.25rem; + } + + .tooltip-placement-example-row:nth-child(1) sl-tooltip:first-child sl-button, + .tooltip-placement-example-row:nth-child(5) sl-tooltip:first-child sl-button { + margin-left: calc(40px + 0.25rem); + } + + .tooltip-placement-example-row:nth-child(2) sl-tooltip:nth-child(2) sl-button, + .tooltip-placement-example-row:nth-child(3) sl-tooltip:nth-child(2) sl-button, + .tooltip-placement-example-row:nth-child(4) sl-tooltip:nth-child(2) sl-button { + margin-left: calc((40px * 3) + (0.25rem * 3)); + } +</style> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const css = ` + .tooltip-placement-example { + width: 250px; + } + + .tooltip-placement-example-row:after { + content: ''; + display: table; + clear: both; + } + + .tooltip-placement-example sl-button { + float: left; + width: 2.5rem; + margin-right: 0.25rem; + margin-bottom: 0.25rem; + } + + .tooltip-placement-example-row:nth-child(1) sl-tooltip:first-child sl-button, + .tooltip-placement-example-row:nth-child(5) sl-tooltip:first-child sl-button { + margin-left: calc(40px + 0.25rem); + } + + .tooltip-placement-example-row:nth-child(2) sl-tooltip:nth-child(2) sl-button, + .tooltip-placement-example-row:nth-child(3) sl-tooltip:nth-child(2) sl-button, + .tooltip-placement-example-row:nth-child(4) sl-tooltip:nth-child(2) sl-button { + margin-left: calc((40px * 3) + (0.25rem * 3)); + } +`; + +const App = () => ( + <> + <div className="tooltip-placement-example"> + <div className="tooltip-placement-example-row"> + <SlTooltip content="top-start" placement="top-start"> + <SlButton /> + </SlTooltip> + + <SlTooltip content="top" placement="top"> + <SlButton /> + </SlTooltip> + + <SlTooltip content="top-end" placement="top-end"> + <SlButton /> + </SlTooltip> + </div> + + <div className="tooltip-placement-example-row"> + <SlTooltip content="left-start" placement="left-start"> + <SlButton /> + </SlTooltip> + + <SlTooltip content="right-start" placement="right-start"> + <SlButton /> + </SlTooltip> + </div> + + <div className="tooltip-placement-example-row"> + <SlTooltip content="left" placement="left"> + <SlButton /> + </SlTooltip> + + <SlTooltip content="right" placement="right"> + <SlButton /> + </SlTooltip> + </div> + + <div className="tooltip-placement-example-row"> + <SlTooltip content="left-end" placement="left-end"> + <SlButton /> + </SlTooltip> + + <SlTooltip content="right-end" placement="right-end"> + <SlButton /> + </SlTooltip> + </div> + + <div className="tooltip-placement-example-row"> + <SlTooltip content="bottom-start" placement="bottom-start"> + <SlButton /> + </SlTooltip> + + <SlTooltip content="bottom" placement="bottom"> + <SlButton /> + </SlTooltip> + + <SlTooltip content="bottom-end" placement="bottom-end"> + <SlButton /> + </SlTooltip> + </div> + </div> + + <style>{css}</style> + </> +); +
+ Set the trigger
attribute to click
to toggle the tooltip on click instead of
+ hover.
+
+<sl-tooltip content="Click again to dismiss" trigger="click"> + <sl-button>Click to Toggle</sl-button> +</sl-tooltip> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => ( + <SlTooltip content="Click again to dismiss" trigger="click"> + <SlButton>Click to Toggle</SlButton> + </SlTooltip> +); +
+ Tooltips can be controlled programmatically by setting the trigger
attribute to
+ manual
. Use the open
attribute to control when the tooltip is shown.
+
+<sl-button style="margin-right: 4rem;">Toggle Manually</sl-button> + +<sl-tooltip content="This is an avatar" trigger="manual" class="manual-tooltip"> + <sl-avatar label="User"></sl-avatar> +</sl-tooltip> + +<script> + const tooltip = document.querySelector('.manual-tooltip'); + const toggle = tooltip.previousElementSibling; + + toggle.addEventListener('click', () => (tooltip.open = !tooltip.open)); +</script> +
+import { useState } from 'react'; +import SlAvatar from '@onsonr/nebula/dist/react/avatar'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => { + const [open, setOpen] = useState(false); + + return ( + <> + <SlButton style={{ marginRight: '4rem' }} onClick={() => setOpen(!open)}> + Toggle Manually + </SlButton> + + <SlTooltip open={open} content="This is an avatar" trigger="manual"> + <SlAvatar /> + </SlTooltip> + </> + ); +}; +
+ You can control the size of tooltip arrows by overriding the --sl-tooltip-arrow-size
design
+ token. To remove them, set the value to 0
as shown below.
+
+<sl-tooltip content="This is a tooltip" style="--sl-tooltip-arrow-size: 0;"> + <sl-button>No Arrow</sl-button> +</sl-tooltip> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => ( + <div style={{ '--sl-tooltip-arrow-size': '0' }}> + <SlTooltip content="This is a tooltip"> + <SlButton>Above</SlButton> + </SlTooltip> + + <SlTooltip content="This is a tooltip" placement="bottom"> + <SlButton>Below</SlButton> + </SlTooltip> + </div> +); +
+ To override it globally, set it in a root block in your stylesheet after the Nebula stylesheet is loaded. +
++:root { + --sl-tooltip-arrow-size: 0; +} +
+ Use the content
slot to create tooltips with HTML content. Tooltips are designed only for text
+ and presentational elements. Avoid placing interactive content, such as buttons, links, and form controls,
+ in a tooltip.
+
+<sl-tooltip> + <div slot="content">I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML!</div> + + <sl-button>Hover me</sl-button> +</sl-tooltip> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => ( + <SlTooltip> + <div slot="content"> + I'm not <strong>just</strong> a tooltip, I'm a <em>tooltip</em> with HTML! + </div> + + <SlButton>Hover Me</SlButton> + </SlTooltip> +); +
+ Use the --max-width
custom property to change the width the tooltip can grow to before wrapping
+ occurs.
+
+<sl-tooltip style="--max-width: 80px;" content="This tooltip will wrap after only 80 pixels."> + <sl-button>Hover me</sl-button> +</sl-tooltip> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const App = () => ( + <SlTooltip style={{ '--max-width': '80px' }} content="This tooltip will wrap after only 80 pixels."> + <SlButton>Hover Me</SlButton> + </SlTooltip> +); +
+ Tooltips will be clipped if they’re inside a container that has overflow: auto|hidden|scroll
.
+ The hoist
attribute forces the tooltip to use a fixed positioning strategy, allowing it to
+ break out of the container. In this case, the tooltip will be positioned relative to its
+ containing block, which is usually the viewport unless an ancestor uses a transform
, perspective
,
+ or filter
.
+ Refer to this page
+ for more details.
+
+<div class="tooltip-hoist"> + <sl-tooltip content="This is a tooltip"> + <sl-button>No Hoist</sl-button> + </sl-tooltip> + + <sl-tooltip content="This is a tooltip" hoist> + <sl-button>Hoist</sl-button> + </sl-tooltip> +</div> + +<style> + .tooltip-hoist { + position: relative; + border: solid 2px var(--sl-panel-border-color); + overflow: hidden; + padding: var(--sl-spacing-medium); + } +</style> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlTooltip from '@onsonr/nebula/dist/react/tooltip'; + +const css = ` + .tooltip-hoist { + border: solid 2px var(--sl-panel-border-color); + overflow: hidden; + padding: var(--sl-spacing-medium); + position: relative; + } +`; + +const App = () => ( + <> + <div class="tooltip-hoist"> + <SlTooltip content="This is a tooltip"> + <SlButton>No Hoist</SlButton> + </SlTooltip> + + <SlTooltip content="This is a tooltip" hoist> + <SlButton>Hoist</SlButton> + </SlTooltip> + </div> + + <style>{css}</style> + </> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tooltip/tooltip.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tooltip/tooltip.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/tooltip/tooltip.js';
To import this component as a React component:
++import SlTooltip from '@onsonr/nebula/dist/react/tooltip';
Name | +Description | +
---|---|
(default) | ++ The tooltip’s target element. Avoid slotting in more than one element, as subsequent ones will be + ignored. + | +
+ content
+ |
+
+ The content to render in the tooltip. Alternatively, you can use the content attribute.
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ content
+ |
+
+ The tooltip’s content. If you need to display HTML, use the content slot instead.
+ |
+ + |
+ string
+ |
+
+ ''
+ |
+
+ placement
+ |
+ + The preferred placement of the tooltip. Note that the actual placement may vary as needed to keep + the tooltip inside of the viewport. + | ++ |
+
+ 'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' |
+ 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end'
+ |
+
+ 'top'
+ |
+
+ disabled
+ |
+ Disables the tooltip so it won’t show when triggered. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ distance
+ |
+ The distance in pixels from which to offset the tooltip away from its target. | ++ |
+ number
+ |
+
+ 8
+ |
+
+ open
+ |
+ + Indicates whether or not the tooltip is open. You can use this in lieu of the show/hide methods. + | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ skidding
+ |
+ The distance in pixels from which to offset the tooltip along its target. | ++ |
+ number
+ |
+
+ 0
+ |
+
+ trigger
+ |
+
+ Controls how the tooltip is activated. Possible options include click ,
+ hover , focus , and manual . Multiple options can be passed by
+ separating them with a space. When manual is used, the tooltip must be activated programmatically.
+ |
+ + |
+ string
+ |
+
+ 'hover focus'
+ |
+
+ hoist
+ |
+
+ Enable this option to prevent the tooltip from being clipped when the component is placed inside a
+ container with
+ overflow: auto|hidden|scroll . Hoisting uses a fixed positioning strategy that works in
+ many, but not all, scenarios.
+ |
+ + |
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-show |
+ onSlShow |
+ Emitted when the tooltip begins to show. | +- | +
sl-after-show |
+ onSlAfterShow |
+ Emitted after the tooltip has shown and all animations are complete. | +- | +
sl-hide |
+ onSlHide |
+ Emitted when the tooltip begins to hide. | +- | +
sl-after-hide |
+ onSlAfterHide |
+ Emitted after the tooltip has hidden and all animations are complete. | +- | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
show() |
+ Shows the tooltip. | +- | +
hide() |
+ Hides the tooltip | +- | +
+ Learn more about methods. +
+ +Name | +Description | +Default | +
---|---|---|
--max-width |
+ The maximum width of the tooltip before its content will wrap. | ++ |
--hide-delay |
+ The amount of time to wait before hiding the tooltip when hovering. | ++ |
--show-delay |
+ The amount of time to wait before showing the tooltip when hovering. | ++ |
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper, an <sl-popup> element. |
+
base__popup |
+
+ The popup’s exported popup part. Use this to target the tooltip’s popup container.
+ |
+
base__arrow |
+ The popup’s exported arrow part. Use this to target the tooltip’s arrow. |
+
body |
+ The tooltip’s body where its content is rendered. | +
+ Learn more about customizing CSS parts. +
+ +Name | +Description | +
---|---|
tooltip.show |
+ The animation to use when showing the tooltip. | +
tooltip.hide |
+ The animation to use when hiding the tooltip. | +
+ Learn more about customizing animations. +
+ +This component automatically imports the following dependencies.
+ +<sl-popup>
<sl-tree-item> | SlTreeItem
+ + A tree item serves as a hierarchical node that lives inside a tree. +
+ ++<sl-tree> + <sl-tree-item> + Item 1 + <sl-tree-item>Item A</sl-tree-item> + <sl-tree-item>Item B</sl-tree-item> + <sl-tree-item>Item C</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item 2</sl-tree-item> + <sl-tree-item>Item 3</sl-tree-item> +</sl-tree> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => ( + <SlTree> + <SlTreeItem> + Item 1 + <SlTreeItem>Item A</SlTreeItem> + <SlTreeItem>Item B</SlTreeItem> + <SlTreeItem>Item C</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item 2</SlTreeItem> + <SlTreeItem>Item 3</SlTreeItem> + </SlTree> +); +
A tree item can contain other tree items. This allows the node to be expanded or collapsed by the user.
+ ++<sl-tree> + <sl-tree-item> + Item 1 + <sl-tree-item> + Item A + <sl-tree-item>Item Z</sl-tree-item> + <sl-tree-item>Item Y</sl-tree-item> + <sl-tree-item>Item X</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item B</sl-tree-item> + <sl-tree-item>Item C</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item 2</sl-tree-item> + <sl-tree-item>Item 3</sl-tree-item> +</sl-tree> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => ( + <SlTree> + <SlTreeItem> + Item 1 + <SlTreeItem> + Item A + <SlTreeItem>Item Z</SlTreeItem> + <SlTreeItem>Item Y</SlTreeItem> + <SlTreeItem>Item X</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item B</SlTreeItem> + <SlTreeItem>Item C</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item 2</SlTreeItem> + <SlTreeItem>Item 3</SlTreeItem> + </SlTree> +); +
Use the selected
attribute to select a tree item initially.
+<sl-tree> + <sl-tree-item selected> + Item 1 + <sl-tree-item>Item A</sl-tree-item> + <sl-tree-item>Item B</sl-tree-item> + <sl-tree-item>Item C</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item 2</sl-tree-item> + <sl-tree-item>Item 3</sl-tree-item> +</sl-tree> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => ( + <SlTree> + <SlTreeItem selected> + Item 1 + <SlTreeItem>Item A</SlTreeItem> + <SlTreeItem>Item B</SlTreeItem> + <SlTreeItem>Item C</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item 2</SlTreeItem> + <SlTreeItem>Item 3</SlTreeItem> + </SlTree> +); +
Use the expanded
attribute to expand a tree item initially.
+<sl-tree> + <sl-tree-item expanded> + Item 1 + <sl-tree-item expanded> + Item A + <sl-tree-item>Item Z</sl-tree-item> + <sl-tree-item>Item Y</sl-tree-item> + <sl-tree-item>Item X</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item B</sl-tree-item> + <sl-tree-item>Item C</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item 2</sl-tree-item> + <sl-tree-item>Item 3</sl-tree-item> +</sl-tree> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => ( + <SlTree> + <SlTreeItem expanded> + Item 1 + <SlTreeItem expanded> + Item A + <SlTreeItem>Item Z</SlTreeItem> + <SlTreeItem>Item Y</SlTreeItem> + <SlTreeItem>Item X</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item B</SlTreeItem> + <SlTreeItem>Item C</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item 2</SlTreeItem> + <SlTreeItem>Item 3</SlTreeItem> + </SlTree> +); +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tree-item/tree-item.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tree-item/tree-item.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/tree-item/tree-item.js';
To import this component as a React component:
++import SlTreeItem from '@onsonr/nebula/dist/react/tree-item';
Name | +Description | +
---|---|
(default) | +The default slot. | +
+ expand-icon
+ |
+ The icon to show when the tree item is expanded. | +
+ collapse-icon
+ |
+ The icon to show when the tree item is collapsed. | +
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ expanded
+ |
+ Expands the tree item. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ selected
+ |
+ Draws the tree item in a selected state. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ disabled
+ |
+ Disables the tree item. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
+ lazy
+ |
+ Enables lazy loading behavior. | +
+ |
+
+ boolean
+ |
+
+ false
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-expand |
+ onSlExpand |
+ Emitted when the tree item expands. | +- | +
sl-after-expand |
+ onSlAfterExpand |
+ Emitted after the tree item expands and all animations are complete. | +- | +
sl-collapse |
+ onSlCollapse |
+ Emitted when the tree item collapses. | +- | +
sl-after-collapse |
+ onSlAfterCollapse |
+ Emitted after the tree item collapses and all animations are complete. | +- | +
sl-lazy-change |
+ onSlLazyChange |
+ Emitted when the tree item’s lazy state changes. | +- | +
sl-lazy-load |
+ onSlLazyLoad |
+
+ Emitted when a lazy item is selected. Use this event to asynchronously load data and append items to
+ the tree before expanding. After appending new items, remove the lazy attribute to
+ remove the loading state and update the tree.
+ |
+ - | +
+ Learn more about events. +
+ +Name | +Description | +Arguments | +
---|---|---|
getChildrenItems() |
+ Gets all the nested tree items in this node. | +
+ { includeDisabled = true }: { includeDisabled?: boolean }
+ |
+
+ Learn more about methods. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
item |
+ The tree item’s container. This element wraps everything except slotted tree item children. | +
item--disabled |
+ Applied when the tree item is disabled. | +
item--expanded |
+ Applied when the tree item is expanded. | +
item--indeterminate |
+ Applied when the selection is indeterminate. | +
item--selected |
+ Applied when the tree item is selected. | +
indentation |
+ The tree item’s indentation container. | +
expand-button |
+ The container that wraps the tree item’s expand button and spinner. | +
spinner |
+ The spinner that shows when a lazy tree item is in the loading state. | +
spinner__base |
+ The spinner’s base part. | +
label |
+ The tree item’s label. | +
children |
+ The container that wraps the tree item’s nested children. | +
checkbox |
+ The checkbox that shows when using multiselect. | +
checkbox__base |
+ The checkbox’s exported base part. |
+
checkbox__control |
+ The checkbox’s exported control part. |
+
checkbox__control--checked |
+ The checkbox’s exported control--checked part. |
+
checkbox__control--indeterminate |
+ The checkbox’s exported control--indeterminate part. |
+
checkbox__checked-icon |
+ The checkbox’s exported checked-icon part. |
+
checkbox__indeterminate-icon |
+ The checkbox’s exported indeterminate-icon part. |
+
checkbox__label |
+ The checkbox’s exported label part. |
+
+ Learn more about customizing CSS parts. +
+ +This component automatically imports the following dependencies.
+ +<sl-checkbox>
<sl-icon>
<sl-spinner>
<sl-tree> | SlTree
+ + Trees allow you to display a hierarchical list of selectable tree items. + Items with children can be expanded and collapsed as desired by the user. +
+ ++<sl-tree> + <sl-tree-item> + Deciduous + <sl-tree-item>Birch</sl-tree-item> + <sl-tree-item> + Maple + <sl-tree-item>Field maple</sl-tree-item> + <sl-tree-item>Red maple</sl-tree-item> + <sl-tree-item>Sugar maple</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Oak</sl-tree-item> + </sl-tree-item> + + <sl-tree-item> + Coniferous + <sl-tree-item>Cedar</sl-tree-item> + <sl-tree-item>Pine</sl-tree-item> + <sl-tree-item>Spruce</sl-tree-item> + </sl-tree-item> + + <sl-tree-item> + Non-trees + <sl-tree-item>Bamboo</sl-tree-item> + <sl-tree-item>Cactus</sl-tree-item> + <sl-tree-item>Fern</sl-tree-item> + </sl-tree-item> +</sl-tree> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => ( + <SlTree> + <SlTreeItem> + Deciduous + <SlTreeItem>Birch</SlTreeItem> + <SlTreeItem> + Maple + <SlTreeItem>Field maple</SlTreeItem> + <SlTreeItem>Red maple</SlTreeItem> + <SlTreeItem>Sugar maple</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Oak</SlTreeItem> + </SlTreeItem> + + <SlTreeItem> + Coniferous + <SlTreeItem>Cedar</SlTreeItem> + <SlTreeItem>Pine</SlTreeItem> + <SlTreeItem>Spruce</SlTreeItem> + </SlTreeItem> + + <SlTreeItem> + Non-trees + <SlTreeItem>Bamboo</SlTreeItem> + <SlTreeItem>Cactus</SlTreeItem> + <SlTreeItem>Fern</SlTreeItem> + </SlTreeItem> + </SlTree> +); +
The selection
attribute lets you change the selection behavior of the tree.
single
to allow the selection of a single item (default).multiple
to allow the selection of multiple items.leaf
to only allow leaf nodes to be selected.+<sl-select id="selection-mode" value="single" label="Selection"> + <sl-option value="single">Single</sl-option> + <sl-option value="multiple">Multiple</sl-option> + <sl-option value="leaf">Leaf</sl-option> +</sl-select> + +<br /> + +<sl-tree class="tree-selectable"> + <sl-tree-item> + Item 1 + <sl-tree-item> + Item A + <sl-tree-item>Item Z</sl-tree-item> + <sl-tree-item>Item Y</sl-tree-item> + <sl-tree-item>Item X</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item B</sl-tree-item> + <sl-tree-item>Item C</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Item 2</sl-tree-item> + <sl-tree-item>Item 3</sl-tree-item> +</sl-tree> + +<script> + const selectionMode = document.querySelector('#selection-mode'); + const tree = document.querySelector('.tree-selectable'); + + selectionMode.addEventListener('sl-change', () => { + tree.querySelectorAll('sl-tree-item').forEach(item => (item.selected = false)); + tree.selection = selectionMode.value; + }); +</script> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => { + const [selection, setSelection] = useState('single'); + + return ( + <> + <SlSelect label="Selection" value={selection} onSlChange={event => setSelection(event.target.value)}> + <SlMenuItem value="single">single</SlMenuItem> + <SlMenuItem value="multiple">multiple</SlMenuItem> + <SlMenuItem value="leaf">leaf</SlMenuItem> + </SlSelect> + + <br /> + + <SlTree selection={selection}> + <SlTreeItem> + Item 1 + <SlTreeItem> + Item A + <SlTreeItem>Item Z</SlTreeItem> + <SlTreeItem>Item Y</SlTreeItem> + <SlTreeItem>Item X</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item B</SlTreeItem> + <SlTreeItem>Item C</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Item 2</SlTreeItem> + <SlTreeItem>Item 3</SlTreeItem> + </SlTree> + </> + ); +}; +
+ Indent guides can be drawn by setting --indent-guide-width
. You can also change the color,
+ offset, and style, using --indent-guide-color
, --indent-guide-style
, and
+ --indent-guide-offset
, respectively.
+
+<sl-tree class="tree-with-lines"> + <sl-tree-item expanded> + Deciduous + <sl-tree-item>Birch</sl-tree-item> + <sl-tree-item expanded> + Maple + <sl-tree-item>Field maple</sl-tree-item> + <sl-tree-item>Red maple</sl-tree-item> + <sl-tree-item>Sugar maple</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Oak</sl-tree-item> + </sl-tree-item> + + <sl-tree-item> + Coniferous + <sl-tree-item>Cedar</sl-tree-item> + <sl-tree-item>Pine</sl-tree-item> + <sl-tree-item>Spruce</sl-tree-item> + </sl-tree-item> + + <sl-tree-item> + Non-trees + <sl-tree-item>Bamboo</sl-tree-item> + <sl-tree-item>Cactus</sl-tree-item> + <sl-tree-item>Fern</sl-tree-item> + </sl-tree-item> +</sl-tree> + +<style> + .tree-with-lines { + --indent-guide-width: 1px; + } +</style> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => ( + <SlTree class="tree-with-lines" style={{ '--indent-guide-width': '1px' }}> + <SlTreeItem expanded> + Deciduous + <SlTreeItem>Birch</SlTreeItem> + <SlTreeItem expanded> + Maple + <SlTreeItem>Field maple</SlTreeItem> + <SlTreeItem>Red maple</SlTreeItem> + <SlTreeItem>Sugar maple</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Oak</SlTreeItem> + </SlTreeItem> + + <SlTreeItem> + Coniferous + <SlTreeItem>Cedar</SlTreeItem> + <SlTreeItem>Pine</SlTreeItem> + <SlTreeItem>Spruce</SlTreeItem> + </SlTreeItem> + + <SlTreeItem> + Non-trees + <SlTreeItem>Bamboo</SlTreeItem> + <SlTreeItem>Cactus</SlTreeItem> + <SlTreeItem>Fern</SlTreeItem> + </SlTreeItem> + </SlTree> +); +
+ Use the lazy
attribute on a tree item to indicate that the content is not yet present and will
+ be loaded later. When the user tries to expand the node, the loading
state is set to
+ true
and the sl-lazy-load
event will be emitted to allow you to load data
+ asynchronously. The item will remain in a loading state until its content is changed.
+
+ If you want to disable this behavior after the first load, simply remove the lazy
attribute
+ and, on the next expand, the existing content will be shown instead.
+
+<sl-tree> + <sl-tree-item lazy>Available Trees</sl-tree-item> +</sl-tree> + +<script type="module"> + const lazyItem = document.querySelector('sl-tree-item[lazy]'); + + lazyItem.addEventListener('sl-lazy-load', () => { + // Simulate asynchronous loading + setTimeout(() => { + const subItems = ['Birch', 'Cedar', 'Maple', 'Pine']; + + for (const item of subItems) { + const treeItem = document.createElement('sl-tree-item'); + treeItem.innerText = item; + lazyItem.append(treeItem); + } + + // Disable lazy mode once the content has been loaded + lazyItem.lazy = false; + }, 1000); + }); +</script> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => { + const [childItems, setChildItems] = useState([]); + const [lazy, setLazy] = useState(true); + + const handleLazyLoad = () => { + // Simulate asynchronous loading + setTimeout(() => { + setChildItems(['Birch', 'Cedar', 'Maple', 'Pine']); + + // Disable lazy mode once the content has been loaded + setLazy(false); + }, 1000); + }; + + return ( + <SlTree> + <SlTreeItem lazy={lazy} onSlLazyLoad={handleLazyLoad}> + Available Trees + {childItems.map(item => ( + <SlTreeItem>{item}</SlTreeItem> + ))} + </SlTreeItem> + </SlTree> + ); +}; +
+ Use the expand-icon
and collapse-icon
slots to change the expand and collapse
+ icons, respectively. To disable the animation, override the rotate
property on the
+ expand-button
part as shown below.
+
+<sl-tree class="custom-icons"> + <sl-icon name="plus-square" slot="expand-icon"></sl-icon> + <sl-icon name="dash-square" slot="collapse-icon"></sl-icon> + + <sl-tree-item> + Deciduous + <sl-tree-item>Birch</sl-tree-item> + <sl-tree-item> + Maple + <sl-tree-item>Field maple</sl-tree-item> + <sl-tree-item>Red maple</sl-tree-item> + <sl-tree-item>Sugar maple</sl-tree-item> + </sl-tree-item> + <sl-tree-item>Oak</sl-tree-item> + </sl-tree-item> + + <sl-tree-item> + Coniferous + <sl-tree-item>Cedar</sl-tree-item> + <sl-tree-item>Pine</sl-tree-item> + <sl-tree-item>Spruce</sl-tree-item> + </sl-tree-item> + + <sl-tree-item> + Non-trees + <sl-tree-item>Bamboo</sl-tree-item> + <sl-tree-item>Cactus</sl-tree-item> + <sl-tree-item>Fern</sl-tree-item> + </sl-tree-item> +</sl-tree> + +<style> + .custom-icons sl-tree-item::part(expand-button) { + /* Disable the expand/collapse animation */ + rotate: none; + } +</style> +
+import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => ( + <SlTree> + <SlIcon name="plus-square" slot="expand-icon"></SlIcon> + <SlIcon name="dash-square" slot="collapse-icon"></SlIcon> + + <SlTreeItem> + Deciduous + <SlTreeItem>Birch</SlTreeItem> + <SlTreeItem> + Maple + <SlTreeItem>Field maple</SlTreeItem> + <SlTreeItem>Red maple</SlTreeItem> + <SlTreeItem>Sugar maple</SlTreeItem> + </SlTreeItem> + <SlTreeItem>Oak</SlTreeItem> + </SlTreeItem> + + <SlTreeItem> + Coniferous + <SlTreeItem>Cedar</SlTreeItem> + <SlTreeItem>Pine</SlTreeItem> + <SlTreeItem>Spruce</SlTreeItem> + </SlTreeItem> + + <SlTreeItem> + Non-trees + <SlTreeItem>Bamboo</SlTreeItem> + <SlTreeItem>Cactus</SlTreeItem> + <SlTreeItem>Fern</SlTreeItem> + </SlTreeItem> + </SlTree> +); +
Decorative icons can be used before labels to provide hints for each node.
+ ++<sl-tree class="tree-with-icons"> + <sl-tree-item expanded> + <sl-icon name="folder"></sl-icon> + Documents + + <sl-tree-item> + <sl-icon name="folder"> </sl-icon> + Photos + <sl-tree-item> + <sl-icon name="image"></sl-icon> + birds.jpg + </sl-tree-item> + <sl-tree-item> + <sl-icon name="image"></sl-icon> + kitten.jpg + </sl-tree-item> + <sl-tree-item> + <sl-icon name="image"></sl-icon> + puppy.jpg + </sl-tree-item> + </sl-tree-item> + + <sl-tree-item> + <sl-icon name="folder"></sl-icon> + Writing + <sl-tree-item> + <sl-icon name="file"></sl-icon> + draft.txt + </sl-tree-item> + <sl-tree-item> + <sl-icon name="file-pdf"></sl-icon> + final.pdf + </sl-tree-item> + <sl-tree-item> + <sl-icon name="file-bar-graph"></sl-icon> + sales.xls + </sl-tree-item> + </sl-tree-item> + </sl-tree-item> +</sl-tree> +
+import SlIcon from '@onsonr/nebula/dist/react/icon'; +import SlTree from '@onsonr/nebula/dist/react/tree'; +import SlTreeItem from '@onsonr/nebula/dist/react/tree-item'; + +const App = () => { + return ( + <SlTree class="tree-with-icons"> + <SlTreeItem expanded> + <SlIcon name="folder" /> + Root + <SlTreeItem> + <SlIcon name="folder" /> + Folder 1<SlTreeItem> + <SlIcon name="files" /> + File 1 - 1 + </SlTreeItem> + <SlTreeItem disabled> + <SlIcon name="files" /> + File 1 - 2 + </SlTreeItem> + <SlTreeItem> + <SlIcon name="files" /> + File 1 - 3 + </SlTreeItem> + </SlTreeItem> + <SlTreeItem> + <SlIcon name="files" /> + Folder 2<SlTreeItem> + <SlIcon name="files" /> + File 2 - 1 + </SlTreeItem> + <SlTreeItem> + <SlIcon name="files" /> + File 2 - 2 + </SlTreeItem> + </SlTreeItem> + <SlTreeItem> + <SlIcon name="files" /> + File 1 + </SlTreeItem> + </SlTreeItem> + </SlTree> + ); +}; +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tree/tree.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/tree/tree.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/tree/tree.js';
To import this component as a React component:
++import SlTree from '@onsonr/nebula/dist/react/tree';
Name | +Description | +
---|---|
(default) | +The default slot. | +
+ expand-icon
+ |
+
+ The icon to show when the tree item is expanded. Works best with <sl-icon> .
+ |
+
+ collapse-icon
+ |
+
+ The icon to show when the tree item is collapsed. Works best with <sl-icon> .
+ |
+
+ Learn more about using slots. +
+ +Name | +Description | +Reflects | +Type | +Default | +
---|---|---|---|---|
+ selection
+ |
+ + The selection behavior of the tree. Single selection allows only one node to be selected at a time. + Multiple displays checkboxes and allows more than one node to be selected. Leaf allows only leaf + nodes to be selected. + | ++ |
+ 'single' | 'multiple' | 'leaf'
+ |
+
+ 'single'
+ |
+
updateComplete |
+ + A read-only promise that resolves when the component has + finished updating. + | ++ | + | + |
+ Learn more about + attributes and properties. +
+ +Name | +React Event | +Description | +Event Detail | +
---|---|---|---|
sl-selection-change |
+ onSlSelectionChange |
+ Emitted when a tree item is selected or deselected. | +
+ { selection: SlTreeItem[] }
+ |
+
+ Learn more about events. +
+ +Name | +Description | +Default | +
---|---|---|
--indent-size |
+ The size of the indentation for nested items. | +var(–sl-spacing-medium) | +
--indent-guide-color |
+ The color of the indentation line. | +var(–sl-color-neutral-200) | +
--indent-guide-offset |
+ + The amount of vertical spacing to leave between the top and bottom of the indentation line’s + starting position. + | +0 | +
--indent-guide-style |
+ The style of the indentation line, e.g. solid, dotted, dashed. | +solid | +
--indent-guide-width |
+ The width of the indentation line. | +0 | +
+ Learn more about + customizing CSS custom properties. +
+ +Name | +Description | +
---|---|
base |
+ The component’s base wrapper. | +
+ Learn more about customizing CSS parts. +
+<sl-visually-hidden> | SlVisuallyHidden
+ + The visually hidden utility makes content accessible to assistive devices without displaying it on the + screen. +
+ ++ According to + The A11Y Project, “there are real world situations where visually hiding content may be appropriate, while the content + should remain available to assistive technologies, such as screen readers. For instance, hiding a search + field’s label as a common magnifying glass icon is used in its stead.” +
++ Since visually hidden content can receive focus when tabbing, the element will become visible when something + inside receives focus. This behavior is intentional, as sighted keyboard user won’t be able to determine + where the focus indicator is without it. +
+ ++<div style="min-height: 1.875rem;"> + <sl-visually-hidden> + <a href="#">Skip to main content</a> + </sl-visually-hidden> +</div> +
+ In this example, the link will open a new window. Screen readers will announce “opens in a new window” even + though the text content isn’t visible to sighted users. +
+ ++<a href="https://example.com/" target="_blank"> + Visit External Page + <sl-icon name="box-arrow-up-right"></sl-icon> + <sl-visually-hidden>opens in a new window</sl-visually-hidden> +</a> +
+ Adding a label may seem redundant at times, but they’re very helpful for unsighted users. Rather than omit + them, you can provide context to unsighted users with visually hidden content that will be announced by + assistive devices such as screen readers. +
+ ++<sl-card style="width: 100%; max-width: 360px;"> + <header> + <sl-visually-hidden>Personal Info</sl-visually-hidden> + </header> + <sl-input label="Name" style="margin-bottom: .5rem;"></sl-input> + <sl-input label="Email" type="email"></sl-input> +</sl-card> +
+ 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 + cherry pick this component. +
+ ++ To import this component from + the CDN + using a script tag: +
++<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/visually-hidden/visually-hidden.js"></script>
+ To import this component from + the CDN + using a JavaScript import: +
++import 'https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/components/visually-hidden/visually-hidden.js';
To import this component using a bundler:
++import '@onsonr/nebula/dist/components/visually-hidden/visually-hidden.js';
To import this component as a React component:
++import SlVisuallyHidden from '@onsonr/nebula/dist/react/visually-hidden';
Name | +Description | +
---|---|
(default) | +The content to be visually hidden. | +
+ Learn more about using slots. +
++ Angular + plays nice + with custom elements, so you can use Nebula in your Angular apps with ease. +
+To add Nebula to your Angular app, install the package from npm.
++npm install @onsonr/nebula +
+ Next, include a theme. In this example, we’ll import the light theme. +
+
+ Its also important to load the components by using a <script>
tag into the index.html
+ file. However, the Angular way to do it is by adding a script configurations into your angular.json file as
+ follows:
+
+"architect": { + "build": { + ... + "options": { + ... + "styles": [ + "src/styles.scss", + "@onsonr/nebula/dist/themes/light.css" + ], + "scripts": [ + "@onsonr/nebula/dist/shoelace.js" + ] + ... +
+ Next, set the base path for icons and
+ other assets in the main.ts
. In this example, we’ll use the CDN as a base path.
+
+import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path'; + +setBasePath('https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/'); +
+ If you’d rather not use the CDN for assets, you can create a build task that copies
+ node_modules/@onsonr/nebula/dist/assets
into a public folder in your app. Then you can point
+ the base path to that folder instead.
+
Then make sure to apply the custom elements schema as shown below.
++import { BrowserModule } from '@angular/platform-browser'; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; + +import { AppComponent } from './app.component'; + +@NgModule({ + declarations: [AppComponent], + imports: [BrowserModule], + providers: [], + bootstrap: [AppComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class AppModule {} +
+import { SlDrawer } from '@onsonr/nebula'; + +@Component({ + selector: 'app-drawer-example', + template: '<div id="page"><button (click)="showDrawer()">Show drawer</button><sl-drawer #drawer label="Drawer" class="drawer-focus" style="--size: 50vw"><p>Drawer content</p></sl-drawer></div>' +}) +export class DrawerExampleComponent implements OnInit { + + // use @ViewChild to get a reference to the #drawer element within component template + @ViewChild('drawer') + drawer?: ElementRef<SlDrawer>; + + ... + + constructor(...) { + } + + ngOnInit() { + } + + ... + + showDrawer() { + // use nativeElement to access Nebula components + this.drawer?.nativeElement.show(); + } +} +
Now you can start using Nebula components in your app!
++ Are you using Nebula with Angular? + Help us improve this page! +
++ Nebula offers a React version of every component to provide an idiomatic experience for React users. You can + easily toggle between HTML and React examples throughout the documentation. +
+To add Nebula to your React app, install the package from npm.
++npm install @onsonr/nebula +
+ Next, include a theme and set the + base path for icons and other assets. In + this example, we’ll import the light theme and use the CDN as a base path. +
++// App.jsx +import '@onsonr/nebula/dist/themes/light.css'; +import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path'; + +setBasePath('https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/'); +
+ If you’d rather not use the CDN for assets, you can create a
+ build task
+ that copies node_modules/@onsonr/nebula/dist/assets
into your app’s
+ public
directory. Then you can point the base path to that folder instead.
+
Now you can start using components!
++ Preact users facing type errors using components may benefit from setting “paths” in their tsconfig.json so + that react types will instead resolve to preact/compat as described in + Preact’s typescript documentation. +
+
+ Every Nebula component is available to import as a React component. Note that we’re importing the
+ <SlButton>
React component instead of the <sl-button>
+ custom element in the example below.
+
+import SlButton from '@onsonr/nebula/dist/react/button'; + +const MyComponent = () => <SlButton variant="primary">Click me</SlButton>; + +export default MyComponent; +
Previously, it was recommended to import from a single entrypoint like so:
++import { SlButton } from '@onsonr/nebula/dist/react'; +
+ However, tree-shaking extra Nebula components proved to be a challenge. As a result, we now recommend + cherry-picking components you want to use, rather than importing from a single entrypoint. +
++- import { SlButton } from '@onsonr/nebula/dist/react'; ++ import SlButton from '@onsonr/nebula/dist/react/button'; +
You can find a copy + paste import for each component in the “importing” section of its documentation.
+
+ Many Nebula components emit
+ custom events. For example, the input component emits the sl-input
event
+ when it receives input. In React, you can listen for the event using onSlInput
.
+
Here’s how you can bind the input’s value to a state variable.
++import { useState } from 'react'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +function MyComponent() { + const [value, setValue] = useState(''); + + return <SlInput value={value} onSlInput={event => setValue(event.target.value)} />; +} + +export default MyComponent; +
+ If you’re using TypeScript, it’s important to note that event.target
will be a reference to the
+ underlying custom element. You can use (event.target as any).value
as a quick fix, or you can
+ strongly type the event target as shown below.
+
+import { useState } from 'react'; +import SlInput from '@onsonr/nebula/dist/react/input'; +import type SlInputElement from '@onsonr/nebula/dist/components/input/input'; + +function MyComponent() { + const [value, setValue] = useState(''); + + return <SlInput value={value} onSlInput={event => setValue((event.target as SlInputElement).value)} />; +} + +export default MyComponent; +
You can also import the event type for use in your callbacks, shown below.
++import { useCallback, useState } from 'react'; +import SlInput, { type SlInputEvent } from '@onsonr/nebula/dist/react/input'; +import type SlInputElement from '@onsonr/nebula/dist/components/input/input'; + +function MyComponent() { + const [value, setValue] = useState(''); + const onInput = useCallback((event: SlInputEvent) => { + setValue(event.detail); + }, []); + + return <SlInput value={value} onSlInput={event => setValue((event.target as SlInputElement).value)} />; +} + +export default MyComponent; +
+ Testing with web components can be challenging if your test environment runs in a Node environment (i.e. it + doesn’t run in a real browser). Fortunately, + Jest has + made a number of strides to support web components and provide additional browser APIs. However, it’s still + not a complete replication of a browser environment. +
+Here are some tips that will help smooth things over if you’re having trouble with Jest + Nebula.
++ If you’re looking for a fast, modern testing alternative, consider + Web Test Runner. +
+
+ Jest underwent a major revamp and received support for web components in
+ version 26.5.0
+ when it introduced
+ JSDOM 16.2.0. This release also included a number of mocks for built-in browser functions such as
+ MutationObserver
, document.createRange
, and others.
+
+ If you’re using
+ Create React App, you can update react-scripts
which will also update Jest.
+
+npm install react-scripts@latest +
+ Some components use window.matchMedia
, but this function isn’t supported by JSDOM so you’ll
+ need to mock it yourself.
+
In src/setupTests.js
, add the following.
+Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // deprecated + removeListener: jest.fn(), // deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn() + })) +}); +
+ For more details, refer to Jest’s + manual mocking + documentation. +
++ ES Modules are a + well-supported browser standard. This is how Nebula is distributed, but most React apps expect CommonJS. As a result, you’ll probably run + into the following error. +
++Error: Unable to import outside of a module +
+ To fix this, add the following to your package.json
which tells the transpiler to process
+ Nebula modules.
+
+{ + "jest": { + "transformIgnorePatterns": ["node_modules/(?!(@shoelace))"] + } +} +
+ These instructions are for apps created via Create React App. If you’re using Jest directly, you can add
+ transformIgnorePatterns
directly into jest.config.js
.
+
+ For more details, refer to Jest’s
+ transformIgnorePatterns
customization
+ documentation.
+
+ Are you using Nebula with React? + Help us improve this page! +
++ Vue + plays nice + with custom elements, so you can use Nebula in your Vue apps with ease. +
++ These instructions are for Vue 2. If you’re using Vue 3 or above, please see the + Vue 3 instructions. +
+To add Nebula to your Vue app, install the package from npm.
++npm install @onsonr/nebula +
+ Next, include a theme and set the + base path for icons and other assets. In + this example, we’ll import the light theme and use the CDN as a base path. +
++import '@onsonr/nebula/dist/themes/light.css'; +import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path'; + +setBasePath('https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/'); +
+ If you’d rather not use the CDN for assets, you can create a build task that copies
+ node_modules/@onsonr/nebula/dist/assets
into a public folder in your app. Then you can point
+ the base path to that folder instead.
+
+ You’ll need to tell Vue to ignore Nebula components. This is pretty easy because they all start with
+ sl-
.
+
+import Vue from 'vue'; +import App from './App.vue'; + +Vue.config.ignoredElements = [/sl-/]; + +const app = new Vue({ + render: h => h(App) +}); + +app.$mount('#app'); +
Now you can start using Nebula components in your app!
+
+ When binding complex data such as objects and arrays, use the .prop
modifier to make Vue bind
+ them as a property instead of an attribute.
+
+<sl-color-picker :swatches.prop="mySwatches" /> +
+ One caveat is there’s currently + no support for v-model on custom elements, but you can still achieve two-way binding manually. +
++<!-- This doesn't work --> +<sl-input v-model="name"></sl-input> +<!-- This works, but it's a bit longer --> +<sl-input :value="name" @input="name = $event.target.value"></sl-input> +
+ If that’s too verbose for your liking, you can use a custom directive instead.
+ This utility
+ adds a custom directive that will work just like v-model
but for Nebula components. To install
+ it, use this command.
+
+npm install @shoelace-style/vue-sl-model@1 +
Next, import the directive and enable it like this.
++import Vue from 'vue'; +import NebulaModelDirective from '@shoelace-style/vue-sl-model'; +import App from './App.vue'; + +Vue.use(NebulaModelDirective); +Vue.config.ignoredElements = [/sl-/]; + +const app = new Vue({ + render: h => h(App) +}); + +app.$mount('#app'); +
Now you can use the v-sl-model
directive to keep your data in sync!
+<sl-input v-sl-model="name"></sl-input> +
+ Are you using Nebula with Vue? + Help us improve this page! +
++ Vue + plays nice + with custom elements, so you can use Nebula in your Vue apps with ease. +
++ These instructions are for Vue 3 and above. If you’re using Vue 2, please see the + Vue 2 instructions. +
+To add Nebula to your Vue app, install the package from npm.
++npm install @onsonr/nebula +
+ Next, include a theme and set the + base path for icons and other assets. In + this example, we’ll import the light theme and use the CDN as a base path. +
++// main.js or main.ts +import '@onsonr/nebula/dist/themes/light.css'; +import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path'; + +setBasePath('https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/'); +
+ If you’d rather not use the CDN for assets, you can create a build task that copies
+ node_modules/@onsonr/nebula/dist/assets
into a public folder in your app. Then you can point
+ the base path to that folder instead.
+
+ If you haven’t configured your Vue.js project to work with custom elements/web components, follow + the instructions here + based on your project type to ensure your project will not throw an error when it encounters a custom + element. +
+Now you can start using Nebula components in your app!
+
+ Once you have configured your application for custom elements, you should be able to use Nebula in your
+ application without it causing any errors. Unfortunately, this doesn’t register the custom elements to
+ behave like components built using Vue. To provide autocomplete information and type safety for your
+ components, you can import the Nebula Vue types into your tsconfig.json
to get better
+ integration in your standard Vue and JSX templates.
+
+{ + "compilerOptions": { + "types": ["@onsonr/nebula/dist/types/vue"] + } +} +
+<template> + <div class="container"> + <h1>QR code generator</h1> + + <sl-input maxlength="255" clearable label="Value" v-model="qrCode"></sl-input> + + <sl-qr-code :value="qrCode"></sl-qr-code> + </div> +</template> + +<script setup> + import { ref } from 'vue'; + import '@onsonr/nebula/dist/components/qr-code/qr-code.js'; + import '@onsonr/nebula/dist/components/input/input.js'; + + const qrCode = ref(); +</script> + +<style> + .container { + max-width: 400px; + margin: 0 auto; + } + + sl-input { + margin: var(--sl-spacing-large) 0; + } +</style> +
+ When binding complex data such as objects and arrays, use the .prop
modifier to make Vue bind
+ them as a property instead of an attribute.
+
+<sl-color-picker :swatches.prop="mySwatches" /> +
+ One caveat is there’s currently + no support for v-model on custom elements, but you can still achieve two-way binding manually. +
++<!-- This doesn't work --> +<sl-input v-model="name"></sl-input> +<!-- This works, but it's a bit longer --> +<sl-input :value="name" @input="name = $event.target.value"></sl-input> +
+ If that’s too verbose for your liking, you can use a custom directive instead.
+ This utility
+ adds a custom directive that will work just like v-model
but for Nebula components.
+
+ Are you using Nebula with Vue? + Help us improve this page! +
+
+ Slots in Nebula/web components are functionally the same as basic slots in Vue. Slots can be assigned to
+ elements using the slot
attribute followed by the name of the slot it is being assigned to.
+
Here is an example:
++<sl-drawer label="Drawer" placement="start" class="drawer-placement-start" :open="drawerIsOpen"> + This drawer slides in from the start. + <div slot="footer"> + <sl-button variant="primary" @click=" drawerIsOpen = false">Close</sl-button> + </div> +</sl-drawer> +
+ Nebula components can be customized at a high level through design tokens. This gives you control over theme + colors and general styling. For more advanced customizations, you can make use of CSS parts and custom + properties to target individual components. +
++ Nebula makes use of several design tokens to provide a consistent appearance across components. You can + customize them and use them in your own application with pure CSS — no preprocessor required. +
++ Design tokens offer a high-level way to customize the library with minimal effort. There are no + component-specific variables, however, as design tokens are intended to be generic and highly reusable. To + customize an individual component, refer to the section entitled CSS Parts. +
+
+ Design tokens are accessed through CSS custom properties that are defined in your theme. Because design
+ tokens live at the page level, they’re prefixed with --sl-
to avoid collisions with other
+ libraries.
+
+ To customize a design token, simply override it in your stylesheet using a :root
block. Here’s
+ an example that changes the primary theme to purple based on existing
+ color primitives.
+
+:root { + /* Changes the primary theme color to purple using primitives */ + --sl-color-primary-50: var(--sl-color-purple-50); + --sl-color-primary-100: var(--sl-color-purple-100); + --sl-color-primary-200: var(--sl-color-purple-200); + --sl-color-primary-300: var(--sl-color-purple-300); + --sl-color-primary-400: var(--sl-color-purple-400); + --sl-color-primary-500: var(--sl-color-purple-500); + --sl-color-primary-600: var(--sl-color-purple-600); + --sl-color-primary-700: var(--sl-color-purple-700); + --sl-color-primary-800: var(--sl-color-purple-800); + --sl-color-primary-900: var(--sl-color-purple-900); + --sl-color-primary-950: var(--sl-color-purple-950); +} +
+ Many design tokens are described further along in this documentation. For a complete list, refer to
+ src/themes/light.css
in the project’s
+ source code.
+
+ Whereas design tokens offer a high-level way to customize the library, CSS parts offer a low-level way to + customize individual components. Again, this is done with pure CSS — no preprocessor required. +
+
+ Nebula components use a
+ shadow DOM
+ to encapsulate their styles and behaviors. As a result, you can’t simply target their internals with the
+ usual CSS selectors. Instead, components expose “parts” that can be targeted with the
+ CSS part selector, or ::part()
.
+
Here’s an example that modifies buttons with the tomato-button
class.
+<sl-button class="tomato-button"> Tomato Button </sl-button> + +<style> + .tomato-button::part(base) { + background: var(--sl-color-neutral-0); + border: solid 1px tomato; + } + + .tomato-button::part(base):hover { + background: rgba(255, 99, 71, 0.1); + } + + .tomato-button::part(base):active { + background: rgba(255, 99, 71, 0.2); + } + + .tomato-button::part(base):focus-visible { + box-shadow: 0 0 0 3px rgba(255, 99, 71, 0.33); + } + + .tomato-button::part(label) { + color: tomato; + } +</style> +
+ At first glance, this approach might seem a bit verbose or even limiting, but it comes with a few important + advantages: +
+
+ Customizations can be made to components with explicit selectors, such as ::part(icon)
,
+ rather than implicit selectors, such as .button > div > span + .icon
, that are much
+ more fragile.
+
+ The internal structure of a component will likely change as it evolves. By exposing CSS parts through an + API, the internals can be reworked without fear of breaking customizations as long as its parts remain + intact. +
++ It encourages us to think more about how components are designed and how customizations should be + allowed before users can take advantage of them. Once we opt a part into the component’s API, it’s + guaranteed to be supported and can’t be removed until a major version of the library is released. +
++ Most (but not all) components expose parts. You can find them in each component’s API documentation under + the “CSS Parts” section. +
+
+ For convenience, some components expose CSS custom properties you can override. These are not design tokens,
+ nor do they have the same --sl-
prefix since they’re scoped to a component.
+
You can set custom properties on a component in your stylesheet.
++sl-avatar { + --size: 6rem; +} +
This will also work if you need to target a subset of components with a specific class.
++sl-avatar.your-class { + --size: 6rem; +} +
Alternatively, you can set them inline directly on the element.
++<sl-avatar style="--size: 6rem;"></sl-avatar> +
+ Not all components expose CSS custom properties. For those that do, they can be found in the component’s API + documentation. +
++ Some components use animation, such as when a dialog is shown or hidden. Animations are performed using the + Web Animations API + rather than CSS. However, you can still customize them through Nebula’s animation registry. If a component + has customizable animations, they’ll be listed in the “Animation” section of its documentation. +
+
+ To customize a default animation, use the setDefaultAnimation()
method. The function accepts an
+ animation name (found in the component’s docs) and an object with keyframes
, and
+ options
or null
to disable the animation.
+
This example will make all dialogs use a custom show animation.
++import { setDefaultAnimation } from '@onsonr/nebula/dist/utilities/animation-registry.js'; + +// Change the default animation for all dialogs +setDefaultAnimation('dialog.show', { + keyframes: [ + { transform: 'rotate(-10deg) scale(0.5)', opacity: '0' }, + { transform: 'rotate(0deg) scale(1)', opacity: '1' } + ], + options: { + duration: 500 + } +}); +
+ To support RTL languages in your animation, you can pass an additional property called
+ rtlKeyframes
. This property shares the same type as keyframes
and will be
+ automatically used when the component’s directionality is RTL. If rtlKeyframes
is not
+ provided, keyframes
will be used as a fallback.
+
+ If you only want to target a single component, use the setAnimation()
method instead. This
+ function accepts an element, an animation name, and an object comprised of animation
+ keyframes
and options
.
+
In this example, only the target dialog will use a custom show animation.
++import { setAnimation } from '@onsonr/nebula/dist/utilities/animation-registry.js'; + +// Change the animation for a single dialog +const dialog = document.querySelector('#my-dialog'); + +setAnimation(dialog, 'dialog.show', { + keyframes: [ + { transform: 'rotate(-10deg) scale(0.5)', opacity: '0' }, + { transform: 'rotate(0deg) scale(1)', opacity: '1' } + ], + options: { + duration: 500 + } +}); +
+ To learn more about creating Web Animations, refer to the documentation for
+ Element.animate()
.
+
+ Animations respect the users prefers-reduced-motion
setting. When this setting is enabled,
+ animations will not be played. To disable animations for all users, pass in null
instead of a
+ keyframes/options object.
+
+ Every Nebula component makes use of a
+ shadow DOM
+ to encapsulate markup, styles, and behavior. One caveat of this approach is that native
+ <form>
elements do not recognize form controls located inside a shadow root.
+
+ Nebula solves this problem by using the
+ formdata
+ event, which is
+ available in all modern browsers. This means, when a form is submitted, Nebula form controls will automatically append their values to the
+ FormData
object that’s used to submit the form. In most cases, things will “just work.”
+ However, if you’re using a form serialization library, it might need to be adapted to recognize Nebula form
+ controls.
+
+ Nebula uses event listeners to intercept the form’s formdata
and submit
events.
+ This allows it to inject data and trigger validation as necessary. If you’re also attaching an event
+ listener to the form, you must attach it after Nebula form controls are connected to the DOM,
+ otherwise your logic will run before Nebula has a chance to inject form data and validate form controls.
+
+ Serialization is just a fancy word for collecting form data. If you’re relying on standard form submissions,
+ e.g. <form action="...">
, you can probably skip this section. However, most modern apps
+ use the
+ Fetch API
+ or a library such as
+ axios
+ to submit forms using JavaScript.
+
+ The
+ FormData
+ interface offers a standard way to serialize forms in the browser. You can create a
+ FormData
object from any <form>
element like this.
+
+const form = document.querySelector('form'); +const data = new FormData(form); + +// All form control data is available in a FormData object +
+ However, some folks find FormData
tricky to work with or they need to pass a JSON payload to
+ their server. To accommodate this, Nebula offers a serialization utility that gathers form data and returns
+ a simple JavaScript object instead.
+
+import { serialize } from '@onsonr/nebula/dist/utilities/form.js'; + +const form = document.querySelector('form'); +const data = serialize(form); + +// All form control data is available in a plain object +
+ This results in an object with name/value pairs that map to each form control. If more than one form control
+ shares the same name, the values will be passed as an array, e.g.
+ { name: ['value1', 'value2'] }
.
+
+ Client-side validation can be enabled through the browser’s
+ Constraint Validation API
+ for Nebula form controls. You can activate it using attributes such as required
,
+ pattern
, minlength
, maxlength
, etc. Nebula implements many of the
+ same attributes as native form controls, but check the documentation for a list of supported properties for
+ each component.
+
+ If you don’t want to use client-side validation, you can suppress this behavior by adding
+ novalidate
to the surrounding <form>
element.
+
+ If this syntax looks unfamiliar, don’t worry! Most of what you’re learning on this page is platform + knowledge that applies to regular form controls, too. +
++ Client-side validation can be used to improve the UX of forms, but it is not a replacement for server-side + validation. You should always validate and sanitize user input on the server! +
+
+ To make a field required, use the required
attribute. Required fields will automatically
+ receive a *
after their labels. This is configurable through the
+ --sl-input-required-content
custom property.
+
The form will not be submitted if a required field is incomplete.
+ ++<form class="input-validation-required"> + <sl-input name="name" label="Name" required></sl-input> + <br /> + <sl-select label="Favorite Animal" clearable required> + <sl-option value="birds">Birds</sl-option> + <sl-option value="cats">Cats</sl-option> + <sl-option value="dogs">Dogs</sl-option> + <sl-option value="other">Other</sl-option> + </sl-select> + <br /> + <sl-textarea name="comment" label="Comment" required></sl-textarea> + <br /> + <sl-checkbox required>Check me before submitting</sl-checkbox> + <br /><br /> + <sl-button type="submit" variant="primary">Submit</sl-button> +</form> + +<script type="module"> + const form = document.querySelector('.input-validation-required'); + + // Wait for controls to be defined before attaching form listeners + await Promise.all([ + customElements.whenDefined('sl-button'), + customElements.whenDefined('sl-checkbox'), + customElements.whenDefined('sl-input'), + customElements.whenDefined('sl-option'), + customElements.whenDefined('sl-select'), + customElements.whenDefined('sl-textarea') + ]).then(() => { + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); + }); +</script> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlCheckbox from '@onsonr/nebula/dist/react/checkbox'; +import SlInput from '@onsonr/nebula/dist/react/input'; +import SlMenuItem from '@onsonr/nebula/dist/react/menu-item'; +import SlSelect from '@onsonr/nebula/dist/react/select'; +import SlTextarea from '@onsonr/nebula/dist/react/textarea'; + +const App = () => { + function handleSubmit(event) { + event.preventDefault(); + alert('All fields are valid!'); + } + + return ( + <form onSubmit={handleSubmit}> + <SlInput name="name" label="Name" required /> + <br /> + <SlSelect label="Favorite Animal" clearable required> + <SlMenuItem value="birds">Birds</SlMenuItem> + <SlMenuItem value="cats">Cats</SlMenuItem> + <SlMenuItem value="dogs">Dogs</SlMenuItem> + <SlMenuItem value="other">Other</SlMenuItem> + </SlSelect> + <br /> + <SlTextarea name="comment" label="Comment" required></SlTextarea> + <br /> + <SlCheckbox required>Check me before submitting</SlCheckbox> + <br /> + <br /> + <SlButton type="submit" variant="primary"> + Submit + </SlButton> + </form> + ); +}; +
+ To restrict a value to a specific
+ pattern, use the pattern
attribute. This example only allows the letters A-Z, so the form will not
+ submit if a number or symbol is entered. This only works with <sl-input>
elements.
+
+<form class="input-validation-pattern"> + <sl-input name="letters" required label="Letters" pattern="[A-Za-z]+"></sl-input> + <br /> + <sl-button type="submit" variant="primary">Submit</sl-button> + <sl-button type="reset" variant="default">Reset</sl-button> +</form> + +<script type="module"> + const form = document.querySelector('.input-validation-pattern'); + + // Wait for controls to be defined before attaching form listeners + await Promise.all([ + customElements.whenDefined('sl-button'), + customElements.whenDefined('sl-input') + ]).then(() => { + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); + }); +</script> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => { + function handleSubmit(event) { + event.preventDefault(); + alert('All fields are valid!'); + } + + return ( + <form onSubmit={handleSubmit}> + <SlInput name="letters" required label="Letters" pattern="[A-Za-z]+" /> + <br /> + <SlButton type="submit" variant="primary"> + Submit + </SlButton> + </form> + ); +}; +
+ Some input types will automatically trigger constraints, such as email
and url
.
+
+<form class="input-validation-type"> + <sl-input type="email" label="Email" placeholder="you@example.com" required></sl-input> + <br /> + <sl-input type="url" label="URL" placeholder="https://example.com/" required></sl-input> + <br /> + <sl-button type="submit" variant="primary">Submit</sl-button> + <sl-button type="reset" variant="default">Reset</sl-button> +</form> + +<script type="module"> + const form = document.querySelector('.input-validation-type'); + + // Wait for controls to be defined before attaching form listeners + await Promise.all([ + customElements.whenDefined('sl-button'), + customElements.whenDefined('sl-input') + ]).then(() => { + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); + }); +</script> +
+import SlButton from '@onsonr/nebula/dist/react/button'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => { + function handleSubmit(event) { + event.preventDefault(); + alert('All fields are valid!'); + } + + return ( + <form onSubmit={handleSubmit}> + <SlInput type="email" label="Email" placeholder="you@example.com" required /> + <br /> + <SlInput type="url" label="URL" placeholder="https://example.com/" required /> + <br /> + <SlButton type="submit" variant="primary"> + Submit + </SlButton> + </form> + ); +}; +
+ To create a custom validation error, pass a non-empty string to the setCustomValidity()
method.
+ This will override any existing validation constraints. The form will not be submitted when a custom
+ validity is set and the browser will show a validation error when the containing form is submitted. To make
+ the input valid again, call setCustomValidity()
again with an empty string.
+
+<form class="input-validation-custom"> + <sl-input label="Type “shoelace”" required></sl-input> + <br /> + <sl-button type="submit" variant="primary">Submit</sl-button> + <sl-button type="reset" variant="default">Reset</sl-button> +</form> + +<script type="module"> + const form = document.querySelector('.input-validation-custom'); + const input = form.querySelector('sl-input'); + + // Wait for controls to be defined before attaching form listeners + await Promise.all([ + customElements.whenDefined('sl-button'), + customElements.whenDefined('sl-input') + ]).then(() => { + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); + + input.addEventListener('sl-input', () => { + if (input.value === 'shoelace') { + input.setCustomValidity(''); + } else { + input.setCustomValidity("Hey, you're supposed to type 'shoelace' before submitting this!"); + } + }); + }); +</script> +
+import { useRef, useState } from 'react'; +import SlButton from '@onsonr/nebula/dist/react/button'; +import SlInput from '@onsonr/nebula/dist/react/input'; + +const App = () => { + const input = useRef(null); + const [value, setValue] = useState(''); + + function handleInput(event) { + setValue(event.target.value); + + if (event.target.value === 'shoelace') { + input.current.setCustomValidity(''); + } else { + input.current.setCustomValidity("Hey, you're supposed to type 'shoelace' before submitting this!"); + } + } + + function handleSubmit(event) { + event.preventDefault(); + alert('All fields are valid!'); + } + + return ( + <form onSubmit={handleSubmit}> + <SlInput ref={input} label="Type 'shoelace'" required value={value} onSlInput={handleInput} /> + <br /> + <SlButton type="submit" variant="primary"> + Submit + </SlButton> + </form> + ); +}; +
+ Custom validation can be applied to any form control that supports the
+ setCustomValidity()
method. It is not limited to inputs and textareas.
+
+ Due to the many ways form controls are used, Nebula doesn’t provide out of the box validation styles for + form controls as part of its default theme. Instead, the following attributes will be applied to reflect a + control’s validity as users interact with it. You can use them to create custom styles for any of the + validation states you’re interested in. +
+data-required
- the form control is requireddata-optional
- the form control is optionaldata-invalid
- the form control is currently invaliddata-valid
- the form control is currently validdata-user-invalid
- the form control is currently invalid and the user has interacted with it
+ data-user-valid
- the form control is currently valid and the user has interacted with it
+
+ These attributes map to the browser’s built-in pseudo classes for validation:
+ :required
,
+ :optional
,
+ :invalid
,
+ :valid
, and the proposed
+ :user-invalid
+ and
+ :user-valid
.
+
+ In the future, data attributes will be replaced with custom pseudo classes such as
+ :--valid
and :--invalid
. Nebula is using data attributes as a workaround until
+ browsers support custom states through
+ ElementInternals.states
.
+
+ You can target validity using any of the aforementioned data attributes, but it’s usually preferable to
+ target data-user-invalid
and data-user-valid
since they get applied only after a
+ user interaction such as typing or submitting. This prevents empty form controls from appearing invalid
+ immediately, which often results in a poor user experience.
+
+ This example demonstrates custom validation styles using data-user-invalid
and
+ data-user-valid
. Try Typing in the fields to see how validity changes with user input.
+
+<form class="validity-styles"> + <sl-input + name="name" + label="Name" + help-text="What would you like people to call you?" + autocomplete="off" + required + ></sl-input> + + <sl-select name="animal" label="Favorite Animal" help-text="Select the best option." clearable required> + <sl-option value="birds">Birds</sl-option> + <sl-option value="cats">Cats</sl-option> + <sl-option value="dogs">Dogs</sl-option> + <sl-option value="other">Other</sl-option> + </sl-select> + + <sl-checkbox value="accept" required>Accept terms and conditions</sl-checkbox> + + <sl-button type="submit" variant="primary">Submit</sl-button> + <sl-button type="reset" variant="default">Reset</sl-button> +</form> + +<script type="module"> + const form = document.querySelector('.validity-styles'); + + // Wait for controls to be defined before attaching form listeners + await Promise.all([ + customElements.whenDefined('sl-button'), + customElements.whenDefined('sl-checkbox'), + customElements.whenDefined('sl-input'), + customElements.whenDefined('sl-option'), + customElements.whenDefined('sl-select') + ]).then(() => { + form.addEventListener('submit', event => { + event.preventDefault(); + alert('All fields are valid!'); + }); + }); +</script> + +<style> + .validity-styles sl-input, + .validity-styles sl-select, + .validity-styles sl-checkbox { + display: block; + margin-bottom: var(--sl-spacing-medium); + } + + /* user invalid styles */ + .validity-styles sl-input[data-user-invalid]::part(base), + .validity-styles sl-select[data-user-invalid]::part(combobox), + .validity-styles sl-checkbox[data-user-invalid]::part(control) { + border-color: var(--sl-color-danger-600); + } + + .validity-styles [data-user-invalid]::part(form-control-label), + .validity-styles [data-user-invalid]::part(form-control-help-text), + .validity-styles sl-checkbox[data-user-invalid]::part(label) { + color: var(--sl-color-danger-700); + } + + .validity-styles sl-checkbox[data-user-invalid]::part(control) { + outline: none; + } + + .validity-styles sl-input:focus-within[data-user-invalid]::part(base), + .validity-styles sl-select:focus-within[data-user-invalid]::part(combobox), + .validity-styles sl-checkbox:focus-within[data-user-invalid]::part(control) { + border-color: var(--sl-color-danger-600); + box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-300); + } + + /* User valid styles */ + .validity-styles sl-input[data-user-valid]::part(base), + .validity-styles sl-select[data-user-valid]::part(combobox), + .validity-styles sl-checkbox[data-user-valid]::part(control) { + border-color: var(--sl-color-success-600); + } + + .validity-styles [data-user-valid]::part(form-control-label), + .validity-styles [data-user-valid]::part(form-control-help-text), + .validity-styles sl-checkbox[data-user-valid]::part(label) { + color: var(--sl-color-success-700); + } + + .validity-styles sl-checkbox[data-user-valid]::part(control) { + background-color: var(--sl-color-success-600); + outline: none; + } + + .validity-styles sl-input:focus-within[data-user-valid]::part(base), + .validity-styles sl-select:focus-within[data-user-valid]::part(combobox), + .validity-styles sl-checkbox:focus-within[data-user-valid]::part(control) { + border-color: var(--sl-color-success-600); + box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-success-300); + } +</style> +
+ By default, Nebula form controls use the browser’s tooltip-style error messages. No mechanism is provided to + show errors inline, as there are too many opinions on how that would work when combined with native form + controls and other custom elements. You can, however, implement your own solution using the following + technique. +
+
+ To disable the browser’s error messages, you need to cancel the sl-invalid
event. Then you can
+ apply your own inline validation errors. This example demonstrates a primitive way to do this.
+
+<form class="inline-validation"> + <sl-input + name="name" + label="Name" + help-text="What would you like people to call you?" + autocomplete="off" + required + ></sl-input> + + <div id="name-error" aria-live="polite" hidden></div> + + <sl-button type="submit" variant="primary">Submit</sl-button> + <sl-button type="reset" variant="default">Reset</sl-button> +</form> + +<script type="module"> + const form = document.querySelector('.inline-validation'); + const nameError = document.querySelector('#name-error'); + + // Wait for controls to be defined before attaching form listeners + await Promise.all([ + customElements.whenDefined('sl-button'), + customElements.whenDefined('sl-input') + ]).then(() => { + // A form control is invalid + form.addEventListener( + 'sl-invalid', + event => { + // Suppress the browser's constraint validation message + event.preventDefault(); + + nameError.textContent = `Error: ${event.target.validationMessage}`; + nameError.hidden = false; + + event.target.focus(); + }, + { capture: true } // you must use capture since sl-invalid doesn't bubble! + ); + + // Handle form submit + form.addEventListener('submit', event => { + event.preventDefault(); + nameError.hidden = true; + nameError.textContent = ''; + setTimeout(() => alert('All fields are valid'), 50); + }); + + // Handle form reset + form.addEventListener('reset', event => { + nameError.hidden = true; + nameError.textContent = ''; + }); + }); +</script> + +<style> + #name-error { + font-size: var(--sl-input-help-text-font-size-medium); + color: var(--sl-color-danger-700); + } + + #name-error ~ sl-button { + margin-top: var(--sl-spacing-medium); + } + + .inline-validation sl-input { + display: block; + } + + /* user invalid styles */ + .inline-validation sl-input[data-user-invalid]::part(base) { + border-color: var(--sl-color-danger-600); + } + + .inline-validation [data-user-invalid]::part(form-control-label), + .inline-validation [data-user-invalid]::part(form-control-help-text) { + color: var(--sl-color-danger-700); + } + + .inline-validation sl-input:focus-within[data-user-invalid]::part(base) { + border-color: var(--sl-color-danger-600); + box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-300); + } + + /* User valid styles */ + .inline-validation sl-input[data-user-valid]::part(base) { + border-color: var(--sl-color-success-600); + } + + .inline-validation [data-user-valid]::part(form-control-label), + .inline-validation [data-user-valid]::part(form-control-help-text) { + color: var(--sl-color-success-700); + } + + .inline-validation sl-input:focus-within[data-user-valid]::part(base) { + border-color: var(--sl-color-success-600); + box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-success-300); + } +</style> +
+ This example is meant to demonstrate the concept of providing your own error messages inline. It is not + intended to scale to more complex forms. Users who want this functionality are encouraged to build a more + appropriate validation solution using the techniques shown below. Depending on how you implement this + feature, custom error messages may affect the accessibility of your form controls. +
+
+ At this time, using
+ HTMLFormElement.elements
+ will not return Nebula form controls because the browser is unaware of their status as custom element form
+ controls. Fortunately, Nebula provides an elements()
function that does something very similar.
+ However, instead of returning an
+ HTMLFormControlsCollection
, it returns an array of HTML and Nebula form controls in the order they appear in the DOM.
+
+import { getFormControls } from '@onsonr/nebula/dist/utilities/form.js'; + +const form = document.querySelector('#my-form'); +const formControls = getFormControls(form); + +console.log(formControls); // e.g. [input, sl-input, ...] +
+ You probably don’t need this function! If you’re gathering form data for submission, you probably want to + use Data Serialization instead. +
++ You can load Nebula via CDN or by installing it locally. If you’re using a framework, make sure to check out + the pages for React, Vue, and + Angular for additional information. +
++ The experimental autoloader is the easiest and most efficient way to use Nebula. A lightweight script + watches the DOM for unregistered Nebula elements and lazy loads them for you — even if they’re + added dynamically. +
++ While convenient, autoloading may lead to a + Flash of Undefined Custom Elements. The linked article describes some ways to alleviate it. +
+ ++<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/themes/light.css" /> +<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/shoelace-autoloader.js"></script> +
+ The traditional CDN loader registers all Nebula elements up front. Note that, if you’re only using a + handful of components, it will be much more efficient to stick with the autoloader. However, you can + also cherry pick components if you want to load specific ones up front. +
+ ++<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/themes/light.css" /> +<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/shoelace.js" ></script> +
+ The code above will load the light theme. If you want to use the
+ dark theme instead, update the stylesheet as shown below
+ and add <html class="sl-theme-dark">
to your page.
+
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/themes/dark.css" /> +
+ If you want to load the light or dark theme based on the user’s prefers-color-scheme
setting,
+ use the stylesheets below. The media
attributes ensure that only the user’s preferred theme
+ stylesheet loads and the onload
attribute sets the appropriate
+ theme class on the <html>
element.
+
+<link + rel="stylesheet" + media="(prefers-color-scheme:light)" + href="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/themes/light.css" +/> +<link + rel="stylesheet" + media="(prefers-color-scheme:dark)" + href="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/themes/dark.css" + onload="document.documentElement.classList.add('sl-theme-dark');" +/> +
Now you can start using Nebula!
+If you don’t want to use the CDN, you can install Nebula from npm with the following command.
++npm install @onsonr/nebula +
+ It’s up to you to make the source files available to your app. One way to do this is to create a route in
+ your app called /shoelace
that serves static files from
+ node_modules/@onsonr/nebula
.
+
+ Once you’ve done that, add the following tags to your page. Make sure to update href
and
+ src
so they point to the route you created.
+
+<link rel="stylesheet" href="/shoelace/dist/themes/light.css" /> +<script type="module" src="/shoelace/dist/shoelace.js"></script> +
Alternatively, you can use a bundler.
+
+ For clarity, the docs will usually show imports from @onsonr/nebula
. If you’re not using a
+ module resolver or bundler, you’ll need to adjust these paths to point to the folder Nebula is in.
+
+ Some components rely on assets (icons, images, etc.) and Nebula needs to know where they’re located. For
+ convenience, Nebula will try to auto-detect the correct location based on the script you’ve loaded it from.
+ This assumes assets are colocated with shoelace.js
or shoelace-autoloader.js
and
+ will “just work” for most users.
+
+ However, if you’re cherry picking or bundling Nebula, + you’ll need to set the base path. You can do this one of two ways. +
++<!-- Option 1: the data-shoelace attribute --> +<script src="bundle.js" data-shoelace="/path/to/shoelace/dist"></script> + +<!-- Option 2: the setBasePath() method --> +<script src="bundle.js"></script> +<script type="module"> + import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path.js'; + setBasePath('/path/to/shoelace/dist'); +</script> +
+ An easy way to make sure the base path is configured properly is to check if + icons are loading. +
+
+ Most of the magic behind assets is handled internally by Nebula, but if you need to reference the base path
+ for any reason, the same module exports a function called getBasePath()
. An optional string
+ argument can be passed, allowing you to get the full path to any asset.
+
+<script type="module"> + import { getBasePath, setBasePath } from '@onsonr/nebula/dist/utilities/base-path.js'; + + setBasePath('/path/to/assets'); + + // ... + + // Get the base path, e.g. /path/to/assets + const basePath = getBasePath(); + + // Get the path to an asset, e.g. /path/to/assets/file.ext + const assetPath = getBasePath('file.ext'); +</script> +
+ Cherry picking can be done from the CDN or from + npm. This approach will load only the components you need up front, while + limiting the number of files the browser has to download. The disadvantage is that you need to import each + individual component. +
++ Here’s an example that loads only the button component. Again, if you’re not using a module resolver, you’ll + need to adjust the path to point to the folder Nebula is in. +
++<link rel="stylesheet" href="/path/to/shoelace/dist/themes/light.css" /> + +<script type="module" data-shoelace="/path/to/shoelace/dist"> + import '@onsonr/nebula/dist/components/button/button.js'; + + // <sl-button> is ready to use! +</script> +
+ You can copy and paste the code to import a component from the “Importing” section of the component’s + documentation. Note that some components have dependencies that are automatically imported when you cherry + pick. If a component has dependencies, they will be listed in the “Dependencies” section of its docs. +
+
+ Never cherry pick components or utilities from shoelace.js
as this will cause the browser to
+ load the entire library. Instead, cherry pick from specific modules as shown above.
+
+ You will see files named chunk.[hash].js
in the chunks
directory. Never import
+ these files directly, as they are generated and change from version to version.
+
+ Nebula is distributed as a collection of standard ES modules that + all modern browsers can understand. However, importing a lot of modules can result in a lot of HTTP requests and potentially longer load + times. Using a CDN can alleviate this, but some users may wish to further optimize their imports with a + bundler. +
+To use Nebula with a bundler, first install Nebula along with your bundler of choice.
++npm install @onsonr/nebula +
+ Now it’s time to configure your bundler. Configurations vary for each tool, but here are some examples to + help you get started. +
+Once your bundler is configured, you’ll be able to import Nebula components and utilities.
++import '@onsonr/nebula/dist/themes/light.css'; +import '@onsonr/nebula/dist/components/button/button.js'; +import '@onsonr/nebula/dist/components/icon/icon.js'; +import '@onsonr/nebula/dist/components/input/input.js'; +import '@onsonr/nebula/dist/components/rating/rating.js'; +import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path.js'; + +// Set the base path to the folder you copied Nebula's assets to +setBasePath('/path/to/shoelace/dist'); + +// <sl-button>, <sl-icon>, <sl-input>, and <sl-rating> are ready to use! +
+ Component modules include side effects for registration purposes. Because of this, importing directly from
+ @onsonr/nebula
may result in a larger bundle size than necessary. For optimal tree shaking,
+ always cherry pick, i.e. import components and utilities from their respective files, as shown above.
+
+ By default, imports to components will auto-register themselves. This may not be ideal in all cases. To + import just the component’s class without auto-registering it’s tag we can do the following: +
++- import SlButton from '@onsonr/nebula/dist/components/button/button.js'; ++ import SlButton from '@onsonr/nebula/dist/components/button/button.component.js'; +
+ Notice how the import ends with .component.js
. This is the current convention to convey the
+ import does not register itself.
+
+ While you can override the class or re-register the shoelace class under a different tag name, if you do + so, many components won’t work as expected. +
+
+ You’ll notice that the CDN links all start with /cdn/<path>
and npm imports use
+ /dist/<path>
. The /cdn
files are bundled separately from the
+ /dist
files. The /cdn
files come pre-bundled, which means all dependencies are
+ inlined so you do not need to worry about loading additional libraries. The /dist
files
+ DO NOT come pre-bundled, allowing your bundler of choice to more efficiently deduplicate
+ dependencies, resulting in smaller bundles and optimal code sharing.
+
TL;DR:
+@onsonr/nebula/cdn
is for CDN users@onsonr/nebula/dist
is for npm users
+ This change was introduced in v2.5.0
to address issues around installations from npm loading
+ multiple versions of libraries (such as the Lit) that Nebula uses internally.
+
+ Components can be localized by importing the appropriate translation file and setting the desired
+ lang
attribute
+ and/or
+ dir
attribute
+ on the <html>
element. Here’s an example that renders Nebula components in Spanish.
+
+<html lang="es"> + <head> + <script type="module" src="/path/to/shoelace/dist/translations/es.js"></script> + </head> + + <body> + ... + </body> +</html> +
+ Through the magic of a mutation observer, changing the lang
attribute will automatically update
+ all localized components to use the new locale.
+
+ Nebula ships with a number of translations. The default is English (US), which also serves as the fallback + locale. As such, you do not need to import the English translation. To see a list of all available + translations in the latest version, + refer to this directory. +
+The location of translations depends on how you’re consuming Nebula.
+@onsonr/nebula/dist/translations/[lang].js
+ You do not need to load translations up front. You can import them dynamically even after updating the
+ lang
attribute. Once a translation is registered, localized components will update
+ automatically.
+
+// Same as setting <html lang="de"> +document.documentElement.lang = 'de'; + +// Import the translation +import('/path/to/shoelace/dist/translations/de.js'); +
+ The locale set by <html lang="...">
is the default locale for the document. If a country
+ code is provided, e.g. es-PE
for Peruvian Spanish, the localization library will resolve it
+ like this:
+
es-PE
es
en
+ Nebula uses English as a fallback to provide a better experience than rendering nothing or throwing an + error. +
+
+ To contribute new translations or improvements to existing translations, please submit a pull request on
+ GitHub. Translations are located in
+ src/translations
+ and can be edited directly on GitHub if you don’t want to clone the repo locally.
+
+ Regional translations are welcome! For example, if a German translation (de
) exists it’s
+ perfectly acceptable to submit a German (Switzerland) (de-CH
) translation.
+
+ If you have any questions, please start a + discussion + or ask in the + community chat. +
++ Nebula provides a localization mechanism for component internals. This is not designed to be used as + localization tool for your entire application. You should use a more appropriate tool such as + i18next + if you need to localize content in your app. +
+
+ You can use a different locale for an individual component by setting its lang
and/or
+ dir
attributes. Here’s a contrived example to demonstrate.
+
+<html lang="es"> + ... + + <body> + <sl-button><!-- Spanish --></sl-button> + <sl-button lang="ru"><!-- Russian --></sl-button> + </body> +</html> +
+ For performance reasons, the lang
and dir
attributes must be on the component
+ itself, not on an ancestor element.
+
+<html lang="es"> + ... + + <body> + <div lang="ru"> + <sl-button><!-- still in Spanish --></sl-button> + </div> + </body> +</html> +
+ This limitation exists because there’s no efficient way to determine the current locale of a given element + in a DOM tree. I consider this a gap in the platform and + I’ve proposed a couple properties + to make this possible. +
+
+ You can provide your own translations if you have specific needs or if you don’t want to wait for a
+ translation to land upstream. The easiest way to do this is to copy src/translations/en.ts
into
+ your own project and translate the terms inside. When your translation is done, you can import it and use it
+ just like a built-in translation.
+
+ Let’s create a Spanish translation as an example. The following assumes you’re using TypeScript, but you can + also create translations with regular JavaScript. +
++import { registerTranslation } from '@onsonr/nebula/dist/utilities/localize'; +import type { Translation } from '@onsonr/nebula/dist/utilities/localize'; + +const translation: Translation = { + $code: 'es', + $name: 'Español', + $dir: 'ltr', + + term1: '...', + term2: '...', + ... +}; + +registerTranslation(translation); + +export default translation; +
Once your translation has been compiled to JavaScript, import it and activate it like this.
++<html lang="es"> + <head> + <script type="module" src="/path/to/es.js"></script> + </head> + + <body> + ... + </body> +</html> +
+ If your translation isn’t working, make sure you’re using the same localize module when importing
+ registerTranslation
. If you’re using a different module, your translation won’t be
+ recognized.
+
+ Nebula is designed to be highly customizable through pure CSS. Out of the box, you can choose from a light + or dark theme. Alternatively, you can design your own theme. +
+
+ A theme is nothing more than a stylesheet that uses the Nebula API to define design tokens and apply custom
+ styles to components. To create a theme, you will need a decent understanding of CSS, including
+ CSS Custom Properties
+ and the
+ ::part
selector.
+
+ For component developers, built-in themes are also available as JavaScript modules that export
+ Lit CSSResult
+ objects. You can find them in dist/themes/*.styles.js
.
+
+ All themes are scoped to classes using the sl-theme-{name}
convention, where
+ {name}
is a lowercase, hyphen-delimited value representing the name of the theme. The included
+ light and dark themes use sl-theme-light
and sl-theme-dark
, respectively. A custom
+ theme called “Purple Power”, for example, would use a class called sl-theme-purple-power
+
+ All selectors must be scoped to the theme’s class to ensure interoperability with other themes. You should
+ also scope them to :host
so they can be imported and applied to custom element shadow roots.
+
+:host, +.sl-theme-purple-power { + /* ... */ +} +
+ To activate a theme, import it and apply the theme’s class to the <html>
element. This
+ example imports and activates the built-in dark theme.
+
+<html class="sl-theme-dark"> + <head> + <link rel="stylesheet" href="path/to/shoelace/dist/themes/dark.css" /> + </head> + + <body> + ... + </body> +</html> +
+ There is one exception to this rule — the light theme does not need to be activated. For
+ convenience, the light theme is scoped to :root
and will be activated by default when
+ imported.
+
+ You can activate themes on various containers throughout the page. This example uses the light theme with a + dark-themed sidebar. +
++<html> + <head> + <link rel="stylesheet" href="path/to/shoelace/dist/themes/light.css" /> + <link rel="stylesheet" href="path/to/shoelace/dist/themes/dark.css" /> + </head> + + <body> + <nav class="sl-theme-dark"> + <!-- dark-themed sidebar --> + </nav> + + <!-- light-themed content --> + </body> +</html> +
It’s for this reason that themes must be scoped to specific classes.
++ There are two ways to create themes. The easiest way is to customize a built-in theme. The advanced way is + to create a new theme from scratch. Which method you choose depends on your project’s requirements and the + amount of effort you’re willing to commit to. +
++ The easiest way to customize Nebula is to override one of the built-in themes. You can do this by importing + the light or dark theme as-is, then creating a separate stylesheet that overrides + design tokens and adds + component styles to your liking. You must import + your theme after the built-in theme. +
+If you’re customizing the light theme, you should scope your styles to the following selectors.
++:root, +:host, +.sl-theme-light { + /* your custom styles here */ +} +
If you’re customizing the dark theme, you should scope your styles to the following selectors.
++:host, +.sl-theme-dark { + /* your custom styles here */ +} +
+ By customizing a built-in theme, you’ll maintain a smaller stylesheet containing only the changes you’ve + made. Contrast this to creating a new theme, where you need to + explicitly define every design token required by the library. This approach is more “future-proof,” as new + design tokens that emerge in subsequent versions of Nebula will be accounted for by the built-in theme. +
++ While this approach is easier to maintain, the drawback is that your theme can’t be activated independently + — it’s tied to the built-in theme you’re extending. +
++ Creating a new theme is more of an undertaking than + customizing an existing one. At a minimum, you must implement + all of the required design tokens. The easiest way to do this is by “forking” one of the built-in themes and + modifying it from there. +
++ Start by changing the selector to match your theme’s name. Assuming your new theme is called “Purple Power”, + your theme should be scoped like this. +
++:host, +.sl-theme-purple-power { + /* your custom styles here */ +} +
+ By creating a new theme, you won’t be relying on a built-in theme as a foundation. Because the theme is + decoupled from the built-ins, you can activate it independently as an alternative to the built-ins. This is + the recommended approach if you’re looking to open source your theme for others to use. +
++ You will, however, need to maintain your theme more carefully, as new versions of Nebula may introduce new + design tokens that your theme won’t have accounted for. Because of this, it’s recommended that you clearly + specify which version(s) of Nebula your theme is designed to work with and keep it up to date as new + versions of Nebula are released. +
++ The built-in dark theme uses an inverted color scale so, if you’re using design tokens as intended, you’ll + get dark mode for free. While this isn’t the same as a professionally curated dark theme, it provides an + excellent baseline for one and you’re encouraged to customize it depending on your needs. +
++ The dark theme works by taking the light theme’s color tokens and “flipping” the + scale so 100 becomes 900, 200 becomes 800, 300 becomes 700, etc. Next, the luminance of each primitive was + fine-tuned to avoid true black, which is often undesirable in dark themes, and provide a richer experience. + The result is a custom dark palette that complements the light theme and makes it easy to offer light and + dark modes with minimal effort. +
+To install the dark theme, add the following to the <head>
section of your page.
+<link + rel="stylesheet" + href="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/themes/dark.css" +/> +
+ To activate the theme, apply the sl-theme-dark
class to the <html>
element.
+
+<html class="sl-theme-dark"> + ... +</html> +
+ Nebula doesn’t try to auto-detect the user’s light/dark mode preference. This should be done at the + application level. As a best practice, to provide a dark theme in your app, you should: +
+prefers-color-scheme
+ and use its value by default
+
+ Nebula avoids using the prefers-color-scheme
media query because not all apps support dark
+ mode, and it would break things for the ones that don’t.
+
+ Nebula components are just regular HTML elements, or + custom elements + to be precise. You can use them like any other element. Each component has detailed documentation that + describes its full API, including properties, events, methods, and more. +
++ If you’re new to custom elements, often referred to as “web components,” this section will familiarize you + with how to use them. +
+
+ Many components have properties that can be set using attributes. For example, buttons accept a
+ size
attribute that maps to the size
property which dictates the button’s size.
+
+<sl-button size="small">Click me</sl-button> +
+ Some properties are boolean, so they only have true/false values. To activate a boolean property, add the + corresponding attribute without a value. +
++<sl-button disabled>Click me</sl-button> +
+ In rare cases, a property may require an array, an object, or a function. For example, to customize the
+ color picker’s list of preset swatches, you set the swatches
property to an array of colors.
+ This must be done with JavaScript.
+
+<sl-color-picker></sl-color-picker> + +<script> + const colorPicker = document.querySelector('sl-color-picker'); + colorPicker.swatches = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']; +</script> +
Refer to a component’s documentation for a complete list of its properties.
+
+ You can listen for standard events such as click
, mouseover
, etc. as you normally
+ would. However, it’s important to note that many events emitted within a component’s shadow root will be
+ retargeted
+ to the host element. This may result in, for example, multiple click
handlers executing even if
+ the user clicks just once. Furthermore, event.target
will point to the host element, making
+ things even more confusing.
+
+ As a result, you should almost always listen for custom events instead. For example, instead of listening to
+ click
to determine when an <sl-checkbox>
gets toggled, listen to
+ sl-change
.
+
+<sl-checkbox>Check me</sl-checkbox> + +<script> + const checkbox = document.querySelector('sl-checkbox'); + checkbox.addEventListener('sl-change', event => { + console.log(event.target.checked ? 'checked' : 'not checked'); + }); +</script> +
+ All custom events are prefixed with sl-
to prevent collisions with standard events and other
+ libraries. Refer to a component’s documentation for a complete list of its custom events.
+
+ Some components have methods you can call to trigger various behaviors. For example, you can set focus on a
+ Nebula input using the focus()
method.
+
+<sl-input></sl-input> + +<script> + const input = document.querySelector('sl-input'); + input.focus(); +</script> +
Refer to a component’s documentation for a complete list of its methods and their arguments.
+
+ Many components use slots to accept content inside of them. The most common slot is the
+ default slot, which includes any content inside the component that doesn’t have a
+ slot
attribute.
+
For example, a button’s default slot is used to populate its label.
++<sl-button>Click me</sl-button> +
+ Some components also have named slots. A named slot can be populated by adding a child element with
+ the appropriate slot
attribute. Notice how the icon below has the
+ slot="prefix"
attribute? This tells the component to place the icon into its
+ prefix
slot.
+
+<sl-button> + <sl-icon slot="prefix" name="gear"></sl-icon> + Settings +</sl-button> +
+ The location of a named slot doesn’t matter. You can put it anywhere inside the component and the browser + will move it to the right place automatically! +
+Refer to a component’s documentation for a complete list of available slots.
+
+ Custom elements cannot have self-closing tags. Similar to <script>
and
+ <textarea>
, you must always include the full closing tag.
+
+<!-- Don't do this --> +<sl-input /> + +<!-- Always do this --> +<sl-input></sl-input> +
+ You might expect similarly named elements to share the same API as native HTML elements, but this is not + always the case. Nebula components are not designed to be one-to-one replacements for their + HTML counterparts. While they usually share the same API, there may be subtle differences. +
+
+ For example, <button>
and <sl-button>
both have a
+ type
attribute, but the native one defaults to submit
while the Nebula one
+ defaults to button
since this is a better default for most users.
+
+ Don’t make assumptions about a component’s API! To prevent unexpected behaviors, please + take the time to review the documentation and make sure you understand what each attribute, property, + method, and event is intended to do. +
++ Web components are registered with JavaScript, so depending on how and when you load Nebula, you may notice + a + Flash of Undefined Custom Elements (FOUCE) + when the page loads. There are a couple ways to prevent this, both of which are described in the linked + article. +
+
+ One option is to use the
+ :defined
+ CSS pseudo-class to “hide” custom elements that haven’t been registered yet. You can scope it to specific
+ tags or you can hide all undefined custom elements as shown below.
+
+:not(:defined) { + visibility: hidden; +} +
+ As soon as a custom element is registered, it will immediately appear with all of its styles, effectively
+ eliminating FOUCE. Note the use of visibility: hidden
instead of display: none
to
+ reduce shifting as elements are registered. The drawback to this approach is that custom elements can
+ potentially appear one by one instead of all at the same time.
+
+ Another option is to use
+ customElements.whenDefined()
, which returns a promise that resolves when the specified element gets registered. You’ll probably want to
+ use it with
+ Promise.allSettled()
+ in case an element fails to load for some reason.
+
+ A clever way to use this method is to hide the <body>
with opacity: 0
and
+ add a class that fades it in as soon as all your custom elements are defined.
+
+<style> + body { + opacity: 0; + } + + body.ready { + opacity: 1; + transition: 0.25s opacity; + } +</style> + +<script type="module"> + await Promise.allSettled([ + customElements.whenDefined('sl-button'), + customElements.whenDefined('sl-card'), + customElements.whenDefined('sl-rating') + ]); + + // Button, card, and rating are registered now! Add + // the `ready` class so the UI fades in. + document.body.classList.add('ready'); +</script> +
+ Nebula components are built with + Lit, a tiny + library that makes authoring custom elements easier, more maintainable, and a lot of fun! As a Nebula user, + here is some helpful information about rendering and updating you should probably be aware of. +
++ To optimize performance and reduce re-renders, Lit batches component updates. This means changing multiple + attributes or properties at the same time will result in just a single re-render. In most cases, this isn’t + an issue, but there may be times you’ll need to wait for the component to update before continuing. +
+
+ Consider this example. We’re going to change the checked
property of the checkbox and observe
+ its corresponding checked
attribute, which happens to reflect.
+
+const checkbox = document.querySelector('sl-checkbox'); +checkbox.checked = true; + +console.log(checkbox.hasAttribute('checked')); // false +
+ Most developers will expect this to be true
instead of false
, but the component
+ hasn’t had a chance to re-render yet so the attribute doesn’t exist when hasAttribute()
is
+ called. Since changes are batched, we need to wait for the update before proceeding. This can be done using
+ the updateComplete
property, which is available on all Lit-based components.
+
+const checkbox = document.querySelector('sl-checkbox'); +checkbox.checked = true; + +checkbox.updateComplete.then(() => { + console.log(checkbox.hasAttribute('checked')); // true +}); +
This time we see an empty string, which means the boolean attribute is now present!
+
+ Avoid using setTimeout()
or requestAnimationFrame()
in situations like this.
+ They might work, but it’s far more reliable to use updateComplete
instead.
+
+ Nebula ships with a file called vscode.html-custom-data.json
that can be used to describe it’s
+ custom elements to Visual Studio Code. This enables code completion for Nebula components (also known as
+ “code hinting” or “IntelliSense”). To enable it, you need to tell VS Code where the file is.
+
.vscode
at the root of your project
+ settings.json
+{ + "html.customData": ["./node_modules/@onsonr/nebula/dist/vscode.html-custom-data.json"] +} +
+ If settings.json
already exists, simply add the above line to the root of the object. Note that
+ you may need to restart VS Code for the changes to take effect.
+
+ If you are using a
+ JetBrains IDE
+ and you are installing Nebula from NPM, the editor will automatically detect the
+ web-types.json
file from the package and you should immediately see component information in
+ your editor.
+
+ If you are installing from the CDN, you can
+ download a local copy
+ and add it to the root of your project. Be sure to add a reference to the web-types.json
file
+ in your package.json
in order for your editor to properly detect it.
+
+{ + ... + "web-types": "./web-types.json" + ... +} +
If you are using types from multiple projects, you can add an array of references.
++{ + ... + "web-types": [ + ..., + "./web-types.json" + ] + ... +} +
+ Most popular editors support custom code completion with a bit of configuration. Please + submit a feature request + for your editor of choice. PRs are also welcome! +
+Add the following code to your page.
+ ++<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/themes/light.css" /> +<script type="module" src="https://cdn.jsdelivr.net/npm/@onsonr/nebula@0.0.10/cdn/shoelace-autoloader.js"></script> +
Now you have access to all of Nebula’s components! Try adding a button:
+ + + ++ This will activate Nebula’s experimental autoloader, which registers components on the fly as you use + them. To learn more about it, or for other ways to install Nebula, refer to the + installation instructions. +
++ TL;DR – we finally have a way to create + our own HTML elements + and use them in any framework we want! +
++ Thanks to the popularity of frameworks such as Angular, Vue, and React, component-driven development has + become a part of our every day lives. Components help us encapsulate styles and behaviors into reusable + building blocks. They make a lot of sense in terms of design, development, and testing. +
+Unfortunately, framework-specific components fail us in a number of ways:
++ Web components solve these problems. They’re + supported by all modern browsers, they’re framework-agnostic, and they’re + part of the standard, so we know they’ll be supported for many years to come. +
+This is the technology that Nebula is built on.
++ Nebula provides a collection of professionally designed, highly customizable UI components built on a + framework agnostic technology. Why spend hundreds of hours (or more) building a design system from scratch? + Why make a component library that only works with one framework? +
+With Nebula, you can:
++ If your organization is looking to build a design system, + Nebula will save you thousands of dollars. All the foundational components you need are right here, ready to be customized for your brand. And since + it’s built on web standards, browsers will continue to support it for many years to come. +
++ Whether you use Nebula as a starting point for your organization’s design system or for a fun personal + project, there’s no limit to what you can do with it. +
+Nebula is tested in the latest two versions of the following browsers.
+ + + + + +Critical bug fixes in earlier versions will be addressed based on their severity and impact.
++ If you need to support IE11 or pre-Chromium Edge, this library isn’t for you. Although web components can + (to some degree) be polyfilled for legacy browsers, supporting them is outside the scope of this project. If + you’re using Nebula in such a browser, you’re gonna have a bad time. ⛷ +
++ Nebula was created in New Hampshire by + Cory LaViska. It’s available under the terms of the + MIT license. +
+Special thanks to the following projects and individuals that help make Nebula possible.
++ Nebula recognizes the need for all users, regardless of ability and device, to have undeterred access to the + websites and applications that are created with it. This is an important goal of the project. +
++ Oftentimes, people will ask “is Nebula accessible?” I’m reluctant to answer because accessibility isn’t + binary — there’s no simple “yes” or “no” response to provide. What seems accessible to a sighted user might + be completely inaccessible to a non-sighted user. And even if you optimize for various screen readers, you + still have to account for low-level vision, color blindness, hearing impairments, mobility impairments, and + more. +
++ Accessibility is something you have to continuously strive for. No individual contributor — or perhaps even + an entire team — can claim their software is 100% accessible because of the sheer diversity of abilities, + devices, assistive technologies, and individual use cases. +
++ Furthermore, accessibility doesn’t stop at the component level. Using accessible building blocks doesn’t + magically make the rest of your webpage or application compliant. There is no library or overlay that will + make your software “fully accessible” without putting in the effort. It’s also worth noting that web + components are still somewhat bleeding edge, so browsers, assistive devices, and + even specifications + are still evolving to help improve accessibility on the web platform. +
++ My commitment to Nebula users is this: Everything I develop will be built with accessibility in mind. I will + test and improve every component to the best of my ability and knowledge. I will work around upstream + issues, such as browser bugs and limitations, to the best of my ability and within reason. +
++ I’m fully aware that I may not get it right every time for every user, so I invite the community to + participate in this ongoing effort by submitting + issues, + pull requests, and + discussions. Many accessibility improvements have already been made thanks to contributors submitting code, feedback, + and suggestions. +
++ This is the path forward. Together, we will continue to make Nebula accessible to as many users as possible. +
+
+ — Cory LaViska
+ Creator of Nebula
+
+ Nebula follows
+ Semantic Versioning. Breaking changes in components with the
+ Components with the
+ New versions of Nebula are released as-needed and generally occur when a critical mass of changes have + accumulated. At any time, you can see what’s coming in the next release by visiting + next.shoelace.style. +
+base__popup
part to <sl-dropdown>
+ #2078
+ <sl-tab>
closable
property now reflects.
+ #2041
+ <sl-tab-group>
now implements a proper “roving tabindex” and
+ <sl-tab>
is no longer tabbable by default. This aligns closer to the APG pattern for
+ tabs.
+ #2041
+ dir
on the parent menu item
+ #1992
+ <sl-relative-time>
would announce the full time instead of the
+ relative time in screen readers
+ customElements.define
we no longer register with anonymous classes by default
+ #2079
+ <sl-radio-group>
where if a click did not contain a
+ <sl-radio>
it would show a console error.
+ #2009
+ <sl-split-panel>
that caused it not to recalculate it’s position when
+ going from being display: none;
to its original display value.
+ #1942
+ <dialog>
where when it showed it would cause a layout shift.
+ #1967
+ <sl-tooltip>
that allowed unwanted text properties to leak in
+ #1947
+ <sl-button-group>
classes
+ #1974
+ <sl-textarea>
that may throw errors on
+ disconnectedCallback
in test environments
+ #1985
+ <sl-color-picker>
that would log a non-passive event listener warning
+ #2005
+ <sl-range>
that caused the tooltip position to be incorrect in some
+ cases
+ #1979
+ contextElement
to VirtualElements
in
+ <sl-popup>
+ #1874
+ spinner
and spinner__base
parts to <sl-tree-item>
+ #1937
+ sync
property to <sl-dropdown>
so the menu can easily sync sizes
+ with the trigger element
+ #1935
+ <sl-icon>
that did not properly apply mutators to spritesheets
+ #1927
+ .sl-scroll-lock
causing layout shifts
+ #1895
+ <sl-rating>
that caused the rating to not reset in some circumstances
+ #1877
+ <sl-select>
that caused the menu to not close when rendered in a shadow
+ root
+ #1878
+ <sl-tree>
that caused a new stacking context resulting in tooltips being
+ clipped
+ #1709
+ <sl-tab-group>
that caused the scroll controls to toggle indefinitely
+ when zoomed in Safari
+ #1839
+ <sl-select>
where the tag size wouldn’t update with the control’s size
+ #1886
+ <sl-checkbox>
and <sl-switch>
where the color of the
+ required content wasn’t applying correctly
+ <sl-checkbox>
where help text was incorrectly styled
+ #1897
+ <sl-input>
that prevented the control from receiving focus when clicking
+ over the clear button
+ <sl-carousel>
that caused the carousel to be out of sync when used with
+ reduced motion settings
+ #1887
+ <sl-button-group>
that caused styles to stop working when using
+ className
on buttons in React
+ #1926
+ <sl-checkbox>
+ #1860
+ <sl-switch>
+ #1800
+ <sl-option>
that caused HTML tags to be included in
+ getTextLabel()
+ <sl-carousel>
that caused slides to not switch correctly
+ #1862
+ <sl-menu>
elements
+ #1835
+ hover-bridge
feature to <sl-popup>
to support better tooltip
+ accessibility
+ #1734
+ loading
attribute and the spinner
and spinner__base
part
+ to <sl-menu-item>
+ #1700
+ .js
extensions.
+ #1770
+ <sl-tree>
when providing custom expand / collapse icons
+ #1922
+ <sl-dialog>
not accounting for elements with hidden dialog controls like
+ <video>
+ #1755
+ <sl-input>
and <sl-textarea>
that made it work
+ differently from <input>
and <textarea>
when using defaults
+ #1746
+ <sl-select>
that prevented it from closing when tabbing to another
+ select inside a shadow root
+ #1763
+ <sl-spinner>
that caused the animation to appear strange in certain
+ circumstances
+ #1787
+ <sl-dialog>
with focus trapping
+ #1813
+ <sl-radio-group>
elements to log an error in the console
+ #1795
+ scrollbar-gutter
property
+ #1805
+ <sl-option>
that caused slotted content to show up when calling
+ getTextLabel()
+ #1730
+ <sl-color-picker>
that caused picker values to not match the preview
+ color
+ #1831
+ <sl-carousel>
where pagination dots don’t update when swiping slide in
+ iOS Safari
+ #1748
+ <sl-carousel>
where trying to swipe doesn’t change the slide in Firefox
+ for Android
+ #1748
+ <sl-tooltip>
so they persist when hovering over the
+ tooltip and dismiss when pressing Esc
+ #1734
+ CloseWatcher
API
+ #1788
+ <sl-carousel>
and moved all mouse
+ related logic into the component
+ #1748
+ form.checkValidity()
and it will use Nebula’s custom
+ checkValidity()
handler.
+ #1708
+ form
property, but not an attribute.
+ #1707
+ "sl-change"
event would always fire simultaneously with
+ "sl-input"
event in <sl-color-picker>
. The
+ <sl-change>
event now only fires when a user stops dragging a slider or stops dragging
+ on the color canvas.
+ #1689
+ <sl-carousel>
component that caused an error to be thrown when rendered
+ with Lit
+ #1684
+ <sl-carousel>
component
+ #1605
+ <sl-select>
+ #1667
+ dist/react/index.js
to be blank
+ #1659
+ RangeError
+ <sl-copy-button>
that prevented exported tooltip parts from being styled
+ #1586
+ <sl-menu>
that caused it not to fire the sl-select
event if
+ you clicked an element inside of a <sl-menu-item>
+ #1599
+ @shoelace-style/localize
to 3.1.0@lib-labs/react
to stable @lit/react
modal
property to <sl-dialog>
and
+ <sl-drawer>
to support third-party modals
+ #1571
+ <sl-switch>
that resulted in improper spacing between the label and the
+ required asterisk
+ #1540
+ <sl-icon>
that caused icons to not load when the default library used a
+ sprite sheet
+ #1572
+ @ctrl/tinycolor
to 4.0.1
+ #1542
+ --isolatedModules
and --verbatimModuleSyntax
to
+ tsconfig.json
. For anyone directly importing event types, they no longer provide a default
+ export due to these options being enabled. For people using the events/event.js
file
+ directly, there is no change.
+ <sl-menu-item>
+ #1410
+ --submenu-offset
custom property to <sl-menu-item>
+ #1410
+ <sl-dialog>
when wrapped by other
+ elements not checking the assigned elements of <slot>
s.
+ #1537
+ ref
attribute in React Wrappers.
+ #1526
+ <sl-radio-button>
to render incorrectly with gaps
+ #1523
+ <sl-tree>
to work more like users expect
+ #1521
+ <sl-menu-item>
so labels truncate properly instead of getting chopped and
+ overflowing
+ React.Component
around @lit-labs/react
wrapper.
+ #1531
+ @lit-labs/react
to v2.0.1.
+ #1531
+ <sl-copy-button>
component
+ #1473
+ <sl-dropdown>
where pressing Up or Down when
+ focused on the trigger wouldn’t focus the first/last menu items
+ #1472
+ <sl-tree>
+ #1492
+ <sl-input>
to prevent the component’s
+ width from shifting when toggled
+ #1496
+ <sl-tooltip>
to prevent user selection so the tooltip doesn’t get highlighted
+ when dragging selections
+ sideEffects
key from package.json
. Update React docs to use
+ cherry-picking.
+ #1485
+ displayName
to React Wrappers for better debugging.
+ #1450
+ <sl-qr-code>
+ #1416
+ <sl-menu-item>
elements
+ #1429
+ <sl-popup>
+ #1449
+ spinner
part to <sl-button>
+ #1460
+ shoelace.js
and shoelace-autoloader.js
to exportmaps.
+ #1450
+ sideEffects
key in
+ package.json
.
+ #1450
+ <sl-tree>
where it was auto-defining <sl-tree-item>
.
+ #1450
+ <sl-dialog>
. We now manually
+ handle focus ordering as well as added offsetParent()
check for tabbable boundaries in
+ Safari. Test cases added for <sl-dialog>
inside a shadowRoot
+ #1403
+ valueAsDate
on <sl-input>
where it would always set
+ type="date"
for the underlying <input>
element. It now falls back to the
+ native browser implementation for the in-memory input. This may cause unexpected behavior if you’re using
+ valueAsDate
on any input elements that aren’t type="date"
.
+ #1399
+ <sl-qr-code>
where the background
attribute was never
+ passed to the QR code
+ #1416
+ <sl-dropdown>
where aria attributes were incorrectly applied to the
+ default <slot>
causing Lighthouse errors
+ #1417
+ <sl-carousel>
that caused navigation to work incorrectly in some case
+ #1420
+ <sl-tree>
that caused focus to be stolen when removing focused tree
+ items
+ #1430
+ <sl-dialog>
and <sl-drawer>
that caused nested modals
+ to respond too eagerly to the Esc key
+ #1457
+ <sl-details>
to use <details>
internally for better
+ semantics and to enable search to find in supportive browsers when collapsed
+ #1470
+ <sl-select>
+ #1387
+ + This release + unbundles Lit + (and other dependencies) from Nebula. There are now two distributions for the project: +
+cdn/
– a bundled, CDN-ready distributiondist/
– an unbundled, npm-ready distribution
+ If you’re a CDN user, you must update your path to point to cdn/
instead of
+ dist/
. You can copy and paste the latest paths from the
+ installation page.
+
cdn/
distribution for bundled dependencies (imports for npm users remain the same)
+ #1369
+ checkbox
part and related exported parts to <sl-tree-item>
so
+ you can target it with CSS
+ #1318
+ submenu-icon
part to <sl-menu-item>
(submenus have not been
+ implemented yet, but this part is required to allow customizations)
+ <sl-icon>
via a custom resolver.
+ <sl-split-panel>
+ #1343
+ <sl-radio-group>
wouldn’t update the size of
+ child elements
+ <sl-select>
and <sl-color-picker>
where the
+ size
attribute wasn’t being reflected
+ #1318
+ <sl-radio-group>
where <sl-radio>
would not get
+ checked if <sl-radio-group>
was defined first.
+ #1364
+ <sl-input>
that caused date pickers to look filled in even when empty in
+ Safari
+ #1341
+ <sl-radio-group>
that sometimes caused dual scrollbars in containers
+ that overflowed
+ #1380
+ <sl-carousel>
not loading the English language pack automatically.
+ #1384
+ <sl-button>
so it can accept children of variable heights
+ #1317
+ <sl-rating>
by partially rendering unseen icons
+ #1310
+ <html>
instead of <body>
since
+ the latter gets replaced by some frameworks
+ #1338
+ discover()
function to the experimental autoloader’s exports
+ #1236
+ size
attribute to <sl-radio-group>
so labels and controls will
+ be sized consistently
+ #1301
+ <sl-animated-image>
+ #1246
+ <sl-animation>
+ #1274
+ <sl-tree-item>
that prevented long labels from wrapping
+ #1243
+ <sl-tree-item>
that caused labels to be misaligned when text wraps
+ #1244
+ <sl-checkbox>
+ #1272
+ <sl-avatar>
that caused the initials to show up behind images with
+ transparency
+ #1260
+ <sl-split-panel>
that prevented the divider from being focusable in some
+ browsers
+ #1288
+ <sl-tab-group>
to affect scrolling when initializing
+ #1292
+ <sl-menu-item>
that allowed the hover state to show when focused
+ #1282
+ <sl-carousel>
that prevented interactive elements from receiving clicks
+ #1262
+ <sl-input>
that caused valueAsDate
and
+ valueAsNumber
to not be set synchronously in some cases
+ #1302
+ <sl-carousel>
when used inside a flex container
+ #1235
+ <sl-tree-item>
to support buttons and other interactive
+ elements
+ #1234
+ <sl-include>
to prevent an apparent memory leak in some
+ browsers
+ #1284
+ <sl-select>
, <sl-split-panel>
, and
+ <sl-details>
by ensuring slots don’t have roles
+ #1287
+ subpath
argument to getBasePath()
to make it easier to generate full
+ paths to any file
+ custom-elements.json
to package exportstag__base
, tag__content
, tag__remove-button
,
+ tag__remove-button__base
parts to <sl-select>
+ <sl-rating>
that allowed the sl-change
event to be emitted
+ when disabled
+ #1220
+ <sl-input>
that caused min
and max
to
+ stop working when type="date"
+ #1224
+ <sl-carousel>
+ #1218
+ <sl-option>
so it converts non-string values to strings for convenience
+ #1226
+ svg
part to <sl-icon>
getForm()
method to all form controls
+ #1180
+ <sl-select>
that caused the display label to render incorrectly in
+ Chrome after form validation
+ #1197
+ <sl-input>
that prevented users from applying their own value for
+ autocapitalize
, autocomplete
, and autocorrect
when using
+ type="password
+ #1205
+ <sl-tab-group>
that prevented scroll controls from showing when
+ dynamically adding tabs
+ #1208
+ <sl-input>
that caused the calendar icon to be clipped in Firefox
+ #1213
+ <sl-tab>
that caused sl-tab-show
to be emitted when
+ activating the close button
+ <sl-spinner>
that caused --track-color
to be invisible with
+ certain colors
+ <sl-menu-item>
that caused the focus color to show when selecting menu
+ items with a mouse or touch device
+ <sl-select>
that caused sl-change
and
+ sl-input
to be emitted too early
+ #1201
+ <sl-popup>
to positioned nested popups
+ incorrectly
+ #1135
+ <sl-tree>
that caused the tree item to collapse when clicking a child
+ item, dragging the mouse, and releasing it on the parent node
+ #1082
+ @shoelace-style/localize
to 3.1.0@floating-ui/dom
to 1.2.1
+ When using <input type="password">
the default value for autocapitalize
,
+ autocomplete
, and autocorrect
may be affected due to the bug fixed in
+ #1205restore the previous behavior.
+
sl-focus
and sl-blur
events to <sl-color-picker>
+ focus()
and blur()
methods to <sl-color-picker>
+ sl-invalid
event to all form controls to enable custom validation logic
+ #1167
+ validity
and validationMessage
properties to all form controls
+ #1167
+ rel
attribute to <sl-button>
to allow users to create button
+ links that point to specific targets
+ #1200
+ <sl-animated-image>
where the play and pause buttons were transposed
+ #1147
+ web-types.json
from being generated
+ #1154
+ <sl-color-picker>
that prevented sl-change
and
+ sl-input
from emitting when using the eye dropper
+ #1157
+ <sl-dropdown>
that prevented keyboard users from selecting menu items
+ when using the keyboard
+ #1165
+ <sl-select>
that caused the
+ form-control-help-text
part to not be in the same location as other form controls
+ #1178
+ <sl-checkbox>
and <sl-switch>
that caused the browser
+ to scroll incorrectly when focusing on a control in a container with overflow
+ #1169
+ <sl-menu-item>
that caused the click
event to be emitted
+ when the item was disabled
+ #1113
+ novalidate
was used on the containing form
+ #1164
+ <sl-checkbox>
that caused the required asterisk to appear before the
+ label in Chrome
+ <sl-dropdown>
in Safari so keyboard interaction works the same
+ as in other browsers
+ #1177
+ <sl-switch>
when used in forced-colors / Windows High
+ Contrast mode
+ #1114
+ + This is the first stable release of Nebula 2, meaning breaking changes to the API will no longer be accepted + for this version. Development of Nebula 2.0 started in January 2020. The first beta was released on + July 15, 2020. Since then, Nebula has grown quite a bit! Here are some stats from the project as of January 24, 2023: +
++ I’d like to extend a very special thank you to every single contributor who worked to make this possible. + Everyone who’s filed a bug, submitted a PR, requested a feature, started a discussion, helped with testing, + and advocated for the project. You are just as responsible for Nebula’s success as I am. I’d also like to + thank the folks at + Font Awesome + for recognizing Nebula’s potential and + believing in me + to make it happen. +
+Thank you! And keep building awesome stuff!
+Without further ado, here are the notes for this release.
+inert
attribute on <sl-menu-item>
to allow hidden
+ menu items to not accept focus
+ #1107
+ tag
part to <sl-select>
sl-hover
event to <sl-rating>
+ #1125
+ @documentation
tag with a link to the docs for each componentform
attribute to all form controls to allow placing them outside of a
+ <form>
element
+ #1130
+ getFormControls()
function as an alternative to
+ HTMLFormElement.elements
+ header-actions
slot in <sl-dialog>
and
+ <sl-drawer>
+ hue-slider-handle
and opacity-slider-handle
parts to
+ <sl-color-picker>
and correct other part names in the docs
+ #1142
+ <sl-select>
that prevented placeholders from showing when
+ multiple
was used
+ #1109
+ <sl-select>
that caused tags to not be rounded when using the
+ pill
attribute
+ #1117
+ <sl-select>
where the sl-change
and
+ sl-input
events didn’t weren’t emitted when removing tags
+ #1119
+ <sl-select>
that caused the listbox to scroll to the first selected item
+ when selecting multiple items
+ #1138
+ <sl-select>
where the input color and input hover color wasn’t using the
+ correct design tokens
+ #1143
+ <sl-color-picker>
that logged a console error when parsing swatches with
+ whitespace
+ <sl-color-picker>
that caused selected colors to be wrong due to
+ incorrect HSV calculations
+ <sl-color-picker>
that prevented the initial value from being set
+ correct when assigned as a property
+ #1141
+ <sl-radio-button>
that caused the checked button’s right border to be
+ incorrect
+ #1110
+ <sl-spinner>
that caused the animation to stop working correctly in
+ Safari
+ #1121
+ <sl-tab-panel>
to be hidden when inactive<sl-radio-group>
to be
+ undefined
depending on where the radio was activated
+ #1134
+ <sl-icon>
that caused icons to sometimes be clipped in Safari<sl-checkbox>
,
+ <sl-radio>
, and <sl-switch>
+ <sl-radio-group>
that caused an extra margin between the host element
+ and the internal fieldset
+ #1139
+ NebulaFormControl
interface to remove the invalid
property,
+ allowing a more intuitive API for controlling validation internally
+ FormSubmitController
to FormControlController
to better
+ reflect what it’s used for
+
+ This release includes a complete rewrite of <sl-select>
to improve accessibility and
+ simplify its internals.
+
<sl-select>
+ <sl-option>
instead of <sl-menu-item>
for options
+ now
+ suffix
slot was removed because it was confusing to users and its position made the
+ clear button inaccessible
+ max-tags-visible
attribute has been renamed to max-options-visible
+ sl-label-change
event from <sl-menu-item>
(listen
+ for slotchange
instead)
+ <sl-menu>
(this was added specifically
+ for <sl-select>
which no longer uses <sl-menu>
)
+ <sl-color-picker>
are no longer present by default (but you
+ can set them using the swatches
attribute now)
+ <sl-menu-item>
so checked items are
+ announced as such
+ type="checkbox"
before applying the
+ checked
attribute
+ checked
state on their own when selected<sl-option>
componentswatches
to be an attribute of <sl-color-picker>
so
+ swatches can be defined declaratively (it was previously a property; use a ;
to separate
+ color values)
+ <sl-tree-item>
where the checked/indeterminate states could get out of
+ sync when using the multiple
option
+ #1076
+ <sl-tree>
that caused sl-selection-change
to emit before
+ the DOM updated
+ #1096
+ <sl-switch>
from submitting a default value of
+ on
when no value was provided
+ #1103
+ <sl-textarea>
that caused the scrollbar to show sometimes when using
+ resize="auto"
+ <sl-input>
and <sl-textarea>
that caused its
+ validation states to be out of sync in some cases
+ #1063
+ private
keyword (these were previously private only by
+ convention, but now TypeScript will warn you)
+ <sl-menu-item>
to be consistent with
+ <sl-option>
+ <sl-tree>
and <sl-tree-item>
from experimental
+ to stable
+ @lit-labs/react
+ #1090
+ --sl-toggle-size
design token to
+ --sl-toggle-size-medium
+ --sl-toggle-size-small
and --sl-toggle-size-large
design tokenssize
attribute to <sl-checkbox>
, <sl-radio>
,
+ and <sl-switch>
+ #1071
+ sl-input
event to <sl-checkbox>
,
+ <sl-color-picker>
, <sl-radio>
, <sl-range>
, and
+ <sl-switch>
+ <sl-color-picker>
+ #1072
+ <sl-color-picker>
that sometimes prevented the color from updating when
+ clicking or tapping on the controls
+ <sl-color-picker>
that prevented text from being entered in the color
+ input
+ <sl-input>
that caused the sl-change
event to be
+ incorrectly emitted when the value was set programmatically
+ #917
+ <sl-input>
and <sl-textarea>
that made it impossible
+ to disable spell checking
+ #1061
+ <sl-drawer>
when using the
+ contained
attribute
+ #1051
+ <sl-checkbox>
and <sl-radio>
that caused the checked
+ icons to not scale property when resized
+ <sl-color-picker>
to use @ctrl/tinycolor
instead of
+ color
saving ~67KB
+ #1072
+ formdata
event polyfill since it’s now available in the last two versions of all
+ major browsers
+ date
in <sl-relative-time>
to
+ the current date instead of the Unix epoch
+ handle-icon
part and slot from
+ <sl-image-comparer>
(use handle
instead)
+ handle
slot from <sl-split-panel>
(use the
+ divider
slot instead)
+ --box-shadow
custom property from
+ <sl-alert>
(apply a box shadow to ::part(base)
instead)
+ play-icon
and pause-icon
parts (use the
+ play-icon
and pause-icon
slots instead)
+ header-actions
slot to <sl-dialog>
and
+ <sl-drawer>
+ expand-icon
and collapse-icon
slots to
+ <sl-details>
and refactored the icon animation
+ #1046
+ play-icon
and pause-icon
slots to
+ <sl-animated-image>
so you can customize the default icons
+ isTreeItem()
export to a static method of <sl-tree-item>
<sl-tree-item>
where sl-selection-change
was emitted when
+ the selection didn’t change
+ #1030
+ <sl-button-group>
that caused the border to render incorrectly when
+ hovering over icons inside buttons
+ #1035
+ flip-fallback-strategy
in <sl-popup>
that
+ caused the fallback strategy to be initial
instead of best-fit
, which is
+ inconsistent with Floating UI’s default
+ #1036
+ <sl-tab-group>
that sometimes caused the active tab indicator to not
+ animate
+ <sl-tree-item>
that caused the expand/collapse icon slot to be out of
+ sync when the node is open initially
+ handle-icon
slot in <sl-image-comparer>
(it now
+ points to the <slot>
, not the slot’s fallback content)
+ <sl-dropdown>
so it matches with nested
+ <sl-menu>
elements
+ <sl-alert>
so the alert is announced and the close button has
+ a label
+ <sl-progress-ring>
so slotted labels are announced along with
+ visually hidden labels
+ translate
, rotate
, and
+ scale
instead of transform
+ <sl-skeleton>
x
icon in the system icon library with x-lg
to improve icon
+ consistency
+ <sl-dropdown>
that caused containing dialogs, drawers, etc. to close
+ when pressing Escape while focused
+ #1024
+ <sl-tree-item>
that allowed lazy nodes to be incorrectly selected
+ #1023
+ <sl-tree-item>
+ #1026
+ hoist
from working correctly in
+ <sl-dropdown>
after a recent update
+ #1024
+ fieldset
property from <sl-radio-group>
(use CSS
+ parts if you want to keep the border)
+ #965
+ base
and label
parts from
+ <sl-radio-group>
(use form-control
and
+ form-control__label
instead)
+ #965
+ base
part from <sl-icon>
(style the host element
+ directly instead)
+ invalid
attribute from form controls (use
+ [data-invalid]
to target it with CSS)
+ data-required
- indicates that a value is requireddata-optional
- indicates that a value is NOT requireddata-invalid
- indicates that the form control is invaliddata-valid
- indicates that the form control is validdata-user-invalid
- indicates the form control is invalid and the user has interacted
+ with it
+ data-user-valid
- indicates the form control is valid and the user has interacted with it
+ checkValidity()
method to all form controlsreportValidity()
method to <sl-range>
button--checked
to <sl-radio-button>
and
+ control--checked
to <sl-radio>
to style just the checked state
+ #933
+ <sl-menu>
, <sl-menu-item>
,
+ <sl-menu-label>
, <sl-rating>
, <sl-relative-time>
,
+ <sl-skeleton>
, <sl-tab-panel>
and <sl-tag>
+ #935
+ #949
+ #956
+ --indicator-transition-duration
custom property to
+ <sl-progress-ring>
+ #986
+ --sl-input-required-content-color
custom property to all form controls
+ #948
+ sl-show
and sl-hide
events in
+ <sl-details>
+ #993
+ focus()
and blur()
methods to <sl-radio-button>
stepUp()
and stepDown()
methods to <sl-input>
and
+ <sl-range>
+ #1013
+ showPicker()
method to <sl-input>
+ #1013
+ handle-icon
part to <sl-image-comparer>
caret
, check
, grip-vertical
, indeterminate
, and
+ radio
icons to the system library and removed check-lg
+ #985
+ loading
attribute to <sl-avatar>
to allow lazy loading of image
+ avatars
+ #1006
+ formenctype
attribute to <sl-button>
+ #1009
+ exports
to package.json
and removed the main
and
+ module
properties
+ #1007
+ <sl-card>
that prevented the border radius to apply correctly to the
+ header
+ #934
+ <sl-button-group>
where the inner border disappeared on focus
+ #980
+ <sl-input>
to wobble
+ #996
+ <sl-icon>
that prevented color from being set on the host element
+ #999
+ <sl-dropdown>
where the keydown
event erroneously
+ propagated to ancestors when pressing Escape
+ #990
+ <sl-dialog>
and
+ <sl-drawer>
+ #925
+ <sl-dialog>
and
+ <sl-drawer>
in some cases
+ <sl-radio-group>
that prevented the invalid
property from
+ correctly reflecting validity sometimes
+ #992
+ <sl-tree>
that prevented selections from working correctly on
+ dynamically added tree items
+ #963
+ custom-elements.json
so they point to the dist file instead of the
+ source file
+ #725
+ reportValidity()
in <sl-color-picker>
+ <sl-badge>
to improve padding and render relative to the current font size
+ <sl-color-picker>
so it’s usable in forced-colors mode<sl-dialog>
and <sl-drawer>
so the panel is more
+ visible in forced-colors mode
+ <sl-menu-item>
so selections are visible in forced-colors mode<sl-progress-bar>
so it’s visible in forced-colors mode<sl-radio-button>
so checked states are visible in forced-colors mode
+ <sl-range>
so the thumb, track, and tooltips are visible in forced-colors
+ mode
+ <sl-rating>
so icons are visible in forced-colors mode<sl-split-panel>
so the divider is visible in forced-colors mode<sl-tree-item>
so selected items are visible in forced-colors mode<sl-tab-group>
so tabs are cleaner and easier to understand in
+ forced-colors mode
+ <sl-select>
so you can customize the menu width
+ #1018
+ @summary
to get them within documentation tools
+ #962
+ NebulaFormControl
interface to improve type safety and
+ consistency
+ @shoelace-style/localize
t0 3.0.3 to support for extended language codes
+ This release removes the <sl-responsive-media>
component. When this component was
+ introduced, support for
+ aspect-ratio
) wasn’t great. These days,
+ the property is supported
+ by all of Nebula’s target browsers, making a dedicated component redundant.
+
<sl-responsive-media>
(use the well-supported
+ aspect-ratio
CSS property instead)
+ toggle-password
attribute of <sl-input>
to
+ password-toggle
for consistency
+ <sl-tree-item>
sl-lazy-change
event to <sl-tree-item>
expand-button
part to <sl-tree-item>
+ #893
+ password-visible
attribute to <sl-input>
+ #913
+ <sl-popup>
that didn’t account for the arrow’s diagonal size<sl-popup>
that caused arrow placement to be incorrect with RTL<sl-progress-ring>
that caused the indeterminate animation to stop
+ working in Safari
+ #891
+ <sl-range>
that caused it to overflow a container at 100% width
+ #905
+ <sl-tree-item>
that prevented custom expand/collapse icons from
+ rendering
+ <sl-tree-item>
where the expand-icon
and
+ collapse-icon
slots were reversed
+ <sl-tree-item>
that prevented the keyboard from working after lazy
+ loading
+ #882
+ <sl-textarea>
that prevented the textarea from resizing automatically
+ when setting the value programmatically
+ #912
+ <sl-radio-group>
where the button-group__base
part was
+ documented but not exposed
+ #909
+ <sl-range>
that caused the active track color to render on the wrong
+ side in RTL
+ #916
+ NebulaElement
to reduce imports and
+ improve DX
+ <sl-animated-image>
, <sl-popup>
, and
+ <sl-split-panel>
from experimental to stable
+ sync
and arrow-placement
attributes to <sl-popup>
+ auto-size
attribute of the experimental <sl-popup>
component
+ so it accepts horizontal
, vertical
, and both
instead of a boolean
+ value
+ flip-fallback-placement
attribute of the experimental
+ <sl-popup>
component to flip-fallback-placements
+ flip-fallback-strategy
in the experimental
+ <sl-popup>
component to accept best-fit
and initial
instead
+ of bestFit
and initialPlacement
+ <sl-dropdown>
that caused the panel to resize horizontally when the
+ trigger is clipped by the viewport
+ #860
+ <sl-tree>
where dynamically changing slotted items wouldn’t update the
+ tree properly
+ <sl-split-panel>
that caused the panel to stack when clicking on the
+ divider in mobile versions of Chrome
+ #862
+ <sl-popup>
that prevented flip fallbacks from working as intended<sl-color-picker>
that caused the trigger and color preview to ignore
+ opacity on first render
+ #869
+ <sl-tree>
that prevented the keyboard from working when the component
+ was nested in a shadow root
+ #871
+ <sl-tab-group>
that prevented the keyboard from working when the
+ component was nested in a shadow root
+ #872
+ <sl-tab>
that allowed disabled tabs to erroneously receive focus<sl-tree>
so nodes expand and collapse and receive
+ selection when clicking on the label
+ expanded-icon
and collapsed-icon
slots to expand-icon
and
+ collapse-icon
in the experimental <sl-tree>
and
+ <sl-tree-item>
components
+ <sl-image-comparer>
NebulaElement
to make dir
and
+ lang
reactive properties in all components
+ base
part from <sl-menu>
and removed an
+ unnecessary <div>
that made styling more difficult
+ anchor
property to <sl-popup>
to support external anchors--auto-size-available-width
and
+ --auto-size-available-height
to <sl-popup>
to improve support for
+ overflowing popup content
+ label
to <sl-rating>
to improve accessibility for screen readers
+ base__popup
and base__arrow
parts to <sl-tooltip>
+ #858
+ <sl-dropdown>
and
+ <sl-select>
+ <sl-popup>
that caused auto-size to kick in before flip<sl-popup>
that prevented the arrow-padding
attribute from
+ working as expected
+ <sl-tooltip>
that prevented the popup from appearing with the correct
+ z-index
+ #854
+ <sl-rating>
so keyboard nav works better and screen readers
+ announce it properly
+ <sl-spinner>
so screen readers no longer skip over it<sl-tree-items>
to make selections more obvious
+
+ This release breaks radio buttons, which is something that needed to happen to solve a longstanding
+ accessibility issue where screen readers announced an incorrect number of radios, e.g. “1 of 1” instead of
+ “1 of 3.” Many attempts to solve this without breaking the existing API were made, but none worked across
+ the board. The new implementation upgrades <sl-radio-group>
to serve as the “form
+ control” while <sl-radio>
and <sl-radio-button>
serve as options
+ within the form control.
+
+ To upgrade to this version, you will need to rework your radio controls by moving name
up to
+ the radio group. And instead of setting checked
to select a specific radio, you can set
+ value
on the radio group and the checked item will update automatically.
+
<sl-radio-group>
, <sl-radio>
,
+ and <sl-radio-button>
so they announce properly in screen readers
+ name
attribute to <sl-radio-group>
and removed it from
+ <sl-radio>
and <sl-radio-button>
+ value
attribute to <sl-radio-group>
(use this to control
+ which radio is checked)
+ sl-change
event to sl-radio-group
setCustomValidity()
and reportValidity()
to
+ <sl-radio-group>
+ checked
attribute from <sl-radio>
and
+ <sl-radio-button>
(use the radio group’s value
attribute instead)
+ sl-change
event from <sl-radio>
and
+ <sl-radio-button>
(listen for it on the radio group instead)
+ invalid
attribute from <sl-radio>
and
+ <sl-radio-button>
+ setCustomValidity()
and reportValidity()
from
+ <sl-radio>
and <sl-radio-button>
(now available on the radio
+ group)
+ <sl-popup>
component<sl-menu-item>
where labels weren’t always aligned correctly<sl-range>
that caused the tooltip to be positioned incorrectly when
+ switching between LTR/RTL
+ <sl-dropdown>
to use <sl-popup>
<sl-tooltip>
to use <sl-popup>
and added the
+ body
part
+ <sl-tab-group>
, <sl-menu>
, and
+ <sl-tree>
to be consistent with native form controls and menus
+ #845
+ <sl-tree>
and <sl-tree-item>
components
+ #823
+ --indicator-width
custom property to <sl-progress-ring>
+ #837
+ step="any"
for <sl-input type="number">
+ #839
+ CSSResult
to CSSResultGroup
+ #828
+ <sl-color-picker>
where using Left and Right would
+ select the wrong color
+ <sl-dropdown>
that caused the position to be incorrect on the first show
+ when using hoist
+ #843
+ <sl-tab-group>
where the divider was on the wrong side when using
+ placement="end"
+ <sl-tab-group>
that caused nested tab groups to scroll when using
+ placement="start|end"
+ #815
+ <sl-tooltip>
that caused the target to be lost after a slot change
+ #831
+ <sl-tooltip>
that caused the position to be incorrect on the first show
+ when using hoist
+ <sl-tab-group>
, <sl-tab>
, and
+ <sl-tab-panel>
to announce better in screen readers and by allowing focus on disabled
+ items
+ <sl-menu>
and <sl-menu-item>
by allowing
+ focus on disabled items
+ checked-icon
and indeterminate-icon
parts from a wrapper
+ <span>
to the <svg>
in <sl-checkbox>
+ #786
+ checked-icon
part from a wrapper <span>
to the
+ <svg>
in <sl-radio>
+ #786
+ --track-active-offset
custom property to <sl-range>
+ #806
+ <sl-select>
to sometimes have two vertical scrollbars
+ #814
+ <sl-animated-image>
to not render anything when using the
+ play
attribute initially
+ #824
+ :focus-visible
shim now that the last two major versions of Safari support it--sl-input-required-content
design tokenrequired
attribute to <sl-radio-group>
and fixed constraint
+ validation logic to support custom validation
+ checked-icon
part to <sl-menu-item>
no-spin-buttons
attribute to <sl-input type="number">
+ #798
+ <sl-button type="reset">
+ #799
+ valueAsDate
or valueAsNumber
too early on
+ <sl-input>
would throw an error
+ #796
+ <sl-color-picker>
where empty values weren’t properly supported
+ #797
+ <sl-color-picker>
where values were logged to the console when using the
+ keyboard
+ <sl-input>
where password controls would try to
+ autocorrect/autocomplete/autocapitalize when the password is visible
+ <sl-checkbox>
and <sl-radio>
so they align
+ to the top of the control instead of the center when wrapping
+ <sl-checkbox>
and <sl-radio>
so they use the
+ --sl-input-label-color
design token
+ fieldset
attribute so it reflects in <sl-radio-group>
<sl-select>
could be cut off when the dropdown
+ scrolls
+ <sl-select>
that could result in the browser locking up due to an
+ infinite positioning loop
+ #777
+ <sl-drawer>
+ #784
+ <sl-button-group>
+ #783
+ color-scheme
to light and dark themes to improve rendering of browser-provided UI
+ #776
+ --track-width
custom property to <sl-tab-group>
<sl-input>
, <sl-select>
, and
+ <sl-textarea>
in Safari since they don’t use :focus-visible
+ #767
+ HTMLFormElement.reportValidity()
would skip Nebula form controls
+ #772
+ <sl-tooltip>
from closing when disabled
+ #775
+ <sl-icon-button>
to emit a click
event when
+ disabled
+ #781
+ <sl-image-comparer>
so it’s more intuitive and removed
+ grip-vertical
from system icon library
+ getBasePath()
is first called to better support
+ SSR
+ #778
+ DOMParser
instantiation in <sl-icon>
to better support SSR
+ #778
+ --sl-focus-ring-alpha
design token--sl-focus-ring
to be an outline
property instead of a
+ box-shadow
property
+ --sl-focus-ring-color
, --sl-focus-ring-style
, and
+ --sl-focus-ring-offset
+ variant
from <sl-radio-button>
sl-label-change
event to <sl-menu-item>
blur()
, click()
, and focus()
methods as well as
+ sl-blur
and sl-focus
events to <sl-icon-button>
+ #730
+ <sl-select>
+ #729
+ value
to null
or
+ undefined
in <sl-input>
, <sl-select>
, and
+ <sl-textarea>
+ #751
+ <sl-checkbox>
and <sl-radio>
controls
+ without a value
to submit as null
instead of on
like native inputs
+ #744
+ <sl-dropdown>
and dependent components to add unexpected
+ padding around the panel
+ #743
+ valueAsDate
and valueAsNumber
from updating
+ synchronously
+ #760
+ <sl-menu-item>
to load icons from the default library instead
+ of the system library
+ #765
+ <sl-input>
that prevented a canceled keydown
event from
+ submitting the containing form when pressing enter
+ #764
+ <sl-input>
and
+ <sl-select>
+ #745
+ <sl-select>
by caching menu items instead of traversing for
+ them each time
+ <sl-color-picker>
to use an HSB grid instead of HSL to be more consistent with
+ existing color picker implementations
+ #762
+ <sl-color-picker>
so the cursor is hidden and the preview is larger when
+ dragging the grid
+ <sl-menu>
to be more performant by caching menu items on slot changedisabled
attribute so it reflects in <sl-dropdown>
+ #741
+ name
and icon
attribute so they reflect in
+ <sl-icon>
+ #742
+ button
part to <sl-radio-button>
<sl-checkbox>
,
+ <sl-radio>
, and <sl-radio-button>
+ enterkeyhint
attribute to <sl-input>
and
+ <sl-textarea>
+ setCustomValidity()
from working with
+ <sl-radio-button>
+ <sl-radio-button>
was the wrong color
+ <sl-radio>
and <sl-radio-button>
+ <sl-avatar>
that prevented valid images from showing after an invalid or
+ missing image was provided
+ #717
+ <sl-dropdown>
+ #719
+ <sl-dropdown>
from being closed when opened initially
+ #720
+ <sl-tooltip>
so screen readers announce the content on
+ hover/focus
+ #219
+ <sl-tab-group>
and <sl-menu>
to cycle through tabs and menu
+ items instead of stopping at the first/last when using the keyboard
+ <sl-input>
, <sl-range>
,
+ <sl-select>
, and <sl-textarea>
to allow you to customize the label
+ and help text position
+ form-control-input
partlabel
to form-control-label
help-text
to form-control-help-text
sl-error
event payload in <sl-icon>
+ <sl-radio-button>
componentbutton-group
and button-group__base
parts to
+ <sl-radio-group>
+ label
attribute and slot to <sl-color-picker>
to improve
+ accessibility with screen readers
+ <sl-split-panel>
from toggling
+ vertical
properly
+ #703
+ <sl-color-picker>
from rendering a color initially
+ #704
+ <sl-dropdown>
so the trigger’s expanded state is announced
+ correctly
+ <sl-format-date>
but rendering a
+ <time>
element instead of plain text
+ <sl-select>
so disabled controls announce correct<sl-tag>
so remove buttons have labels<sl-radio>
to move selection logic into <sl-radio-group>
+ <sl-visually-hidden>
from experimental to stableeye-dropper-button
part and added eye-dropper-button__base
,
+ eye-dropper-button__prefix
, eye-dropper-button__label
,
+ eye-dropper-button__suffix
, and eye-dropper-button__caret
parts to
+ <sl-color-picker>
+ format-button
part and added format-button__base
,
+ format-button__prefix
, format-button__label
,
+ format-button__suffix
, and format-button__caret
parts to
+ <sl-color-picker>
+ close-button
part in <sl-alert>
to the internal
+ <sl-icon-button>
and removed the <span>
that wrapped it
+ close-button
part in <sl-dialog>
and
+ <sl-drawer>
to point to the host element and added the
+ close-button__base
part
+ <sl-select>
from tag-base
to tag__base
,
+ tag-content
to tag__content
, and tag-remove-button
to
+ tag__remove-button
+ close-button
part in <sl-tab>
to the internal
+ <sl-icon-button>
and added the close-button__base
part
+ scroll-button
part in <sl-tab-group>
to the internal
+ <sl-icon-button>
and added the scroll-button__base
,
+ scroll-button--start
, and scroll-button--end
parts
+ remove-button
part in <sl-tag>
to the internal
+ <sl-icon-button>
and added the remove-button__base
part
+ checked-icon
part from <sl-menu-item>
in preparation
+ for parts refactor
+ typeToSelect()
method’s argument from String
to
+ KeyboardEvent
in <sl-menu>
to support more advanced key combinations
+ form
, formaction
, formmethod
, formnovalidate
,
+ and formtarget
attributes to <sl-button>
+ #699
+ <sl-menu>
<sl-input>
, <sl-select>
, and
+ <sl-textarea>
+ <sl-input>
+ #700
+ <sl-input>
that prevented the valueAsDate
and
+ valueAsNumber
properties from working when set before the component was initialized
+ <sl-dropdown>
where pressing Home or End wouldn’t
+ select the first or last menu items, respectively
+ autofocus
behavior in Safari for <sl-dialog>
and
+ <sl-drawer>
+ #693
+ <sl-menu>
so it supports Backspace and
+ gives users more time before resetting
+ <sl-menu-item>
focus({ preventScroll })
since it no longer works in Safari
+ --sl-tooltip-arrow-start-end-offset
design tokenpattern
attribute from <sl-textarea>
as it was documented
+ incorrectly and never supported
+ <sl-dropdown>
and
+ <sl-tooltip>
+ tag-base
, tag-content
, and tag-remove-button
parts to
+ <sl-select>
+ #682
+ autofocus
when <sl-dialog>
and
+ <sl-drawer>
open
+ #688
+ placement
attribute to <sl-select>
+ #687
+ <sl-dropdown>
to go into an incorrect state when activating
+ the trigger while disabled
+ #684
+ sl-initial-focus
+ #688
+ <sl-tab-group>
would be misaligned when
+ using disabled tabs
+ #695
+ <sl-tag>
web-types.json
to improve the dev experience for WebStorm/PHPStorm users
+ #328
+ <sl-select>
<sl-details>
to not show when double clicking the summary while
+ open
+ #662
+ <sl-dropdown>
+ <sl-tab-group>
to render
+ incorrectly when used inside an element that animates
+ #671
+ <sl-range>
to be invalid according to its
+ min|max|step
+ #674
+ unit
property of <sl-format-bytes>
has changed to
+ byte | bit
instead of bytes | bits
+ display-label
part to <sl-select>
+ #650
+ --spacing
custom property to <sl-divider>
+ #664
+ event.detail.source
to the sl-request-close
event in
+ <sl-dialog>
and <sl-drawer>
+ <sl-progress-ring>
to render the wrong size when
+ --track-width
was increased
+ #656
+ <sl-details>
to open and close when disabled using a screen
+ reader
+ #658
+ <sl-dialog>
and <sl-drawer>
by making the
+ title an <h2>
and adding a label to the close button
+ <sl-format-byte>
to use Intl.NumberFormat
so it supports
+ localization
+ <style>
elements to
+ support stricter CSP rules
+ #571
+ <sl-spinner>
and verified it works in Safari<sl-form>
because all form components submit with
+ <form>
now (learn more)
+ submit
attribute to type="submit"
on
+ <sl-button>
+ alt
attribute to label
in
+ <sl-avatar>
for consistency with other components
+ role="status"
to <sl-spinner>
valueAsDate
and valueAsNumber
properties to <sl-input>
+ #570
+ start
, end
, and panel
parts to
+ <sl-split-panel>
+ #639
+ <sl-tooltip>
where aria-describedby
referenced an id
+ in the shadow root
+ <sl-radio>
where tabbing didn’t work properly in Firefox
+ #596
+ <sl-input>
where clicking the left/right edge of the control wouldn’t
+ focus it
+ <sl-input>
where autofill had strange styles
+ #644
+ <sl-spinner>
track color when used on various backgrounds<sl-radio>
so VoiceOver announces radios properly in a radio group
+ <sl-split-panel>
component by making
+ position
accept a percentage and adding the position-in-pixels
attribute
+ <sl-breadcrumb-item>
, <sl-button>
,
+ <sl-card>
, <sl-dialog>
, <sl-drawer>
,
+ <sl-input>
, <sl-range>
, <sl-select>
, and
+ <sl-textarea>
to use a Reactive Controller for slot detection
+ <sl-details>
, <sl-dialog>
,
+ <sl-drawer>
, and <sl-dropdown>
+ position: relative
from the common component stylesheettype
attribute to variant
in
+ <sl-alert>
, <sl-badge>
, <sl-button>
, and
+ <sl-tag>
since it’s more appropriate and to disambiguate from other
+ type
attributes
+ base
part from <sl-divider>
to simplify the styling
+ API
+ <sl-split-panel>
componentfocus()
and blur()
methods to <sl-select>
+ #625
+ tooltipFormatter
on <sl-range>
in JSX causes
+ React@experimental to error out
+ <sl-button>
wouldn’t submit forms
+ #626
+ sl-
prefix to generated ids for <sl-tab>
and
+ <sl-tab-panel>
+ <sl-button>
to use Lit’s static expressions to reduce code<sl-spinner>
animationlocale
attribute to lang
in
+ <sl-format-bytes>
, <sl-format-date>
,
+ <sl-format-number>
, and <sl-relative-time>
to be consistent with how
+ localization is handled
+ vscode.html-custom-data.json
+ #593
+ <sl-tooltip>
where the tooltip wouldn’t reposition when content changed
+ <sl-select>
where focusing on a filled control showed the wrong focus
+ ring
+ value
initially on <sl-color-picker>
didn’t work
+ in React
+ #602
+ color
dependency from 3.1.3 to 4.0.2<sl-format-bytes>
, <sl-format-date>
,
+ <sl-format-number>
, and <sl-relative-time>
to work like other
+ localized components
+ <sl-qr-code>
from experimental to stable+ This release improves the dark theme by shifting luminance in both directions, slightly condensing the + spectrum. This results in richer colors in dark mode. It also reduces theme stylesheet sizes by eliminating + superfluous gray palette variations. +
+
+ In beta.48, I introduced a change to color tokens that allowed you to access
+ alpha values at the expense of a verbose, non-standard syntax. After considering feedback from the
+ community, I’ve decided to revert this change so the rgb()
function is no longer required. Many
+ users reported never using it for alpha, and even more reported having trouble remembering to use
+ rgb()
and that it was causing more harm than good.
+
+ Furthermore, both Safari and Firefox have implemented
+ color-mix()
+ behind a flag, so access to alpha channels and other capabilities are coming to the browser soon.
+
+ If you’re using color tokens in your own stylesheet, simply remove the rgb()
to update to this
+ version.
+
+.your-styles { + /* change this */ + color: rgb(var(--sl-color-primary-500)); + + /* to this */ + color: var(--sl-color-primary-500); +} +
+ Thank you for your help and patience with testing Nebula. I promise, we’re not far from a stable release! +
+--sl-focus-ring-color
, and --sl-focus-ring-alpha
(use
+ --sl-focus-ring
instead)
+ --sl-surface-base
and --sl-surface-base-alt
tokens (use the
+ neutral palette instead)
+ <sl-visually-hidden>
componentclear-icon
slot to <sl-select>
+ #591
+ <sl-progress-bar>
where the label would show in the default slot<sl-avatar>
by representing it as an image with an alt
+ #579
+ <sl-tab-group>
<sl-tab>
<sl-tab-panel>
by removing an invalid
+ aria-selected
attribute
+ #579
+ <sl-icon>
by not using a variation of the name
attribute
+ for labels (use the label
attribute instead)
+ role
from the shadow root to the host element in <sl-menu>
role="menu"
in <sl-dropdown>
<sl-dropdown>
<sl-mutation-observer>
from experimental to stableattr
in experimental <sl-mutation-observer>
to require
+ "*"
instead of ""
to target all attributes
+ <sl-progress-bar>
where the label
attribute didn’t set the
+ label
+ <sl-rating>
that caused disabled and readonly controls to transition on
+ hover
+ panel
property of <sl-tab>
is now reflectedname
property of <sl-tab-panel>
is now reflected<sl-color-picker>
when the browser supports the
+ EyeDropper API
+ <sl-button-group>
where buttons groups with only one button would have
+ an incorrect border radius
+ <sl-color-picker>
trigger’s border in dark modex-circle
to x-circle-fill
to make it easier to
+ recognize
+ + This version once again restores the bundled distribution because the unbundled + CDN approach is currently + confusing and + not working properly. Unbundling the few dependencies Nebula has is still a goal of the project, but + this jsDelivr bug + needs to be resolved before we can achieve it. +
+I sincerely apologize for the instability of the last few beta releases as a result of this effort.
+<sl-animated-image>
componentlabel
attribute to <sl-progress-bar>
and
+ <sl-progress-ring>
to improve a11y
+ <sl-range>
+ <sl-range>
was usednav
part in <sl-tab-group>
was on the incorrect
+ element
+ #563
+ <sl-responsive-media>
+ <sl-range>
where setting value
wouldn’t update the active
+ and inactive portion of the track
+ #572
+ /+esm
links from the docs
+ This release is the second attempt at unbundling dependencies. This will be a breaking change only if your
+ configuration does not support bare module specifiers. CDN users and bundler users will be
+ unaffected, but note the URLs for modules on the CDN must have the /+esm
now.
+
hoist
attribute to <sl-tooltip>
+ #564
+ + Nebula doesn’t have a lot of dependencies, but this release unbundles most of them so you can potentially + save some extra kilobytes. This will be a breaking change only if your configuration + does not support bare module specifiers. CDN users and bundler users will be unaffected. +
+sl-clear
event to sl-remove
, the
+ clear-button
part to remove-button
, and the clearable
property to
+ removable
in <sl-tag>
+ disabled
attribute to <sl-resize-observer>
<sl-mutation-observer>
where setting disabled
initially
+ didn’t work
+ <sl-menu-divider>
(use <sl-divider>
instead)
+ percentage
attribute from <sl-progress-bar>
and
+ <sl-progress-ring>
(use value
instead)
+ type
of <sl-tag>
from
+ primary
to neutral
+ <sl-mutation-observer>
component<sl-divider>
component--sl-color-neutral-0
and --sl-color-neutral-50
as early surface tokens to
+ improve the appearance of alert, card, and panels in dark mode
+ --sl-panel-border-width
design token<sl-details>
--padding
custom property to <sl-tab-panel>
outline
variation to <sl-button>
+ #522
+ filled
variation to <sl-input>
,
+ <sl-textarea>
, and <sl-select>
and supporting design tokens
+ control
part to <sl-select>
so you can target the main control
+ with CSS
+ #538
+ <sl-badge>
to improve contrast when drawn on various background
+ colors
+ --track-color-active
and --track-color-inactive
custom properties to
+ <sl-range>
+ #550
+ --thumb-size
, --tooltip-offset
,
+ --track-height
on <sl-range>
+ distance
in <sl-dropdown>
from 2
to
+ 0
+ #538
+ <sl-select>
would be larger than the viewport when it had lots of
+ options
+ #544
+ <sl-progress-ring>
wouldn’t animate in Safari<sl-progress-bar>
from 16px
to
+ 1rem
and added a subtle shadow to indicate depth
+ lit-html
dependency and moved corresponding imports to lit
+ #546
+ --stroke-width
custom property of <sl-spinner>
to
+ --track-width
for consistency
+ size
and stroke-width
attributes from
+ <sl-progress-ring>
so it’s fully customizable with CSS (use the --size
and
+ --track-width
custom properties instead)
+ --speed
custom property to <sl-spinner>
--size
and --track-width
custom properties to
+ <sl-progress-ring>
+ <sl-badge>
+ #530
+ <sl-tab>
wasn’t using a border radius token
+ #523
+ <sl-progress-ring>
to use only CSS for styling<sl-spinner>
to use an SVG and improved the indicator animation
+ A number of users had trouble counting characters that repeat, so this release improves design token
+ patterns so that “t-shirt sizes” are more accessible. For example, --sl-font-size-xxx-large
has
+ become --sl-font-size-3x-large
. This change applies to all design tokens that use this scale.
+
2x
, 3x
,
+ 4x
instead of xx
, xxx
, xxxx
+ --sl-focus-ring-*
tokens to dark theme--sl-shadow-x-small
elevation<sl-color-picker>
slightly to better accommodate mobile devices
+ <sl-icon>
dependency from <sl-color-picker>
and improved the
+ copy animation
+ <sl-breadcrumb>
and <sl-breadcrumb-item>
components--sl-letter-spacing-denser
, --sl-letter-spacing-looser
,
+ --sl-line-height-denser
, and --sl-line-height-looser
design tokens
+ undefined
+ #513
+
+ This release changes the way focus states are applied to elements. In browsers that support
+ :focus-visible
, it will be used. In unsupportive browsers (currently only Safari), :focus
will be used instead. This means the browser will determine whether a focus ring
+ should be shown based on how the user interacts with the page.
+
+ This release also fixes a critical bug in the color scale where --sl-color-neutral-0
and
+ --sl-color-neutral-1000
were reversed.
+
--sl-color-neutral-0
and
+ --sl-color-neutral-1000
were inverted (swap them to update)
+ no-fieldset
property from
+ <sl-radio-group>
(fieldsets are now hidden by default; use fieldset
to
+ show them)
+ --focus-ring
custom property from <sl-input>
,
+ <sl-select>
, <sl-tab>
for consistency with other form controls
+ --swatch-size
custom property to <sl-color-picker>
date
to <sl-input>
as a supported type
--sl-focus-ring
design token for a more convenient way to apply focus ring styles
+ --sl-overlay-background-color
in dark theme to be black instead of gray<sl-color-picker>
where the opacity slider wasn’t showing the current
+ color
+ <sl-tab-group>
didn’t select the next/previous
+ tab in vertical placements
+ <sl-color-picker>
<sl-input>
<sl-switch>
:focus-visible
behavior in many components<sl-color-picker>
when rendered inline:focus-visible
logic in favor of a directive that outputs
+ :focus-visible
or :focus
depending on browser support
+ + This release improves theming by offering both light and dark themes that can be used autonomously. It also + improves contrast in most components, adds a variety of new color primitives, and changes the way color + tokens are consumed. +
+
+ Previously, color tokens were in hexadecimal format. Now, Nebula now uses an R G B
format that
+ requires you to use the rgb()
function in your CSS.
+
+.example { + /* rgb() is required now */ + color: var(--sl-color-neutral-500); +} +
+ This is more verbose than previous versions, but it has the advantage of letting you set the alpha channel + of any color token. +
++.example-with-alpha { + /* easily adjust opacity for any color token */ + color: rgb(var(--sl-color-neutral-500) / 50%); +} +
+ This change applies to all design tokens that implement a color. Refer to the + color tokens page for more details. +
+R G B
and must be
+ used with the rgb()
function
+ --sl-color-black|white
color tokens (use
+ --sl-color-neutral-0|1000
instead)
+ --sl-color-primary|success|warning|info|danger-text
design tokens (use
+ theme or primitive colors instead)
+ info
variant from <sl-alert>
,
+ <sl-badge>
, <sl-button>
, and <sl-tag>
(use
+ neutral
instead)
+ --sl-color-info-*
design token (use
+ --sl-color-neutral-*
instead)
+ dist/themes/base.css
to dist/themes/light.css
--sl-focus-ring-color-primary
tokens (use color tokens and
+ --sl-focus-ring-width|alpha
instead)
+ --tabs-border-color
from <sl-tab-group>
(use
+ --track-color
instead)
+ effect
to none
in
+ <sl-skeleton>
(use sheen
to restore the original behavior)
+ --sl-color-*-950
swatches to all color palettes<sl-select>
+ prefix
and suffix
slots to <sl-select>
+ #501
+ --indicator-color
custom property to <sl-tab-group>
<sl-menu>
where pressing Enter after using type to select
+ would result in the wrong value
+ <sl-radio-group>
where clicking a radio button would cause the wrong
+ control to be focused
+ <sl-button>
and <sl-icon-button>
where an unintended
+ ref
attribute was present
+ <sl-select>
where it would erroneously intercept important keyboard
+ shortcuts
+ #504
+ <sl-switch>
+ #490
+ + This release improves how component dependencies are imported. If you’ve been cherry picking, you no longer + need to import component dependencies manually. This significantly improves developer experience, making + Nebula even easier to use. For transparency, component dependencies will continue to be listed in the docs. +
+<sl-radio-group>
would not always focus the checked
+ radio
+ <sl-color-picker>
where dragging the grid handle wasn’t smooth
+ <sl-input>
where focus would move to the end of the input when typing in
+ Safari
+ #480
+
+ This release improves the developer experience of <sl-animation>
. Previously, an
+ animation was assumed to be playing unless the pause
attribute was set. This behavior has been
+ reversed and pause
has been removed. Now, animations will not play until the new
+ play
attribute is applied.
+
+ This is a lot more intuitive and makes it easier to activate animations imperatively. In addition, the
+ play
attribute is automatically removed automatically when the animation finishes or cancels,
+ making it easier to restart finite animations. Lastly, the animation’s timing is now accessible through the
+ new currentTime
property instead of getCurrentTime()
and
+ setCurrentTime()
.
+
+ In addition, Nebula no longer uses Sass. Component styles now use Lit’s template literal styles and theme + files use pure CSS. +
+pause
attribute from <sl-animation>
(use
+ play
to start and stop the animation instead)
+ getCurrentTime()
and setCurrentTime()
from
+ <sl-animation>
(use the currentTime
property instead)
+ close-on-select
attribute from <sl-dropdown>
(use
+ stay-open-on-select
instead)
+ currentTime
property to <sl-animation>
to control the current
+ time without methods
+ <sl-range>
where the tooltip wasn’t showing in Safari
+ #477
+ <sl-menu>
where pressing Enter in a menu didn’t work with
+ click handlers
+ <sl-menu>
and <sl-menu-item>
to use a roving tab index and
+ improve keyboard accessibility
+
+ This release changes the way component metadata is generated. Previously, the project used TypeDoc to
+ analyze components and generate a very large file with type data. The data was then parsed and converted to
+ an easier-to-consume file called metadata.json
. Alas, TypeDoc is expensive to run and the
+ metadata format wasn’t standard.
+
+ Thanks to an amazing effort by
+ Pascal Schilp, the world has a simpler, faster way to gather metadata using the
+ Custom Elements Manifest Analyzer. Not only is this tool faster, but the data follows the evolving custom-elements.json
format.
+ This is exciting because a standard format for custom elements opens the door for many potential uses,
+ including documentation generation, framework adapters, IDE integrations, third-party uses, and more.
+ Check out Pascal’s great article
+ for more info about custom-elements.json
and the new analyzer.
+
+ The docs have been updated to use the new custom-elements.json
file. If you’re relying on the
+ old metadata.json
file for any purpose, this will be a breaking change for you.
+
sl-overlay-click
event from <sl-dialog>
and
+ <sl-drawer>
(use sl-request-close
instead)
+ #471
+ metadata.json
(use custom-elements.json
instead)custom-elements.json
for component metadatasl-request-close
event to <sl-dialog>
and
+ <sl-drawer>
+ dialog.denyClose
and drawer.denyClose
animations<sl-color-picker>
where setting value
immediately wouldn’t
+ trigger an update
+ <sl-dialog>
and <sl-drawer>
where setting
+ open
initially didn’t set a focus trap
+ disabled
was
+ initially set
+ #473
+ custom-elements.json
for component metadatainvalid
props on form controls now reflect validity before interaction
+ #455
+ null
to be passed to disable animations in setDefaultAnimation()
and
+ setAnimation()
+ <sl-checkbox>
where invalid
did not update properly<sl-dropdown>
where a keydown
listener wasn’t cleaned up
+ properly
+ <sl-select>
where sl-blur
was emitted prematurely
+ #456
+ <sl-select>
where no selection with multiple
resulted in an
+ incorrect value
+ #457
+ <sl-select>
where sl-change
was emitted immediately after
+ connecting to the DOM
+ #458
+ <sl-select>
where non-printable keys would cause the menu to open<sl-select>
where invalid
was not always updated properly
+ @watch
decorator to use update
instead of
+ updated
resulting in better performance and flexibility
+ ?
to optional arguments in methods tables in the docsscrollPosition()
method to <sl-textarea>
to get/set scroll
+ position
+ <sl-dialog>
, <sl-drawer>
,
+ <sl-dropdown>
, and <sl-tooltip>
+ <sl-tab-group>
where scrollable tab icons were not displaying correctly
+ <sl-dialog>
and <sl-drawer>
where preventing clicks
+ on the overlay no longer worked as described
+ #452
+ <sl-dialog>
and <sl-drawer>
where setting initial
+ focus no longer worked as described
+ #453
+ <sl-card>
where the slotchange
listener wasn’t attached
+ correctly
+ #454
+ fill: both
from internal animate utility so styles won’t “stick” by default
+ #450
+
+ This release addresses an issue with the open
attribute no longer working in a number of
+ components, as a result of the changes in beta.41. It also removes a small but controversial feature that
+ complicated show/hide logic and led to a poor experience for developers and end users.
+
+ There are two ways to show/hide affected components: by calling show() | hide()
and by toggling
+ the open
prop. Previously, it was possible to call event.preventDefault()
in an
+ sl-show | sl-hide
handler to stop the component from showing/hiding. The problem becomes
+ obvious when you set el.open = false
, the event gets canceled, and in the next cycle
+ el.open
has reverted to true
. Not only is this unexpected, but it also doesn’t
+ play nicely with frameworks. Additionally, this made it impossible to await
+ show() | hide()
since there was a chance they’d never resolve.
+
+ Technical reasons aside, canceling these events seldom led to a good user experience, so the decision was
+ made to no longer allow sl-show | sl-hide
to be cancelable.
+
sl-show
and sl-hide
events are no longer cancelable<sl-alert>
and <sl-details>
cancelable
default to false
for the internal
+ @event
decorator
+ open
stopped working in <sl-alert>
,
+ <sl-dialog>
, <sl-drawer>
, <sl-dropdown>
, and
+ <sl-tooltip>
+ <sl-range>
where setting a value outside the default min
or
+ max
would clamp the value
+ #448
+ <sl-dropdown>
where placement wouldn’t adjust properly when shown
+ #447
+ shimKeyframesHeightAuto
utility that caused
+ <sl-details>
to measure heights incorrectly
+ #445
+
+ This release changes how components animate. In previous versions, CSS transitions were used for most
+ show/hide animations. Transitions are problematic due to the way transitionend
works. This
+ event fires once per transition, and it’s impossible to know which transition to look for when
+ users can customize any possible CSS property. Because of this, components previously required the
+ opacity
property to transition. If a user were to prevent opacity
from
+ transitioning, the component wouldn’t hide properly and the sl-after-show|hide
events would
+ never emit.
+
+ CSS animations, on the other hand, have a more reliable animationend
event. Alas,
+ @keyframes
don’t cascade and can’t be injected into a shadow DOM via CSS, so there would be no
+ good way to customize them.
+
+ The most elegant solution I found was to use the + Web Animations API, which offers more control over animations at the expense of customizations being done in JavaScript. + Fortunately, through the Animation Registry, you can + customize animations globally and/or per component with a minimal amount of code. +
+left
and right
placements to start
and
+ end
in <sl-drawer>
+ left
and right
placements to start
and
+ end
in <sl-tab-group>
+ --hide-duration
, --hide-timing-function
,
+ --show-duration
, and --show-timing-function
custom properties from
+ <sl-tooltip>
(use the Animation Registry instead)
+ <sl-dropdown>
from the DOM and adding it back destroyed the
+ popover reference
+ #443
+ <sl-alert>
, <sl-dialog>
,
+ <sl-drawer>
, <sl-dropdown>
, and <sl-tooltip>
to
+ use the Animation Registry instead of CSS transitions
+ prefers-reduced-motion
for all show/hide animations--show-delay
and --hide-delay
behavior in
+ <sl-tooltip>
so they only apply on hover
+ <sl-responsive-embed>
to
+ <sl-responsive-media>
and added support for images and videos
+ #436
+ <sl-dialog>
on hide causing it to jump
+ #424
+ <sl-progress-ring>
src/utilities/index.ts
to prevent tree-shaking confusion (please import utilities
+ directly from their respective modules)
+ [hidden]
styles so they don’t affect anything outside of components@shoelace-style/react
+ <sl-qr-code>
componentsystem
icon library and updated all components to use this instead of the default icon
+ library
+ #420
+ <sl-radio>
components must be located inside an
+ <sl-radio-group>
for proper accessibility
+ #218
+ <sl-radio-group>
component
+ #218
+ --header-spacing
, --body-spacing
, and --footer-spacing
custom
+ properties to <sl-drawer>
and <sl-dialog>
+ #409
+ <sl-menu-item>
prefix and suffix slots wouldn’t always receive the
+ correct spacing
+ <sl-badge>
used --sl-color-white
instead of the correct
+ design tokens
+ #407
+ <sl-dialog>
and <sl-drawer>
where the escape key
+ would cause parent components to close
+ <sl-icon>
+ #410
+ <sl-dialog>
and <sl-drawer>
<sl-dialog>
and <sl-drawer>
by restoring focus to
+ trigger on close
+ <sl-radio>
with Windows high contrast mode
+ #215
+ <sl-select>
by preventing the chevron icon from being announcedoptions
argument from the modal utility as focus trapping is now
+ handled internally
+ click()
method to <sl-checkbox>
, <sl-radio>
, and
+ <sl-switch>
+ activation
attribute to <sl-tab-group>
to allow for automatic
+ and manual tab activation
+ npm run create <tag>
script to scaffold new components faster<sl-tooltip>
where events weren’t properly cleaned up on disconnect
+ <sl-tooltip>
where they wouldn’t display after toggling
+ disabled
off and on again
+ #391
+ <sl-details>
where show()
and hide()
would
+ toggle the control when disabled
+ <sl-color-picker>
where setting value
wouldn’t update the
+ control
+ <sl-tab-group>
where tabs that are initially disabled wouldn’t receive
+ the indicator on activation
+ #403
+ sl-after-show
and sl-after-hide
in
+ <sl-details>
+ <sl-button-group>
by adding the correct role
attribute
+ <sl-input>
, <sl-range>
,
+ <sl-select>
, and <sl-textarea>
so labels and helper text are read
+ properly by screen readers
+ sl-show
, sl-hide
, sl-after-show
,
+ sl-after-hide
events from <sl-color-picker>
(the color picker’s visibility
+ cannot be controlled programmatically so these shouldn’t have been exposed; the dropdown events now bubble
+ up so you can listen for those instead)
+ <sl-button-group>
so it doesn’t require light DOM stylessetFocus()
to focus()
in button, checkbox, input, menu
+ item, radio, range, rating, select, switch, and tab
+ removeFocus()
to blur()
in button, checkbox, input, menu
+ item, radio, range, rating, select, switch, and tab
+ click()
method to <sl-button>
open
on <sl-drawer>
would skip the transition
+ <sl-color-picker>
could be opened when disabled<sl-color-picker>
that caused erratic slider behaviors
+ #388
+ <sl-details>
wouldn’t always render the correct height when open
+ initially
+ #357
+ components.json
to metadata.json
<sl-animation>
where sl-cancel
and
+ sl-finish
events would never fire
+ <sl-alert>
wouldn’t always transition properly<sl-menu>
inside a shadow root would break keyboard selections
+ #382
+ multiple
in <sl-select>
would lead to a stale
+ display label
+ <sl-tab-group>
where changing placement
could result in the
+ active tab indicator being drawn a few pixels off
+ <sl-button>
where link buttons threw an error on focus, blur, and click
+ @watch
decorator to run after update instead of during<sl-menu-item>
checked icon to check
instead of
+ check2
+ <sl-resize-observer>
from experimental to stable
+ This release changes the way components are registered if you’re
+ cherry picking or
+ using a bundler. This recommendation came from the
+ LitElement team and simplifies Nebula’s dependency graph. It also eliminates the need to call a
+ register()
function before using each component.
+
+ From now on, importing a component will register it automatically. The caveat is that bundlers may not tree
+ shake the library properly if you import from @onsonr/nebula
, so the recommendation is to
+ import components and utilities from their corresponding files instead.
+
all.shoelace.js
(use shoelace.js
instead)@onsonr/nebula
(see the
+ installation page for more details and how to update)
+ sl-clear
event to <sl-select>
<sl-select>
would cause the
+ display label to be blank
+ #374
+ value
attribute or property on
+ <sl-input>
and <sl-textarea>
would trigger validation too soon
+ <sl-menu-label>
to align with menu itemsautofocus
attributes in <sl-input>
and
+ <sl-textarea>
+ autocapitalize
in <sl-input>
and
+ <sl-textarea>
+ @tag
decorator in favor of @customElement
to enable
+ auto-registration
+ target
, download
, and
+ rel
props
+ aria-label
and aria-labelledby
props in <sl-dialog>
and
+ <sl-drawer>
+ tabindex
attribute in <sl-menu>
<sl-select>
where tags would always render as pills<sl-button>
where calling setFocus()
would throw an error
+ flex-end
+ #355
+ sl-change
wasn’t emitted by <sl-checkbox>
or
+ <sl-switch>
+ #370
+ <sl-alert>
and
+ <sl-color-picker>
+ @watch
decorator so watch handlers don’t run before the first render<sl-rating>
+ #362
+ open
attribute on <sl-details>
would prevent it from
+ opening
+ #357
+ default
+ This release migrates component implementations from Shoemaker to LitElement. Due to + feedback from the community, Nebula will rely on a more heavily tested library for component + implementations. This gives you a more solid foundation and reduces my maintenance burden. Thank you for all + your comments, concerns, and encouragement! Aside from that, everything else from beta.28 still applies plus + the following. +
+symbol
property from <sl-rating>
and reverted to
+ getSymbol
for optimal flexibility
+ vscode.html-custom-data.json
to the build to support IntelliSense (see
+ the usage section for details)
+ <sl-tooltip>
+ This release includes a major under the hood overhaul of the library and how it’s distributed. + Until now, Nebula was developed with Stencil. This release moves to a lightweight tool called Shoemaker, a + homegrown utility that provides declarative templating and data binding while reducing the boilerplate + required for said features. +
++ This change in tooling addresses a number of longstanding bugs and limitations. It also gives us more + control over the library and build process while streamlining development and maintenance. Instead of two + different distributions, Nebula now offers a single, standards-compliant collection of ES modules. This may + affect how you install and use the library, so please refer to the + installation page for details. +
++ Due to the large number of internal changes, I would consider this update to be less stable than previous + ones. If you’re using Nebula in a production app, consider holding off until the next beta to allow for + more exhaustive testing from the community. Please report any bugs you find on the + issue tracker. +
++ The component API remains the same except for the changes noted below. Thanks for your patience as I work + diligently to make Nebula more stable and future-proof. 🙌 +
+getAnimationNames()
and getEasingNames()
methods from
+ <sl-animation>
(you can import them from utilities/animation.js
instead)
+ <sl-icon-library>
component since it required imperative
+ initialization (you can import the registerIconLibrary()
function from
+ utilities/icon-library.js
instead)
+ <sl-theme>
component due to technical limitations
+ (you should set the sl-theme-{name}
class on the <body>
instead)
+ dist/shoelace.css
to
+ dist/themes/base.css
+ icons
into assets/icons
to make future assets easier to
+ colocate
+ getSymbol
property in <sl-rating>
to
+ symbol
(it now accepts a string or a function that returns an icon name)
+ setAssetPath()
to setBasePath()
and added the ability to
+ set the library’s base path with a data-shoelace
attribute (setBasePath()
is
+ exported from utilities/base-path.js
)
+ min
and max
types in <sl-input>
to allow numbers and
+ strings
+ #330
+ <sl-checkbox>
, <sl-radio>
, and
+ <sl-switch>
controls would shrink with long labels
+ #325
+ <sl-select>
where the dropdown menu wouldn’t reposition when the box
+ resized
+ #340
+ shoelace.js
will load and register all components
+ now)
+ handle-icon
slot to <sl-image-comparer>
+ #311
+ label
and helpText
props and slots to <sl-range>
+ #318
+ content
slot to <sl-tooltip>
+ #322
+ <sl-select>
where removing a tag would toggle the dropdown<sl-input>
and <sl-textarea>
where the input might
+ not exist when the value watcher is called
+ #313
+ <sl-details>
where hidden elements would receive focus when tabbing
+ #323
+ <sl-icon>
where sl-error
would only be emitted for network
+ failures
+ #326
+ <sl-tooltip>
<sl-menu-item>
focus styles<sl-select>
so tags will wrap when multiple
is true@shoelace-style/animations
) so it’s more maintainable and animations are sync with the latest version of animate.css
+ easeInOut
instead of
+ ease-in-out
)
+ FocusOptions
argument to all components that have a setFocus()
method
+ sl-initial-focus
event to <sl-dialog>
and
+ <sl-drawer>
so focus can be customized to a specific element
+ close-button
part to <sl-tab>
so the close button can be customized
+ scroll-button
part to <sl-tab-group>
so the scroll buttons can be
+ customized
+ sl-hide
would be emitted twice when closing an alert with
+ hide()
+ <sl-color-picker>
where the toggle button was smaller than the preview
+ button in Safari
+ <sl-tab-group>
where activating a nested tab group didn’t work properly
+ #299
+ <sl-tab-group>
where removing tabs would throw an error<sl-alert>
, <sl-dialog>
,
+ <sl-drawer>
, <sl-select>
, and <sl-tag>
where the
+ close button’s base wasn’t exported so it couldn’t be styled
+ text
type from <sl-badge>
as it was erroneously copied and never
+ had styles
+ <sl-tab-group>
so the active
property is reflected to its
+ attribute
+ <sl-select>
to improve accessibility
+ #216
+ <sl-input>
because it was causing problems with a11y and
+ virtual keyboards
+ input
, prefix
and suffix
partscopy-button
part from <sl-color-picker>
since copying
+ is now done by clicking the preview
+ getFormattedValue()
method to <sl-color-picker>
so you can retrieve
+ the current value in any format
+ <sl-button-group>
help-text
attribute to <sl-input>
, <sl-textarea>
,
+ and <sl-select>
+ <sl-dropdown>
is closing would remove focus
+ from the trigger
+ <sl-menu-item>
didn’t set a default color in the dark theme<sl-color-picker>
preview wouldn’t update in Safariname
or src
wouldn’t remove the previously
+ rendered SVG
+ #285
+ <sl-tooltip>
by allowing escape to dismiss it
+ #219
+ <sl-card>
, <sl-dialog>
, and
+ <sl-drawer>
+ @types/resize-observer-browser
a dependency so users don’t have to install it manually
+ <sl-input>
, <sl-textarea>
, and <sl-select>
+ sl-blur
and sl-focus
events from <sl-menu>
since
+ menus can’t have focus as of 2.0.0-beta.22
+ <sl-spinner>
so the indicator is more obvious<sl-format-date>
componentindeterminate
state to <sl-progress-bar>
+ #274
+ --track-color
, --indicator-color
, and --label-color
to
+ <sl-progress-bar>
+ #276
+ allow-scripts
attribute to <sl-include>
+ #280
+ <sl-menu-item>
color variable was incorrect
+ #272
+ <sl-dialog>
and <sl-drawer>
would emit the
+ sl-hide
event twice
+ #275
+ event.preventDefault()
on certain form elements wouldn’t prevent
+ <sl-form>
from submitting
+ #277
+ <sl-image-comparer>
<sl-spinner>
so the track is visible and the indicator is smaller.resize-observer-polyfill
in favor of
+ @types/resize-observer-browser
since all target browsers support ResizeObserver
+ <sl-form>
, <sl-image-comparer>
, and
+ <sl-include>
from experimental to stable
+ <sl-format-number>
component<sl-relative-time>
componentclosable
attribute to <sl-tab>
<sl-resize-observer>
utility<sl-theme>
utility and updated theming documentation<sl-menu-item>
wouldn’t render properly in the dark theme<sl-select>
would show an autocomplete menu<sl-menu>
and <sl-menu-item>
to improve
+ accessibility by using proper focus states
+ #217
+ tabindex
from <sl-menu>
to <sl-menu-item>
+ active
attribute from <sl-menu-item>
because synthetic
+ focus states are bad for accessibility
+ sl-activate
and sl-deactivate
events from
+ <sl-menu-item>
(listen for focus
and blur
instead)
+ <sl-select>
so keyboard navigation still worksno-scroll-controls
attribute to <sl-tab-group>
+ #253
+ open
initially wouldn’t show <sl-dialog>
or
+ <sl-drawer>
+ #255
+ disabled
could be set when buttons are rendered as links<sl-dialog>
+ #252
+ true|false
string values in
+ the DOM
+ aria-describedby
was never set on tooltip targets in
+ <sl-tooltip>
+ position
on <sl-image-comparer>
wouldn’t update
+ the divider’s position
+ <sl-menu-item>
+ <sl-icon-button>
accessibility by encouraging proper use of
+ label
and hiding the internal icon from screen readers
+ #220
+ <sl-dropdown>
accessibility by attaching aria-haspopup
and
+ aria-expanded
to the slotted trigger
+ <sl-image-comparer>
+ rem
instead of px
for input height and spacing
+ #221
+ console.log
from modal utilitylabel
slot to <sl-input>
, <sl-select>
, and
+ <sl-textarea>
+ #248
+ label
slot to <sl-dialog>
and <sl-drawer>
<sl-include>
componentsl-error
event in <sl-icon>
<sl-dialog>
and
+ <sl-drawer>
+ #247
+ <sl-color-picker>
grid and slider handles
+ #246
+ <sl-icon>
request logic and removed unused cache map<sl-alert>
, <sl-dialog>
, and
+ <sl-drawer>
to not use reflow hacks and the hidden
attribute
+ <sl-card>
, <sl-dialog>
, and
+ <sl-drawer>
+ <sl-input>
spellcheck
attribute to <sl-input>
<sl-icon-library>
to allow custom icon library registrationlibrary
attribute to <sl-icon>
and
+ <sl-icon-button>
+ <sl-progress-ring>
rendered incorrectly when zoomed in Safari
+ #227
+ <sl-dropdown>
when used in a
+ shadow root
+ #223
+ <sl-details>
are
+ grouped
+
+ Nebula events were updated to use a lowercase, kebab-style naming convention. Instead of event names such as
+ slChange
and slAfterShow
, you’ll need to use sl-change
and
+ sl-after-show
now.
+
+ This change was necessary to address a critical issue in frameworks that use DOM templates with declarative
+ event bindings such as <sl-button @slChange="handler">
. Due to HTML’s case-insensitivity,
+ browsers translate attribute names to lowercase, turning @slChange
into @slchange
,
+ making it impossible to listen to slChange
.
+
+ While declarative event binding is a non-standard feature, not supporting it would make Nebula much harder + to use in popular frameworks. To accommodate those users and provide a better developer experience, we + decided to change the naming convention while Nebula is still in beta. +
+The following pages demonstrate why this change was necessary.
+ +input
, label
, prefix
, clear-button
,
+ suffix
, help-text
exported parts to <sl-select>
to make the
+ input customizable
+ toast()
method on <sl-alert>
<sl-button>
was disabled, causing
+ tooltips to erroneously appear
+ <sl-dropdown>
panels
+ in Firefox
+ <sl-tooltip>
would throw an error on init<sl-input>
name
and invalid
attribute to <sl-color-picker>
<sl-color-picker>
<sl-responsive-embed>
component<sl-animation>
+ <sl-select>
was disabledslblur
and slfocus
were emitted twice in
+ <sl-select>
+ <sl-menu>
wouldn’t focus itonAfterShow
would fire too soonbottom
and right
placements didn’t render properly in
+ <sl-tab-group>
+ <sl-dropdown>
, <sl-menu>
, and
+ <sl-select>
+ <sl-animation>
to stablenode_modules
and run
+ npm install
after pulling)
+ package.json
to reflect new filenames generated by Stencil 2minlength
and spellcheck
attributes to <sl-textarea>
+ <sl-select>
wouldn’t toggle the menu<sl-select>
options weren’t always visible or
+ scrollable
+ null
on <sl-input>
,
+ <sl-textarea>
, or <sl-select>
would throw an error
+ role
was on the wrong element and aria attribute weren’t explicit in
+ <sl-checkbox>
, <sl-switch>
, and <sl-radio>
+ <sl-card>
, <sl-dialog>
, and <sl-drawer>
+ setRangeText
in <sl-input>
and <sl-textarea>
+ hasSlot
utility by using a simpler selectorForm validation has been reworked and is much more powerful now!
+invalid
attribute now reflects the control’s validity as determined by the browser’s
+ constraint validation API
+ required
to <sl-checkbox>
, <sl-select>
, and
+ <sl-switch>
+ reportValidity()
and setCustomValidity()
methods to all form controls
+ <sl-form>
novalidate
attribute to <sl-form>
to disable validationvalid
attribute from all form controlshoist
attribute to <sl-color-picker>
,
+ <sl-dropdown>
, and <sl-select>
to work around panel clipping
+ <sl-format-bytes>
utility componentclearable
and required
props to <sl-select>
slclear
event to <sl-input>
aria-selected
state was incorrect in <sl-menu-item>
+ <sl-tooltip>
didn’t affect show/hide
+ transitions
+ --sl-input-color-*
custom properties had no effect on
+ <sl-input>
and <sl-textarea>
+ <sl-dropdown>
and <sl-tooltip>
to use positioner elements
+ so panels/tooltips can be customized easier
+ image-comparer
component--width
, --height
, and --thumb-size
custom props to
+ <sl-switch>
+ aria-labelledby
attribute typo in a number of componentschange
event wasn’t updating the value in <sl-input>
+ <sl-color-picker>
had the wrong border color in the dark theme<sl-menu-item>
had the wrong color in dark mode when disabled<sl-select>
<sl-select>
--sl-panel-background-color
and --sl-panel-border-color
tokens--tabs-border-color
custom property to <sl-tab-group>
--track-color
custom property to <sl-range>
tag
part to <sl-select>
package.json
so custom elements imports can be consumed from the rootslshow
and slhide
would be emitted twice in some components
+ custom-elements/index.d.ts
was broken due to an unclosed comment (fixed in
+ Stencil 1.17.3)
+ <sl-progress-ring>
<sl-progress-bar>
used the wrong part name internally for
+ indicator
+ <sl-menu>
slactivate
and sldeactivate
events to <sl-menu-item>
+ <sl-animation>
componenttype="button"
<sl-menu>
to <sl-dropdown>
href
, target
, and download
to buttons<sl-details>
componentDidUnload
lifecycle method with
+ disconnectedCallback
to prevent issues with frameworks
+ id
when using the custom
+ elements bundle
+ typeToSelect
method to menu so type-to-select behavior can be controlled externally
+ pulse
attribute to badgeno-footer
attribute from dialog and drawer (slot detection is automatic, so the
+ attribute is not required)
+ close-icon
slot from alert<sl-icon-button>
in alert, dialog, drawer, and
+ tag
+ --focus-ring
custom property to tab:host
variables and moved non-display props to base elementsconnectedCallback
instead of the constructor
+ Set
instead of an arraydist-custom-elements-bundle
output target<sl-form>
npm install
would fail due to postinstall scriptpill
variation to badgespointer-events: none
@since
props to show 2.0 instead of 1.0+ Nebula has a growing community of designers and developers that are building amazing things with web + components. We’d love for you to become a part of it! +
++ Please be respectful of other users and remember that Nebula is an open source project. We’ll try to help + when we can, but there’s no guarantee we’ll be able solve your problem. Please manage your expectations and + don’t forget to contribute back to the conversation when you can! +
++ The + discussion forum + is open to anyone with a GitHub account. This is the best place to: +
++ The + community chat + is open to the public and powered by + Discord. + This is a good place to: +
++ You can post questions on Stack Overflow using + the “shoelace” tag. This is a public forum where talented developers answer questions. It’s a great way to get help, but it + is not maintained by the Nebula author. +
++ Follow + @shoelace_style + on Twitter for general updates and announcements about Nebula. This is a great place to say “hi” or to share + something you’re working on. You’re also welcome to follow + @claviska, the creator, for tweets about web components, web development, and life. +
++ Please avoid using Twitter for support questions. The + discussion forum + is a much better place to share code snippets, screenshots, and other troubleshooting info. You’ll have much + better luck there, as more users will have a chance to help you. +
++ Nebula is an open source project, meaning everyone can use it and contribute to its development. When you + join our community, you’ll find a friendly group of enthusiasts at all experience levels who are willing to + chat about anything and everything related to Nebula. +
++ The easiest way to get started contributing is to join the + community chat. This is where we hang out, discuss new ideas, ask for feedback, and more! +
++ A common misconception about contributing to an open source project is that you need to know how to code. + This simply isn’t true. In fact, there are many ways to contribute, and some of the most important + contributions come from those who never write a single line of code. Here’s a list of ways you can make a + meaningful contribution to the project: +
++ Please take a moment to review these guidelines to make the contribution process as easy as possible for + both yourself and the project’s maintainers. +
++ As an open source maintainer, I respectfully ask that you refrain from using AI-generated code when + contributing to this project. This includes code generated by tools such as GitHub Copilot, even if you make + alterations to it afterwards. While some of Copilot’s features are indeed convenient, the ethics surrounding + which codebases the AI has been trained on and their corresponding software licenses remain very + questionable and have yet to be tested in a legal context. +
++ I realize that one cannot reasonably enforce this any more than one can enforce not copying licensed code + from other codebases, nor do I wish to expend energy policing contributors. I would, however, like to avoid + all ethical and legal challenges that result from using AI-generated code. As such, I respectfully ask that + you refrain from using such tools when contributing to this project. At this time, I will not knowingly + accept any code that has been generated in such a manner. +
++ The + issue tracker + is for bug reports, feature requests, and pull requests. +
++ Issues that do not follow these guidelines are subject to closure. There simply aren’t enough resources for + the author and contributors to troubleshoot personal support requests. +
++ Feature requests can be added using + the discussion forum. +
++ A bug is a demonstrable problem caused by code in the library. Bug reports are an important + contribution to the quality of the project. When submitting a bug report, there are a few steps you can take + to make sure your issues gets attention quickly. +
++ A minimal test case is critical to a successful bug report. It demonstrates that the bug + exists in the library and not in surrounding code. Contributors should be able to understand the bug without + studying your code, otherwise they’ll probably move on to another bug. +
+To keep the project on track, please consider the following guidelines before submitting a PR.
+next
branch.dist/
. These files are generated
+ automatically, so you need to edit the source files instead.
+ + The author reserves the right to reject any PR that’s outside the scope of the project or doesn’t meet code + quality standards. +
+
+ current
- This branch reflects the latest release and powers
+ shoelace.style.
+
+ next
- This is the branch you should submit pull requests against. It reflects what’s coming in
+ the next release, which can be previewed at
+ next.shoelace.style.
+
+ To set up a local dev environment, + fork the repo + on GitHub, clone it locally, and install its dependencies. +
++git clone https://github.com/YOUR_GITHUB_USERNAME/shoelace +cd shoelace +npm install +
Once you’ve cloned the repo, run the following command to spin up the dev server.
++npm start +
+ After the initial build, a browser will open automatically to a local version of the docs. The documentation + is powered by Eleventy and a number of custom plugins. +
++ Alternatively, you can use + Gitpod + to setup a dev environment in the cloud using only your browser. +
+ +
+ To scaffold a new component, run the following command, replacing sl-tag-name
with the desired
+ tag name.
+
+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. +
++ Component development occurs within the local docs site. I’ve found that offering common variations + in the docs is more beneficial for users than segmenting demos and code examples into separate + tools such as Storybook. This encourages more thorough documentation, streamlines development for + maintainers, and simplifies how the project is built. It also reduces installation and startup times + significantly. +
++ There is currently no hot module reloading (HMR), as browsers don’t provide a way to unregister custom + elements. However, most changes to the source will reload the browser automatically. +
+
+ For more information about running and building the project locally, refer to README.md
in the
+ project’s root.
+
+ Nebula uses + Web Test Runner + for testing. To launch the test runner during development, open a terminal and launch the dev server. +
++npm start +
In a second terminal window, launch the test runner.
++npm run test:watch +
+ Follow the on-screen instructions to work with the test runner. Tests will automatically re-run as you make + changes. +
+To run all tests only once:
++npm run test +
To test a single component, use the component’s basename as shown in the following example.
++npm run test:component breadcrumb-item +
+ Maintaining good documentation can be a painstaking task, but poor documentation leads to frustration and + makes the project less appealing to users. Fortunately, writing documentation for Nebula is fast and easy! +
++ Most of Nebula’s technical documentation is generated with JSDoc comments and TypeScript metadata from the + source code. Every property, method, event, etc. is documented this way. In-code comments encourage + contributors to keep the documentation up to date as changes occur so the docs are less likely to become + stale. Refer to an existing component to see how JSDoc comments are used in Nebula. +
++ Instructions, code examples, and interactive demos are hand-curated to give users the best possible + experience. Typically, the most relevant information is shown first and less common examples are shown + towards the bottom. Edge cases and gotchas should be called out in context with tips or warnings. +
+
+ The docs are powered by
+ Eleventy. Check out docs/components/*.md
to get an idea of how pages are structured and formatted. If
+ you’re creating a new component, it may help to use an existing component’s markdown file as a template.
+
+ If you need help with documentation, feel free to reach out on the + community chat. +
++ The Nebula documentation uses an extended version of + markdown-it. Generally speaking, it follows the + Commonmark spec + while sprinkling in some additional features. +
+
+ To render a code preview, use the standard code field syntax and append :preview
to the
+ language.
+
+```html:preview +[code goes here] +``` +
+ You can also append :expanded
to expand the code by default, and :no-codepen
to
+ disable the CodePen button. The order of these modifiers doesn’t matter, but no spaces should exist between
+ the language and the modifiers.
+
+```html:preview:expanded:no-codepen +[code goes here] +``` +
This particular syntax was chosen for a few reasons:
+Special callouts can be added using the following syntax.
++:::tip +This is a tip/informational callout +::: + +:::warning +This is a warning callout +::: + +:::danger +This is a danger callout +::: +
To place content that’s indirectly related, use the following syntax.
++:::aside +This content is indirectly related and will appear in an `<aside>` element. +::: +
To provide additional details that can be expanded/collapses, use the following syntax.
++:::details Title Here +The details here are expandable. +::: +
To link to a GitHub issue, PR, or discussion, use the following syntax.
++[#1234] +
+ The following is a non-exhaustive list of conventions, patterns, and best practices we try to follow. As a + contributor, we ask that you make a good faith effort to follow them as well. This ensures consistency and + maintainability throughout the project. +
++ If in doubt, use your best judgment and the maintainers will be happy to guide you during the code review + process. If you’d like clarification on something before submitting a PR, feel free to reach out on the + community chat. +
++ This section can be a lot to digest in one sitting, so don’t feel like you need to take it all in right + now. Most contributors will be better off skimming this section and reviewing the relevant content as + needed. +
++ Nebula is built with accessibility in mind. Creating generic components that are fully accessible to users + with varying capabilities across a multitude of circumstances is a daunting challenge. Oftentimes, the + solution to an a11y problem is not written in black and white and, therefore, we may not get it right the + first time around. There are, however, guidelines we can follow in our effort to make Nebula an accessible + foundation from which applications and websites can be built. +
++ We take this commitment seriously, so please ensure your contributions have this goal in mind. If you need + help with anything a11y-related, please reach out to the community for + assistance. If you discover an accessibility concern within the library, please file a bug on the + issue tracker. +
++ It’s important to remember that, although accessibility starts with foundational components, it doesn’t end + with them. It everyone’s responsibility to encourage best practices and ensure we’re providing an optimal + experience for all of our users. +
++ Most code formatting is handled automatically by + Prettier + via commit hooks. However, for the best experience, you should + install it in your editor + and enable format on save. +
+Please do not make any changes to prettier.config.cjs
without consulting the maintainers.
+ Components should be composable, meaning you can easily reuse them with and within other components. This + reduces the overall size of the library, expedites feature development, and maintains a consistent user + experience. +
+
+ All components have a host element, which is a reference to the <sl-*>
element itself.
+ Make sure to always set the host element’s display
property to the appropriate value depending
+ on your needs, as the default is inline
per the custom element spec.
+
+:host { + display: block; +} +
+ Aside from display
, avoid setting styles on the host element when possible. The reason for this
+ is that styles applied to the host element are not encapsulated. Instead, create a base element that wraps
+ the component’s internals and style that instead. This convention also makes it easier to use BEM in
+ components, as the base element can serve as the “block” entity.
+
+ When authoring components, please try to follow the same structure and conventions found in other + components. Classes, for example, generally follow this structure: +
+@query
decorators@state
decorators@property
decoratorsconnectedCallback()
, disconnectedCallback()
,
+ firstUpdated()
, etc.)
+ @watch
decoratorsrender()
method
+ Please avoid using the public
keyword for class fields. It’s simply too verbose when combined
+ with decorators, property names, and arguments. However, please do add private
in
+ front of any property or method that is intended to be private.
+
+ This might seem like a lot, but it’s fairly intuitive once you start working with the library. However, + don’t let this structure prevent you from submitting a PR. + Code can change + and nobody will chastise you for “getting it wrong.” At the same time, encouraging consistency helps keep + the library maintainable and easy for others to understand. (A lint rule that helps with things like this + would be a very welcome PR!) +
++ All components use a + shadow DOM, so styles are completely encapsulated from the rest of the document. As a result, class names used + inside a component won’t conflict with class names outside the component, so we’re free to + name them anything we want. +
++ Internally, each component uses the + BEM methodology + for class names. There is no technical requirement to do this — it’s purely the preference of the author to + enforce consistency and clarity throughout components. As such, all contributions are expected to follow + this pattern. +
+
+ Boolean props should always default to false
, otherwise there’s no way for the user to
+ unset them using only attributes. To keep the API as friendly and consistent as possible, use a property
+ such as noHeader
and a corresponding kebab-case attribute such as no-header
.
+
+ When naming boolean props that hide or disable things, prefix them with no-
, e.g.
+ no-spin-buttons
and avoid using other verbs such as hide-
and
+ disable-
for consistency.
+
+ When a component relies on the presence of slotted content to do something, don’t assume its initial state + is permanent. Slotted content can be added or removed any time and components must be aware of this. A good + practice to manage this is: +
+@slotchange={this.handleSlotChange}
to the slots you want to watchhandleSlotChange
method and use the hasSlot
utility to update state
+ variables for the the respective slot(s)
+ <slot>
elements in a component — always use
+ hidden
so the slot remains in the DOM and the slotchange
event can be captured
+ See the source of card, dialog, or drawer for examples.
+
+ A pattern has been established in <sl-details>
and <sl-tree-item>
for
+ expand/collapse icons that animate on open/close. In short, create two slots called
+ expand-icon
and collapse-icon
and render them both in the DOM, using CSS to
+ show/hide only one based on the current open state. Avoid conditionally rendering them. Also avoid using
+ dynamic slot names, such as <slot name=${open ? 'open' : 'closed'}>
, because Firefox will
+ not animate them.
+
+ There should be a container element immediately surrounding both slots. The container should be animated
+ with CSS by default and it should have a part so the user can override the animation or disable it. Please
+ refer to the source and documentation for <sl-details>
and/or
+ <sl-tree-item>
for details.
+
When providing fallback content inside of <slot>
elements, avoid adding parts, e.g.:
+<slot name="icon"> + <sl-icon part="close-icon"></sl-icon> +</slot> +
+ This creates confusion because the part will be documented, but it won’t work when the user slots in their + own content. The recommended way to customize this example is for the user to slot in their own content and + target its styles with CSS as needed. +
+
+ Components must only emit custom events, and all custom events must start with sl-
as a
+ namespace. For compatibility with frameworks that utilize DOM templates, custom events must have lowercase,
+ kebab-style names. For example, use sl-change
instead of slChange
.
+
+ This convention avoids the problem of browsers lowercasing attributes, causing some frameworks to be unable + to listen to them. This problem isn’t specific to one framework, but + Vue’s documentation + provides a good explanation of the problem. +
+
+ When change events are emitted by Nebula components, they should be named sl-change
and they
+ should only be emitted as a result of user input. Programmatic changes, such as setting
+ el.value = '…'
should not result in a change event being emitted. This is consistent
+ with how native form controls work.
+
To expose custom properties as part of a component’s API, scope them to the :host
block.
+:host { + --color: var(--sl-color-primary-500); + --background-color: var(--sl-color-neutral-100); +} +
+ Then use the following syntax for comments so they appear in the generated docs. Do not use the
+ --sl-
prefix, as that is reserved for design tokens that live in the global scope.
+
+/** + * @cssproperty --color: The component's text color. + * @cssproperty --background-color: The component's background color. + */ +export default class SlExample { + // ... +} +
+ When an item within a keyboard navigable set is disabled (e.g. tabs, trees, menu items, etc.), the disabled + item should not receive focus via keyboard, click, or tap. It should be skipped just like in + operating system menus and in native HTML form controls. There is no exception to this. If a particular item + requires focus for assistive devices to provide a good user experience, the item should not be disabled and, + upon activation, it should inform the user why the respective action cannot be completed. +
++ When designing a component’s API, standard properties are generally used to change the behavior of + a component, whereas CSS custom properties (“CSS variables”) are used to change the appearance of a + component. Remember that properties can’t respond to media queries, but CSS variables can. +
++ There are some exceptions to this (e.g. when it significantly improves developer experience), but a good + rule of thumbs is “will this need to change based on screen size?” If so, you probably want to use a CSS + variable. +
++ There are two ways to enable customizations for components. One way is with CSS custom properties (“CSS + variables”), the other is with CSS parts (“parts”). +
+
+ CSS variables are scoped to the host element and can be reused throughout the component. A good example of a
+ CSS variable would be --border-width
, which might get reused throughout a component to ensure
+ borders share the same width for all internal elements.
+
+ Parts let you target a specific element inside the component’s shadow DOM but, by design, you can’t target a + part’s children or siblings. You can only customize the part itself. Use a part when you need to + allow a single element inside the component to accept styles. +
++ This convention can be relaxed when the developer experience is greatly improved by not following these + suggestions. +
++ While CSS parts can be named + virtually anything, within Nebula they must use the kebab-case convention and lowercase letters. Additionally, + a BEM-inspired naming convention + is used to distinguish parts, subparts, and states. +
+
+ When composing elements, use part
to export the host element and exportparts
to
+ export its parts.
+
+render() { + return html` + <div part="base"> + <sl-icon part="icon" exportparts="base:icon__base" ...></sl-icon> + </div> + `; +} +
+ This results in a consistent, easy to understand structure for parts. In this example, the
+ icon
part will target the host element and the icon__base
part will target the
+ icon’s base
part.
+
+ TL;DR – a component is a dependency if and only if it’s rendered inside another component’s shadow root. +
+
+ Many Nebula components use other Nebula components internally. For example,
+ <sl-button>
uses both <sl-icon>
and
+ <sl-spinner>
for its caret icon and loading state, respectively. Since these components
+ appear in the button’s shadow root, they are considered dependencies of Button. Since dependencies are
+ automatically loaded, users only need to import the button and everything will work as expected.
+
+ Contrast this to <sl-select>
and <sl-option>
. At first, one might
+ assume that Option is a dependency of Select. After all, you can’t really use Select without slotting in at
+ least one Option. However, Option is not a dependency of Select! The reason is because no Option is
+ rendered in the Select’s shadow root. Since the options are provided by the user, it’s up to them to import
+ both components independently.
+
+ People often suggest that Nebula should auto-load Select + Option, Menu + Menu Item, Breadcrumb + Breadcrumb + Item, etc. Although some components are designed to work together, they’re technically not dependencies so + eagerly loading them may not be desirable. What if someone wants to roll their own component with a superset + of features? They wouldn’t be able to if Nebula automatically imported it! +
+
+ Similarly, in the case of <sl-radio-group>
there was originally only
+ <sl-radio>
, but now you can use either <sl-radio>
or
+ <sl-radio-button>
as child elements. Which component(s) should be auto-loaded
+ dependencies in this case? Had Radio been a dependency of Radio Group, users that only wanted Radio Buttons
+ would be forced to register both with no way to opt out and no way to provide their own customized version.
+
+ For non-dependencies, the user should decide what gets registered, even if it comes with a minor + inconvenience. +
+Form controls should support submission and validation through the following conventions:
+name
, value
, and disabled
properties in
+ the same manner as HTMLInputElement
+ setCustomValidity()
method so the user can set a custom
+ validation message
+ reportValidity()
method that report their validity during form
+ submission
+ invalid
property that reflects their validityrequired
,
+ pattern
, minlength
, maxlength
, etc. when possible
+ <form>
element
+ Avoid inlining SVG icons inside of templates. If a component requires an icon, make sure
+ <sl-icon>
is a dependency of the component and use the
+ system library:
+
+<sl-icon library="system" name="..."></sl-icon> +
+ This will render the icons instantly whereas the default library will fetch them from a remote source. If an
+ icon isn’t available in the system library, you will need to add it to library.system.ts
. Using
+ the system library ensures that all icons load instantly and are customizable by users who wish to provide a
+ custom resolver for the system library.
+
What to test for a given component:
++const myComponent = await fixture<SlAlert>(html`<sl-my-component>SomeContent</sl-my-component>`); + +await expect(myComponent).to.be.accessible(); +
Guidelines for writing tests:
++ Border radius tokens are used to give sharp edges a more subtle, rounded effect. They use rem units so they + scale with the base font size. The pixel values displayed are based on a 16px font size. +
+Token | +Value | +Example | +
---|---|---|
--sl-border-radius-small |
+ 0.1875rem (3px) | ++ |
--sl-border-radius-medium |
+ 0.25rem (4px) | ++ |
--sl-border-radius-large |
+ 0.5rem (8px) | ++ |
--sl-border-radius-x-large |
+ 1rem (16px) | ++ |
--sl-border-radius-circle |
+ 50% | ++ |
--sl-border-radius-pill |
+ 9999px | ++ + | +
+ Color tokens help maintain consistent use of color throughout your app. Nebula provides palettes for theme + colors and primitives that you can use as a foundation for your design system. +
+
+ Color tokens are referenced using the --sl-color-{name}-{n}
CSS custom property, where
+ {name}
is the name of the palette and {n}
is the numeric value of the desired
+ swatch.
+
+ Theme tokens give you a semantic way to reference colors in your app. The primary palette is typically based + on a brand color, whereas success, neutral, warning, and danger are used to visualize actions that + correspond to their respective meanings. +
+--sl-color-primary-{n}
+ --sl-color-success-{n}
+ --sl-color-warning-{n}
+ --sl-color-danger-{n}
+ --sl-color-neutral-{n}
+ --sl-color-neutral-{n}
+ + Looking for an easy way to customize your theme? + Try the color token generator! +
++ Additional palettes are provided in the form of color primitives. Use these when you need arbitrary colors + that don’t have semantic meaning. Color primitives are derived from the fantastic + Tailwind color palette. +
+--sl-color-gray-{n}
+ --sl-color-red-{n}
+ --sl-color-orange-{n}
+ --sl-color-amber-{n}
+ --sl-color-yellow-{n}
+ --sl-color-lime-{n}
+ --sl-color-green-{n}
+ --sl-color-emerald-{n}
+ --sl-color-teal-{n}
+ --sl-color-cyan-{n}
+ --sl-color-sky-{n}
+ --sl-color-blue-{n}
+ --sl-color-indigo-{n}
+ --sl-color-violet-{n}
+ --sl-color-purple-{n}
+ --sl-color-fuchsia-{n}
+ --sl-color-pink-{n}
+ --sl-color-rose-{n}
+
+ Elevation tokens are used to give elements the appearance of being raised off the page. Use them with the
+ box-shadow
property. These are especially useful for menus, popovers, and dialogs.
+
Token | +Example | +
---|---|
--sl-shadow-x-small |
+ + |
--sl-shadow-small |
+ + |
--sl-shadow-medium |
+ + |
--sl-shadow-large |
+ + |
--sl-shadow-x-large |
+ + |
+ All of the design tokens described herein are considered relatively stable. However, some changes might + occur in future versions to address mission critical bugs or improvements. If such changes occur, they + will not be considered breaking changes and will be clearly documented in the + changelog. +
++ Most design tokens are consistent across the light and dark theme. Those that vary will show both values. +
+
+ Currently, the source of design tokens is considered to be
+ light.css
. The dark theme,
+ dark.css, mirrors all of the same tokens with dark mode-specific values where appropriate. Work is planned to
+ move all design tokens to a single file, perhaps JSON or YAML, in the near future.
+
+ Focus ring tokens control the appearance of focus rings. Note that form inputs use
+ --sl-input-focus-ring-*
tokens instead.
+
Token | +Value | +
---|---|
--sl-focus-ring-color |
+
+ var(--sl-color-primary-600) (light theme)var(--sl-color-primary-700)
+ (dark theme)
+ |
+
--sl-focus-ring-style |
+ solid |
+
--sl-focus-ring-width |
+ 3px |
+
--sl-focus-ring |
+ var(--sl-focus-ring-style) var(--sl-focus-ring-width) var(--sl-focus-ring-color) |
+
--sl-focus-ring-offset |
+ 1px |
+
+ Button tokens control the appearance of buttons. In addition, buttons also currently use some form input
+ tokens such as --sl-input-height-*
and --sl-input-border-*
. More button tokens may
+ be added in the future to make it easier to style them more independently.
+
Token | +Value | +
---|---|
--sl-button-font-size-small |
+ var(--sl-font-size-x-small) |
+
--sl-button-font-size-medium |
+ var(--sl-font-size-small) |
+
--sl-button-font-size-large |
+ var(--sl-font-size-medium) |
+
+ Form input tokens control the appearance of form controls such as input, + select, textarea, etc. +
+Token | +Value | +
---|---|
--sl-input-height-small |
+ 1.875rem (30px @ 16px base) |
+
--sl-input-height-medium |
+ 2.5rem (40px @ 16px base) |
+
--sl-input-height-large |
+ 3.125rem (50px @ 16px base) |
+
--sl-input-background-color |
+ var(--sl-color-neutral-0) |
+
--sl-input-background-color-hover |
+ var(--sl-input-background-color) |
+
--sl-input-background-color-focus |
+ var(--sl-input-background-color) |
+
--sl-input-background-color-disabled |
+ var(--sl-color-neutral-100) |
+
--sl-input-border-color |
+ var(--sl-color-neutral-300) |
+
--sl-input-border-color-hover |
+ var(--sl-color-neutral-400) |
+
--sl-input-border-color-focus |
+ var(--sl-color-primary-500) |
+
--sl-input-border-color-disabled |
+ var(--sl-color-neutral-300) |
+
--sl-input-border-width |
+ 1px |
+
--sl-input-required-content |
+ * |
+
--sl-input-required-content-offset |
+ -2px |
+
--sl-input-required-content-color |
+ var(--sl-input-label-color) |
+
--sl-input-border-radius-small |
+ var(--sl-border-radius-medium) |
+
--sl-input-border-radius-medium |
+ var(--sl-border-radius-medium) |
+
--sl-input-border-radius-large |
+ var(--sl-border-radius-medium) |
+
--sl-input-font-family |
+ var(--sl-font-sans) |
+
--sl-input-font-weight |
+ var(--sl-font-weight-normal) |
+
--sl-input-font-size-small |
+ var(--sl-font-size-small) |
+
--sl-input-font-size-medium |
+ var(--sl-font-size-medium) |
+
--sl-input-font-size-large |
+ var(--sl-font-size-large) |
+
--sl-input-letter-spacing |
+ var(--sl-letter-spacing-normal) |
+
--sl-input-color |
+ var(--sl-color-neutral-700) |
+
--sl-input-color-hover |
+ var(--sl-color-neutral-700) |
+
--sl-input-color-focus |
+ var(--sl-color-neutral-700) |
+
--sl-input-color-disabled |
+ var(--sl-color-neutral-900) |
+
--sl-input-icon-color |
+ var(--sl-color-neutral-500) |
+
--sl-input-icon-color-hover |
+ var(--sl-color-neutral-600) |
+
--sl-input-icon-color-focus |
+ var(--sl-color-neutral-600) |
+
--sl-input-placeholder-color |
+ var(--sl-color-neutral-500) |
+
--sl-input-placeholder-color-disabled |
+ var(--sl-color-neutral-600) |
+
--sl-input-spacing-small |
+ var(--sl-spacing-small) |
+
--sl-input-spacing-medium |
+ var(--sl-spacing-medium) |
+
--sl-input-spacing-large |
+ var(--sl-spacing-large) |
+
--sl-input-focus-ring-color |
+ hsl(198.6 88.7% 48.4% / 40%) |
+
--sl-input-focus-ring-offset |
+ 0 |
+
Filled form input tokens control the appearance of form controls using the filled
variant.
Token | +Value | +
---|---|
--sl-input-filled-background-color |
+ var(--sl-color-neutral-100) |
+
--sl-input-filled-background-color-hover |
+ var(--sl-color-neutral-100) |
+
--sl-input-filled-background-color-focus |
+ var(--sl-color-neutral-100) |
+
--sl-input-filled-background-color-disabled |
+ var(--sl-color-neutral-100) |
+
--sl-input-filled-color |
+ var(--sl-color-neutral-800) |
+
--sl-input-filled-color-hover |
+ var(--sl-color-neutral-800) |
+
--sl-input-filled-color-focus |
+ var(--sl-color-neutral-700) |
+
--sl-input-filled-color-disabled |
+ var(--sl-color-neutral-800) |
+
Form label tokens control the appearance of labels in form controls.
+Token | +Value | +
---|---|
--sl-input-label-font-size-small |
+ var(--sl-font-size-small) |
+
--sl-input-label-font-size-medium |
+ var(--sl-font-size-medium ) |
+
--sl-input-label-font-size-large |
+ var(--sl-font-size-large) |
+
--sl-input-label-color |
+ inherit |
+
Help text tokens control the appearance of help text in form controls.
+Token | +Value | +
---|---|
--sl-input-help-text-font-size-small |
+ var(--sl-font-size-x-small) |
+
--sl-input-help-text-font-size-medium |
+ var(--sl-font-size-small) |
+
--sl-input-help-text-font-size-large |
+ var(--sl-font-size-medium) |
+
--sl-input-help-text-color |
+ var(--sl-color-neutral-500) |
+
+ Toggle tokens control the appearance of toggles such as checkbox, + radio, switch, etc. +
+Token | +Value | +
---|---|
--sl-toggle-size-small |
+ 0.875rem (14px @ 16px base) |
+
--sl-toggle-size-medium |
+ 1.125rem (18px @ 16px base) |
+
--sl-toggle-size-large |
+ 1.375rem (22px @ 16px base) |
+
+ Overlay tokens control the appearance of overlays as used in dialog, + drawer, etc. +
+Token | +Value | +
---|---|
--sl-overlay-background-color |
+ hsl(240 3.8% 46.1% / 33%) |
+
+ Panel tokens control the appearance of panels such as those used in dialog, + drawer, menu, etc. +
+Token | +Value | +
---|---|
--sl-panel-background-color |
+ var(--sl-color-neutral-0) |
+
--sl-panel-border-color |
+ var(--sl-color-neutral-200) |
+
--sl-panel-border-width |
+ 1px |
+
+ Tooltip tokens control the appearance of tooltips. This includes the + tooltip component as well as other implementations, such + range tooltips. +
+Token | +Value | +
---|---|
--sl-tooltip-border-radius |
+ var(--sl-border-radius-medium) |
+
--sl-tooltip-background-color |
+ var(--sl-color-neutral-800) |
+
--sl-tooltip-color |
+ var(--sl-color-neutral-0) |
+
--sl-tooltip-font-family |
+ var(--sl-font-sans) |
+
--sl-tooltip-font-weight |
+ var(--sl-font-weight-normal) |
+
--sl-tooltip-font-size |
+ var(--sl-font-size-small) |
+
--sl-tooltip-line-height |
+ var(--sl-line-height-dense) |
+
--sl-tooltip-padding |
+ var(--sl-spacing-2x-small) var(--sl-spacing-x-small) |
+
--sl-tooltip-arrow-size |
+ 6px |
+
Spacing tokens are used to provide consistent spacing between content in your app.
+Token | +Value | +Example | +
---|---|---|
--sl-spacing-3x-small |
+ 0.125rem (2px) | ++ + | +
--sl-spacing-2x-small |
+ 0.25rem (4px) | ++ + | +
--sl-spacing-x-small |
+ 0.5rem (8px) | ++ + | +
--sl-spacing-small |
+ 0.75rem (12px) | ++ + | +
--sl-spacing-medium |
+ 1rem (16px) | ++ + | +
--sl-spacing-large |
+ 1.25rem (20px) | ++ + | +
--sl-spacing-x-large |
+ 1.75rem (28px) | ++ + | +
--sl-spacing-2x-large |
+ 2.25rem (36px) | ++ + | +
--sl-spacing-3x-large |
+ 3rem (48px) | ++ + | +
--sl-spacing-4x-large |
+ 4.5rem (72px) | ++ + | +
Transition tokens are used to provide consistent transitions throughout your app.
+Token | +Value | +Example | +
---|---|---|
--sl-transition-x-slow |
+ 1000ms | ++ |
--sl-transition-slow |
+ 500ms | ++ |
--sl-transition-medium |
+ 250ms | ++ |
--sl-transition-fast |
+ 150ms | ++ |
--sl-transition-x-fast |
+ 50ms | ++ |
Typography tokens are used to maintain a consistent set of font styles throughout your app.
+The default font stack is designed to be simple and highly available on as many devices as possible.
+Token | +Value | +Example | +
---|---|---|
--sl-font-sans |
+ + -apple-system, BlinkMacSystemFont, ‘Segoe UI’, Roboto, Helvetica, Arial, sans-serif, ‘Apple Color + Emoji’, ‘Segoe UI Emoji’, ‘Segoe UI Symbol’ + | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-font-serif |
+ Georgia, ‘Times New Roman’, serif | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-font-mono |
+ SFMono-Regular, Consolas, ‘Liberation Mono’, Menlo, monospace; | ++ The quick brown fox jumped over the lazy dog. + | +
+ Font sizes use rem
units so they scale with the base font size. The pixel values displayed are
+ based on a 16px font size.
+
Token | +Value | +Example | +
---|---|---|
--sl-font-size-2x-small |
+ 0.625rem (10px) | +Aa | +
--sl-font-size-x-small |
+ 0.75rem (12px) | +Aa | +
--sl-font-size-small |
+ 0.875rem (14px) | +Aa | +
--sl-font-size-medium |
+ 1rem (16px) | +Aa | +
--sl-font-size-large |
+ 1.25rem (20px) | +Aa | +
--sl-font-size-x-large |
+ 1.5rem (24px) | +Aa | +
--sl-font-size-2x-large |
+ 2.25rem (36px) | +Aa | +
--sl-font-size-3x-large |
+ 3rem (48px) | +Aa | +
--sl-font-size-4x-large |
+ 4.5rem (72px) | +Aa | +
Token | +Value | +Example | +
---|---|---|
--sl-font-weight-light |
+ 300 | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-font-weight-normal |
+ 400 | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-font-weight-semibold |
+ 500 | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-font-weight-bold |
+ 700 | ++ The quick brown fox jumped over the lazy dog. + | +
Token | +Value | +Example | +
---|---|---|
--sl-letter-spacing-denser |
+ -0.03em | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-letter-spacing-dense |
+ -0.015em | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-letter-spacing-normal |
+ normal | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-letter-spacing-loose |
+ 0.075em | ++ The quick brown fox jumped over the lazy dog. + | +
--sl-letter-spacing-looser |
+ 0.15em | ++ The quick brown fox jumped over the lazy dog. + | +
Token | +Value | +Example | +
---|---|---|
--sl-line-height-denser |
+ 1 | +
+
+ The quick brown fox jumped over the lazy dog.
+ The quick brown fox jumped over the lazy + dog. The quick brown fox jumped over the lazy dog. + |
+
--sl-line-height-dense |
+ 1.4 | +
+
+ The quick brown fox jumped over the lazy dog.
+ The quick brown fox jumped over the lazy + dog. The quick brown fox jumped over the lazy dog. + |
+
--sl-line-height-normal |
+ 1.8 | +
+
+ The quick brown fox jumped over the lazy dog.
+ The quick brown fox jumped over the lazy + dog. The quick brown fox jumped over the lazy dog. + |
+
--sl-line-height-loose |
+ 2.2 | +
+
+ The quick brown fox jumped over the lazy dog.
+ The quick brown fox jumped over the lazy + dog. The quick brown fox jumped over the lazy dog. + |
+
--sl-line-height-looser |
+ 2.6 | +
+
+ The quick brown fox jumped over the lazy dog.
+ The quick brown fox jumped over the lazy + dog. The quick brown fox jumped over the lazy dog. + |
+
Z-indexes are used to stack components in a logical manner.
+Token | +Value | +
---|---|
--sl-z-index-drawer |
+ 700 | +
--sl-z-index-dialog |
+ 800 | +
--sl-z-index-dropdown |
+ 900 | +
--sl-z-index-alert-group |
+ 950 | +
--sl-z-index-tooltip |
+ 1000 | +
This page explains how to integrate Nebula with an Astro app.
++ This is a community-maintained document. Please ask the community if + you have questions about this integration. You can also + suggest improvements + to make it better. +
++ In the following tutorial you will notice that Nebula cannot be imported in the frontmatter of Astro files. + This is because Nebula relies on globals from the DOM to be present. +
++ There is a + Lit + Astro integration for SSR + , however it will not work with Nebula in its current state due to some reliance on DOM APIs that aren’t + shimmed. We are working to resolve this. +
+To get started using Nebula with Astro, the following packages must be installed.
++npm install @onsonr/nebula rollup-plugin-copy +
In /src/pages/index.astro
, set the base path and import Nebula.
+--- +// import default stylesheet +import "@onsonr/nebula/dist/themes/light.css"; +--- + +<html> + <body> + <sl-button>Button</sl-button> + </body> +</html> + +<script> + // setBasePath to tell Nebula where to load icons from. + import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path.js'; + setBasePath('/shoelace-assets/'); + + // Load all components. + import '@onsonr/nebula'; +</script> +
+ If you want to cherry pick components, replace the main Nebula import with ‘import + ”@onsonr/nebula/dist/components/button/button.js”;’ for whichever component you would like. +
+
+ You only have to import in the main index.astro
file. The components can be used anywhere
+ throughout the project.
+
In /src/pages/index.astro
, set custom animations after the Nebula import.
+--- +import { setBasePath } from "@onsonr/nebula/dist/utilities/base-path.js"; +setBasePath("dist/assets"); +--- + +<html> + <body> + <sl-tooltip content="This is a tooltip"> + <sl-button>Hover Me</sl-button> + </sl-tooltip> + </body> +</html> + +<script> + // setBasePath to tell Nebula where to load icons from. + import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path.js'; + setBasePath('/shoelace-assets/'); + + // Load all components. + import '@onsonr/nebula'; + + const duration = 3000; + import { setDefaultAnimation } from '@onsonr/nebula/dist/utilities/animation-registry.js'; + + setDefaultAnimation('tooltip.show', { + keyframes: [ + { opacity: 0, scale: 0.8 }, + { opacity: 1, scale: 1 } + ], + options: { duration: duration, easing: 'ease' } + }); +</script> +
+ You’ll notice the above steps never added our icons into our /public
directory. To solve this,
+ we can install rollup-plugin-copy
to copy Nebula’s assets into your public directory.
+
Here’s what your Astro config should look like:
++// astro.config.mjs + +import { defineConfig } from 'astro/config'; +import copy from 'rollup-plugin-copy'; + +// https://astro.build/config +export default defineConfig({ + vite: { + plugins: [ + copy({ + // Copy only on first build. We dont want to trigger additional server reloads. + copyOnce: true, + hook: 'buildStart', + targets: [ + { src: 'node_modules/@onsonr/nebula/dist/assets/*', dest: 'public/shoelace-assets/assets/' } + ] + }) + ] + } +}); +
+ This page explains how to integrate Nebula with a + Laravel 9 + app using Vite. For additional details refer to the + Bundling Assets (Vite) + section in the official Laravel docs. +
++ This is a community-maintained document. Please ask the community if + you have questions about this integration. You can also + suggest improvements + to make it better. +
+This integration has been tested with the following:
+
+ These instructions assume a default
+ Laravel 9 install
+ that uses
+ Vite to
+ bundle assets. Be sure to run npm install
to install the default Laravel front-end dependencies
+ before installing Nebula.
+
+npm install @onsonr/nebula +
Import the Nebula default theme (stylesheet) in /resources/css/app.css
:
+@import '/node_modules/@onsonr/nebula/dist/themes/light.css'; +
+ Import each Nebula component you plan to use in /resources/js/bootstrap.js
. Use the full path
+ to each component (as outlined in the
+ Cherry Picking instructions). You can find the full import statement for a component in the Importing section of the
+ component’s documentation (use the Bundler import). Your imports should look similar to:
+
+import '@onsonr/nebula/dist/components/button/button.js'; +import '@onsonr/nebula/dist/components/icon/icon.js'; +import '@onsonr/nebula/dist/components/dialog/dialog.js'; +
+ Since Vite has no way to copy arbitrary assets into your build (like webpack), you need to manually copy the
+ Nebula static assets to your project’s public folder. Run this command from your project’s root directory to
+ copy the Nebula static assets to the ./public/assets
folder:
+
+cp -aR node_modules/@onsonr/nebula/dist/assets/ ./public/assets +
+ Add the base path to your Nebula assets (icons, images, etc.) in /resources/js/bootstrap.js
.
+ The path must point to the same folder where you copy assets to in the next step.
+
+import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path.js'; +setBasePath('/'); +
Example /resources/js/bootstrap.js
file:
+import { setBasePath } from '@onsonr/nebula/dist/utilities/base-path.js'; +setBasePath('/'); + +import '@onsonr/nebula/dist/components/button/button.js'; +import '@onsonr/nebula/dist/components/icon/icon.js'; +import '@onsonr/nebula/dist/components/dialog/dialog.js'; +
+ Laravel pre-configures the Vite entry points in vite.config.js
as
+ resources/css/app.css
and resources/js/app.js
. If you use a different location for
+ your CSS and/or Javascript entry point, update this configuration to accordingly.
+
+plugins: [ + laravel({ + input: ["resources/css/app.css", "resources/js/app.js"], + refresh: true, + }), + ], +
Run the Vite development server or production build.
++## run the Vite development bundle +npm run dev + +## build a production bundle (with versioning) +npm run build +
+ Add the @vite()
Blade directive to the <head>
of your application’s root
+ template.
+
+<head> + ... @vite(['resources/css/app.css', 'resources/js/app.js']) +</head> +
Have fun using Nebula components in your Laravel app!
+This page explains how to integrate Nebula with a NextJS app.
++ This is a community-maintained document. Please ask the community if + you have questions about this integration. You can also + suggest improvements + to make it better. +
+There are 2 guides available:
+ ++ If you haven’t already, create your NextJS app. You can find the documentation for that here: + https://nextjs.org/docs/getting-started/installation +
+
+ After you’ve created your app, the first step to using Nebula is modifying your package.json
to
+ have "type": "module"
in it since Nebula ships ES Modules.
+
+// package.json +{ + "type": "module" +} +
To get started using Nebula with NextJS, the following packages must be installed.
++npm install @onsonr/nebula copy-webpack-plugin +
+ Nebula for obvious reasons, and the copy-webpack-plugin
will be used later for adding our icons
+ to our public/
folder.
+
+ We’ll start with modifying our next.config.js
to copy Nebula’s assets and to properly work with
+ ESM.
+
Here’s what your next.config.js
should look like:
+ In order to add Nebula’s assets to the final build output, we need to modify next.config.js
to
+ look like this.
+
+// next.config.js + +import { dirname, resolve } from 'path'; +import { fileURLToPath } from 'url'; +import CopyPlugin from 'copy-webpack-plugin'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { esmExternals: 'loose' }, + webpack: (config, options) => { + config.plugins.push( + new CopyPlugin({ + patterns: [ + { + from: resolve(__dirname, 'node_modules/@onsonr/nebula/dist/assets/'), + to: resolve(__dirname, 'public/shoelace-assets/assets/') + } + ] + }) + ); + return config; + } +}; + +export default nextConfig; +
+ This will copy the files from node_modules/@onsonr/nebula/dist/assets
into your
+ public/shoelace-assets
folder on every development serve or build. You may want to avoid
+ committing these into your repo. To do so, simply add public/shoelace-assets
into your
+ .gitignore
folder
+
+ Once we’ve got our webpack config / next config setup, lets modify our app/layout.tsx
to
+ include Nebula’s default theme.
+
+// app/layout.tsx +import './globals.css'; +import '@onsonr/nebula/dist/themes/light.css'; +// We can also import the dark theme here as well. +// import "@onsonr/nebula/dist/themes/dark.css"; +
+ Now, we need to create a NebulaSetup
component that will be a client component in charge of
+ setting the basePath
for our assets / icons.
+
To do so, create a file called app/shoelace-setup.tsx
+'use client'; +// ^ Make sure to have 'use client'; because `setBasePath()` requires access to `document`. + +import { setBasePath } from "@onsonr/nebula/dist/utilities/base-path.js" + +export default function NebulaSetup({ + children, +}: { + children: React.ReactNode +}) { + setBasePath(`/shoelace-assets/`); + return <>{children}</> +} +
Don’t forget to mark your Nebula components and Nebula setup with ‘use client’.
+Then we’ll add this setup component into app/layout.tsx
Our layout.tsx
Should now look something like this:
+import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; ++ import "@onsonr/nebula/dist/themes/light.css"; + ++ import NebulaSetup from "./shoelace-setup"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <html lang="en"> + <body className={inter.className}> ++ <NebulaSetup> + {children} ++ </NebulaSetup> + </body> + </html> + ); +} +
+ Now that we have the setup in place, we can write an app/page.tsx
to use Nebula components in
+ combination with the dynamic()
component loader from NextJS.
+
+ Here’s what that would look like, do note the "use client";
at the top of the component is
+ required.
+
+// app/page.tsx +'use client'; + +import React from "react"; +import dynamic from "next/dynamic"; + +const SlButton = dynamic( + // Notice how we use the full path to the component. If you only do `import("@onsonr/nebula/dist/react")` you will load the entire component library and not get tree shaking. + () => import("@onsonr/nebula/dist/react/button/index.js"), + { + loading: () => <p>Loading...</p>, + ssr: false, + }, +); + +const SlIcon = dynamic( + () => import("@onsonr/nebula/dist/react/icon/index.js"), + { + loading: () => <p>Loading...</p>, + ssr: false, + }, +); + +export default function Home() { + return ( + <main> + <SlButton>Test</SlButton> + <SlIcon name="gear" /> + </main> + ); +} +
Now you should be up and running with NextJS + Nebula!
++ If you’re stuck, there’s an + example repo here + you can checkout. +
+To get started using Nebula with NextJS, the following packages must be installed.
++yarn add @onsonr/nebula copy-webpack-plugin next-compose-plugins next-transpile-modules +
+ Because Nebula utilizes ESM, we need to modify our package.json
to support ESM packages. Simply
+ add the following to your root of package.json
:
+
+"type": "module" +
+ There’s one more step to enable ESM in NextJS, but we’ll tackle that in our Next configuration modification. +
+The next step is to import Nebula’s default theme (stylesheet) in your _app.js
file:
+import '@onsonr/nebula/dist/themes/light.css'; +
+ After importing the theme, you’ll need to import the JavaScript files for Nebula. However, this is a bit + tricky to do in NextJS thanks to the SSR environment not having any of the required browser APIs to define + endpoints. +
+
+ We’ll want to create a component that uses
+ React’s useLayoutEffect
+ to add in the custom components before the first render:
+
+function CustomEls({ URL }) { + // useRef to avoid re-renders + const customEls = useRef(false); + + useLayoutEffect(() => { + if (customEls.current) { + return; + } + + import('@onsonr/nebula/dist/utilities/base-path').then(({ setBasePath }) => { + setBasePath(`${URL}/static/static`); + + // This imports all components + import('@onsonr/nebula/dist/react'); + // If you're wanting to selectively import components, replace this line with your own definitions + + // import("@onsonr/nebula/dist/components/button/button"); + customEls.current = true; + }); + }, [URL, customEls]); + + return null; +} +
+ If we use useEffect
instead of useLayoutEffect
, the initial render will occur
+ with the expected sl-
props applied, but the subsequent render (caused by the
+ useEffect
) will remove those props as the custom components initialize. We must use
+ useLayoutEffect
to have expected behavior
+
+ This will import all Nebula components for convenience. To selectively import components, refer to the + Using webpack section of the docs. +
+
+ You may be wondering where the URL
property is coming from. We’ll address that in the next few
+ sections.
+
+ While we need to use useLayoutEffect
for the initial render, NextJS will throw a warning at us
+ for trying to use useLayoutEffect
in SSR, which is disallowed. To fix this problem, we’ll
+ conditionally render the CustomEls
component to only render in the browser
+
+function MyApp({ Component, pageProps, URL }) { + const isBrowser = typeof window !== 'undefined'; + return ( + <> + {isBrowser && <CustomEls URL={URL} />} + <Component {...pageProps} /> + </> + ); +} +
This page explains how to integrate Nebula with a Rails app.
++ This is a community-maintained document. Please ask the community if + you have questions about this integration. You can also + suggest improvements + to make it better. +
+This integration has been tested with the following:
+To get started using Nebula with Rails, the following packages must be installed.
++yarn add @onsonr/nebula copy-webpack-plugin +
+ The next step is to import Nebula’s default theme (stylesheet) in
+ app/javascript/stylesheets/application.scss
.
+
+@import '@onsonr/nebula/dist/themes/light'; +@import '@onsonr/nebula/dist/themes/dark'; // Optional dark theme +
+ Fore more details about themes, please refer to + Theme Basics. +
+
+ After importing the theme, you’ll need to import the JavaScript files for Nebula. Add the following code to
+ app/javascript/packs/application.js
.
+
+import '../stylesheets/application.scss' +import { setBasePath, SlAlert, SlAnimation, SlButton, ... } from '@onsonr/nebula' + +// ... + +const rootUrl = document.currentScript.src.replace(/\/packs.*$/, '') + +// Path to the assets folder (should be independent from the current script source path +// to work correctly in different environments) +setBasePath(rootUrl + '/packs/js/') +
+ Next we need to add Nebula’s assets to the final build output. To do this, modify
+ config/webpack/environment.js
to look like this.
+
+const { environment } = require('@rails/webpacker'); + +// Nebula config +const path = require('path'); +const CopyPlugin = require('copy-webpack-plugin'); + +// Add shoelace assets to webpack's build process +environment.plugins.append( + 'CopyPlugin', + new CopyPlugin({ + patterns: [ + { + from: path.resolve(__dirname, '../../node_modules/@onsonr/nebula/dist/assets'), + to: path.resolve(__dirname, '../../public/packs/js/assets') + } + ] + }) +); + +module.exports = environment; +
+ The final step is to add the corresponding pack_tags
to the page. You should have the following
+ tags
in the <head>
section of
+ app/views/layouts/application.html.erb
.
+
+<!doctype html> +<html> + <head> + <!-- ... --> + + <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag + 'application', 'data-turbolinks-track': 'reload' %> + </head> + <body> + <%= yield %> + </body> +</html> +
Now you can start using Nebula components with Rails!
+