@paradox-design/components
v0.3.0
Published
Paradox Design System components — framework-agnostic web components built with Lit
Maintainers
Readme
@paradox-design/components
Paradox Design System components as web components built with Lit. They work in any framework and without one.
Installation
npm install @paradox-design/components @paradox-design/tokens @paradox-design/themesTokens and a theme are required — components contain no hard-coded values; every color and dimension is read from CSS custom properties.
lit is a peer dependency: npm 7+ and pnpm install it automatically, and the
application ends up with a single copy shared by all components. If your project already
uses Lit, it must be version 3.
Upgrade all
@paradox-design/*packages together, to their latest versions. In0.xa minor release may introduce breaking changes between packages.
Quick start
import '@paradox-design/tokens/css';
import '@paradox-design/themes';
import '@paradox-design/components/define';<html data-pdx-theme="default">
<pdx-button variant="primary">Save</pdx-button>
</html>Registering individual components
Importing @paradox-design/components/define registers everything. When bundle size
matters, register only what you use:
import '@paradox-design/components/button/define';Importing from the package root (import { PdxButton } from '@paradox-design/components')
gives you the classes only, without registration — useful when you want to extend a
component or give it your own tag name.
<pdx-button>
Properties
| Attribute | Type | Default | Description |
| ------------ | ----------------------------------------------- | --------- | ------------------------------------- |
| variant | primary | secondary | danger | ghost | primary | Family of action tokens |
| size | sm | md | lg | md | Height, spacing and icon size |
| disabled | boolean | false | Disables the button natively |
| loading | boolean | false | Operation in progress — blocks clicks |
| full-width | boolean | false | Stretches to the container width |
| type | button | submit | reset | button | Behavior towards the form |
| href | string | — | Turns the button into a link (<a>) |
| target | string | — | As in a native <a> |
| aria-label | string | — | Required for an icon-only button |
Slots
| Slot | Purpose |
| --------- | --------------------- |
| default | Label |
| prefix | Icon before the label |
| suffix | Icon after the label |
Parts
base, label, prefix, suffix, spinner — available through ::part().
Forms
The button works with forms despite Shadow DOM:
<form>
<input name="title" required />
<pdx-button type="submit">Save</pdx-button>
</form>Why this needed dedicated work A native
<button type="submit">hidden in a shadow root cannot see the outer<form>— the shadow boundary cuts it off from its owner. The button looks correct and does absolutely nothing. The component solves this withformAssociatedandElementInternals, callingrequestSubmit(), which runs native validation.form.submit()would skip it and submit the form despite errors.
Loading state vs disabled state
They are not the same, and the component deliberately keeps them apart:
disabled— the action is unavailable, the element leaves keyboard navigation;loading— an operation is in progress, the element stays in the focus order and reportsaria-disabledandaria-busy.
If the loading state set native
disabled, focus would jump to the start of the document mid-operation, and the screen reader would lose context.
<pdx-icon>
An icon from the active icon set. Out of the box that is the Paradox set
(@paradox-design/icon-set) — no configuration needed:
<pdx-icon name="check" label="Done"></pdx-icon>A client with their own icons replaces the set entirely, once, before the first render:
import { setIconSet } from '@paradox-design/icons';
setIconSet({
check: '<svg viewBox="0 0 24 24">…</svg>',
});Sets are never mixed — see @paradox-design/icons. Already rendered
icons re-resolve after setIconSet().
| Attribute | Description |
| --------- | --------------------------------------------------------------------------- |
| name | Name in the active icon set |
| label | Label for screen readers. Without it the icon is decorative (aria-hidden) |
Size and color are inherited from the text, so inside a button it adapts on its own.
Customizing the look
From the least to the most invasive:
Theme tokens — change the look of all components at once;
Component tokens — work through plain CSS custom properties:
pdx-button { --pdx-button-radius: 0; }Full list for
<pdx-button>:| Token | Meaning | | ----------------------------- | -------------------------------- | |
--pdx-button-bg| Background | |--pdx-button-bg-hover| Background on hover | |--pdx-button-bg-active| Background while pressed | |--pdx-button-bg-disabled| Background in the disabled state | |--pdx-button-fg| Text and icon color | |--pdx-button-fg-disabled| Text color in the disabled state | |--pdx-button-border-color| Border color | |--pdx-button-border-width| Border width | |--pdx-button-radius| Corner radius | |--pdx-button-height| Height | |--pdx-button-padding-inline| Horizontal padding | |--pdx-button-gap| Gap between icon and label | |--pdx-button-icon-size| Icon size | |--pdx-button-font-family| Font family | |--pdx-button-font-size| Font size | |--pdx-button-font-weight| Font weight |For
<pdx-icon>:--pdx-icon-sizeand--pdx-icon-color.::part()— full control over a selected internal element:pdx-button::part(base) { letter-spacing: 0.05em; }
License
MIT © Paradox Software
