Guide
Get started
Install Slab, import the elements you use, and make them yours. Everything you need, in order.
01 Install it
Slab is one package, slab-elements, with Lit as a peer dependency.
pnpm add slab-elements lit
02 Import what you use
Each element is its own module, and importing it registers its tag. Import the ones you use, and nothing else ships. An element made of parts, like Tabs, has a module for each part.
import 'slab-elements/switch.js';
import 'slab-elements/tabs.js';
import 'slab-elements/tabs.tab.js';
import 'slab-elements/tabs.panel.js';
Any bundler resolves the imports, or an import map does without one.
03 Use it like a native element
Attributes in, events out, the same as every built-in element. The events are the ones its native element fires, like "change", "input" and "toggle", and they bubble, so you can listen higher up too.
<slab-switch label="Notifications" name="notifications"></slab-switch>
<script type="module">
import 'slab-elements/switch.js';
const toggle = document.querySelector('slab-switch');
toggle.addEventListener('change', () => {
console.log(toggle.checked);
});
</script>
Every control needs a label, and one without throws rather than going unnamed. hide-label hides it from view and keeps it for screenreaders. Like a native control, the checked and value attributes are where it starts, and the properties are where it is now.
04 In your framework
There's no wrapper package. The tag is the component, in React, Vue, Svelte, Rails, or plain HTML. React listens for events through a ref, Svelte needs nothing, and Vue needs to know which tags are custom elements. The home page shows the same Disclosure in each.
import vue from '@vitejs/plugin-vue';
export default {
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('slab-'),
},
},
}),
],
};
05 Put it in a form
Slab's controls are form-associated, so they work in a plain <form> the way an <input> does. They submit their name and value, reset with the form, and come back when the browser restores the page. Validation is the native element's too: required, pattern and the rest mean what they mean on an <input>, and checkValidity(), reportValidity() and setCustomValidity() work the same.
<form>
<slab-text-field
label="Email"
name="email"
type="email"
required
></slab-text-field>
<slab-checkbox
label="Send me updates"
name="updates"
value="yes"
></slab-checkbox>
<button>Sign up</button>
</form>
There's a <slab-form> too, if you'd like the form itself to be a Slab element.
06 Style it
Nothing ships styled, so an element has no size and nothing to see until you give it one. Reach its pieces with ::part() and its conditions with :state(). Every component's page lists both.
slab-checkbox::part(checkbox) {
block-size: 1rem;
border: 1px solid currentcolor;
inline-size: 1rem;
}
slab-checkbox:state(checked)::part(checkbox) {
background-color: currentcolor;
}
07 Make it your own
To go further, subclass it, set styles, and register it under a tag of your own. Slab's functional styles, like a label that's hidden but still read, survive yours.
import Checkbox from 'slab-elements/checkbox.js';
import { css } from 'lit';
customElements.define(
'acme-checkbox',
class extends Checkbox {
static styles = css`
/* ... */
`;
},
);
Every component's page has a styled version to start from, with its source to copy and change.
08 Your words, in any language
Slab brings no text of its own. Where an element has to make some, mostly names for screenreaders, it's English until you set an attribute, like Alert's dismiss-label. Text built from a value, like Pagination's "Page 3", is a function, so you can word and order it for your language.
pagination.pageLabel = (page) => `Seite ${page}`;
09 TypeScript
The types come with the package. Each element adds its tag to HTMLElementTagNameMap, so document.querySelector('slab-switch') is typed as Slab's Switch. There are no JSX types yet, so in React with TypeScript, declare the tags you use yourself.
10 Browsers
Slab is tested in Chromium, Firefox and WebKit. Where a browser hasn't caught up on a feature, Slab uses it where it's there and leaves the platform's behavior where it isn't. Without CSS anchor positioning, for example, a popover is centered rather than placed beside its anchor. Every element still opens, closes, submits and is announced, everywhere.