Vanilla
Install
Section titled “Install”pnpm add @timelessui/componentsCSS and registration
Section titled “CSS and registration”Import styles and definitions from an application module:
import '@timelessui/components/css/tokens.css'import '@timelessui/components/css/button.css'import '@timelessui/components/css/toggle.css'import '@timelessui/components/define/ui-toggle-group'Then author normal HTML:
<ui-toggle-group selection="single" aria-label="Alignment"> <button class="ui-button ui-toggle" value="left" aria-pressed="true">Left</button> <button class="ui-button ui-toggle" value="center" aria-pressed="false">Center</button></ui-toggle-group>The same markup is emitted by a server or copied into a static HTML document. The complete Light DOM is readable before registration, then the explicit definition upgrades it in the browser.
Attributes, properties, and events
Section titled “Attributes, properties, and events”Properties are useful for live state. Attributes provide authored defaults and reset state:
const group = document.querySelector('ui-toggle-group')group.addEventListener('ui-before-change', (event) => { if (!canChange(event.detail.value)) event.preventDefault()})group.addEventListener('ui-change', (event) => save(event.detail.value))Each element declares the detail type it dispatches, so event.detail is typed per element rather
than shared: a ui-toggle-group change carries ToggleGroupChangeDetail. No hydration runtime is
required.
Editors and TypeScript
Section titled “Editors and TypeScript”There is no JSX layer here, so nothing type-checks your HTML by default. Two things close that gap:
- Register the shipped editor data once, and
ui-*tags, their attributes, and their permitted values complete in any.htmlfile. See Editor setup. - For markup assembled in TypeScript,
@timelessui/components/attributesgives CSS-only components a typed surface, and@timelessui/components/validatereports authored values that no contract permits:
import { uiAttributeString } from '@timelessui/components/attributes'
const markup = `<button ${uiAttributeString('button', { variant: 'primary' })} type="button">Publish</button>`
if (import.meta.env.DEV) { const { validateTimelessMarkup } = await import('@timelessui/components/validate') validateTimelessMarkup()}The validator walks the document and warns about a data-ui-* attribute no contract declares or a
value outside its permitted set — the class of typo that renders without complaint.