polymorph-ui-components-mcp 0.0.1

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 (64) hide show
  1. package/build/docs/Accordion.md +45 -0
  2. package/build/docs/Avatar.md +80 -0
  3. package/build/docs/Badge.md +58 -0
  4. package/build/docs/Banner.md +134 -0
  5. package/build/docs/Book.md +111 -0
  6. package/build/docs/Browser.md +92 -0
  7. package/build/docs/Button.md +154 -0
  8. package/build/docs/CHANGELOG.md +0 -0
  9. package/build/docs/Calendar.md +129 -0
  10. package/build/docs/Carousel.md +73 -0
  11. package/build/docs/CheckListItem.md +76 -0
  12. package/build/docs/Checkbox.md +84 -0
  13. package/build/docs/Choicebox.md +71 -0
  14. package/build/docs/ColorPicker.md +83 -0
  15. package/build/docs/Combobox.md +336 -0
  16. package/build/docs/CommandMenu.md +208 -0
  17. package/build/docs/ContextMenu.md +132 -0
  18. package/build/docs/GUIDELINES.md +380 -0
  19. package/build/docs/Gauge.md +46 -0
  20. package/build/docs/GridItem.md +78 -0
  21. package/build/docs/Icon.md +66 -0
  22. package/build/docs/IconStack.md +68 -0
  23. package/build/docs/Img.md +56 -0
  24. package/build/docs/Input.md +151 -0
  25. package/build/docs/InputButton.md +246 -0
  26. package/build/docs/KeyboardInput.md +104 -0
  27. package/build/docs/ListItem.md +156 -0
  28. package/build/docs/Loader.md +55 -0
  29. package/build/docs/LoadingDots.md +45 -0
  30. package/build/docs/Menu.md +139 -0
  31. package/build/docs/Modal.md +221 -0
  32. package/build/docs/ModalAnimation.md +29 -0
  33. package/build/docs/OverlayAnimation.md +21 -0
  34. package/build/docs/Pagination.md +102 -0
  35. package/build/docs/Phone.md +82 -0
  36. package/build/docs/Pill.md +122 -0
  37. package/build/docs/Progress.md +53 -0
  38. package/build/docs/Radio.md +75 -0
  39. package/build/docs/RelativeTime.md +76 -0
  40. package/build/docs/Scroller.md +194 -0
  41. package/build/docs/Select.md +177 -0
  42. package/build/docs/Sheet.md +133 -0
  43. package/build/docs/Shimmer.md +76 -0
  44. package/build/docs/Slider.md +68 -0
  45. package/build/docs/Snippet.md +99 -0
  46. package/build/docs/SplitButton.md +157 -0
  47. package/build/docs/SplitInput.md +157 -0
  48. package/build/docs/Step.md +52 -0
  49. package/build/docs/Stepper.md +85 -0
  50. package/build/docs/Table.md +252 -0
  51. package/build/docs/Tabs.md +117 -0
  52. package/build/docs/ThemeSwitcher.md +125 -0
  53. package/build/docs/Toast.md +140 -0
  54. package/build/docs/Toggle.md +70 -0
  55. package/build/docs/Toolbar.md +100 -0
  56. package/build/docs/Tooltip.md +86 -0
  57. package/build/docs/_index.json +218 -0
  58. package/build/docs/templates/changelog.hbs +36 -0
  59. package/build/index.d.ts +2 -0
  60. package/build/index.js +163 -0
  61. package/build/index.js.map +1 -0
  62. package/build/services/registry.d.ts +11 -0
  63. package/build/types.d.ts +4 -0
  64. package/package.json +34 -0
@@ -0,0 +1,380 @@
1
+ # Code Guidelines
2
+
3
+ Guidelines for writing TypeScript and Svelte code in this project. These rules apply to all components, utilities, and modules.
4
+
5
+ ---
6
+
7
+ ## 1. No Double-Bang (`!!`) Coercion
8
+
9
+ Do not use `!!` to convert values to booleans. It obscures intent and bypasses proper type narrowing.
10
+
11
+ ```ts
12
+ // Bad
13
+ let showImage = $derived(!!src && !imageError);
14
+
15
+ // Good
16
+ let showImage = $derived(typeof src === 'string' && src.length > 0 && !imageError);
17
+ ```
18
+
19
+ ---
20
+
21
+ ## 2. Safe Index Access
22
+
23
+ ### Arrays and strings: use `.at()` instead of bracket notation with numeric literals
24
+
25
+ `.at()` returns `T | undefined`, making the caller handle the missing case explicitly. It also supports negative indices for end-relative access.
26
+
27
+ ```ts
28
+ // Bad
29
+ const first = words[0];
30
+ const last = words[words.length - 1];
31
+
32
+ // Good
33
+ const first = words.at(0);
34
+ const last = words.at(-1);
35
+ ```
36
+
37
+ ### DOM lists (`NodeList`, `TouchList`, etc.): use `.item()` instead of bracket notation
38
+
39
+ These types do not support `.at()`. The `.item()` method returns `T | null`, which makes the null case explicit.
40
+
41
+ ```ts
42
+ // Bad
43
+ const el = focusable[0];
44
+ const touch = event.touches[0];
45
+
46
+ // Good
47
+ const el = focusable.item(0);
48
+ const touch = event.touches.item(0);
49
+ ```
50
+
51
+ ### String character access: use `.charAt()` instead of bracket notation
52
+
53
+ `.charAt()` returns `''` for out-of-bounds access instead of `undefined`, making the behavior predictable.
54
+
55
+ ```ts
56
+ // Bad
57
+ const initial = word[0];
58
+
59
+ // Good
60
+ const initial = word.charAt(0);
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 3. No Falsy/Truthy Checks for Non-Boolean Values
66
+
67
+ Do not rely on JavaScript's implicit falsy coercion (`if (x)`, `if (!x)`, `x || default`, `x && expr`) for values that are not booleans. Use explicit type narrowing with `typeof` instead.
68
+
69
+ Do **not** use `=== undefined` or `!== undefined` — always prefer `typeof` checks, which are safer (no risk of `undefined` being shadowed) and express the expected type clearly.
70
+
71
+ ### Strings (`string | undefined`)
72
+
73
+ ```ts
74
+ // Bad
75
+ if (!name) { ... }
76
+ if (title) { ... }
77
+ aria-label={title || 'Sheet'}
78
+
79
+ // Good
80
+ if (typeof name !== 'string' || name.length === 0) { ... }
81
+ if (typeof title === 'string') { ... }
82
+ aria-label={title ?? 'Sheet'}
83
+ ```
84
+
85
+ ### Functions (`(() => void) | undefined`)
86
+
87
+ ```ts
88
+ // Bad
89
+ {#if onclick}
90
+
91
+ // Good
92
+ {#if typeof onclick === 'function'}
93
+ ```
94
+
95
+ ### Objects / Snippets (`T | undefined`)
96
+
97
+ Use `typeof` to check for the expected type rather than comparing against `undefined`. Snippets are functions at runtime; objects are `'object'`.
98
+
99
+ ```ts
100
+ // Bad
101
+ {#if footer}
102
+ {#if previousIcon}
103
+ {#if view.properties}
104
+ {#if footer !== undefined}
105
+ {#if previousIcon !== undefined}
106
+
107
+ // Good — snippets are functions
108
+ {#if typeof footer === 'function'}
109
+ {#if typeof previousIcon === 'function'}
110
+
111
+ // Good — objects / records
112
+ {#if typeof view.properties === 'object'}
113
+ ```
114
+
115
+ ### DOM references (`HTMLElement | null`)
116
+
117
+ ```ts
118
+ // Bad
119
+ if (sheetPanel) { ... }
120
+
121
+ // Good
122
+ if (sheetPanel !== null) { ... }
123
+ ```
124
+
125
+ ### Numeric values (array `.length`, counts, etc.)
126
+
127
+ ```ts
128
+ // Bad
129
+ {#if views.length}
130
+
131
+ // Good
132
+ {#if views.length > 0}
133
+ ```
134
+
135
+ ### When `||` vs `??` matters
136
+
137
+ Use `??` (nullish coalescing) when the intent is to fall back only for `null` / `undefined`. Use `||` only when you genuinely want to fall back for all falsy values (including `0`, `''`, `false`).
138
+
139
+ ```ts
140
+ // Bad - falls back for '' which may be a valid value
141
+ const label = title || 'Default';
142
+
143
+ // Good - falls back only for null/undefined
144
+ const label = title ?? 'Default';
145
+ ```
146
+
147
+ ---
148
+
149
+ ## 4. Reuse Existing Components
150
+
151
+ Before implementing behaviour that already exists in the component library (error handling, fallback rendering, loading states, etc.), check if an existing component already handles it. Compose components rather than duplicating logic.
152
+
153
+ **Example:** The `Img` component handles image error-based fallback rendering. The `Avatar` component should reuse `Img` instead of implementing its own `<img onerror={...}>` logic.
154
+
155
+ ```svelte
156
+ <!-- Bad - duplicating Img's error handling logic -->
157
+ <img {src} {alt} onerror={handleError} />
158
+
159
+ <!-- Good - reusing the Img component -->
160
+ <Img {src} {alt} onerror={handleImageError} />
161
+ ```
162
+
163
+ When a child component does not expose the exact hook you need, extend its interface (e.g. add an `onerror` callback prop) rather than bypassing it with a raw HTML element.
164
+
165
+ ---
166
+
167
+ ## 5. Remove Redundant Guards
168
+
169
+ Do not guard against conditions that are already guaranteed by the type system or the runtime.
170
+
171
+ ```ts
172
+ // Bad - event parameter is always provided by the handler
173
+ if (event && Object.keys(event).length > 0 && typeof event.clientX !== 'undefined') {
174
+
175
+ // Good - MouseEvent always has clientX
176
+ if (typeof event.clientX !== 'undefined') {
177
+ ```
178
+
179
+ ```ts
180
+ // Bad - TouchList is always present on TouchEvent
181
+ if (event.touches && Object.keys(event.touches).length > 0) {
182
+
183
+ // Good
184
+ if (event.touches.length > 0) {
185
+ ```
186
+
187
+ ```ts
188
+ // Bad - redundant null check before strict equality
189
+ if (event.target && event.target === overlayDiv) {
190
+
191
+ // Good - strict equality already handles null
192
+ if (event.target === overlayDiv) {
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 6. Reuse the `Button` Component for Generic Action Buttons
198
+
199
+ Replace raw `<button>` elements with the reusable `Button` component for generic action buttons: submit, dismiss, close, copy, action, navigation arrows, and similar.
200
+
201
+ **Do not replace** specialized interactive elements: calendar day cells, pagination page buttons, tab items, command menu items, theme switcher segments, emotion selector buttons, page indicator dots.
202
+
203
+ ### CSS Variable Bridging
204
+
205
+ When replacing `<button>` with `<Button>`, wrap `<Button>` in a `<div>` with the original class name, and set `--button-*` CSS variables on that div to map to the component-specific CSS variables. This preserves the existing CSS variable API for consumers.
206
+
207
+ ```svelte
208
+ <!-- Before -->
209
+ <button class="close-btn" onclick={onclose} aria-label="Close">
210
+ {@render closeIcon()}
211
+ </button>
212
+
213
+ <!-- After -->
214
+ <div class="close-btn">
215
+ <Button onclick={onclose} ariaLabel="Close">
216
+ {#snippet children()}
217
+ {@render closeIcon()}
218
+ {/snippet}
219
+ </Button>
220
+ </div>
221
+
222
+ <style>
223
+ .close-btn {
224
+ --button-background: var(--sheet-close-bg, transparent);
225
+ --button-border: none;
226
+ --button-color: var(--sheet-close-color, #666);
227
+ --button-hover-background: var(--sheet-close-hover-bg, #f0f0f0);
228
+ }
229
+ </style>
230
+ ```
231
+
232
+ ### Props Available on `Button`
233
+
234
+ - `text` — Optional label text
235
+ - `icon` — Snippet for an icon
236
+ - `children` — Snippet for arbitrary content (use instead of `text` + `icon` for custom layouts)
237
+ - `ariaLabel` — Maps to `aria-label` on the underlying `<button>`
238
+ - `disabled` — Disables the button (reconciled with legacy `enable` prop)
239
+ - `onclick` / `onkeyup` — Event handlers
240
+ - `type` — Button type (`'button'` / `'submit'` / `'reset'`)
241
+ - `testId` — Maps to `data-pw` for test automation
242
+
243
+ ---
244
+
245
+ ## 7. `$bindable` Usage
246
+
247
+ Use `$bindable` only when the component **internally mutates** the prop (e.g., `open = false` in a close handler, `checked = !checked` on click). This is Svelte 5's intended mechanism for components that self-manage state transitions. Components should be **self-sufficient** — they work out of the box without requiring the parent to wire up callbacks.
248
+
249
+ **Do not use `$bindable`** for props that the component only reads. If the component never assigns to the prop, a regular prop is sufficient.
250
+
251
+ ```svelte
252
+ <!-- Good — component internally sets open = false on close -->
253
+ let {((open = $bindable(false)), onclose)}: Props = $props();
254
+
255
+ <!-- Bad — component never assigns to initialPage, only reads it -->
256
+ let {(initialPage = $bindable(0))}: Props = $props();
257
+ ```
258
+
259
+ When `$bindable` is used, also provide a corresponding callback prop (`onclose`, `onchange`, `ontoggle`, `onpagechange`, etc.) so the parent can react to state changes without relying on two-way binding. Both patterns are supported — consumers choose one:
260
+
261
+ - **`bind:`** — parent observes state reactively, no callback needed
262
+ - **callback** — parent reacts imperatively in a handler, no `bind:` needed
263
+
264
+ **Warning:** Consumers should avoid using **both** `bind:prop` and a callback that sets the same prop, as this creates two mutation channels for the same value and risks infinite update loops.
265
+
266
+ ---
267
+
268
+ ## 8. No `auto` for Layout/Spacing CSS Properties
269
+
270
+ Do not use `auto` as a value or CSS variable default for `margin`, `padding`, `width`, `height`, `top`, `bottom`, `left`, or `right`. The meaning of `auto` varies by property and layout context, making it unpredictable.
271
+
272
+ **Allowed:** `auto` is permitted for `overflow`, `overflow-x`, `overflow-y`, `pointer-events`, and `cursor`, where its behavior is well-defined and has no equivalent replacement.
273
+
274
+ ### Replacements
275
+
276
+ | Instead of | Use |
277
+ | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
278
+ | `width: auto` / `height: auto` | `fit-content` |
279
+ | `margin-left: auto` (flex push-right) | `flex: 1` on the preceding sibling |
280
+ | `margin: auto` (centering) | `top: 50%; left: 50%; transform: translate(-50%, -50%)` for absolute positioning, or flexbox centering on the parent |
281
+ | `bottom: auto` / `right: auto` (positioning) | Remove the declaration entirely — only set the sides that apply |
282
+
283
+ ```css
284
+ /* Bad */
285
+ width: var(--toast-width, auto);
286
+ margin-left: auto;
287
+ bottom: var(--menu-dropdown-bottom, auto);
288
+
289
+ /* Good */
290
+ width: var(--toast-width, fit-content);
291
+ /* preceding sibling has flex: 1, pushing this element right */
292
+ /* only declare the sides that apply */
293
+ top: var(--menu-dropdown-top, 100%);
294
+ left: var(--menu-dropdown-left, 0);
295
+ ```
296
+
297
+ ---
298
+
299
+ ## 9. Custom Classes via `classes` Prop
300
+
301
+ Every component exposes a `classes` prop (`classes?: string`) that attaches directly to the topmost container element. This allows consumers to apply custom CSS classes without wrapper elements.
302
+
303
+ ```svelte
304
+ <!-- Usage -->
305
+ <Button classes="my-custom-class another-class" text="Click me" />
306
+
307
+ <!-- Renders as -->
308
+ <div class="button-container my-custom-class another-class">
309
+ <button>Click me</button>
310
+ </div>
311
+ ```
312
+
313
+ ### Implementation Pattern
314
+
315
+ In the component template, append `{classes ?? ''}` to the topmost element's class attribute:
316
+
317
+ ```svelte
318
+ <!-- Single top-level element -->
319
+ <div class="container {classes ?? ''}">...</div>
320
+
321
+ <!-- Conditional top-level elements (e.g., Avatar) — add to ALL branches -->
322
+ {#if typeof onclick === 'function'}
323
+ <button class="avatar {classes ?? ''}">...</button>
324
+ {:else}
325
+ <div class="avatar {classes ?? ''}">...</div>
326
+ {/if}
327
+ ```
328
+
329
+ In `properties.ts`, add `classes?: string` to the optional properties sub-type.
330
+
331
+ ### Theming with Classes
332
+
333
+ The `classes` prop is the recommended way to implement theme variants (primary, secondary, danger, etc.). Define CSS classes that set the component's CSS variables, then pass the class name via `classes`:
334
+
335
+ ```css
336
+ /* app.css or a shared stylesheet */
337
+ .btn-primary {
338
+ --button-color: #0070f3;
339
+ --button-text-color: white;
340
+ --button-hover-color: #0060df;
341
+ --button-border-radius: 6px;
342
+ }
343
+
344
+ .btn-secondary {
345
+ --button-color: transparent;
346
+ --button-text-color: #0070f3;
347
+ --button-border: 1px solid #0070f3;
348
+ --button-hover-color: #f0f7ff;
349
+ }
350
+
351
+ .btn-danger {
352
+ --button-color: #e00;
353
+ --button-text-color: white;
354
+ --button-hover-color: #c00;
355
+ }
356
+
357
+ .banner-success {
358
+ --banner-background: #e6f9ed;
359
+ --banner-color: #1a7f37;
360
+ --banner-icon-color: #1a7f37;
361
+ }
362
+
363
+ .banner-warning {
364
+ --banner-background: #fff8e6;
365
+ --banner-color: #9a6700;
366
+ --banner-icon-color: #9a6700;
367
+ }
368
+ ```
369
+
370
+ ```svelte
371
+ <!-- Usage — apply theme variants via classes -->
372
+ <Button classes="btn-primary" text="Save" />
373
+ <Button classes="btn-secondary" text="Cancel" />
374
+ <Button classes="btn-danger" text="Delete" />
375
+
376
+ <Banner classes="banner-success" text="Deployment complete" />
377
+ <Banner classes="banner-warning" text="API rate limit approaching" />
378
+ ```
379
+
380
+ This approach keeps components free from hardcoded design presets while giving consumers full control over theming. Multiple variant systems can coexist, and variants compose naturally with space-separated class names.
@@ -0,0 +1,46 @@
1
+ # Gauge
2
+
3
+ A circular visual indicator for displaying percentages. Uses an SVG ring where the filled arc represents the current value. The `value` prop controls the filled portion (0-100) and an optional centered label displays the rounded percentage. The fill arc animates smoothly when the value changes.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script>
9
+ import { Gauge } from 'polymorph-ui-components';
10
+ </script>
11
+
12
+ <Gauge value={75} />
13
+ ```
14
+
15
+ ## Props
16
+
17
+ | Prop | Type | Required | Default | Description |
18
+ | --------- | --------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | value | `number` | Yes | `-` | Current percentage value (0-100). Values are clamped to the 0-100 range. Controls how much of the circular arc is filled. |
20
+ | showLabel | `boolean` | No | `true` | Whether to display the rounded percentage text centered inside the gauge ring. |
21
+ | testId | `string` | No | `-` | Value for the data-pw attribute, used for end-to-end testing selectors. |
22
+ | classes | `string` | No | `-` | CSS class string applied to the component's top-level element. Useful for theming — define classes with CSS variable overrides and pass them to create variant styles. |
23
+
24
+ ## CSS Variables
25
+
26
+ Override these custom properties to theme the component.
27
+
28
+ | Variable | Default | CSS Property | Description |
29
+ | ----------------------------- | --------- | ------------------- | -------------------------------------------------------------- |
30
+ | `--gauge-size` | `120px` | width, height | Diameter of the gauge container. |
31
+ | `--gauge-stroke-width` | `8` | stroke-width | Width of the circular track and fill arc. |
32
+ | `--gauge-track-color` | `#a1a1aa` | stroke | Color of the background ring (unfilled portion of the circle). |
33
+ | `--gauge-bar-color` | `currentColor` | stroke | Color of the filled arc that represents the current value. |
34
+ | `--gauge-transition-duration` | `0.3s` | transition-duration | Duration of the animation when the filled arc changes. |
35
+ | `--gauge-label-font-size` | `24px` | font-size | Font size of the centered percentage label. |
36
+ | `--gauge-label-font-weight` | `600` | font-weight | Font weight of the centered percentage label. |
37
+ | `--gauge-label-font-family` | `inherit` | font-family | Font family of the centered percentage label. |
38
+ | `--gauge-label-color` | `currentColor` | color | Text color of the centered percentage label. |
39
+
40
+ ## Web Component
41
+
42
+ Tag: `<pui-gauge>`
43
+
44
+ ```html
45
+ <pui-gauge value="75" show-label></pui-gauge>
46
+ ```
@@ -0,0 +1,78 @@
1
+ # GridItem
2
+
3
+ A square grid cell with a header icon (top-right), centered main icon, and text label below. Clicking toggles a loading overlay animation (clip-path radial sweep). The `showLoader` state is bindable and toggles on each click. Good for payment method or app icon grids.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script>
9
+ import { GridItem } from 'polymorph-ui-components';
10
+ </script>
11
+
12
+ <GridItem icon={'...'} text={'...'} />
13
+ ```
14
+
15
+ ## Props
16
+
17
+ | Prop | Type | Required | Default | Description |
18
+ | ---------- | ---------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | icon | `string` | Yes | `''` | URL of the main center icon image. |
20
+ | text | `string` | Yes | `''` | Label text displayed below the icon. |
21
+ | headerIcon | `string \| null` | No | `''` | URL of a small icon displayed in the top-right corner of the grid cell (e.g., an offer tag or badge). |
22
+ | showLoader | `boolean` | No | `false` | Bindable. When true, shows a rotating clip-path animation overlay on the icon area. Toggles on each click. |
23
+ | testId | `string` | No | `-` | Test selector value applied as `data-pw` on the outermost element. |
24
+ | classes | `string` | No | `-` | CSS class string applied to the component's top-level element. Useful for theming — define classes with CSS variable overrides and pass them to create variant styles. |
25
+
26
+ ## Events
27
+
28
+ | Event | Type | Description |
29
+ | --------- | -------------------------------- | --------------------------------------------------------------------------------------------- |
30
+ | onclick | `(event: MouseEvent) => void` | Fires when the grid cell is clicked. The showLoader state toggles before this callback fires. |
31
+ | onkeydown | `(event: KeyboardEvent) => void` | Fires when a key is pressed while the grid cell has focus. |
32
+
33
+ ## CSS Variables
34
+
35
+ Override these custom properties to theme the component.
36
+
37
+ | Variable | Default | CSS Property | Description |
38
+ | ------------------------------------ | ---------------------- | ---------------- | ------------------------------------------------------------ |
39
+ | `--grid-item-height` | `98px` | height | Height of the grid cell. |
40
+ | `--grid-item-width` | `66px` | width | Width of the grid cell. |
41
+ | `--grid-item-header-width` | `100%` | width | Width of the header icon row. |
42
+ | `--grid-item-header-justify-content` | `end` | justify-content | Horizontal alignment of the header icon. |
43
+ | `--grid-item-header-position` | `absolute` | position | CSS position of the header icon. |
44
+ | `--grid-item-header-top` | `5px` | top | Top offset of the header icon. |
45
+ | `--grid-item-header-z-index` | `100` | z-index | Z-index of the header icon. |
46
+ | `--grid-item-header-icon-height` | `16px` | height | Height of the header icon image. |
47
+ | `--grid-item-header-icon-width` | `fit-content` | width | Width of the header icon image. |
48
+ | `--grid-item-header-icon-object-fit` | `contain` | object-fit | Object-fit of the header icon image. |
49
+ | `--grid-item-header-icon-z-index` | `2` | z-index | Z-index of the header icon image. |
50
+ | `--grid-item-body-height` | `64px` | height | Height of the main icon container. |
51
+ | `--grid-item-body-width` | `64px` | width | Width of the main icon container. |
52
+ | `--grid-item-background-color` | `transparent` | background-color | Background color of the grid cell body. |
53
+ | `--grid-item-border` | `1px solid currentColor` | border | Border of the grid cell body. |
54
+ | `--grid-item-border-radius` | `8px` | border-radius | Corner rounding of the grid cell body. |
55
+ | `--grid-item-margin` | `8px 0 0 0` | margin | Margin of the grid cell body. |
56
+ | `--grid-item-icon-height` | `32px` | height | Height of the main center icon. |
57
+ | `--grid-item-icon-width` | `fit-content` | width | Width of the main center icon. |
58
+ | `--grid-item-icon-object-fit` | `contain` | object-fit | Object-fit of the main center icon. |
59
+ | `--grid-item-icon-z-index` | `100` | z-index | Z-index of the main center icon. |
60
+ | `--grid-item-footer-margin` | `8px 0 0 0` | margin | Margin above the text label. |
61
+ | `--grid-item-font-size` | `inherit` | font-size | Font size of the text label. |
62
+ | `--grid-item-color` | `inherit` | color | Color of the text label. |
63
+ | `--grid-item-footer-text-overflow` | `ellipsis` | text-overflow | Text overflow style for the label (ellipsis for truncation). |
64
+ | `--grid-item-footer-white-space` | `nowrap` | white-space | White space handling for the label. |
65
+ | `--grid-item-footer-overflow` | `hidden` | overflow | Overflow behavior of the label. |
66
+ | `--grid-item-footer-width` | `100%` | width | Width of the label area. |
67
+ | `--grid-item-footer-text-align` | `center` | text-align | Text alignment of the label. |
68
+ | `--grid-item-footer-height` | `fit-content` | height | Height of the label area. |
69
+ | `--grid-item-loader-border` | `32px solid currentColor` | border | Border style for the loading animation overlay. |
70
+ | `--grid-item-loader-duration` | `3s` | animation | Duration of the loading animation. |
71
+
72
+ ## Web Component
73
+
74
+ Tag: `<pui-grid-item>`
75
+
76
+ ```html
77
+ <pui-grid-item icon="/icon.svg" text="Item"></pui-grid-item>
78
+ ```
@@ -0,0 +1,66 @@
1
+ # Icon
2
+
3
+ A clickable icon component that displays an image or inline SVG with an optional text label. The entire container is a button for accessibility. Layout direction (row or column) is controlled via CSS variable `--icon-container-direction`. Supports two rendering modes: image URL via `icon` prop, or raw SVG markup via `svg` prop.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script>
9
+ import { Icon } from 'polymorph-ui-components';
10
+ </script>
11
+
12
+ <!-- Image-based icon -->
13
+ <Icon icon="/icons/home.svg" text="Home" />
14
+
15
+ <!-- Inline SVG icon -->
16
+ <Icon svg={'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>'} text="Star" />
17
+ ```
18
+
19
+ ## Props
20
+
21
+ | Prop | Type | Required | Default | Description |
22
+ | ------- | ---------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | icon | `string` | No | `-` | URL of the icon image to display. Rendered as `<img src>`. |
24
+ | svg | `string` | No | `-` | Raw SVG markup string to render inline via `{@html}`. When provided, takes priority over `icon`. Inherits `currentColor` for styling. **Must be trusted content** — never pass unsanitized user input (see Security note below). |
25
+ | text | `string \| null` | No | `-` | Optional text label displayed below the icon. |
26
+ | classes | `string` | No | `-` | CSS class string applied to the component's top-level element. Useful for theming — define classes with CSS variable overrides and pass them to create variant styles. |
27
+ | testId | `string` | No | `-` | Test selector value applied as `data-pw` on the outermost element. |
28
+
29
+ > **Note:** At least one of `icon` or `svg` should be provided. If both are provided, `svg` takes priority.
30
+
31
+ > **Security:** The `svg` prop uses `{@html}` for rendering and is **not sanitized**. Only pass trusted SVG markup — never pass unsanitized user input, as it may enable XSS attacks (e.g. `<svg onload="...">`).
32
+
33
+ ## Events
34
+
35
+ | Event | Type | Description |
36
+ | --------- | -------------------------------- | --------------------------------------------------------------- |
37
+ | onclick | `(event: MouseEvent) => void` | Fires when the icon container is clicked. |
38
+ | onkeydown | `(event: KeyboardEvent) => void` | Fires when a key is pressed while the icon container has focus. |
39
+
40
+ ## CSS Variables
41
+
42
+ Override these custom properties to theme the component.
43
+
44
+ | Variable | Default | CSS Property | Description |
45
+ | ---------------------------- | -------------- | -------------- | -------------------------------------------------------------------------- |
46
+ | `--icon-container-padding` | `4px` | padding | Inner padding of the icon container. |
47
+ | `--icon-container-direction` | `column` | flex-direction | Layout direction of icon + text (column for vertical, row for horizontal). |
48
+ | `--icon-height` | `20px` | height | Height of the icon image or SVG container. |
49
+ | `--icon-width` | `20px` | width | Width of the icon image or SVG container. |
50
+ | `--icon-padding` | `4px` | padding | Padding around the icon image or SVG container. |
51
+ | `--icon-svg-color` | `currentColor` | color | Color of the inline SVG icon (only applies when using `svg` prop). |
52
+ | `--icon-text-padding` | `4px` | padding | Padding around the text label. |
53
+ | `--icon-text-direction` | `column` | flex-direction | Layout direction of the text area. |
54
+ | `--icon-text-font-size` | `inherit` | font-size | Font size of the text label. |
55
+
56
+ ## Web Component
57
+
58
+ Tag: `<pui-icon>`
59
+
60
+ ```html
61
+ <!-- Image-based -->
62
+ <pui-icon icon="/icons/home.svg" text="Home"></pui-icon>
63
+
64
+ <!-- Inline SVG -->
65
+ <pui-icon svg="<svg viewBox='0 0 24 24' fill='currentColor'><path d='M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z'/></svg>" text="Star"></pui-icon>
66
+ ```
@@ -0,0 +1,68 @@
1
+ # IconStack
2
+
3
+ A horizontal stack of circular icons/avatars with negative margin layering (each icon overlaps the previous one). Each item can be either an `image` (renders an `<img>`) or `text` (renders a text span). Icons are z-indexed so the first icon appears on top. Commonly used for showing multiple user avatars or bank icons in a compact space.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script>
9
+ import { IconStack } from 'polymorph-ui-components';
10
+ </script>
11
+
12
+ <IconStack />
13
+ ```
14
+
15
+ ## Props
16
+
17
+ | Prop | Type | Required | Default | Description |
18
+ | ------- | ----------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | icons | `IconStackItem[]` | Yes | `-` | Array of IconStackItem objects. Each item has a `type` ('image' or 'text') and a `content` string (URL for images, display text for text items). |
20
+ | testId | `string` | No | `-` | Test selector value applied as `data-pw` on the outermost element. |
21
+ | classes | `string` | No | `-` | CSS class string applied to the component's top-level element. Useful for theming — define classes with CSS variable overrides and pass them to create variant styles. |
22
+
23
+ ## CSS Variables
24
+
25
+ Override these custom properties to theme the component.
26
+
27
+ | Variable | Default | CSS Property | Description |
28
+ | ---------------------------------- | --------------------- | --------------- | ------------------------------------------------------- |
29
+ | `--icon-stack-container-margin` | `0px` | margin | Margin of the icon stack container. |
30
+ | `--icon-stack-icon-width` | `36px` | width | Width of each icon circle. |
31
+ | `--icon-stack-icon-height` | `36px` | height | Height of each icon circle. |
32
+ | `--icon-stack-icon-bg` | `white` | background | Background color of each icon circle. |
33
+ | `--icon-stack-icon-radius` | `50%` | border-radius | Corner rounding of each icon (50% for circle). |
34
+ | `--icon-stack-icon-margin` | `0px 0px 0px -14px` | margin | Margin of each icon (negative left for overlap effect). |
35
+ | `--icon-stack-icon-align-items` | `center` | align-items | Vertical alignment of content inside each icon. |
36
+ | `--icon-stack-icon-border` | `1px solid currentColor` | border | Border of each icon circle. |
37
+ | `--icon-stack-icon-justify-content` | `center` | justify-content | Horizontal alignment of content inside each icon. |
38
+ | `--icon-stack-icon-shadow` | `0 2px 4px #00000026` | box-shadow | Box shadow of each icon circle. |
39
+ | `--icon-stack-img-width` | `36px` | width | Width of the image inside each icon. |
40
+ | `--icon-stack-img-height` | `36px` | height | Height of the image inside each icon. |
41
+ | `--icon-stack-text-container-width` | `36px` | width | Width of text-type icon containers. |
42
+ | `--icon-stack-text-container-height` | `36px` | height | Height of text-type icon containers. |
43
+ | `--icon-stack-text-container-align-items` | `center` | align-items | Vertical alignment inside text-type icons. |
44
+ | `--icon-stack-text-container-justify-content` | `center` | justify-content | Horizontal alignment inside text-type icons. |
45
+ | `--icon-stack-text-color` | `inherit` | color | Text color inside text-type icons. |
46
+ | `--icon-stack-text-size` | `inherit` | font-size | Font size inside text-type icons. |
47
+ | `--icon-stack-text-weight` | `inherit` | font-weight | Font weight inside text-type icons. |
48
+ | `--icon-stack-text-align` | `center` | text-align | Text alignment inside text-type icons. |
49
+
50
+ ## Type Reference
51
+
52
+ Custom types used by this component's props and events:
53
+
54
+ ### IconStackItem
55
+
56
+ ```typescript
57
+ type IconStackItem = { type: 'image' | 'text'; content: string };
58
+ ```
59
+
60
+ ## Web Component
61
+
62
+ Tag: `<pui-icon-stack>`
63
+
64
+ ```html
65
+ <pui-icon-stack></pui-icon-stack>
66
+ ```
67
+
68
+ > **Note:** The `icons` prop is an array — set it via JavaScript property.