polymorph-ui-components
v0.5.0
Published
A themeable Svelte 5 UI component library with CSS custom property driven styling
Maintainers
Readme
polymorph-ui-components
One set of components. Every design system. Zero overrides.
A themeable Svelte 5 component library where every visual property is a CSS custom property. Unstyled by default — you bring the design system, the components render it.
npm install polymorph-ui-componentsWhy it's different
Most component libraries ship with a fixed look. Re-skinning them means fighting !important, patching internals through :global(), or forking the whole thing.
polymorph takes the opposite stance: components own behavior, accessibility, and structure — and stay completely opinion-free about appearance. Every color, space, radius, shadow, and font is a var(--…) you control from your own stylesheet. No source changes. No wrapper divs. No fights.
<!-- The same Button, themed three different ways — no overrides -->
<div class="brand"><Button text="Continue" /></div>
<div class="danger"><Button text="Delete" /></div>
<div class="ghost"><Button text="Cancel" /></div>
<style>
.brand {
--button-color: #6d28d9;
--button-text-color: #fff;
--button-border-radius: 8px;
}
.danger {
--button-color: #e11d48;
--button-text-color: #fff;
}
.ghost {
--button-color: transparent;
--button-text-color: #111;
--button-border: 1px solid #ddd;
}
</style>📐 The full reasoning behind this approach lives in DESIGN_PRINCIPLES.md.
✨ Highlights
| | |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| 🎨 Theme with CSS variables | Every visual decision is a var(--component-element-property). Define a theme once on an ancestor; the whole subtree inherits it. |
| 🧩 50+ behavioral primitives | Modals, sheets, menus, comboboxes, calendars, tables, toasts, steppers — focus traps, roving tabindex, and state machines included. |
| 🌍 Framework-agnostic | Ships as a Svelte library and as native Web Components (<pui-*>). Use it in React, Vue, Angular, or plain HTML. |
| 🤖 AI-native | A bundled MCP server exposes every component's props, events, and CSS variables to your AI coding assistant. |
| ♿ Accessible by default | ARIA wiring, keyboard navigation, focus management, and semantic HTML are baseline — not a premium add-on. |
| ⚡ Built on Svelte 5 | $props, $state, $derived, $bindable, and Snippet throughout. Fully typed, tree-shakeable. |
🚀 Quick Start
<script lang="ts">
import { Button, Input, Toggle } from 'polymorph-ui-components';
let dark = $state(false);
</script>
<Button text="Submit" onclick={() => console.log('clicked')} />
<Input
value=""
placeholder="Enter email"
dataType="email"
onstatechange={(state) => console.log(state)}
/>
<Toggle checked={dark} text="Dark mode" onclick={(on) => (dark = on)} />🎨 Theming in 30 seconds
Every component reads its visuals from CSS variables with sensible neutral defaults. Define them on any ancestor and you have a theme:
<div class="my-design-system">
<Button text="Save" onclick={save} />
<Input value="" placeholder="Search…" />
</div>
<style>
.my-design-system {
--button-color: #0070f3;
--button-text-color: #fff;
--button-border-radius: 6px;
--button-padding: 10px 20px;
--input-background: #fafafa;
--input-border: 1px solid #eaeaea;
--input-radius: 6px;
--input-focus-border: 1px solid #0070f3;
--modal-border-radius: 12px;
--modal-overlay-background-color: #00000066;
}
</style>Variants are your CSS classes, not our props. Need a danger button? Write .btn-danger { --button-color: #e11d48 } and pass classes="btn-danger". Because variables cascade, you can even scope different themes to different parts of the same page.
The naming convention is predictable everywhere:
--{component}-{element}-{property}--button-color · --input-error-msg-text-color · --modal-footer-primary-button-border-radius
Each component documents its complete variable surface in docs/.
🌍 Use it anywhere (Web Components)
The same components compile to framework-agnostic custom elements — theming works identically because it's pure CSS.
<script type="module" src="polymorph-ui-components/wc"></script>
<pui-button text="Save"></pui-button>
<pui-input placeholder="Search…"></pui-input>
<style>
pui-button {
--button-color: #0070f3;
--button-text-color: #fff;
}
</style>Drop them into React, Vue, Angular, Astro, or a plain .html file — no build step required.
🤖 AI-native (MCP server)
A companion Model Context Protocol server ships as a separate package so your AI assistant can query the component catalogue directly — props, events, types, and every CSS variable.
npm install polymorph-ui-components-mcp// .mcp.json (or any MCP client config)
{
"mcpServers": {
"polymorph-ui": {
"command": "npx",
"args": ["-y", "polymorph-ui-components-mcp"]
}
}
}Now you can ask: "Using the polymorph-ui MCP, theme the Select to match my brand and map my custom option rendering onto its snippets." — and the assistant has the real API in context.
🧩 Components
| Component | Description | |
| --------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| Button | Action trigger with circular loader, progress bar, icon/children snippets, and aria-expanded. | docs |
| Input | Text field with built-in validation (email, phone, password, custom patterns), text transformers, and textarea mode. | docs |
| InputButton | Input fused with action buttons — search bars, OTP entry, coupon codes. | docs |
| Select | Dropdown with searchable single-select and multi-select (dismissible pills) plus custom content slots. | docs |
| Combobox | Autocomplete input with filtered listbox, aria-activedescendant, and keyboard navigation. | docs |
| Toggle | Labeled on/off switch with sliding animation. | docs |
| Checkbox | Tri-state checkbox with custom SVG checkmark and aria-checked=mixed. | docs |
| Radio | Grouped radio with custom indicator over a native input. | docs |
| Slider | Range slider with min/max/step and derived fill. | docs |
| Choicebox | Selectable option box with radio/checkbox semantics and custom content. | docs |
| SplitInput | Segmented input (OTP / PIN) with paste distribution and auto-advance. | docs |
| ColorPicker | HSV color picker with pointer-drag saturation panel and hue slider. | docs |
| Calendar | Date / range picker with roving-tabindex grid and Intl formatting. | docs |
| Component | Description | | | ----------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------ | | Modal | Dialog overlay with size/alignment, header, footer, transitions, scroll-lock, and back-press support. | docs | | Sheet | Slide-in panel from any edge with header, scrollable body, footer, and focus trap. | docs | | Menu | Dropdown action menu with keyboard navigation, typeahead, disabled/danger items, and separators. | docs | | ContextMenu | Right-click menu with separators, disabled/danger items, and keyboard navigation. | docs | | CommandMenu | Command palette (Ctrl/Cmd+K) with search, grouped commands, and keyboard navigation. | docs | | Tooltip | Hover/focus tooltip with configurable position and delay. | docs | | ModalAnimation / OverlayAnimation | Fly/fade transition wrappers. | docs |
| Component | Description | | | ---------------------------------- | ----------------------------------------------------------------------- | ----------------------------- | | Table | Sortable data table with sticky headers and per-cell scrolling. | docs | | ListItem | Multi-section list row with images, labels, and accordion expansion. | docs | | Avatar | Circular avatar with image fallback or derived initials. | docs | | Badge | Icon with a numeric/text badge overlay. | docs | | Pill | Compact label/tag, optionally clickable with a11y. | docs | | Icon / IconStack / Img | Clickable icon, overlapping icon stack, image with load-error fallback. | docs | | GridItem | Grid cell with icon, label, and loading overlay. | docs | | RelativeTime | Auto-updating "5 minutes ago" with locale support. | docs | | KeyboardInput | Keyboard-shortcut display with styled key caps. | docs |
| Component | Description | |
| ---------------------- | -------------------------------------------------------------------- | ----------------------------- |
| Tabs | Tabbed interface with animated indicator and overflow scrolling. | docs |
| Pagination | Windowed page navigation with ellipsis truncation. | docs |
| Stepper / Step | Multi-step progress indicator with completed/active/pending states. | docs |
| Accordion | Collapsible container with CSS grid animation. | docs |
| Carousel | Swipeable content slider with autoplay and pagination dots. | docs |
| Scroller | Overflowing list with arrow nav, gradient edges, and drag-to-scroll. | docs |
| CheckListItem | Checklist row with checkbox, label, and checked state. | docs |
| Toolbar | Header bar with back button, title, and customizable content areas. | docs |
| ThemeSwitcher | Segmented light/dark/system control with prefers-color-scheme. | docs |
| Component | Description | | | ------------------------------------------ | ----------------------------------------------------------------------- | ------------------------ | | Toast | Animated slide-in notification with auto-dismiss and per-direction fly. | docs | | Banner | Notification banner with icon snippet, link text, and dismiss. | docs | | Progress | Animated horizontal progress bar (determinate / indeterminate). | docs | | Gauge | Full-circle ring gauge (0–100) with animated SVG arc. | docs | | Loader / LoadingDots / Shimmer | Spinner, animated dots, and skeleton shimmer — all CSS-variable themed. | docs |
| Component | Description | | | ---------------------------------- | ------------------------------------------------------------------------------------------ | --------------------------- | | SplitButton | Primary action button with a dropdown of secondary actions. | docs | | Snippet | Code/command block with a copy button. | docs | | Book / Browser / Phone | Decorative frames — flip-book, browser chrome, device mockup — for previews and marketing. | docs |
♿ Accessibility & Svelte 5
Interactive components own their accessibility: focus traps (Modal, Sheet), roving tabindex (Calendar, Menu, Tabs), aria-activedescendant wiring (Combobox, Select), live regions (Toast), and internal Enter/Space handling on every custom control.
Under the hood it's modern Svelte 5 — $props(), $state()/$derived(), $bindable() two-way props, and Snippet content slots:
<script lang="ts">
import { Sheet } from 'polymorph-ui-components';
let open = $state(false);
</script>
<button onclick={() => (open = true)}>Open</button>
<Sheet bind:open title="Settings" side="right">
{#snippet content()}
<p>Sheet body goes here.</p>
{/snippet}
{#snippet footer()}
<button onclick={() => (open = false)}>Done</button>
{/snippet}
</Sheet>Every component exports its types:
import type {
ButtonProperties,
InputProperties,
ModalProperties,
SelectProperties,
MenuItem
} from 'polymorph-ui-components';🛠 Development
pnpm install # install dependencies
pnpm dev # dev server with hot reload (component playground)
pnpm build # build library (vite + svelte-package + publint)
pnpm test # integration + unit tests
pnpm lint # formatting + lintsrc/lib/{Component}/{Component}.svelte # implementation
src/lib/{Component}/properties.ts # typed props
src/lib/index.ts # public exports
src/wc/ # Web Component wrappers
docs/ # one markdown reference per component
mcp/ # MCP server packageReleases are automated: pushing to release lints, derives the semver bump from the conventional-commit message, bumps the version, generates a changelog, builds, tags a GitHub release, and publishes to npm.
🙏 Credits
polymorph-ui-components is built on top of @juspay/svelte-ui-components — it began as that library and carries forward its CSS-variable-driven theming foundation. It no longer depends on or pulls from the upstream package; instead it builds on that groundwork and evolves independently. Thanks to the original authors for the foundation. 🙌
📄 License
MIT — peer dependencies: svelte ^5.41.2.
