@plastic-js/tsumiki 0.1.58 → 0.1.60

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.
package/README.md CHANGED
@@ -69,8 +69,6 @@ function Example(){
69
69
  | `open` | `(() => boolean)` | — | Controlled open state (getter) — REQUIRED |
70
70
  | `onOpenChange` | `(isOpen: boolean) => void` | — | Called when open state changes |
71
71
  | `disabled` | `boolean` | `false` | Disable the select |
72
- | `clearable` | `boolean` | `false` | Show a "clear selection" row at the top |
73
- | `clearLabel` | `string` | `'None'` | Label for the clear row |
74
72
  | `backdropClassName` | `string` | — | CSS class for the backdrop overlay |
75
73
  | `backdropStyle` | `object` | — | Inline style for the backdrop overlay |
76
74
  | `filter` | `boolean \| (item, query, itemToLabel) => boolean` | — | Enable filter: `true` for substring match, or a custom filter function |
@@ -103,7 +101,7 @@ The search input is auto-focused when the sheet opens and cleared when it closes
103
101
 
104
102
  ## 1. Project Nature
105
103
 
106
- **Tsumiki** (`@plastic-js/tsumiki`) is a mobile-first UI component library for the [Plastic JS](https://github.com/plastic-js/plastic) reactive framework. It provides 51 production-grade, accessible components built on top of headless UI primitives from Ark UI, styled with a Radix Colors-based design token system.
104
+ **Tsumiki** (`@plastic-js/tsumiki`) is a mobile-first UI component library for the [Plastic JS](https://github.com/plastic-js/plastic) reactive framework. It provides 53 production-grade, accessible components built on top of headless UI primitives from Ark UI, styled with a Radix Colors-based design token system.
107
105
 
108
106
  The name "tsumiki" (積み木) means "building blocks" in Japanese — the library is designed to be composable, tree-shakable, and themeable.
109
107
 
@@ -152,7 +150,7 @@ Every tsumiki component wraps a corresponding `@plastic-js/ark` primitive:
152
150
  import { Switch as ArkSwitch } from '@plastic-js/ark'
153
151
  ```
154
152
 
155
- The Ark import is always aliased with an `Ark` prefix to distinguish it from the tsumiki component being defined. All 36 Ark module components have corresponding tsumiki implementations, plus 15 additional custom/project-specific components (Button, Input, Skeleton, ConfirmDialog, mobile variants, etc.).
153
+ The Ark import is always aliased with an `Ark` prefix to distinguish it from the tsumiki component being defined. All 40 Ark-backed components have corresponding tsumiki implementations, plus 13 additional custom/project-specific components (CardNumberInput, ConfirmDialog, Select, Skeleton, mobile variants, etc.).
156
154
 
157
155
  ---
158
156
 
@@ -74,12 +74,13 @@ var intentClasses = {
74
74
  neutral: css({
75
75
  "--_di-border": "var(--tsu-neutral-outline-border)",
76
76
  "--_di-border-focus": "var(--tsu-neutral-outline-border-hover)"
77
- }),
78
- danger: css({
79
- "--_di-border": "var(--tsu-danger-outline-border)",
80
- "--_di-border-focus": "var(--tsu-danger-outline-border-hover)"
81
77
  })
82
78
  };
79
+ var errorClass = css({
80
+ borderColor: "var(--tsu-danger-outline-border) !important",
81
+ "&:focus-visible": { borderColor: "var(--tsu-danger-outline-border) !important" }
82
+ });
83
+ var invalidWrapperClass = css({ "--_di-border-focus": "var(--tsu-danger-outline-border-hover)" });
83
84
  var triggerBaseClass = css({
84
85
  width: "100%",
85
86
  background: "transparent",
@@ -279,7 +280,8 @@ var DatePicker = (props = {}) => {
279
280
  const [local] = splitProps(mergeProps$1({
280
281
  size: "md",
281
282
  disabled: false,
282
- intent: "default"
283
+ intent: "default",
284
+ invalid: false
283
285
  }, props), [
284
286
  "value",
285
287
  "onValueChange",
@@ -287,6 +289,7 @@ var DatePicker = (props = {}) => {
287
289
  "disabled",
288
290
  "size",
289
291
  "intent",
292
+ "invalid",
290
293
  "className"
291
294
  ]);
292
295
  const open = createSignal(false);
@@ -342,7 +345,7 @@ var DatePicker = (props = {}) => {
342
345
  return () => {
343
346
  const _el0 = _tmpl2.cloneNode(true);
344
347
  insert(_el0, () => displayDate ? formatDateLabel(displayDate) : local.placeholder || "Select date");
345
- setProp(_el0, "className", () => `${triggerBaseClass} ${intentClasses[read(local.intent)]} ${triggerSizeClasses[read(local.size)]} ${displayDate ? triggerValueClass : triggerPlaceholderClass}`);
348
+ setProp(_el0, "className", () => `${triggerBaseClass} ${intentClasses[read(local.intent)] ?? intentClasses.default} ${read(local.invalid) ? errorClass : ""} ${triggerSizeClasses[read(local.size)]} ${displayDate ? triggerValueClass : triggerPlaceholderClass}`);
346
349
  return _el0;
347
350
  };
348
351
  }, _el1);
@@ -352,7 +355,7 @@ var DatePicker = (props = {}) => {
352
355
  strokeWidth: 1.5
353
356
  })));
354
357
  setProp(_el2, "className", () => calendarIconClass);
355
- setProp(_el0, "className", () => `${triggerWrapperClass} ${isDisabled() ? triggerDisabledClass : ""} ${local.className || ""}`);
358
+ setProp(_el0, "className", () => `${triggerWrapperClass} ${read(local.invalid) ? invalidWrapperClass : ""} ${isDisabled() ? triggerDisabledClass : ""} ${local.className || ""}`);
356
359
  setProp(_el0, "onClick", () => openSheet);
357
360
  setProp(_el0, "aria-disabled", () => isDisabled());
358
361
  return _el0;
@@ -75,8 +75,7 @@ var variantClasses = {
75
75
  };
76
76
  var intentClasses = {
77
77
  default: css({ "--_input-border": "var(--tsu-accent-outline-border)" }),
78
- neutral: css({ "--_input-border": "var(--tsu-neutral-outline-border)" }),
79
- danger: css({ "--_input-border": "var(--tsu-danger-outline-border)" })
78
+ neutral: css({ "--_input-border": "var(--tsu-neutral-outline-border)" })
80
79
  };
81
80
  var errorClass = css({
82
81
  borderColor: "var(--tsu-danger-outline-border) !important",
@@ -176,7 +175,7 @@ var Input = (props = {}) => {
176
175
  wrapperClass,
177
176
  sizeClasses[read(local.size)],
178
177
  variant === "solid" ? variantClasses.solid : variantClasses.outline,
179
- intentClasses[intent],
178
+ intentClasses[intent] ?? intentClasses.default,
180
179
  effectiveInvalid() && errorClass,
181
180
  effectiveDisabled() && disabledClass,
182
181
  hasPrefix() && hasPrefixClass,
@@ -48,12 +48,12 @@ var intentClasses = {
48
48
  neutral: css({
49
49
  "--_sel-border": "var(--tsu-neutral-outline-border)",
50
50
  "--_sel-border-focus": "var(--tsu-neutral-outline-border-hover)"
51
- }),
52
- danger: css({
53
- "--_sel-border": "var(--tsu-danger-outline-border)",
54
- "--_sel-border-focus": "var(--tsu-danger-outline-border-hover)"
55
51
  })
56
52
  };
53
+ var errorClass = css({
54
+ borderColor: "var(--tsu-danger-outline-border) !important",
55
+ "&:focus-visible": { borderColor: "var(--tsu-danger-outline-border) !important" }
56
+ });
57
57
  var sizeClasses = {
58
58
  xs: css({
59
59
  height: "var(--tsu-comp-height-xs)",
@@ -213,7 +213,8 @@ var Select = (props) => {
213
213
  const [local, rest] = splitProps(mergeProps$1({
214
214
  size: "md",
215
215
  variant: "outline",
216
- intent: "default"
216
+ intent: "default",
217
+ invalid: false
217
218
  }, props), [
218
219
  "value",
219
220
  "onValueChange",
@@ -229,7 +230,8 @@ var Select = (props) => {
229
230
  "backdropStyle",
230
231
  "filter",
231
232
  "variant",
232
- "intent"
233
+ "intent",
234
+ "invalid"
233
235
  ]);
234
236
  const open = (v) => {
235
237
  if (v === void 0) return read(local.open);
@@ -281,6 +283,7 @@ var Select = (props) => {
281
283
  selectedItem,
282
284
  setValue,
283
285
  disabled: () => local.disabled ?? false,
286
+ invalid: () => local.invalid ?? false,
284
287
  size: () => read(local.size),
285
288
  variant: () => read(local.variant),
286
289
  intent: () => {
@@ -307,7 +310,7 @@ var SelectTrigger = (props) => {
307
310
  ]);
308
311
  return jsx("div", mergeProps(() => part("trigger"), {
309
312
  get className() {
310
- return `${triggerBaseClass} ${triggerVariants[ctx.variant() === "solid" ? "solid" : "outline"]} ${intentClasses[ctx.intent()]} ${sizeClasses[ctx.size()] || ""} ${local.className || ""}`;
313
+ return `${triggerBaseClass} ${triggerVariants[ctx.variant() === "solid" ? "solid" : "outline"]} ${intentClasses[ctx.intent()] ?? intentClasses.default} ${ctx.invalid() ? errorClass : ""} ${sizeClasses[ctx.size()] || ""} ${local.className || ""}`;
311
314
  },
312
315
  "data-disabled": () => ctx.disabled() ? "" : void 0,
313
316
  onClick: () => {
@@ -377,7 +380,7 @@ var List = () => {
377
380
  setProp(_el2, "ref", (el) => {
378
381
  filterInputEl = el;
379
382
  });
380
- setProp(_el2, "className", () => `${filterInputClass} ${intentClasses[ctx.intent()]}`);
383
+ setProp(_el2, "className", () => `${filterInputClass} ${intentClasses[ctx.intent()] ?? intentClasses.default} ${ctx.invalid() ? errorClass : ""}`);
381
384
  setProp(_el2, "value", () => ctx.query);
382
385
  setProp(_el2, "onInput", (e) => ctx.query(e.target.value));
383
386
  setProp(_el2, "onClick", (e) => e.stopPropagation());
@@ -82,8 +82,7 @@ var variantClasses = {
82
82
  };
83
83
  var intentClasses = {
84
84
  default: css({ "--_sel-border": "var(--tsu-accent-outline-border)" }),
85
- neutral: css({ "--_sel-border": "var(--tsu-neutral-outline-border)" }),
86
- danger: css({ "--_sel-border": "var(--tsu-danger-outline-border)" })
85
+ neutral: css({ "--_sel-border": "var(--tsu-neutral-outline-border)" })
87
86
  };
88
87
  var errorClass = css({
89
88
  borderColor: "var(--tsu-danger-outline-border) !important",
@@ -5,9 +5,19 @@ import { ark } from "@plastic-js/ark/factory";
5
5
  //#region src/components/Tag.jsx
6
6
  var _tmpl = template("<button type=\"button\" data-part=\"close\" aria-label=\"Close\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" width=\"10\" height=\"10\" aria-hidden=\"true\"><path d=\"M18 6 6 18M6 6l12 12\"></path></svg></button>");
7
7
  var baseClass = css({
8
+ "--_tag-solid-bg": "transparent",
9
+ "--_tag-solid-fg": "transparent",
10
+ "--_tag-surface-bg": "transparent",
11
+ "--_tag-surface-border": "transparent",
12
+ "--_tag-surface-fg": "transparent",
13
+ "--_tag-subtle-bg": "transparent",
14
+ "--_tag-subtle-fg": "transparent",
15
+ "--_tag-outline-border": "transparent",
16
+ "--_tag-outline-fg": "transparent",
8
17
  display: "inline-flex",
9
18
  alignItems: "center",
10
19
  gap: "var(--tsu-spacing-xs)",
20
+ border: "1px solid transparent",
11
21
  whiteSpace: "nowrap",
12
22
  userSelect: "none",
13
23
  borderRadius: "var(--tsu-radius-round)",
@@ -16,45 +26,77 @@ var baseClass = css({
16
26
  transition: "background-color var(--tsu-transition-fast), color var(--tsu-transition-fast), border-color var(--tsu-transition-fast)"
17
27
  });
18
28
  var solidClass = css({
19
- backgroundColor: "var(--_tag-bg)",
20
- color: "var(--_tag-solid-fg)",
21
- border: "1px solid transparent"
29
+ backgroundColor: "var(--_tag-solid-bg)",
30
+ color: "var(--_tag-solid-fg)"
31
+ });
32
+ var surfaceClass = css({
33
+ backgroundColor: "var(--_tag-surface-bg)",
34
+ borderColor: "var(--_tag-surface-border)",
35
+ color: "var(--_tag-surface-fg)"
36
+ });
37
+ var subtleClass = css({
38
+ backgroundColor: "var(--_tag-subtle-bg)",
39
+ color: "var(--_tag-subtle-fg)"
22
40
  });
23
41
  var outlineClass = css({
24
- backgroundColor: "transparent",
25
- color: "var(--_tag-outline-fg)",
26
- border: "1px solid var(--_tag-border)"
42
+ borderColor: "var(--_tag-outline-border)",
43
+ color: "var(--_tag-outline-fg)"
27
44
  });
28
45
  var intentClasses = {
29
46
  neutral: css({
30
- "--_tag-bg": "var(--tsu-neutral-solid-bg)",
47
+ "--_tag-solid-bg": "var(--tsu-neutral-solid-bg)",
31
48
  "--_tag-solid-fg": "var(--tsu-neutral-solid-fg)",
32
- "--_tag-outline-fg": "var(--tsu-text-primary)",
33
- "--_tag-border": "var(--tsu-neutral-outline-border)"
49
+ "--_tag-surface-bg": "var(--tsu-neutral-surface-bg)",
50
+ "--_tag-surface-border": "var(--tsu-neutral-surface-border)",
51
+ "--_tag-surface-fg": "var(--tsu-neutral-surface-fg)",
52
+ "--_tag-subtle-bg": "var(--tsu-neutral-subtle-bg)",
53
+ "--_tag-subtle-fg": "var(--tsu-neutral-subtle-fg)",
54
+ "--_tag-outline-border": "var(--tsu-neutral-outline-border)",
55
+ "--_tag-outline-fg": "var(--tsu-neutral-outline-fg)"
34
56
  }),
35
57
  success: css({
36
- "--_tag-bg": "var(--tsu-success-solid-bg)",
58
+ "--_tag-solid-bg": "var(--tsu-success-solid-bg)",
37
59
  "--_tag-solid-fg": "var(--tsu-success-solid-fg)",
38
- "--_tag-outline-fg": "var(--tsu-success-outline-fg)",
39
- "--_tag-border": "var(--tsu-success-outline-border)"
60
+ "--_tag-surface-bg": "var(--tsu-success-surface-bg)",
61
+ "--_tag-surface-border": "var(--tsu-success-surface-border)",
62
+ "--_tag-surface-fg": "var(--tsu-success-surface-fg)",
63
+ "--_tag-subtle-bg": "var(--tsu-success-subtle-bg)",
64
+ "--_tag-subtle-fg": "var(--tsu-success-subtle-fg)",
65
+ "--_tag-outline-border": "var(--tsu-success-outline-border)",
66
+ "--_tag-outline-fg": "var(--tsu-success-outline-fg)"
40
67
  }),
41
68
  warning: css({
42
- "--_tag-bg": "var(--tsu-warning-solid-bg)",
69
+ "--_tag-solid-bg": "var(--tsu-warning-solid-bg)",
43
70
  "--_tag-solid-fg": "var(--tsu-warning-solid-fg)",
44
- "--_tag-outline-fg": "var(--tsu-warning-outline-fg)",
45
- "--_tag-border": "var(--tsu-warning-outline-border)"
71
+ "--_tag-surface-bg": "var(--tsu-warning-surface-bg)",
72
+ "--_tag-surface-border": "var(--tsu-warning-surface-border)",
73
+ "--_tag-surface-fg": "var(--tsu-warning-surface-fg)",
74
+ "--_tag-subtle-bg": "var(--tsu-warning-subtle-bg)",
75
+ "--_tag-subtle-fg": "var(--tsu-warning-subtle-fg)",
76
+ "--_tag-outline-border": "var(--tsu-warning-outline-border)",
77
+ "--_tag-outline-fg": "var(--tsu-warning-outline-fg)"
46
78
  }),
47
79
  danger: css({
48
- "--_tag-bg": "var(--tsu-danger-solid-bg)",
80
+ "--_tag-solid-bg": "var(--tsu-danger-solid-bg)",
49
81
  "--_tag-solid-fg": "var(--tsu-danger-solid-fg)",
50
- "--_tag-outline-fg": "var(--tsu-danger-outline-fg)",
51
- "--_tag-border": "var(--tsu-danger-outline-border)"
82
+ "--_tag-surface-bg": "var(--tsu-danger-surface-bg)",
83
+ "--_tag-surface-border": "var(--tsu-danger-surface-border)",
84
+ "--_tag-surface-fg": "var(--tsu-danger-surface-fg)",
85
+ "--_tag-subtle-bg": "var(--tsu-danger-subtle-bg)",
86
+ "--_tag-subtle-fg": "var(--tsu-danger-subtle-fg)",
87
+ "--_tag-outline-border": "var(--tsu-danger-outline-border)",
88
+ "--_tag-outline-fg": "var(--tsu-danger-outline-fg)"
52
89
  }),
53
90
  accent: css({
54
- "--_tag-bg": "var(--tsu-accent-solid-bg)",
91
+ "--_tag-solid-bg": "var(--tsu-accent-solid-bg)",
55
92
  "--_tag-solid-fg": "var(--tsu-accent-solid-fg)",
56
- "--_tag-outline-fg": "var(--tsu-accent-outline-fg)",
57
- "--_tag-border": "var(--tsu-accent-outline-border)"
93
+ "--_tag-surface-bg": "var(--tsu-accent-surface-bg)",
94
+ "--_tag-surface-border": "var(--tsu-accent-surface-border)",
95
+ "--_tag-surface-fg": "var(--tsu-accent-surface-fg)",
96
+ "--_tag-subtle-bg": "var(--tsu-accent-subtle-bg)",
97
+ "--_tag-subtle-fg": "var(--tsu-accent-subtle-fg)",
98
+ "--_tag-outline-border": "var(--tsu-accent-outline-border)",
99
+ "--_tag-outline-fg": "var(--tsu-accent-outline-fg)"
58
100
  })
59
101
  };
60
102
  var sizeClasses = {
@@ -121,13 +163,15 @@ var intentMap = {
121
163
  info: "accent"
122
164
  };
123
165
  var read = (v) => typeof v === "function" ? v() : v;
124
- var variantClass = {
166
+ var variantClasses = {
125
167
  solid: solidClass,
168
+ surface: surfaceClass,
169
+ subtle: subtleClass,
126
170
  outline: outlineClass
127
171
  };
128
172
  var Tag = (props = {}) => {
129
173
  const [local, rest] = splitProps(mergeProps$1({
130
- variant: "solid",
174
+ variant: "subtle",
131
175
  intent: "default",
132
176
  size: "sm",
133
177
  closable: false
@@ -143,12 +187,12 @@ var Tag = (props = {}) => {
143
187
  return jsx(ark.span, mergeProps(rest, {
144
188
  "data-part": "root",
145
189
  className: () => {
146
- const variant = read(local.variant) === "outline" ? "outline" : "solid";
190
+ const variant = read(local.variant);
147
191
  const intent = intentMap[read(local.intent)] || read(local.intent);
148
192
  const size = read(local.size);
149
193
  return [
150
194
  baseClass,
151
- variantClass[variant],
195
+ variantClasses[variant],
152
196
  intentClasses[intent],
153
197
  sizeClasses[size],
154
198
  local.className
@@ -1,6 +1,6 @@
1
1
  import { jsx, mergeProps } from "@plastic-js/plastic/jsx-runtime";
2
2
  import { css } from "@emotion/css";
3
- import { ToggleGroup } from "@plastic-js/ark";
3
+ import { ToggleGroup, useFieldContext } from "@plastic-js/ark";
4
4
  import { mergeProps as mergeProps$2, splitProps } from "@plastic-js/plastic";
5
5
  //#region src/components/ToggleGroup.jsx
6
6
  var rootClass = css({
@@ -41,6 +41,7 @@ var intentClasses = {
41
41
  default: css({ "--_tg-border": "var(--tsu-accent-solid-bg)" }),
42
42
  neutral: css({ "--_tg-border": "var(--tsu-neutral-outline-border)" })
43
43
  };
44
+ var errorClass = css({ "--_tg-border": "var(--tsu-danger-outline-border) !important" });
44
45
  var sizeVars = {
45
46
  xs: {
46
47
  "--_tg-height": "var(--tsu-comp-height-xs)",
@@ -78,6 +79,7 @@ var sizeVars = {
78
79
  "--_tg-radius": "var(--tsu-radius-l1-xl)"
79
80
  }
80
81
  };
82
+ var read = (v) => typeof v === "function" ? v() : v;
81
83
  var ToggleGroupItem = (props = {}) => {
82
84
  const [local, rest] = splitProps(props, ["className", "children"]);
83
85
  return jsx(ToggleGroup.Item, mergeProps(rest, {
@@ -90,28 +92,34 @@ var ToggleGroupItem = (props = {}) => {
90
92
  var ToggleGroup$1 = (props = {}) => {
91
93
  const [local, rest] = splitProps(mergeProps$2({
92
94
  size: "md",
93
- intent: "default"
95
+ intent: "default",
96
+ invalid: false
94
97
  }, props), [
95
98
  "className",
96
99
  "children",
97
100
  "size",
98
101
  "intent",
102
+ "invalid",
99
103
  "style"
100
104
  ]);
105
+ const field = useFieldContext();
106
+ const fieldInvalid = () => field?.invalid?.() ?? false;
107
+ const effectiveInvalid = () => read(local.invalid) || fieldInvalid();
101
108
  return jsx(ToggleGroup.Root, mergeProps(rest, {
102
- get className() {
103
- return [
104
- rootClass,
105
- intentClasses[local.intent],
106
- local.className
107
- ].filter(Boolean).join(" ");
108
- },
109
+ className: () => [
110
+ rootClass,
111
+ intentClasses[local.intent],
112
+ effectiveInvalid() && errorClass,
113
+ local.className
114
+ ].filter(Boolean).join(" "),
109
115
  get style() {
110
116
  return {
111
117
  ...sizeVars[local.size],
112
118
  ...local.style
113
119
  };
114
120
  },
121
+ "aria-invalid": () => effectiveInvalid() ? "true" : "false",
122
+ "data-invalid": () => effectiveInvalid() ? "" : void 0,
115
123
  children: () => local.children
116
124
  }));
117
125
  };
package/docs/api.md CHANGED
@@ -129,7 +129,7 @@ Passthrough: Ark `Fieldset.Root` props (`disabled`, `invalid`, …).
129
129
  | Prop | Type | Default | Description |
130
130
  |---|---|---|---|
131
131
  | `variant` | `'outline' \| 'solid'` | `'outline'` | Filled background instead of outlined. |
132
- | `intent` | `string` | `'default'` | Border intent (`'default'`, `'neutral'`, `'danger'`). `neutral` applies only to the outline variant (ignored when `variant='solid'`). |
132
+ | `intent` | `string` | `'default'` | Border intent (`'default'`, `'neutral'`). `neutral` applies only to the outline variant (ignored when `variant='solid'`). For error states use `invalid` instead. |
133
133
  | `size` | `string` | `md` | See size values. |
134
134
  | `disabled` | `boolean` | `false` | Disables the input. |
135
135
  | `invalid` | `boolean` | `false` | Renders the error/invalid style. |
@@ -243,7 +243,8 @@ Mobile-optimized bottom-sheet select (draggable sheet overlay).
243
243
  | `onOpenChange` | `(open) => void` | — | Open/close callback. |
244
244
  | `disabled` | `boolean` | `false` | Disabled. |
245
245
  | `variant` | `'outline' \| 'solid'` | `'outline'` | Filled trigger variant instead of outline. |
246
- | `intent` | `'default' \| 'neutral' \| 'danger'` | `'default'` | Border color for trigger and the search input. `neutral` applies only to the outline variant (ignored when `variant='solid'`). |
246
+ | `intent` | `'default' \| 'neutral'` | `'default'` | Border color for trigger and the search input. `neutral` applies only to the outline variant (ignored when `variant='solid'`). For error states use `invalid` instead. |
247
+ | `invalid` | `boolean` | `false` | Renders the error/invalid style (red border on the trigger and the search input). |
247
248
  | `filter` | `(item, query, itemToLabel) => boolean` | — | Custom filter for the search box. |
248
249
  | `placeholder` | `string` | — | Trigger placeholder. |
249
250
  | `backdropClassName` | `string` | — | Backdrop class. |
@@ -519,6 +520,37 @@ Pure-CSS spinner.
519
520
 
520
521
  Accepts no tsumiki-specific props; `className`/style/DOM props pass through.
521
522
 
523
+ ### Badge
524
+
525
+ Pill-shaped status indicator.
526
+
527
+ **Exports:** `Badge` · Escape hatch: `Badge.origin.Root`
528
+
529
+ | Prop | Type | Default | Description |
530
+ |---|---|---|---|
531
+ | `variant` | `'solid' \| 'surface' \| 'subtle' \| 'outline'` | `'subtle'` | Filled, panel-tinted, tinted, or outlined badge. |
532
+ | `intent` | `string` | `'default'` | Color intent: `'default'`, `'neutral'`, `'accent'`, `'info'`, `'danger'`, `'success'`, `'warning'`. `default`/`info`/`intent` map to `neutral`/`accent`. |
533
+ | `size` | `string` | `md` | See size values. |
534
+ | `dot` | `boolean` | `false` | Show a small status dot before the label. |
535
+ | `className` | `string` | — | Root class. |
536
+ | `children` | `node` | — | Label content. |
537
+
538
+ ### Tag
539
+
540
+ Closable pill label.
541
+
542
+ **Exports:** `Tag` · Escape hatch: `Tag.origin.Root`
543
+
544
+ | Prop | Type | Default | Description |
545
+ |---|---|---|---|
546
+ | `variant` | `'solid' \| 'surface' \| 'subtle' \| 'outline'` | `'subtle'` | Filled, panel-tinted, tinted, or outlined tag. |
547
+ | `intent` | `string` | `'default'` | Color intent: `'default'`, `'neutral'`, `'accent'`, `'info'`, `'danger'`, `'success'`, `'warning'`. `default`/`info` map to `neutral`/`accent`. |
548
+ | `size` | `string` | `sm` | See size values. |
549
+ | `closable` | `boolean` | `false` | Show a close (✕) button; renders `data-part="close"`. |
550
+ | `onClose` | `() => void` | — | Called when the close button is clicked. |
551
+ | `className` | `string` | — | Root class. |
552
+ | `children` | `node` | — | Label content. |
553
+
522
554
  ---
523
555
 
524
556
  ## Feedback
@@ -6,7 +6,7 @@ A single reference for `@plastic-js/tsumiki` covering the component inventory &
6
6
 
7
7
  ## Part 1 — Component Classification
8
8
 
9
- All 51 components are classified along two orthogonal axes.
9
+ All 53 components are classified along two orthogonal axes.
10
10
 
11
11
  ### Axis 1: Development Lifecycle
12
12
 
@@ -28,11 +28,12 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
28
28
 
29
29
  ### Component Matrix
30
30
 
31
- #### Completed · Ark-Backed (32)
31
+ #### Completed · Ark-Backed (34)
32
32
 
33
33
  | Component | Ark Primitive |
34
34
  |-----------|---------------|
35
35
  | Avatar | `ArkAvatar` |
36
+ | Badge | `ark("span")` via factory |
36
37
  | Button | `ark("button")` via factory |
37
38
  | Checkbox | `ArkCheckbox` |
38
39
  | Collapsible | `ArkCollapsible` |
@@ -60,6 +61,7 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
60
61
  | Steps | `ArkSteps` |
61
62
  | Switch | `ArkSwitch` |
62
63
  | Tabs | `ArkTabs` |
64
+ | Tag | `ark("span")` via factory |
63
65
  | TagsInput | `ArkTagsInput` |
64
66
  | Toast | `ArkToast` |
65
67
  | Toggle | `ArkToggle` |
@@ -103,11 +105,11 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
103
105
  ```
104
106
  Ark-Backed Custom-Built
105
107
  ┌─────────────────┬─────────────────┐
106
- Completed │ 32 │ 13 │ 45
108
+ Completed │ 34 │ 13 │ 47
107
109
  ├─────────────────┼─────────────────┤
108
110
  In Development │ 6 │ 0 │ 6
109
111
  └─────────────────┴─────────────────┘
110
- 38 13 51
112
+ 40 13 53
111
113
  ```
112
114
 
113
115
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plastic-js/tsumiki",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "description": "A UI component library for Plastic JS.",
5
5
  "license": "MIT",
6
6
  "author": "tigre",