A collection of self-contained Lit web components sharing a
Tailwind-inspired design token system. Every component is individually
importable and ships its own TypeScript types plus a checked-in
custom-elements.json manifest.
npm install @f-ewald/componentsImport the whole library (registers every component):
import "@f-ewald/components";...or import components individually — this tree-shakes everything else,
including the d3 dependency used only by the chart components:
import "@f-ewald/components/confirm-dialog.js";
import "@f-ewald/components/roman-numeral.js";<confirm-dialog open confirm-label="Delete" cancel-label="Cancel">
Are you sure you want to delete this item?
</confirm-dialog>
<roman-numeral value="2004"></roman-numeral>| Component | Docs |
|---|---|
<address-autocomplete> |
API reference |
<animate-confetti> |
API reference |
<autocomplete-input> |
API reference |
<calendar-entry> |
API reference |
<calendar-month> |
API reference |
<calendar-year> |
API reference |
<chat-message> |
API reference |
<confirm-dialog> |
API reference |
<copy-link-button> |
API reference |
<data-table> |
API reference |
<distance-value> |
API reference |
<distribution-chart> |
API reference |
<dropdown-button> |
API reference |
<editable-text> |
API reference |
<form-select> |
API reference |
<frame-box> |
API reference |
<gallery-item> |
API reference |
<gallery-item-variant> |
API reference |
<icon-button> |
API reference |
<kbd-hint> |
API reference |
<live-timer> |
API reference |
<map-circle> |
API reference |
<map-pin> |
API reference |
<percent-bar-chart> |
API reference |
<photo-gallery> |
API reference |
<popover-panel> |
API reference |
<price-history-chart> |
API reference |
<radio-cards> |
API reference |
<radio-pills> |
API reference |
<relative-time> |
API reference |
<reveal-button> |
API reference |
<roman-numeral> |
API reference |
<slide-panel> |
API reference |
<stat-meter> |
API reference |
<status-pill> |
API reference |
<tile-grid> |
API reference |
<toast-notification> |
API reference |
<ui-button> |
API reference |
<user-avatar> |
API reference |
<weight-bar-chart> |
API reference |
Each doc lists the component's attributes/properties, events, slots, and the
--ui-* CSS custom properties it consumes. For a machine-readable summary of
the whole library in one file, see llms.txt.
Components use Lit css with var(--ui-*, <fallback>) custom properties, so
they render correctly out of the box with zero external CSS — every
token has a sensible default baked in as the var() fallback.
To retheme, override any --ui-* custom property on :root (or a closer
ancestor):
:root {
--ui-primary: #0ea5e9;
--ui-radius: 0.75rem;
}Or import the generated stylesheet as a starting point and edit it:
import "@f-ewald/components/tokens.css";tokens.css also ships a dark palette (see darkTokenValues in
src/tokens.ts), applied automatically via
@media (prefers-color-scheme: dark). A consumer can force either mode
regardless of the OS preference by setting data-theme="dark" or
data-theme="light" on <html> — that attribute wins in both directions.
Components need no changes to support this: every token is read via
var(--ui-x, fallback) at its point of use, so it just follows whatever
:root resolves to. (Note: tokens — the :host stylesheet component
files import alongside their own css block — intentionally declares no
custom properties itself; an earlier version re-declared them there as
--ui-x: var(--ui-x, fallback), which computed to the guaranteed-invalid
value instead of the inherited one, silently discarding whatever :root
set. Don't reintroduce that pattern.)
The full token set is defined in src/tokens.ts.
Typography, spacing, control, icon, radius, panel, chart, and accessibility
measurements follow the canonical
docs/design-language.md contract and are checked
by npm run test:design.
npm run mcp (or node dist/mcp-server.js after npm run build) starts a
stdio MCP server exposing the component
catalog to AI coding assistants, with two tools:
list_components— every tag + one-line description.get_component_docs(tag)— the full generated Markdown doc for one tag (install snippet, usage example, attributes/properties, events, slots, CSS custom properties).
It's read-only over the same custom-elements.json/docs/*.md this package
already generates via npm run docs — no separate data source to maintain.
A consuming project wires it up with a .mcp.json at its repo root:
{
"mcpServers": {
"f-ewald-components": {
"command": "node",
"args": ["/absolute/path/to/components/dist/mcp-server.js"]
}
}
}See the "MCP server" section in CLAUDE.md for more, and
docs/mcp-evaluation.md for why this was built
now rather than earlier.
npm install
npm run devOpens a live playground (index.html) with a rendered, hand-testable example
of every component, plus a copy-paste usage snippet for each. Component
sources are imported directly from src/, so edits hot-reload.
npm run build:demo # static build of the playground, into demo-dist/
npm run build:site # docs + playground Pages artifact, into pages-dist/
npm run preview:site # preview the exact Pages artifact locally
npm run test:site # smoke-test the built documentation and playgroundThe combined site is deployed automatically from main to
GitHub Pages. The workflow reads the
repository and publishes pages-dist/; it has no permission or persisted
credential capable of changing repository files, branches, tags, or pull
requests.
| Command | Purpose |
|---|---|
npm run dev |
Start the Vite playground with HMR. |
npm run typecheck |
Check TypeScript without writing build output. |
npm run build |
Compile src/ with tsc into dist/, and generate dist/tokens.css. |
npm run build:demo |
Build the static playground into demo-dist/. |
npm run build:site |
Build the static documentation and nested playground into pages-dist/. |
npm run preview:site |
Preview the built Pages artifact locally. |
npm run icons |
Regenerate src/icons.ts from the Heroicons package. |
npm run analyze |
Regenerate custom-elements.json via the custom-elements-manifest analyzer. |
npm run docs |
Regenerate the manifest, docs/*.md, and llms.txt. |
npm run mcp |
Run the MCP server (dist/mcp-server.js) directly, for manual testing. |
npm run test |
Run the Playwright suite against the playground. |
npm run test:design |
Check catalog ordering, token fallbacks, and source design contracts. |
npm run test:site |
Run the Playwright smoke suite against pages-dist/. |
- New components live in
src/, are exported fromsrc/index.ts, restyled to the--ui-*design tokens, and get a playground section (index.html) and a Playwright spec (tests/). SeeCLAUDE.mdfor the full checklist. - Relative imports within
src/must use.jsspecifiers (not.ts), so thetsc-emitteddist/output resolves correctly for consumers. - Run
npm run docsafter changing any component's public API so the generated docs andllms.txtstay in sync. npm run build:siteonly reads those checked-in documentation inputs and writes ignoredpages-dist/; it never updates tracked generated files.- Component styling and interaction changes follow
docs/design-language.md.
Releases are published automatically by
.github/workflows/npm-publish.yml
when a strict vX.Y.Z tag is pushed. The workflow validates the tag against
both package manifests, runs the full prepublishOnly gate, and publishes via
npm Trusted Publishing with provenance—no NPM_TOKEN repository secret.
Configure the @f-ewald/components npmjs package once with a GitHub Actions
Trusted Publisher for owner f-ewald, repository components, workflow
npm-publish.yml, no environment, and Allowed actions: npm publish. Then
release only after all source and generated-doc changes are committed:
npm version <patch|minor|major>
git push origin main --follow-tagsnpm version must create the matching annotated vX.Y.Z tag on the version
commit; every version bump requires that tag, beginning with v1.0.0. The
initial local v1.0.0 tag was moved to the release-workflow commit before
its first push; published tags must never be moved.