polymorph-ui-components 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/README.md +354 -0
  2. package/dist/Accordion/Accordion.svelte +28 -0
  3. package/dist/Accordion/Accordion.svelte.d.ts +4 -0
  4. package/dist/Accordion/properties.d.ts +7 -0
  5. package/dist/Accordion/properties.js +1 -0
  6. package/dist/Animations/ModalAnimation.svelte +55 -0
  7. package/dist/Animations/ModalAnimation.svelte.d.ts +12 -0
  8. package/dist/Animations/OverlayAnimation.svelte +14 -0
  9. package/dist/Animations/OverlayAnimation.svelte.d.ts +7 -0
  10. package/dist/Avatar/Avatar.svelte +122 -0
  11. package/dist/Avatar/Avatar.svelte.d.ts +4 -0
  12. package/dist/Avatar/properties.d.ts +15 -0
  13. package/dist/Avatar/properties.js +1 -0
  14. package/dist/Badge/Badge.svelte +53 -0
  15. package/dist/Badge/Badge.svelte.d.ts +4 -0
  16. package/dist/Badge/properties.d.ts +6 -0
  17. package/dist/Badge/properties.js +1 -0
  18. package/dist/Banner/Banner.svelte +158 -0
  19. package/dist/Banner/Banner.svelte.d.ts +4 -0
  20. package/dist/Banner/properties.d.ts +19 -0
  21. package/dist/Banner/properties.js +1 -0
  22. package/dist/Book/Book.svelte +281 -0
  23. package/dist/Book/Book.svelte.d.ts +4 -0
  24. package/dist/Book/properties.d.ts +24 -0
  25. package/dist/Book/properties.js +1 -0
  26. package/dist/Browser/Browser.svelte +176 -0
  27. package/dist/Browser/Browser.svelte.d.ts +3 -0
  28. package/dist/Browser/properties.d.ts +14 -0
  29. package/dist/Browser/properties.js +1 -0
  30. package/dist/Button/Button.svelte +153 -0
  31. package/dist/Button/Button.svelte.d.ts +4 -0
  32. package/dist/Button/properties.d.ts +24 -0
  33. package/dist/Button/properties.js +1 -0
  34. package/dist/Calendar/Calendar.svelte +477 -0
  35. package/dist/Calendar/Calendar.svelte.d.ts +4 -0
  36. package/dist/Calendar/properties.d.ts +30 -0
  37. package/dist/Calendar/properties.js +1 -0
  38. package/dist/Carousel/Carousel.svelte +258 -0
  39. package/dist/Carousel/Carousel.svelte.d.ts +4 -0
  40. package/dist/Carousel/properties.d.ts +20 -0
  41. package/dist/Carousel/properties.js +1 -0
  42. package/dist/CheckListItem/CheckListItem.svelte +65 -0
  43. package/dist/CheckListItem/CheckListItem.svelte.d.ts +4 -0
  44. package/dist/CheckListItem/properties.d.ts +15 -0
  45. package/dist/CheckListItem/properties.js +1 -0
  46. package/dist/Checkbox/Checkbox.svelte +157 -0
  47. package/dist/Checkbox/Checkbox.svelte.d.ts +4 -0
  48. package/dist/Checkbox/properties.d.ts +17 -0
  49. package/dist/Checkbox/properties.js +1 -0
  50. package/dist/Choicebox/Choicebox.svelte +85 -0
  51. package/dist/Choicebox/Choicebox.svelte.d.ts +4 -0
  52. package/dist/Choicebox/properties.d.ts +14 -0
  53. package/dist/Choicebox/properties.js +1 -0
  54. package/dist/ColorPicker/ColorPicker.svelte +583 -0
  55. package/dist/ColorPicker/ColorPicker.svelte.d.ts +4 -0
  56. package/dist/ColorPicker/properties.d.ts +15 -0
  57. package/dist/ColorPicker/properties.js +1 -0
  58. package/dist/Combobox/Combobox.svelte +432 -0
  59. package/dist/Combobox/Combobox.svelte.d.ts +6 -0
  60. package/dist/Combobox/properties.d.ts +42 -0
  61. package/dist/Combobox/properties.js +1 -0
  62. package/dist/CommandMenu/CommandMenu.svelte +452 -0
  63. package/dist/CommandMenu/CommandMenu.svelte.d.ts +4 -0
  64. package/dist/CommandMenu/properties.d.ts +26 -0
  65. package/dist/CommandMenu/properties.js +1 -0
  66. package/dist/ContextMenu/ContextMenu.svelte +308 -0
  67. package/dist/ContextMenu/ContextMenu.svelte.d.ts +4 -0
  68. package/dist/ContextMenu/properties.d.ts +26 -0
  69. package/dist/ContextMenu/properties.js +1 -0
  70. package/dist/Gauge/Gauge.svelte +70 -0
  71. package/dist/Gauge/Gauge.svelte.d.ts +4 -0
  72. package/dist/Gauge/properties.d.ts +9 -0
  73. package/dist/Gauge/properties.js +1 -0
  74. package/dist/GridItem/GridItem.svelte +145 -0
  75. package/dist/GridItem/GridItem.svelte.d.ts +4 -0
  76. package/dist/GridItem/properties.d.ts +15 -0
  77. package/dist/GridItem/properties.js +1 -0
  78. package/dist/Icon/Icon.svelte +61 -0
  79. package/dist/Icon/Icon.svelte.d.ts +4 -0
  80. package/dist/Icon/properties.d.ts +12 -0
  81. package/dist/Icon/properties.js +1 -0
  82. package/dist/IconStack/IconStack.svelte +55 -0
  83. package/dist/IconStack/IconStack.svelte.d.ts +4 -0
  84. package/dist/IconStack/properties.d.ts +9 -0
  85. package/dist/IconStack/properties.js +1 -0
  86. package/dist/Img/Img.svelte +37 -0
  87. package/dist/Img/Img.svelte.d.ts +4 -0
  88. package/dist/Img/properties.d.ts +13 -0
  89. package/dist/Img/properties.js +1 -0
  90. package/dist/Input/Input.svelte +349 -0
  91. package/dist/Input/Input.svelte.d.ts +8 -0
  92. package/dist/Input/properties.d.ts +45 -0
  93. package/dist/Input/properties.js +1 -0
  94. package/dist/InputButton/InputButton.svelte +252 -0
  95. package/dist/InputButton/InputButton.svelte.d.ts +7 -0
  96. package/dist/InputButton/properties.d.ts +22 -0
  97. package/dist/InputButton/properties.js +1 -0
  98. package/dist/KeyboardInput/KeyboardInput.svelte +93 -0
  99. package/dist/KeyboardInput/KeyboardInput.svelte.d.ts +4 -0
  100. package/dist/KeyboardInput/properties.d.ts +12 -0
  101. package/dist/KeyboardInput/properties.js +1 -0
  102. package/dist/ListItem/ListItem.svelte +309 -0
  103. package/dist/ListItem/ListItem.svelte.d.ts +4 -0
  104. package/dist/ListItem/properties.d.ts +34 -0
  105. package/dist/ListItem/properties.js +1 -0
  106. package/dist/Loader/Loader.svelte +90 -0
  107. package/dist/Loader/Loader.svelte.d.ts +4 -0
  108. package/dist/Loader/properties.d.ts +4 -0
  109. package/dist/Loader/properties.js +1 -0
  110. package/dist/LoadingDots/LoadingDots.svelte +59 -0
  111. package/dist/LoadingDots/LoadingDots.svelte.d.ts +3 -0
  112. package/dist/LoadingDots/properties.d.ts +8 -0
  113. package/dist/LoadingDots/properties.js +1 -0
  114. package/dist/Menu/Menu.svelte +356 -0
  115. package/dist/Menu/Menu.svelte.d.ts +4 -0
  116. package/dist/Menu/properties.d.ts +28 -0
  117. package/dist/Menu/properties.js +1 -0
  118. package/dist/Modal/Modal.svelte +357 -0
  119. package/dist/Modal/Modal.svelte.d.ts +4 -0
  120. package/dist/Modal/properties.d.ts +39 -0
  121. package/dist/Modal/properties.js +1 -0
  122. package/dist/Pagination/Pagination.svelte +148 -0
  123. package/dist/Pagination/Pagination.svelte.d.ts +4 -0
  124. package/dist/Pagination/properties.d.ts +14 -0
  125. package/dist/Pagination/properties.js +1 -0
  126. package/dist/Phone/Phone.svelte +234 -0
  127. package/dist/Phone/Phone.svelte.d.ts +3 -0
  128. package/dist/Phone/properties.d.ts +11 -0
  129. package/dist/Phone/properties.js +1 -0
  130. package/dist/Pill/Pill.svelte +130 -0
  131. package/dist/Pill/Pill.svelte.d.ts +4 -0
  132. package/dist/Pill/properties.d.ts +16 -0
  133. package/dist/Pill/properties.js +1 -0
  134. package/dist/Progress/Progress.svelte +68 -0
  135. package/dist/Progress/Progress.svelte.d.ts +4 -0
  136. package/dist/Progress/properties.d.ts +10 -0
  137. package/dist/Progress/properties.js +1 -0
  138. package/dist/Radio/Radio.svelte +124 -0
  139. package/dist/Radio/Radio.svelte.d.ts +4 -0
  140. package/dist/Radio/properties.d.ts +15 -0
  141. package/dist/Radio/properties.js +1 -0
  142. package/dist/RelativeTime/RelativeTime.svelte +109 -0
  143. package/dist/RelativeTime/RelativeTime.svelte.d.ts +4 -0
  144. package/dist/RelativeTime/properties.d.ts +13 -0
  145. package/dist/RelativeTime/properties.js +1 -0
  146. package/dist/Scroller/Scroller.svelte +390 -0
  147. package/dist/Scroller/Scroller.svelte.d.ts +4 -0
  148. package/dist/Scroller/properties.d.ts +30 -0
  149. package/dist/Scroller/properties.js +1 -0
  150. package/dist/Select/Select.svelte +472 -0
  151. package/dist/Select/Select.svelte.d.ts +4 -0
  152. package/dist/Select/properties.d.ts +20 -0
  153. package/dist/Select/properties.js +1 -0
  154. package/dist/Sheet/Sheet.svelte +264 -0
  155. package/dist/Sheet/Sheet.svelte.d.ts +4 -0
  156. package/dist/Sheet/properties.d.ts +19 -0
  157. package/dist/Sheet/properties.js +1 -0
  158. package/dist/Shimmer/Shimmer.svelte +44 -0
  159. package/dist/Shimmer/Shimmer.svelte.d.ts +4 -0
  160. package/dist/Shimmer/properties.d.ts +4 -0
  161. package/dist/Shimmer/properties.js +1 -0
  162. package/dist/Slider/Slider.svelte +147 -0
  163. package/dist/Slider/Slider.svelte.d.ts +4 -0
  164. package/dist/Slider/properties.d.ts +17 -0
  165. package/dist/Slider/properties.js +1 -0
  166. package/dist/Snippet/Snippet.svelte +123 -0
  167. package/dist/Snippet/Snippet.svelte.d.ts +4 -0
  168. package/dist/Snippet/properties.d.ts +15 -0
  169. package/dist/Snippet/properties.js +1 -0
  170. package/dist/SplitButton/SplitButton.svelte +145 -0
  171. package/dist/SplitButton/SplitButton.svelte.d.ts +4 -0
  172. package/dist/SplitButton/properties.d.ts +17 -0
  173. package/dist/SplitButton/properties.js +1 -0
  174. package/dist/SplitInput/SplitInput.svelte +225 -0
  175. package/dist/SplitInput/SplitInput.svelte.d.ts +7 -0
  176. package/dist/SplitInput/properties.d.ts +20 -0
  177. package/dist/SplitInput/properties.js +1 -0
  178. package/dist/Stepper/Step.svelte +88 -0
  179. package/dist/Stepper/Step.svelte.d.ts +4 -0
  180. package/dist/Stepper/Stepper.svelte +64 -0
  181. package/dist/Stepper/Stepper.svelte.d.ts +4 -0
  182. package/dist/Stepper/properties.d.ts +27 -0
  183. package/dist/Stepper/properties.js +1 -0
  184. package/dist/Table/Table.svelte +357 -0
  185. package/dist/Table/Table.svelte.d.ts +4 -0
  186. package/dist/Table/properties.d.ts +26 -0
  187. package/dist/Table/properties.js +1 -0
  188. package/dist/Tabs/Tabs.svelte +303 -0
  189. package/dist/Tabs/Tabs.svelte.d.ts +4 -0
  190. package/dist/Tabs/properties.d.ts +30 -0
  191. package/dist/Tabs/properties.js +1 -0
  192. package/dist/ThemeSwitcher/ThemeSwitcher.svelte +249 -0
  193. package/dist/ThemeSwitcher/ThemeSwitcher.svelte.d.ts +4 -0
  194. package/dist/ThemeSwitcher/properties.d.ts +19 -0
  195. package/dist/ThemeSwitcher/properties.js +1 -0
  196. package/dist/Toast/Toast.svelte +220 -0
  197. package/dist/Toast/Toast.svelte.d.ts +4 -0
  198. package/dist/Toast/properties.d.ts +24 -0
  199. package/dist/Toast/properties.js +1 -0
  200. package/dist/Toggle/Toggle.svelte +99 -0
  201. package/dist/Toggle/Toggle.svelte.d.ts +4 -0
  202. package/dist/Toggle/properties.d.ts +9 -0
  203. package/dist/Toggle/properties.js +1 -0
  204. package/dist/Toolbar/Toolbar.svelte +142 -0
  205. package/dist/Toolbar/Toolbar.svelte.d.ts +4 -0
  206. package/dist/Toolbar/properties.d.ts +16 -0
  207. package/dist/Toolbar/properties.js +1 -0
  208. package/dist/Tooltip/Tooltip.svelte +153 -0
  209. package/dist/Tooltip/Tooltip.svelte.d.ts +4 -0
  210. package/dist/Tooltip/properties.d.ts +13 -0
  211. package/dist/Tooltip/properties.js +1 -0
  212. package/dist/assets/back.svg +3 -0
  213. package/dist/assets/battery.svg +5 -0
  214. package/dist/assets/checkmark.svg +3 -0
  215. package/dist/assets/chevron-down-sm.svg +3 -0
  216. package/dist/assets/chevron-down.svg +3 -0
  217. package/dist/assets/chevron-left-lg.svg +3 -0
  218. package/dist/assets/chevron-left.svg +3 -0
  219. package/dist/assets/chevron-right-lg.svg +3 -0
  220. package/dist/assets/chevron-right.svg +3 -0
  221. package/dist/assets/chevron-up.svg +3 -0
  222. package/dist/assets/close.svg +4 -0
  223. package/dist/assets/copy.svg +4 -0
  224. package/dist/assets/error-circle.svg +5 -0
  225. package/dist/assets/lock.svg +3 -0
  226. package/dist/assets/minus.svg +3 -0
  227. package/dist/assets/monitor.svg +5 -0
  228. package/dist/assets/moon.svg +3 -0
  229. package/dist/assets/palette.svg +7 -0
  230. package/dist/assets/search.svg +4 -0
  231. package/dist/assets/signal.svg +6 -0
  232. package/dist/assets/sort-default.svg +4 -0
  233. package/dist/assets/sun.svg +11 -0
  234. package/dist/assets/swap-vertical.svg +6 -0
  235. package/dist/assets/wifi.svg +3 -0
  236. package/dist/index.d.ts +103 -0
  237. package/dist/index.js +55 -0
  238. package/dist/types.d.ts +42 -0
  239. package/dist/types.js +1 -0
  240. package/dist/utils.d.ts +28 -0
  241. package/dist/utils.js +294 -0
  242. package/package.json +91 -0
package/README.md ADDED
@@ -0,0 +1,354 @@
1
+ <div align="center">
2
+
3
+ # polymorph-ui-components
4
+
5
+ ### One set of components. Every design system. Zero overrides.
6
+
7
+ A **themeable Svelte 5 component library** where _every_ visual property is a CSS custom property.
8
+ Unstyled by default — you bring the design system, the components render it.
9
+
10
+ [![npm](https://img.shields.io/npm/v/polymorph-ui-components?color=ff3e00&label=npm&logo=npm)](https://www.npmjs.com/package/polymorph-ui-components)
11
+ [![Svelte 5](https://img.shields.io/badge/Svelte-5-ff3e00?logo=svelte&logoColor=white)](https://svelte.dev)
12
+ [![Web Components](https://img.shields.io/badge/Web%20Components-ready-29abe2?logo=webcomponentsdotorg&logoColor=white)](#-use-it-anywhere-web-components)
13
+ [![MCP](https://img.shields.io/badge/AI--native-MCP%20server-7c3aed)](#-ai-native-mcp-server)
14
+ [![50+ components](https://img.shields.io/badge/components-50%2B-22c55e)](#-components)
15
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](#-license)
16
+
17
+ ```bash
18
+ npm install polymorph-ui-components
19
+ ```
20
+
21
+ </div>
22
+
23
+ ---
24
+
25
+ ## Why it's different
26
+
27
+ Most component libraries ship with a **fixed look**. Re-skinning them means fighting `!important`, patching internals through `:global()`, or forking the whole thing.
28
+
29
+ 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.
30
+
31
+ ```svelte
32
+ <!-- The same Button, themed three different ways — no overrides -->
33
+ <div class="brand"><Button text="Continue" /></div>
34
+ <div class="danger"><Button text="Delete" /></div>
35
+ <div class="ghost"><Button text="Cancel" /></div>
36
+
37
+ <style>
38
+ .brand {
39
+ --button-color: #6d28d9;
40
+ --button-text-color: #fff;
41
+ --button-border-radius: 8px;
42
+ }
43
+ .danger {
44
+ --button-color: #e11d48;
45
+ --button-text-color: #fff;
46
+ }
47
+ .ghost {
48
+ --button-color: transparent;
49
+ --button-text-color: #111;
50
+ --button-border: 1px solid #ddd;
51
+ }
52
+ </style>
53
+ ```
54
+
55
+ > 📐 The full reasoning behind this approach lives in **[DESIGN_PRINCIPLES.md](./DESIGN_PRINCIPLES.md)**.
56
+
57
+ ---
58
+
59
+ ## ✨ Highlights
60
+
61
+ | | |
62
+ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
63
+ | 🎨 **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. |
64
+ | 🧩 **50+ behavioral primitives** | Modals, sheets, menus, comboboxes, calendars, tables, toasts, steppers — focus traps, roving tabindex, and state machines included. |
65
+ | 🌍 **Framework-agnostic** | Ships as a Svelte library **and** as native Web Components (`<pui-*>`). Use it in React, Vue, Angular, or plain HTML. |
66
+ | 🤖 **AI-native** | A bundled **MCP server** exposes every component's props, events, and CSS variables to your AI coding assistant. |
67
+ | ♿ **Accessible by default** | ARIA wiring, keyboard navigation, focus management, and semantic HTML are baseline — not a premium add-on. |
68
+ | ⚡ **Built on Svelte 5** | `$props`, `$state`, `$derived`, `$bindable`, and `Snippet` throughout. Fully typed, tree-shakeable. |
69
+
70
+ ---
71
+
72
+ ## 🚀 Quick Start
73
+
74
+ ```svelte
75
+ <script lang="ts">
76
+ import { Button, Input, Toggle } from 'polymorph-ui-components';
77
+
78
+ let dark = $state(false);
79
+ </script>
80
+
81
+ <Button text="Submit" onclick={() => console.log('clicked')} />
82
+
83
+ <Input
84
+ value=""
85
+ placeholder="Enter email"
86
+ dataType="email"
87
+ onstatechange={(state) => console.log(state)}
88
+ />
89
+
90
+ <Toggle checked={dark} text="Dark mode" onclick={(on) => (dark = on)} />
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 🎨 Theming in 30 seconds
96
+
97
+ Every component reads its visuals from CSS variables with sensible neutral defaults. Define them on **any ancestor** and you have a theme:
98
+
99
+ ```svelte
100
+ <div class="my-design-system">
101
+ <Button text="Save" onclick={save} />
102
+ <Input value="" placeholder="Search…" />
103
+ </div>
104
+
105
+ <style>
106
+ .my-design-system {
107
+ --button-color: #0070f3;
108
+ --button-text-color: #fff;
109
+ --button-border-radius: 6px;
110
+ --button-padding: 10px 20px;
111
+
112
+ --input-background: #fafafa;
113
+ --input-border: 1px solid #eaeaea;
114
+ --input-radius: 6px;
115
+ --input-focus-border: 1px solid #0070f3;
116
+
117
+ --modal-border-radius: 12px;
118
+ --modal-overlay-background-color: #00000066;
119
+ }
120
+ </style>
121
+ ```
122
+
123
+ **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.
124
+
125
+ The naming convention is predictable everywhere:
126
+
127
+ ```
128
+ --{component}-{element}-{property}
129
+ ```
130
+
131
+ `--button-color` · `--input-error-msg-text-color` · `--modal-footer-primary-button-border-radius`
132
+
133
+ Each component documents its **complete** variable surface in [`docs/`](docs/).
134
+
135
+ ---
136
+
137
+ ## 🌍 Use it anywhere (Web Components)
138
+
139
+ The same components compile to **framework-agnostic custom elements** — theming works identically because it's pure CSS.
140
+
141
+ ```html
142
+ <script type="module" src="polymorph-ui-components/wc"></script>
143
+
144
+ <pui-button text="Save"></pui-button>
145
+ <pui-input placeholder="Search…"></pui-input>
146
+
147
+ <style>
148
+ pui-button {
149
+ --button-color: #0070f3;
150
+ --button-text-color: #fff;
151
+ }
152
+ </style>
153
+ ```
154
+
155
+ Drop them into React, Vue, Angular, Astro, or a plain `.html` file — no build step required.
156
+
157
+ ---
158
+
159
+ ## 🤖 AI-native (MCP server)
160
+
161
+ 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.
162
+
163
+ ```bash
164
+ npm install polymorph-ui-components-mcp
165
+ ```
166
+
167
+ ```jsonc
168
+ // .mcp.json (or any MCP client config)
169
+ {
170
+ "mcpServers": {
171
+ "polymorph-ui": {
172
+ "command": "npx",
173
+ "args": ["-y", "polymorph-ui-components-mcp"]
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ 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.
180
+
181
+ ---
182
+
183
+ ## 🧩 Components
184
+
185
+ <details open>
186
+ <summary><b>Inputs &amp; Form Controls</b></summary>
187
+
188
+ | Component | Description | |
189
+ | --------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------- |
190
+ | **Button** | Action trigger with circular loader, progress bar, icon/children snippets, and `aria-expanded`. | [docs](docs/Button.md) |
191
+ | **Input** | Text field with built-in validation (email, phone, password, custom patterns), text transformers, and textarea mode. | [docs](docs/Input.md) |
192
+ | **InputButton** | Input fused with action buttons — search bars, OTP entry, coupon codes. | [docs](docs/InputButton.md) |
193
+ | **Select** | Dropdown with searchable single-select and multi-select (dismissible pills) plus custom content slots. | [docs](docs/Select.md) |
194
+ | **Combobox** | Autocomplete input with filtered listbox, `aria-activedescendant`, and keyboard navigation. | [docs](docs/Combobox.md) |
195
+ | **Toggle** | Labeled on/off switch with sliding animation. | [docs](docs/Toggle.md) |
196
+ | **Checkbox** | Tri-state checkbox with custom SVG checkmark and `aria-checked=mixed`. | [docs](docs/Checkbox.md) |
197
+ | **Radio** | Grouped radio with custom indicator over a native input. | [docs](docs/Radio.md) |
198
+ | **Slider** | Range slider with min/max/step and derived fill. | [docs](docs/Slider.md) |
199
+ | **Choicebox** | Selectable option box with radio/checkbox semantics and custom content. | [docs](docs/Choicebox.md) |
200
+ | **SplitInput** | Segmented input (OTP / PIN) with paste distribution and auto-advance. | [docs](docs/SplitInput.md) |
201
+ | **ColorPicker** | HSV color picker with pointer-drag saturation panel and hue slider. | [docs](docs/ColorPicker.md) |
202
+ | **Calendar** | Date / range picker with roving-tabindex grid and `Intl` formatting. | [docs](docs/Calendar.md) |
203
+
204
+ </details>
205
+
206
+ <details>
207
+ <summary><b>Overlays &amp; Panels</b></summary>
208
+
209
+ | Component | Description | |
210
+ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------ |
211
+ | **Modal** | Dialog overlay with size/alignment, header, footer, transitions, scroll-lock, and back-press support. | [docs](docs/Modal.md) |
212
+ | **Sheet** | Slide-in panel from any edge with header, scrollable body, footer, and focus trap. | [docs](docs/Sheet.md) |
213
+ | **Menu** | Dropdown action menu with keyboard navigation, typeahead, disabled/danger items, and separators. | [docs](docs/Menu.md) |
214
+ | **ContextMenu** | Right-click menu with separators, disabled/danger items, and keyboard navigation. | [docs](docs/ContextMenu.md) |
215
+ | **CommandMenu** | Command palette (Ctrl/Cmd+K) with search, grouped commands, and keyboard navigation. | [docs](docs/CommandMenu.md) |
216
+ | **Tooltip** | Hover/focus tooltip with configurable position and delay. | [docs](docs/Tooltip.md) |
217
+ | **ModalAnimation** / **OverlayAnimation** | Fly/fade transition wrappers. | [docs](docs/ModalAnimation.md) |
218
+
219
+ </details>
220
+
221
+ <details>
222
+ <summary><b>Display &amp; Data</b></summary>
223
+
224
+ | Component | Description | |
225
+ | ---------------------------------- | ----------------------------------------------------------------------- | ----------------------------- |
226
+ | **Table** | Sortable data table with sticky headers and per-cell scrolling. | [docs](docs/Table.md) |
227
+ | **ListItem** | Multi-section list row with images, labels, and accordion expansion. | [docs](docs/ListItem.md) |
228
+ | **Avatar** | Circular avatar with image fallback or derived initials. | [docs](docs/Avatar.md) |
229
+ | **Badge** | Icon with a numeric/text badge overlay. | [docs](docs/Badge.md) |
230
+ | **Pill** | Compact label/tag, optionally clickable with a11y. | [docs](docs/Pill.md) |
231
+ | **Icon** / **IconStack** / **Img** | Clickable icon, overlapping icon stack, image with load-error fallback. | [docs](docs/Icon.md) |
232
+ | **GridItem** | Grid cell with icon, label, and loading overlay. | [docs](docs/GridItem.md) |
233
+ | **RelativeTime** | Auto-updating "5 minutes ago" with locale support. | [docs](docs/RelativeTime.md) |
234
+ | **KeyboardInput** | Keyboard-shortcut display with styled key caps. | [docs](docs/KeyboardInput.md) |
235
+
236
+ </details>
237
+
238
+ <details>
239
+ <summary><b>Navigation &amp; Structure</b></summary>
240
+
241
+ | Component | Description | |
242
+ | ---------------------- | -------------------------------------------------------------------- | ----------------------------- |
243
+ | **Tabs** | Tabbed interface with animated indicator and overflow scrolling. | [docs](docs/Tabs.md) |
244
+ | **Pagination** | Windowed page navigation with ellipsis truncation. | [docs](docs/Pagination.md) |
245
+ | **Stepper** / **Step** | Multi-step progress indicator with completed/active/pending states. | [docs](docs/Stepper.md) |
246
+ | **Accordion** | Collapsible container with CSS grid animation. | [docs](docs/Accordion.md) |
247
+ | **Carousel** | Swipeable content slider with autoplay and pagination dots. | [docs](docs/Carousel.md) |
248
+ | **Scroller** | Overflowing list with arrow nav, gradient edges, and drag-to-scroll. | [docs](docs/Scroller.md) |
249
+ | **CheckListItem** | Checklist row with checkbox, label, and checked state. | [docs](docs/CheckListItem.md) |
250
+ | **Toolbar** | Header bar with back button, title, and customizable content areas. | [docs](docs/Toolbar.md) |
251
+ | **ThemeSwitcher** | Segmented light/dark/system control with `prefers-color-scheme`. | [docs](docs/ThemeSwitcher.md) |
252
+
253
+ </details>
254
+
255
+ <details>
256
+ <summary><b>Feedback &amp; Loading</b></summary>
257
+
258
+ | Component | Description | |
259
+ | ------------------------------------------ | ----------------------------------------------------------------------- | ------------------------ |
260
+ | **Toast** | Animated slide-in notification with auto-dismiss and per-direction fly. | [docs](docs/Toast.md) |
261
+ | **Banner** | Notification banner with icon snippet, link text, and dismiss. | [docs](docs/Banner.md) |
262
+ | **Progress** | Animated horizontal progress bar (determinate / indeterminate). | [docs](docs/Progress.md) |
263
+ | **Gauge** | Full-circle ring gauge (0–100) with animated SVG arc. | [docs](docs/Gauge.md) |
264
+ | **Loader** / **LoadingDots** / **Shimmer** | Spinner, animated dots, and skeleton shimmer — all CSS-variable themed. | [docs](docs/Loader.md) |
265
+
266
+ </details>
267
+
268
+ <details>
269
+ <summary><b>Actions, Code &amp; Scenery</b></summary>
270
+
271
+ | Component | Description | |
272
+ | ---------------------------------- | ------------------------------------------------------------------------------------------ | --------------------------- |
273
+ | **SplitButton** | Primary action button with a dropdown of secondary actions. | [docs](docs/SplitButton.md) |
274
+ | **Snippet** | Code/command block with a copy button. | [docs](docs/Snippet.md) |
275
+ | **Book** / **Browser** / **Phone** | Decorative frames — flip-book, browser chrome, device mockup — for previews and marketing. | [docs](docs/Browser.md) |
276
+
277
+ </details>
278
+
279
+ ---
280
+
281
+ ## ♿ Accessibility & Svelte 5
282
+
283
+ 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.
284
+
285
+ Under the hood it's modern Svelte 5 — `$props()`, `$state()`/`$derived()`, `$bindable()` two-way props, and `Snippet` content slots:
286
+
287
+ ```svelte
288
+ <script lang="ts">
289
+ import { Sheet } from 'polymorph-ui-components';
290
+ let open = $state(false);
291
+ </script>
292
+
293
+ <button onclick={() => (open = true)}>Open</button>
294
+
295
+ <Sheet bind:open title="Settings" side="right">
296
+ {#snippet content()}
297
+ <p>Sheet body goes here.</p>
298
+ {/snippet}
299
+ {#snippet footer()}
300
+ <button onclick={() => (open = false)}>Done</button>
301
+ {/snippet}
302
+ </Sheet>
303
+ ```
304
+
305
+ Every component exports its types:
306
+
307
+ ```typescript
308
+ import type {
309
+ ButtonProperties,
310
+ InputProperties,
311
+ ModalProperties,
312
+ SelectProperties,
313
+ MenuItem
314
+ } from 'polymorph-ui-components';
315
+ ```
316
+
317
+ ---
318
+
319
+ ## 🛠 Development
320
+
321
+ ```bash
322
+ pnpm install # install dependencies
323
+ pnpm dev # dev server with hot reload (component playground)
324
+ pnpm build # build library (vite + svelte-package + publint)
325
+ pnpm test # integration + unit tests
326
+ pnpm lint # formatting + lint
327
+ ```
328
+
329
+ ```
330
+ src/lib/{Component}/{Component}.svelte # implementation
331
+ src/lib/{Component}/properties.ts # typed props
332
+ src/lib/index.ts # public exports
333
+ src/wc/ # Web Component wrappers
334
+ docs/ # one markdown reference per component
335
+ mcp/ # MCP server package
336
+ ```
337
+
338
+ Releases 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.
339
+
340
+ ---
341
+
342
+ ## 🙏 Credits
343
+
344
+ **polymorph-ui-components** is built on top of [`@juspay/svelte-ui-components`](https://github.com/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. 🙌
345
+
346
+ ---
347
+
348
+ ## 📄 License
349
+
350
+ [MIT](LICENSE) — peer dependencies: `svelte ^5.41.2`.
351
+
352
+ <div align="center">
353
+ <sub>Build any design system. In any framework. Discoverable by humans and AI alike.</sub>
354
+ </div>
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import type { AccordionProperties } from './properties';
3
+
4
+ let { expand = false, children, testId, classes }: AccordionProperties = $props();
5
+ </script>
6
+
7
+ <div class="accordion {classes ?? ''}" class:expanded={expand} data-pw={testId}>
8
+ <div class="accordion-content">
9
+ {@render children?.()}
10
+ </div>
11
+ </div>
12
+
13
+ <style>
14
+ .accordion {
15
+ display: grid;
16
+ grid-template-rows: 0fr;
17
+ overflow: hidden;
18
+ transition: grid-template-rows 0.2s ease-out;
19
+ }
20
+
21
+ .accordion.expanded {
22
+ grid-template-rows: 1fr;
23
+ }
24
+
25
+ .accordion-content {
26
+ min-height: 0;
27
+ }
28
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { AccordionProperties } from './properties';
2
+ declare const Accordion: import("svelte").Component<AccordionProperties, {}, "">;
3
+ type Accordion = ReturnType<typeof Accordion>;
4
+ export default Accordion;
@@ -0,0 +1,7 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type AccordionProperties = {
3
+ expand?: boolean;
4
+ children?: Snippet;
5
+ testId?: string;
6
+ classes?: string;
7
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { fly, fade } from 'svelte/transition';
4
+ import type { ModalAlign } from '../Modal/properties';
5
+ import type { ModalTransition } from '../types';
6
+
7
+ type Props = {
8
+ enable?: boolean;
9
+ align?: ModalAlign;
10
+ transitionType?: ModalTransition;
11
+ children?: Snippet;
12
+ };
13
+
14
+ let { enable = true, align = 'bottom', transitionType = 'ALL', children }: Props = $props();
15
+
16
+ let flyAnimationProperties = $derived.by(() => {
17
+ const base = { x: 0, y: 0, duration: 380 };
18
+
19
+ switch (align) {
20
+ case 'top':
21
+ return { ...base, y: -30 };
22
+ case 'bottom':
23
+ return { ...base, y: 300 };
24
+ default:
25
+ return base;
26
+ }
27
+ });
28
+
29
+ let fadeAnimationProperties = { duration: 300 };
30
+
31
+ let useFlyAnimation = $derived(align === 'top' || align === 'bottom');
32
+ let useOutTransition = $derived(transitionType === 'ALL');
33
+ </script>
34
+
35
+ {#if enable}
36
+ {#if useFlyAnimation && useOutTransition}
37
+ <div in:fly|global={flyAnimationProperties} out:fly|global={flyAnimationProperties}>
38
+ {@render children?.()}
39
+ </div>
40
+ {:else if useFlyAnimation}
41
+ <div in:fly|global={flyAnimationProperties}>
42
+ {@render children?.()}
43
+ </div>
44
+ {:else if useOutTransition}
45
+ <div in:fade|global={fadeAnimationProperties} out:fade|global={fadeAnimationProperties}>
46
+ {@render children?.()}
47
+ </div>
48
+ {:else}
49
+ <div in:fade|global={fadeAnimationProperties}>
50
+ {@render children?.()}
51
+ </div>
52
+ {/if}
53
+ {:else}
54
+ {@render children?.()}
55
+ {/if}
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ModalAlign } from '../Modal/properties';
3
+ import type { ModalTransition } from '../types';
4
+ type Props = {
5
+ enable?: boolean;
6
+ align?: ModalAlign;
7
+ transitionType?: ModalTransition;
8
+ children?: Snippet;
9
+ };
10
+ declare const ModalAnimation: import("svelte").Component<Props, {}, "">;
11
+ type ModalAnimation = ReturnType<typeof ModalAnimation>;
12
+ export default ModalAnimation;
@@ -0,0 +1,14 @@
1
+ <script lang="ts">
2
+ import { fade } from 'svelte/transition';
3
+ import type { Snippet } from 'svelte';
4
+
5
+ type Props = {
6
+ children?: Snippet;
7
+ };
8
+
9
+ let { children }: Props = $props();
10
+ </script>
11
+
12
+ <div out:fade={{ duration: 350 }}>
13
+ {@render children?.()}
14
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Props = {
3
+ children?: Snippet;
4
+ };
5
+ declare const OverlayAnimation: import("svelte").Component<Props, {}, "">;
6
+ type OverlayAnimation = ReturnType<typeof OverlayAnimation>;
7
+ export default OverlayAnimation;
@@ -0,0 +1,122 @@
1
+ <script lang="ts">
2
+ import type { AvatarProperties } from './properties';
3
+ import Img from '../Img/Img.svelte';
4
+
5
+ let { src, alt, name, size = 'medium', testId, onclick, classes }: AvatarProperties = $props();
6
+
7
+ let imageError = $state(false);
8
+
9
+ let showImage = $derived(typeof src === 'string' && src.length > 0 && !imageError);
10
+
11
+ let initials = $derived.by(() => {
12
+ if (typeof name !== 'string' || name.trim().length === 0) {
13
+ return '';
14
+ }
15
+ const words = name.trim().split(/\s+/);
16
+ if (words.length === 0) {
17
+ return '';
18
+ }
19
+ const firstWord = words.at(0);
20
+ const lastWord = words.at(-1);
21
+ const first = typeof firstWord === 'string' && firstWord.length > 0 ? firstWord.charAt(0) : '';
22
+ const last =
23
+ words.length > 1 && typeof lastWord === 'string' && lastWord.length > 0
24
+ ? lastWord.charAt(0)
25
+ : '';
26
+ return (first + last).toUpperCase();
27
+ });
28
+
29
+ function handleImageError(): void {
30
+ imageError = true;
31
+ }
32
+ </script>
33
+
34
+ {#snippet content()}
35
+ {#if showImage && typeof src === 'string'}
36
+ <span class="avatar-img-wrapper">
37
+ <Img {src} {alt} onerror={handleImageError} />
38
+ </span>
39
+ {:else}
40
+ <span class="avatar-initials">{initials}</span>
41
+ {/if}
42
+ {/snippet}
43
+
44
+ {#if typeof onclick === 'function'}
45
+ <button
46
+ class="avatar avatar-{size} {classes ?? ''}"
47
+ type="button"
48
+ aria-label={alt}
49
+ data-pw={testId}
50
+ {onclick}
51
+ >
52
+ {@render content()}
53
+ </button>
54
+ {:else}
55
+ <div class="avatar avatar-{size} {classes ?? ''}" role="img" aria-label={alt} data-pw={testId}>
56
+ {@render content()}
57
+ </div>
58
+ {/if}
59
+
60
+ <style>
61
+ .avatar {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ padding: 0;
66
+ border-radius: var(--avatar-border-radius, 50%);
67
+ border: var(--avatar-border, none);
68
+ box-shadow: var(--avatar-box-shadow, none);
69
+ cursor: var(--avatar-cursor, default);
70
+ overflow: hidden;
71
+ background-color: var(--avatar-background, #a1a1aa);
72
+ font-family: inherit;
73
+ }
74
+
75
+ .avatar:hover {
76
+ opacity: var(--avatar-hover-opacity, 1);
77
+ }
78
+
79
+ .avatar-small {
80
+ width: var(--avatar-small-width, 32px);
81
+ height: var(--avatar-small-height, 32px);
82
+ }
83
+
84
+ .avatar-medium {
85
+ width: var(--avatar-medium-width, 40px);
86
+ height: var(--avatar-medium-height, 40px);
87
+ }
88
+
89
+ .avatar-large {
90
+ width: var(--avatar-large-width, 56px);
91
+ height: var(--avatar-large-height, 56px);
92
+ }
93
+
94
+ .avatar-img-wrapper {
95
+ display: contents;
96
+ --image-width: 100%;
97
+ --image-height: 100%;
98
+ --image-object-fit: var(--avatar-object-fit, cover);
99
+ --image-border-radius: 0px;
100
+ --image-padding: 0px;
101
+ --image-margin: 0px;
102
+ }
103
+
104
+ .avatar-initials {
105
+ color: var(--avatar-text-color, #18181b);
106
+ font-weight: var(--avatar-font-weight, 600);
107
+ font-family: var(--avatar-font-family, inherit);
108
+ user-select: none;
109
+ }
110
+
111
+ .avatar-small .avatar-initials {
112
+ font-size: var(--avatar-small-font-size, 12px);
113
+ }
114
+
115
+ .avatar-medium .avatar-initials {
116
+ font-size: var(--avatar-medium-font-size, 14px);
117
+ }
118
+
119
+ .avatar-large .avatar-initials {
120
+ font-size: var(--avatar-large-font-size, 20px);
121
+ }
122
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { AvatarProperties } from './properties';
2
+ declare const Avatar: import("svelte").Component<AvatarProperties, {}, "">;
3
+ type Avatar = ReturnType<typeof Avatar>;
4
+ export default Avatar;
@@ -0,0 +1,15 @@
1
+ export type AvatarSize = 'small' | 'medium' | 'large';
2
+ export type AvatarProperties = MandatoryAvatarProperties & OptionalAvatarProperties & AvatarEventProperties;
3
+ export type MandatoryAvatarProperties = {
4
+ alt: string;
5
+ };
6
+ export type OptionalAvatarProperties = {
7
+ src?: string;
8
+ name?: string;
9
+ size?: AvatarSize;
10
+ testId?: string;
11
+ classes?: string;
12
+ };
13
+ export type AvatarEventProperties = {
14
+ onclick?: (event: MouseEvent) => void;
15
+ };
@@ -0,0 +1 @@
1
+ export {};