Packages and entrypoints
Packages
Section titled “Packages”| Package | Role |
|---|---|
@timelessui/components |
Everything a consumer needs: CSS, custom elements, contracts, and utilities. This is the only package you install. |
@timelessui/core |
The internal custom-element authoring layer the components are built on. It arrives as a dependency; you do not import it directly, and it is not part of the public API. |
pnpm add @timelessui/componentsEntrypoints
Section titled “Entrypoints”| Import | Contains | Side effects |
|---|---|---|
@timelessui/components |
Contracts, design-token names, permitted attribute values, event types, and every element class. | None |
@timelessui/components/{element} |
One enhanced element’s class and helpers, for example @timelessui/components/dialog. |
None |
@timelessui/components/define/ui-{element} |
Registers one custom element. | Calls customElements.define |
@timelessui/components/define |
Registers every public custom element. | Calls customElements.define |
@timelessui/components/css/{component}.css |
One component’s styles. | Stylesheet |
@timelessui/components/css/components.css |
Every component’s styles, tokens included. | Stylesheet |
@timelessui/components/css/tokens.css |
Design tokens and the cascade-layer order. | Stylesheet |
@timelessui/components/color |
Color parsing, conversion, and gamut utilities. | None |
@timelessui/components/collection |
Locale-aware matching and disabled-aware keyboard navigation. | None |
@timelessui/components/events |
Transition event and detail types. | None |
@timelessui/components/value-state |
Authored-default and live-value helpers. | None |
@timelessui/components/attributes |
Typed attribute builder for CSS-only components. | None |
@timelessui/components/validate |
Development-time check that authored markup matches the contracts. | None |
@timelessui/components/react |
React 19 intrinsic-element declarations. Types only. | None |
@timelessui/components/preact |
Preact intrinsic-element declarations. Types only. | None |
@timelessui/components/solid |
Solid intrinsic-element declarations. Types only. | None |
@timelessui/components/vue |
Vue GlobalComponents declarations. Types only. |
None |
@timelessui/components/svelte |
Svelte svelteHTML.IntrinsicElements declarations. Types only. |
None |
Only enhanced elements have a class entrypoint. CSS-only primitives such as Button, Alert, and Card are classes and attributes rather than JavaScript, so they have no per-component module at all — their permitted values live on the root import instead.
Contracts and permitted values
Section titled “Contracts and permitted values”The root import is the machine-readable version of every component reference page:
import { buttonVariants, componentContracts } from '@timelessui/components'
buttonVariants// ['primary', 'secondary', 'outline', 'ghost', 'danger', 'danger-outline', 'link']
componentContracts.tabs.parts.filter((part) => part.required).map((part) => part.selector)// ["[role='tablist']", "[role='tab']", "[role='tabpanel']"]componentContracts records each component’s root, stylesheets, attributes with their permitted
values and defaults, authored parts, public state, and events. Every attribute that takes a fixed
set of values also names the array that set is exported as, in its set field.
The build proves those values against the stylesheets in both directions: a value a stylesheet
selects must be declared, and a declared value must be selected or be the default, which is the base
rule and so has no selector of its own. pnpm contracts:validate fails either way, which is why the
reference tables cannot drift from the CSS.
Permitted values are also exported individually as as const arrays with matching union types, each
declared exactly once and generated from the same source as the contracts: buttonVariants,
buttonSizes, alertVariants, spinnerVariants, badgeVariants, avatarShapes,
avatarStatuses, cardVariants, linkVariants, listVariants, separatorVariants,
separatorOrientations, skeletonShapes, skeletonWidths, groupOrientations, primitiveSizes,
primitiveDensities, compactDensities, tableAlignments, formControlSizes, fieldLayouts,
formDensities, choiceGroupOrientations, floatingPlacements, tabsOrientations,
tabsActivations, dialogKinds, sheetPositions, popoverRoles, hoverCardVariants,
menuOrientations, toolbarOrientations, toasterPlacements, toasterStacks,
toggleGroupOrientations, toggleGroupSelections, and colorPickerFormats.
Arrays with identical values keep separate names because they are separate public exports.
buttonSizes, primitiveSizes, and formControlSizes are all sm | md | lg, and each names the
set its own components implement.
Design tokens
Section titled “Design tokens”atmosphereTokenGroups names every public custom property, grouped by purpose, and
isAtmosphereToken narrows a string against the set. See Theming for the
values and how to override them.
Custom Elements Manifest
Section titled “Custom Elements Manifest”@timelessui/components/custom-elements.json is exposed through the package’s customElements
field. It declares attributes with union types and defaults, reflecting properties under their real
names, events with the detail type each element actually dispatches, CSS custom properties, and
custom states — for editors, generators, and downstream tooling.
Authored parts appear under a namespaced timeless:parts key rather than the manifest’s cssParts,
each with the selector it is addressed by. cssParts describes ::part(), which only crosses a
shadow boundary; Timeless anatomy is Light DOM you author yourself, so [data-ui-part~='trigger']
is the real contract and claiming a shadow one would be wrong.
Framework typings
Section titled “Framework typings”Every framework typing is generated from the manifest and is types-only: importing one adds no runtime code, no wrapper, and no dependency on that framework. Import the one you need once in your application types.
| Import | Augments |
|---|---|
@timelessui/components/react |
react and react/jsx-runtime JSX |
@timelessui/components/preact |
preact and preact/jsx-runtime JSX |
@timelessui/components/solid |
solid-js JSX |
@timelessui/components/vue |
@vue/runtime-dom GlobalComponents |
@timelessui/components/svelte |
svelteHTML.IntrinsicElements |
Each declares, per element, the attributes an author writes typed to their permitted values, the DOM
properties those attributes reflect under the element’s real property names, and the element’s own
events with its own detail type. data-* and aria-* stay open as an escape hatch.
Two boundaries worth knowing:
- React 19 is required for
on*event props on custom elements and for non-string attribute values. On React 18 the declarations still type your markup; pass primitives as attributes and bind events with a ref. - Angular has no type-level checking for unknown elements beyond
CUSTOM_ELEMENTS_SCHEMA, which disables checking rather than adding it. There is nothing to ship for Angular, so nothing is shipped. Timeless elements work there; their attributes are simply unchecked.
Qwik is not covered. Its JSX module moved between major versions and its custom-event prop convention differs from the frameworks above, so no declaration is published rather than one that might be wrong.
Authoring CSS-only components
Section titled “Authoring CSS-only components”ui-* tags are tags, so every typing above and the editor data complete them precisely. CSS-only
components are a root class plus data-ui-* on a native tag, which no editor can complete per
element. @timelessui/components/attributes is the typed answer:
import { uiAttributes } from '@timelessui/components/attributes'
uiAttributes('button', { variant: 'primary', size: 'lg' })// { class: 'ui-button', 'data-ui-variant': 'primary', 'data-ui-size': 'lg' }uiAttributeString returns the same attributes serialized for a template literal. Both are opt-in
and neither is reachable from the default import. See Editor setup
for the full explanation and for the development-time validator.
Server rendering
Section titled “Server rendering”Every import above is side-effect free except the stylesheets and the define/* entrypoints. Class
imports never call customElements.define, so they are safe in server code and test utilities. Keep
define/* imports in browser-only modules. See
Custom-element registration.