Quick start
Copy a component directory, serve it over HTTP, load the tokens once and register the tag.
1. Copy the files you need
Each component is a directory in dist/components/. Copy the component directory and the shared token stylesheet into your app. Nothing installs from a registry: these files are the artifact.
mkdir -p app/vendor/easy
cp -R dist/components/button app/vendor/easy/
cp -R dist/components/stretch-switch app/vendor/easy/
cp dist/components/tokens.css app/vendor/easy/
The directory contains the class module, define.js (registration only), declarations and a per-component package.json whose sideEffects names only ./define.js. Class modules are side-effect free.
2. Serve it over HTTP
Open the page through a server, not file://. Module scripts are fetched with CORS rules, and a file: origin cannot load the component modules or their imports, so the tags stay undefined. Any static server works:
npx serve app
3. Load the tokens once
<link rel="stylesheet" href="/vendor/easy/tokens.css">
The component modules carry their own component CSS. Do not also load a component's styles.css: that second copy is paid for twice and can drift from the module you shipped.
4. Register the tags
Either load the registration module:
<script type="module" src="/vendor/easy/button/define.js"></script>
or import it from your entry module, which is what a bundler should see:
import './vendor/easy/button/define.js';
import './vendor/easy/stretch-switch/define.js';
define.js registers exactly one tag and is idempotent. If the tag is already defined, it keeps the existing definition and reports the conflict instead of throwing. Importing a class module on its own does not register anything: the tag is defined by define.js (or by calling customElements.define yourself).
5. Use attributes, properties, slots and events
<detent-button>Save</detent-button>
<detent-button is-loading>Saving…</detent-button>
<detent-stretch-switch name="notify">
<span slot="label">Email me</span>
</detent-stretch-switch>
Attributes describe initial state; properties are the live API. Programmatic writes do not emit user-change events.
const toggle = document.querySelector('detent-stretch-switch');
toggle.checked = true; // no detent-change event: this is not a user action
toggle.addEventListener('detent-change', (event) => {
console.log(event.detail.checked);
});
Inside a form, the switch participates natively: the submitted FormData carries notify=on when checked and omits it when unchecked, exactly like a checkbox.
6. Compose through one entry
Import every define.js you need from one module, then let your bundler follow the import graph:
// app.js
import './vendor/easy/button/define.js';
import './vendor/easy/stretch-switch/define.js';
import './vendor/easy/morphing-dialog/define.js';
Some modules share code: 29 of them inline the same spring helper, and a picker-like component imports the catalogued component it embeds. Those imports resolve to sibling directories — for example date-picker/date-picker.js imports ../calendar/calendar.js — so copy the dependency directory too (the catalog row lists it). A bundler can then pay for the shared spring once; unbundled, each module pays for its own copy. The cost page measures both extremes.
Contract edges
- No server-side rendering: the elements upgrade in the browser after their modules load.
- With JavaScript unavailable, only the light-DOM slot content is visible; the component's shadow UI does not exist yet.
- The tag prefix is
detent-; this is the first release, so replaced names have no compatibility aliases.
Documentation pages
Component reference
Every catalogued component has a page at /components/<slug>/; start from the component directory.
Routes
A static host also serves this file at these recorded paths.
/doc/quick-start#/docs/quick-start