@momoi-labs/kiso 0.7.1 → 0.9.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.
- package/kiso/docs/components/README.md +4 -2
- package/kiso/docs/components/app-shell.md +51 -12
- package/kiso/docs/components/chip-input.md +194 -0
- package/kiso/docs/components/sparkline.md +93 -0
- package/kiso/docs/components/table.md +3 -1
- package/kiso/docs/evolution.md +10 -1
- package/kiso/docs/patterns/dashboard.md +3 -2
- package/kiso/docs/patterns/settings.md +1 -0
- package/kiso/docs/tokens.md +54 -36
- package/kiso/ui.css +135 -4
- package/package.json +1 -1
- package/tokens/build/tokens.css +55 -43
- package/tokens/build/tokens.d.ts +34 -12
- package/tokens/build/tokens.json +67 -55
- package/tokens/build/tokens.scss +69 -57
|
@@ -11,6 +11,7 @@ behavioral reference where one exists.
|
|
|
11
11
|
- [Button](button.md): Triggers a visible, text-labeled action without changing the URL.
|
|
12
12
|
- [Card](card.md): Groups related content and actions with visual separation.
|
|
13
13
|
- [Checkbox](checkbox.md): Toggles an option in a list or selects multiple values.
|
|
14
|
+
- [ChipInput](chip-input.md): Collects several structured values in one field, each with its own options.
|
|
14
15
|
- [FormField](form-field.md): Composes Label, a form control, HelperText, and ValidationMessage with consistent ID and ARIA wiring.
|
|
15
16
|
- [HelperText](helper-text.md): Provides persistent, non-error context for a form control.
|
|
16
17
|
- [IconButton](icon-button.md): Triggers a compact icon-only action with a required accessible name.
|
|
@@ -35,12 +36,13 @@ behavioral reference where one exists.
|
|
|
35
36
|
- [KV](kv.md): Describes one object through named facts.
|
|
36
37
|
- [LogView](log-view.md): Displays scrollable log output with follow-tail behavior.
|
|
37
38
|
- [Search](search.md): Filters visible content such as a list or table.
|
|
39
|
+
- [Sparkline](sparkline.md): Draws the shape of one metric series at cell size.
|
|
38
40
|
- [Stat](stat.md): Presents a named metric with optional change and context.
|
|
39
41
|
- [Table / DataTable](table.md): Presents structured records with optional sorting, selection, filtering, and pagination.
|
|
40
42
|
|
|
41
43
|
## Navigation and structure
|
|
42
44
|
|
|
43
|
-
- [AppShell / ApplicationShell](app-shell.md): Provides low-level columns or the complete shared application frame.
|
|
45
|
+
- [AppShell / ApplicationShell](app-shell.md): Provides low-level columns or the complete shared application frame (sidebar console or top-bar surface).
|
|
44
46
|
- [BrandMark](brand-mark.md): Decorative letter or icon beside a product name.
|
|
45
47
|
- [Breadcrumb](breadcrumb.md): Shows the current location within a hierarchy.
|
|
46
48
|
- [Header](header.md): Composes persistent application navigation and global actions.
|
|
@@ -62,7 +64,7 @@ behavioral reference where one exists.
|
|
|
62
64
|
## Required compositions
|
|
63
65
|
|
|
64
66
|
- [FormField](form-field.md) composes [Label](label.md) + [Input](input.md) (or another form control) + [HelperText](helper-text.md) + [ValidationMessage](validation-message.md).
|
|
65
|
-
- [ApplicationShell](app-shell.md) composes [Sidebar](sidebar.md) + [Navigation](navigation.md) + [Header](header.md) + page content.
|
|
67
|
+
- [ApplicationShell](app-shell.md) composes [Sidebar](sidebar.md) + [Navigation](navigation.md) + [Header](header.md) + page content, or a top-bar-only frame with brand in [Header](header.md).
|
|
66
68
|
- [Header](header.md) composes [Link](link.md) + [IconButton](icon-button.md) + optional [DropdownMenu](dropdown-menu.md).
|
|
67
69
|
- [Table / DataTable](table.md) composes [EmptyState](empty-state.md), [Skeleton](skeleton.md), and [Pagination](pagination.md) for empty, loading, and paged states.
|
|
68
70
|
- [PageHeader](page-header.md) composes a title + optional subtitle + [Buttons](button.md).
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
AppShell places persistent [Sidebar](sidebar.md) navigation beside the main
|
|
6
6
|
application content. It owns the page columns, not navigation state.
|
|
7
|
-
ApplicationShell composes the standard
|
|
8
|
-
|
|
7
|
+
ApplicationShell composes the standard frame when a product wants the complete
|
|
8
|
+
shared chrome — either a console with a rail, or a single-surface top bar.
|
|
9
9
|
|
|
10
10
|
## Anatomy
|
|
11
11
|
|
|
12
|
+
Console with rail (default):
|
|
13
|
+
|
|
12
14
|
```
|
|
13
15
|
AppShell
|
|
14
16
|
├── Sidebar
|
|
@@ -17,15 +19,25 @@ AppShell
|
|
|
17
19
|
└── page content
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
Single-surface top bar (`layout="topbar"`):
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
AppShell[data-layout="topbar"]
|
|
26
|
+
└── AppShellMain
|
|
27
|
+
├── Header (brand, primaryAction, header)
|
|
28
|
+
└── page content
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The console slots are direct children. AppShell is a `div`; AppShellMain is a
|
|
21
32
|
`main` with `min-width: 0`, so wide tables and log lines cannot push the
|
|
22
33
|
Sidebar off screen. Sidebar owns its header, body, and footer.
|
|
23
34
|
|
|
24
|
-
ApplicationShell takes `brand`, optional `primaryAction`,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
ApplicationShell takes `brand`, optional `primaryAction`, optional `header`,
|
|
36
|
+
and page content. The default `layout="sidebar"` also takes navigation groups
|
|
37
|
+
and optional `footer`. A destination contains `href`, `label`, optional
|
|
38
|
+
`active`, optional `onClick`, and optional leading or trailing content. The
|
|
39
|
+
caller still decides the current destination and whether a destination follows
|
|
40
|
+
its link or changes a view in place.
|
|
29
41
|
|
|
30
42
|
```tsx
|
|
31
43
|
<ApplicationShell
|
|
@@ -46,6 +58,17 @@ a destination follows its link or changes a view in place.
|
|
|
46
58
|
</ApplicationShell>
|
|
47
59
|
```
|
|
48
60
|
|
|
61
|
+
```tsx
|
|
62
|
+
<ApplicationShell
|
|
63
|
+
layout="topbar"
|
|
64
|
+
brand={<ProductBrand />}
|
|
65
|
+
primaryAction={<CreateProject />}
|
|
66
|
+
header={<BoardChrome />}
|
|
67
|
+
>
|
|
68
|
+
{page}
|
|
69
|
+
</ApplicationShell>
|
|
70
|
+
```
|
|
71
|
+
|
|
49
72
|
When `onClick` is present, ApplicationShell prevents link navigation and calls
|
|
50
73
|
it. Without `onClick`, the destination remains a normal link. Use
|
|
51
74
|
`navigationLabel` to distinguish this navigation landmark when the default
|
|
@@ -53,17 +76,28 @@ it. Without `onClick`, the destination remains a normal link. Use
|
|
|
53
76
|
|
|
54
77
|
## Variants
|
|
55
78
|
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
`layout="sidebar"` (default) mounts Sidebar with brand and primary action in
|
|
80
|
+
SidebarHeader, navigation in SidebarBody, and optional footer. The optional
|
|
81
|
+
`header` slot stays in the main column.
|
|
82
|
+
|
|
83
|
+
`layout="topbar"` omits Sidebar entirely. Brand, optional primary action, and
|
|
84
|
+
the optional `header` slot render together in Header. Do not pass `navigation`
|
|
85
|
+
or `footer` in this mode — there is no rail to host them.
|
|
86
|
+
|
|
87
|
+
Compose [Header](header.md) and [PageHeader](page-header.md) inside the main
|
|
88
|
+
slot as the page requires when using AppShell directly.
|
|
58
89
|
|
|
59
90
|
## Sizes
|
|
60
91
|
|
|
61
92
|
The Sidebar column uses `--size-sidebar`; the main column takes the remaining
|
|
62
93
|
width with a zero minimum. The shell has a minimum height of one viewport.
|
|
63
|
-
At widths of 1023px or less,
|
|
94
|
+
At widths of 1023px or less, a sidebar layout becomes one column and Sidebar is
|
|
64
95
|
hidden. The product must provide access to navigation at that width, for
|
|
65
96
|
example through a [Drawer](drawer.md).
|
|
66
97
|
|
|
98
|
+
`layout="topbar"` is a single main column at every width. It does not rely on
|
|
99
|
+
the 1023px media query to collapse a sidebar track.
|
|
100
|
+
|
|
67
101
|
## States
|
|
68
102
|
|
|
69
103
|
The shell stays in place while page content loads, fails, or becomes empty.
|
|
@@ -77,6 +111,8 @@ Those states belong inside AppShellMain. There is no disabled or active shell.
|
|
|
77
111
|
the main content.
|
|
78
112
|
- Keep navigation available when the Sidebar is hidden. AppShell does not
|
|
79
113
|
create a mobile menu or manage its focus.
|
|
114
|
+
- A top-bar layout has no Sidebar navigation landmark; put destinations in the
|
|
115
|
+
Header or elsewhere in the product chrome.
|
|
80
116
|
|
|
81
117
|
### Keyboard
|
|
82
118
|
|
|
@@ -87,13 +123,16 @@ retain their own keyboard behavior.
|
|
|
87
123
|
|
|
88
124
|
- An application with persistent navigation beside a changing page.
|
|
89
125
|
- A console containing tables, detail panes, and logs in its main column.
|
|
90
|
-
-
|
|
126
|
+
- A single-surface product whose chrome is one top bar (`layout="topbar"`).
|
|
127
|
+
- Use ApplicationShell when the product follows one of the two standard frames.
|
|
91
128
|
- Use AppShell directly when its Sidebar or Header composition differs.
|
|
92
129
|
|
|
93
130
|
## When NOT to use
|
|
94
131
|
|
|
95
132
|
- A standalone login or centered form. Use [Card](card.md) within the page.
|
|
96
133
|
- Two resizable content panes. Use [Split](split.md) inside the main content.
|
|
134
|
+
- Hiding a required rail with CSS. Prefer `layout="topbar"` or AppShell
|
|
135
|
+
composed without Sidebar.
|
|
97
136
|
|
|
98
137
|
## Radix/shadcn mapping
|
|
99
138
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# ChipInput
|
|
2
|
+
|
|
3
|
+
One field that collects several structured values. Each value is a chip: a
|
|
4
|
+
name, an optional editable value, and optional named options that belong to
|
|
5
|
+
that chip alone.
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
ChipInput is for a list that a person builds by typing, where every item
|
|
10
|
+
carries more than a label. A dependency list is the case that produced it:
|
|
11
|
+
`node = latest`, `npm:t3 = latest` with `allow_builds = node-pty`,
|
|
12
|
+
`apt:libssl-dev = latest`. The alternative is one form section per item, which
|
|
13
|
+
grows without limit and buries the list itself.
|
|
14
|
+
|
|
15
|
+
The component owns the box, the chips, the suggestion list, and the keyboard.
|
|
16
|
+
The product owns what a chip means, which suggestions match the query, and
|
|
17
|
+
which options a chip accepts. ChipInput never parses.
|
|
18
|
+
|
|
19
|
+
### Choose the right multi-value control
|
|
20
|
+
|
|
21
|
+
| Need | Control | Why |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| Several values, each with its own options | **ChipInput** | Options stay attached to the value they configure. |
|
|
24
|
+
| Several values from a known, short list | [Select](select.md) with multiple, or [Checkbox](checkbox.md) group | No free text, no per-value options. |
|
|
25
|
+
| One value from a known set | [Select](select.md) | Single choice. |
|
|
26
|
+
| Free text that is not a list | [Input](input.md) or [Textarea](textarea.md) | Nothing to chip. |
|
|
27
|
+
| Filtering what is already on screen | [Search](search.md) | A query, not stored values. |
|
|
28
|
+
| Running a global action | [CommandPalette](command-palette.md) | Commands, not data. |
|
|
29
|
+
|
|
30
|
+
## Anatomy
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
ChipInput
|
|
34
|
+
├── ChipInputBox (the field; a border, chips, and the caret)
|
|
35
|
+
│ ├── Chip (repeated)
|
|
36
|
+
│ │ ├── ChipScope (optional; what kind of value this is)
|
|
37
|
+
│ │ ├── ChipName (required; the identity of the value)
|
|
38
|
+
│ │ ├── ChipValue (optional; edits in place)
|
|
39
|
+
│ │ ├── ChipOption (repeated; one option as name=value)
|
|
40
|
+
│ │ ├── ChipOptionAdd (optional; the segment that takes a new option)
|
|
41
|
+
│ │ └── ChipRemove (required when the chip can be removed)
|
|
42
|
+
│ └── ChipInputField (the input; always last, always present)
|
|
43
|
+
└── ChipInputList (optional; suggestions for the current query)
|
|
44
|
+
├── ChipInputOption (repeated)
|
|
45
|
+
└── ChipInputEmpty (no match)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The box needs a name: a [Label](label.md) bound to `ChipInputField`, or
|
|
49
|
+
`aria-label` on it. Explain the syntax in [HelperText](helper-text.md), not in
|
|
50
|
+
the placeholder.
|
|
51
|
+
|
|
52
|
+
A chip reads left to right like a call: `npm` installs `t3` at `latest` with
|
|
53
|
+
`allow_builds=node-pty`. Its segments are divided the way [Button](button.md)
|
|
54
|
+
groups divide buttons, and the two segments people press, the value and the
|
|
55
|
+
options, carry the raised card fill. Use ChipScope only when the kind is a
|
|
56
|
+
separate fact from the name; a tool with no backend is one segment, not an
|
|
57
|
+
empty one.
|
|
58
|
+
|
|
59
|
+
Each option is its own segment, written the way the configuration file writes
|
|
60
|
+
it: `name=value`, and `name=[a, b]` when the value is a list. The brackets are
|
|
61
|
+
the only difference between the two, so both read as the same kind of fact.
|
|
62
|
+
Committing an option with an empty value removes it, and `ChipOptionAdd` sits
|
|
63
|
+
last as a `+`. The cost is discovery: name the accepted options in
|
|
64
|
+
[HelperText](helper-text.md), because the chip will not list them.
|
|
65
|
+
|
|
66
|
+
Chips are content, not chrome. Do not put an action that leaves the field
|
|
67
|
+
inside a chip.
|
|
68
|
+
|
|
69
|
+
## Variants
|
|
70
|
+
|
|
71
|
+
| Variant | Behavior |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `open` (default) | Any typed entry can become a chip. Suggestions help but do not restrict. |
|
|
74
|
+
| `restricted` | Only a suggestion becomes a chip. A typed entry with no match stays text until it matches. |
|
|
75
|
+
|
|
76
|
+
Both render the same. The product enforces the difference when it turns a
|
|
77
|
+
query into a chip.
|
|
78
|
+
|
|
79
|
+
Do not add a "read-only chips" variant. Values that cannot change are
|
|
80
|
+
[KV](kv.md) or [Badge](badge.md).
|
|
81
|
+
|
|
82
|
+
## Sizes
|
|
83
|
+
|
|
84
|
+
One size. The box is `--size-control-md` tall when empty and grows by row as
|
|
85
|
+
chips wrap; chips are `--size-control-sm`. Width is a layout concern: fill the
|
|
86
|
+
form column.
|
|
87
|
+
|
|
88
|
+
## States
|
|
89
|
+
|
|
90
|
+
| State | Behavior | Tokens / notes |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| default | Chips and a caret ready for the next value. Rules run the height of the chip between segments, and the version segment sits raised on the card surface. | `--color-card`, `--color-input`, `--color-border`. |
|
|
93
|
+
| hover | Quiet border emphasis on the box. A hovered version segment deepens its fill. | `--color-border-strong`, `--color-accent-surface`. |
|
|
94
|
+
| focus | One ring around the whole box, never around the bare input. | `--color-ring`. |
|
|
95
|
+
| editing a segment | The value or the options become an input sized to their content; the chip takes an accent border and a confirm control appears. | `--color-primary`, `--color-accent-surface`. |
|
|
96
|
+
| no options | Only the `+` segment remains, in the subtle foreground. | `--color-subtle-foreground`. |
|
|
97
|
+
| chip invalid | The single chip is marked, not the field. Say why next to the field. | `--color-danger-surface`, `--color-danger-border`, `--color-danger`. |
|
|
98
|
+
| field invalid | `aria-invalid` on the box plus [ValidationMessage](validation-message.md). | `--color-danger`. |
|
|
99
|
+
| disabled | The box and every chip control are unavailable; chips stay readable. | `--color-disabled-surface`, `--color-disabled`. |
|
|
100
|
+
| empty | Placeholder in the input showing the shape of one entry. | `--color-subtle-foreground`. |
|
|
101
|
+
|
|
102
|
+
An invalid chip and an invalid field are different failures. A version that
|
|
103
|
+
does not exist marks the chip; "add at least one dependency" marks the field.
|
|
104
|
+
|
|
105
|
+
## Accessibility
|
|
106
|
+
|
|
107
|
+
- `ChipInputField` is the combobox: `role="combobox"`, `aria-expanded`,
|
|
108
|
+
`aria-controls` on the suggestion list, `aria-activedescendant` on the
|
|
109
|
+
highlighted option, `aria-autocomplete="list"`. The highlight moves; DOM
|
|
110
|
+
focus stays in the input.
|
|
111
|
+
- The suggestion list is `role="listbox"` with `role="option"` children and
|
|
112
|
+
its own accessible name.
|
|
113
|
+
- Every chip control has a name that includes the chip: "Remove npm:t3", "Add
|
|
114
|
+
npm:t3 options", "Edit npm:t3 version, currently latest". A bare "Remove" is
|
|
115
|
+
ambiguous once there are six chips.
|
|
116
|
+
- Chip controls are real buttons in tab order. A long list is a long tab path;
|
|
117
|
+
that is the cost of keeping every control reachable without a roving
|
|
118
|
+
tabindex, and it is why removal is also on `Backspace`.
|
|
119
|
+
- Removing a chip keeps focus in the field. Confirming an edited segment
|
|
120
|
+
returns focus to that segment.
|
|
121
|
+
- Announce a chip added or removed through the surrounding status region, not
|
|
122
|
+
by moving focus.
|
|
123
|
+
|
|
124
|
+
### Keyboard
|
|
125
|
+
|
|
126
|
+
| Key | Action |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| Printable keys | Edit the query in the input. |
|
|
129
|
+
| `ArrowDown` / `ArrowUp` | Move the highlight through the suggestions; wraps. |
|
|
130
|
+
| `Enter` | Commit the highlighted suggestion, or the typed query in the `open` variant. While editing a segment, confirm it. |
|
|
131
|
+
| `Tab` | Commit the highlighted suggestion. With no highlight, leave the field. The box never traps the keyboard. |
|
|
132
|
+
| `Backspace` in an empty input | Remove the last chip. The field keeps focus, so a second press removes the next one. |
|
|
133
|
+
| `Escape` | Close the suggestions. While editing a segment, cancel back to its previous text. |
|
|
134
|
+
| `Shift+Tab` | Move back through the chip controls. |
|
|
135
|
+
|
|
136
|
+
`Enter` on an empty input does not submit the form. A form with a single
|
|
137
|
+
ChipInput must have an explicit submit Button.
|
|
138
|
+
|
|
139
|
+
## When to use
|
|
140
|
+
|
|
141
|
+
- A dependency, package, or tool list where each entry carries a version and
|
|
142
|
+
installer options.
|
|
143
|
+
- Recipients, labels, or scopes where an entry can be qualified.
|
|
144
|
+
- Any repeated "name plus settings" list a person types rather than picks.
|
|
145
|
+
|
|
146
|
+
## When NOT to use
|
|
147
|
+
|
|
148
|
+
- **A fixed, short set of choices.** Checkbox group or Select.
|
|
149
|
+
- **One value.** Input or Select.
|
|
150
|
+
- **Filtering a visible collection.** Search.
|
|
151
|
+
- **Showing a list nobody edits.** KV, Badge, or Table.
|
|
152
|
+
- **More than a few options per chip.** A row of `name=value` segments stops
|
|
153
|
+
reading at about three. If a chip needs a form, the chip is a record: use a
|
|
154
|
+
Table row and edit it in a [Modal / Dialog](modal-dialog.md).
|
|
155
|
+
|
|
156
|
+
## Tokens
|
|
157
|
+
|
|
158
|
+
Box and input follow [Input](input.md): `--color-card`, `--color-input`,
|
|
159
|
+
`--color-border-strong`, `--color-foreground`, `--color-subtle-foreground`,
|
|
160
|
+
`--color-ring`, `--color-disabled`, `--color-disabled-surface`, `--radius-md`,
|
|
161
|
+
`--shadow-xs`, `--size-control-md`, `--spacing-xs` padding,
|
|
162
|
+
`--motion-duration-fast` and `--motion-easing-standard`.
|
|
163
|
+
|
|
164
|
+
Chips: `--color-secondary`, `--color-secondary-foreground`, `--radius-sm`,
|
|
165
|
+
`--size-control-sm`, `--type-size-label`, `--font-mono` and
|
|
166
|
+
`--type-size-metadata` for the segments, `--color-muted-foreground` for the
|
|
167
|
+
scope and for an option's name, `--color-subtle-foreground` for the `=`, the
|
|
168
|
+
brackets and the empty `+`, `--color-border` for the rules between segments,
|
|
169
|
+
`--color-card` for the value and option segments with `--color-accent-surface`
|
|
170
|
+
when hovered, `--color-foreground` for an option's value, `--color-link` for
|
|
171
|
+
the version,
|
|
172
|
+
`--color-primary` and `--color-accent-surface` while editing,
|
|
173
|
+
`--color-accent-surface-hover` on chip controls, `--color-danger-surface`,
|
|
174
|
+
`--color-danger-border` and `--color-danger` when a chip is invalid.
|
|
175
|
+
|
|
176
|
+
Suggestions reuse the menu surface: `--color-popover`, `--color-border`,
|
|
177
|
+
`--radius-lg`, `--shadow-lg`, `--color-selected` and
|
|
178
|
+
`--color-selected-foreground` for the highlight. Label, HelperText, and
|
|
179
|
+
ValidationMessage bring their own tokens. No raw hex/px.
|
|
180
|
+
|
|
181
|
+
## Radix/shadcn mapping
|
|
182
|
+
|
|
183
|
+
No Radix combobox primitive and no shadcn tag input.
|
|
184
|
+
|
|
185
|
+
| Kiso | Reference |
|
|
186
|
+
| --- | --- |
|
|
187
|
+
| Box and field | Native `input` styled like shadcn [Input](https://ui.shadcn.com/docs/components/input), inside a bordered wrapper that owns the focus ring |
|
|
188
|
+
| Suggestions | The [CommandPalette](command-palette.md) listbox pattern: `aria-activedescendant` over `role="option"` children, not a focus-moving menu |
|
|
189
|
+
| Chip options | No reference: one inline segment per option, each swapping to a native `input` like the value |
|
|
190
|
+
| Chip | Kiso chip classes, not [Badge](badge.md): a Badge is not interactive and does not close |
|
|
191
|
+
|
|
192
|
+
Do not map ChipInput to shadcn [Command](https://ui.shadcn.com/docs/components/command)
|
|
193
|
+
or to a multi-select built on Select. Both fight the free-text entry this
|
|
194
|
+
component exists for.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Sparkline
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Shows the shape of one metric series in the space of a table cell or a Stat
|
|
6
|
+
tile. The number beside the line carries the precision; the line carries the
|
|
7
|
+
shape. A Sparkline answers "is this trending up", never "what was the value
|
|
8
|
+
at 09:41".
|
|
9
|
+
|
|
10
|
+
The evidence that forced the v1 deferral open: the self-host console collects
|
|
11
|
+
metrics in-process, one sample per tick on an evenly spaced window, and needs
|
|
12
|
+
the same shape three times over. A cell trend per application row, context
|
|
13
|
+
under a KPI value, and one series per container in a detail view.
|
|
14
|
+
|
|
15
|
+
## Anatomy
|
|
16
|
+
|
|
17
|
+
A single `div` frame around one SVG path. No axis, no grid, no legend, no
|
|
18
|
+
tooltip, no pointer behavior. It is a drawing of a number that is already
|
|
19
|
+
present somewhere nearby, or that names itself when it is not.
|
|
20
|
+
|
|
21
|
+
## Variants
|
|
22
|
+
|
|
23
|
+
| Prop | Values | Meaning |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `tone` | `neutral` (default), `primary` | Neutral uses `--color-border-strong`, the voice of an incidental series. Primary uses `--color-primary` and marks the series a tile is about. At most one sibling raises its voice. |
|
|
26
|
+
| `fill` | `false` (default), `true` | Draws the area under the line in the same color at 0.12 opacity. The fill is flat; there is no gradient. |
|
|
27
|
+
|
|
28
|
+
## Sizes
|
|
29
|
+
|
|
30
|
+
`height` is a number of pixels, default `24`. The known cases: `18` in a
|
|
31
|
+
table cell, `24` in a detail row, `28` under a Stat value. Width is always
|
|
32
|
+
the container; the line stretches with it. Do not place a Sparkline inside a
|
|
33
|
+
fixed-height box that crops it.
|
|
34
|
+
|
|
35
|
+
## Scale
|
|
36
|
+
|
|
37
|
+
By default the domain is the data's own extent, so one series fills its box.
|
|
38
|
+
Pass `min` and `max` to make sibling Sparklines share a scale: comparing
|
|
39
|
+
containers in one table only means something if the rows share one domain. A
|
|
40
|
+
flat series (every value equal) is padded automatically so it still draws a
|
|
41
|
+
line.
|
|
42
|
+
|
|
43
|
+
## States
|
|
44
|
+
|
|
45
|
+
| State | Behavior |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| fewer than two samples | Renders nothing. A flat rule across a cell reads as a border, not as a measurement. Keep the cell's number; reserve height with the surrounding layout. |
|
|
48
|
+
| loading | [Skeleton](skeleton.md) at the same height the Sparkline will occupy. |
|
|
49
|
+
| error | Show the last known number as text; the shape is optional, the value is not. |
|
|
50
|
+
|
|
51
|
+
The component takes plain values and does not model gaps. A series with holes
|
|
52
|
+
is the caller's data problem; interpolate or truncate before passing it in.
|
|
53
|
+
|
|
54
|
+
## Accessibility
|
|
55
|
+
|
|
56
|
+
This is the contract, not a guess:
|
|
57
|
+
|
|
58
|
+
- When the value is readable as text beside the drawing (a table cell, a
|
|
59
|
+
Stat), omit `label`. The component renders `aria-hidden` and is decoration;
|
|
60
|
+
the number is the accessible content.
|
|
61
|
+
- When the Sparkline is the only presentation of the series, pass `label`.
|
|
62
|
+
It renders `role="img"` with that string as `aria-label`. Name the metric
|
|
63
|
+
and the window: "CPU, last 5 minutes, 10-second ticks". Even then, put the
|
|
64
|
+
current value in nearby text when any action depends on it.
|
|
65
|
+
- Announcing every refresh of a live series would be noise. The drawing
|
|
66
|
+
updates silently; rely on the adjacent number for change awareness.
|
|
67
|
+
|
|
68
|
+
### Keyboard
|
|
69
|
+
|
|
70
|
+
No keymap and no tab stop. A Sparkline is never an action.
|
|
71
|
+
|
|
72
|
+
## When to use
|
|
73
|
+
|
|
74
|
+
- Trend cell in a table row: is this application busier than a minute ago.
|
|
75
|
+
- Context under a [Stat](stat.md) value on a dashboard.
|
|
76
|
+
- One series per container in a detail view, rows sharing a scale.
|
|
77
|
+
|
|
78
|
+
## When NOT to use
|
|
79
|
+
|
|
80
|
+
- Analysis that needs an axis, a legend, or crosshair reading. A framed,
|
|
81
|
+
interactive chart is a separate, still-deferred contract.
|
|
82
|
+
- Multi-series overlays. Stack several Sparklines only as separate rows with
|
|
83
|
+
their own labels, never as one drawing with a homemade legend.
|
|
84
|
+
- A single value with no history. Use [Stat](stat.md) alone.
|
|
85
|
+
|
|
86
|
+
## Recharts mapping
|
|
87
|
+
|
|
88
|
+
No Radix primitive exists for this. The component composes Recharts
|
|
89
|
+
`ResponsiveContainer`, `AreaChart`, and `Area` with animation, dots, and
|
|
90
|
+
axes off. Color reaches the path through `currentColor` from the
|
|
91
|
+
`.sparkline` rules in `ui.css`; never pass a hex value or invent a
|
|
92
|
+
categorical palette. The framed `.chart` rules in `ui.css` stay reserved for
|
|
93
|
+
the later interactive chart contract.
|
|
@@ -218,7 +218,9 @@ Alert.
|
|
|
218
218
|
or PageHeader — not a one-row table.
|
|
219
219
|
- **Hierarchical location.** Breadcrumb (navigation slice).
|
|
220
220
|
- **Free-form layout.** Cards or a custom panel; tables imply comparison.
|
|
221
|
-
- **Charts.**
|
|
221
|
+
- **Charts.** Do not stretch Table into a graph. A per-row
|
|
222
|
+
[Sparkline](sparkline.md) trend inside one cell is fine; the table itself
|
|
223
|
+
stays a table.
|
|
222
224
|
- **Global actions / jump-to.** [CommandPalette](command-palette.md), not a
|
|
223
225
|
table of commands.
|
|
224
226
|
- **Filtering UI embedded as magic columns.** Use Search + filters in the
|
package/kiso/docs/evolution.md
CHANGED
|
@@ -10,7 +10,7 @@ the system needs them. This is the honest roadmap for those choices.
|
|
|
10
10
|
| --- | --- | --- |
|
|
11
11
|
| **Component implementation code** | V1 ships Markdown contracts and tokens, not React components. Keeping the specification separate lets product needs shape an implementation instead of freezing an assumed API. This boundary was set in [epic #3](https://github.com/momoi-labs/blueprint/issues/3). | When repeated product implementations make a stable reference API evident. Build it as Kiso v2 or in a separate `kiso-ui` repository, using shadcn/Radix behavior adapted to Kiso rather than copied unchanged. |
|
|
12
12
|
| **Radio / RadioGroup** | Select and Switch cover the v1 choice cases, so [epic #3](https://github.com/momoi-labs/blueprint/issues/3) did not add another selection primitive without a product need. | When a product genuinely needs mutually exclusive selection from a small, fixed set whose options should remain visible. |
|
|
13
|
-
| **
|
|
13
|
+
| **Interactive framed charts** | [#84](https://github.com/momoi-labs/blueprint/issues/84) shipped [Sparkline](components/sparkline.md) for the single-series metric shapes products actually had. No product has yet needed an axis, a legend, or crosshair reading. | When a product needs exploratory reading of a series: axes, multi-series overlays, or hover inspection. Settle the framed `.chart` contract then. |
|
|
14
14
|
| **Figma Tokens Studio integration** | [Epic #2](https://github.com/momoi-labs/blueprint/issues/2) kept the token pipeline focused on its committed outputs. Tokens Studio is a Figma plugin workflow built through Style Dictionary and `@tokens-studio/sd-transforms`, not a standalone emitter. | When design-to-code synchronization through Figma becomes a real team workflow rather than a hypothetical integration. |
|
|
15
15
|
| **DTCG 2025.10 Resolver module** | The multiple-context and theme Resolver considered in [epic #2](https://github.com/momoi-labs/blueprint/issues/2) is a preview draft marked “do not implement.” V1 uses an explicit, stable theme model instead. | When the Resolver module reaches stable status and Kiso has a concrete context or theme problem it would solve. |
|
|
16
16
|
| **`--shadow-lg`** | The elevation scale intentionally stops at `--shadow-sm` and `--shadow-md`; [#25](https://github.com/momoi-labs/blueprint/issues/25) fixed component references without inventing a larger elevation. | When a real overlay or hierarchy cannot be expressed clearly with `--shadow-md`. Propose the token in the source, then regenerate its outputs. |
|
|
@@ -19,6 +19,15 @@ the system needs them. This is the honest roadmap for those choices.
|
|
|
19
19
|
| **Additional components** | The roughly 28-component cut from [epic #3](https://github.com/momoi-labs/blueprint/issues/3) covers the intended v1 product surface. Adding primitives in anticipation would enlarge the interface before their contracts are understood. | When a need recurs across products. Propose a component and its contract; do not invent one locally or copy one in unchanged. |
|
|
20
20
|
| **Heavy governance** | A two-person lab does not need a contribution bureaucracy or design-review board. For v1, the propose-don't-copy rule in [`kiso/AGENTS.md`](../AGENTS.md) is the governance mechanism, as scoped by [epic #5](https://github.com/momoi-labs/blueprint/issues/5). | When more contributors, products, or incompatible proposals make ownership and decision-making unclear. Add only the process needed to resolve an observed coordination problem. |
|
|
21
21
|
|
|
22
|
+
## Accepted additions
|
|
23
|
+
|
|
24
|
+
The growth model below is not theory. What it has produced so far:
|
|
25
|
+
|
|
26
|
+
| What | Evidence | Where |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| **[ChipInput](components/chip-input.md)** | A self-hosted console had to collect a development image's dependencies: a list a person types, where every entry carries a backend, a version, and installer options. One form section per entry grew without limit and buried the list. | Added as a component contract with the segmented chip, the in-place value, and one segment per option. |
|
|
29
|
+
| **[Sparkline](components/sparkline.md)** | The self-host console collects metrics in-process, one sample per tick on an evenly spaced window, and needed the same shape three times over: a trend cell per application row, context under a KPI value, and one series per container. [#84](https://github.com/momoi-labs/blueprint/issues/84) records the evidence. | Added as a component contract and a Recharts-based component, single series, neutral by default, primary only for the series a tile is about. |
|
|
30
|
+
|
|
22
31
|
## Growth model: grow with real products
|
|
23
32
|
|
|
24
33
|
Kiso is not an abstract design-system project. It evolves through product work:
|
|
@@ -35,8 +35,9 @@ Tokens: page canvas `--color-background`, widgets `--color-surface` with
|
|
|
35
35
|
semantic colors, focus `--color-focus`. Prefer quiet surfaces and strong
|
|
36
36
|
hierarchy ([principles](../principles.md)).
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
[Sparkline](../components/sparkline.md) carries metric shape inside Cards and
|
|
39
|
+
table cells; it composes like any other widget payload. An interactive framed
|
|
40
|
+
chart remains deferred.
|
|
40
41
|
|
|
41
42
|
## Flow
|
|
42
43
|
|
|
@@ -19,6 +19,7 @@ immediate vs deferred persistence, and unambiguous save feedback.
|
|
|
19
19
|
| Section nav | [Tabs](../components/tabs.md) or Sidebar sub-nav [Link](../components/link.md)s | Split General / Notifications / API, etc. |
|
|
20
20
|
| Groups | [Card](../components/card.md) | One settings group per Card |
|
|
21
21
|
| Text / choice fields | [FormField](../components/form-field.md) | [Label](../components/label.md) + [Input](../components/input.md) / [Select](../components/select.md) / [Textarea](../components/textarea.md) + [HelperText](../components/helper-text.md) + [ValidationMessage](../components/validation-message.md) |
|
|
22
|
+
| Repeated values with their own options | [ChipInput](../components/chip-input.md) | One field for a list a person types, such as dependencies or scopes, instead of a form section per entry |
|
|
22
23
|
| Booleans | [Switch](../components/switch.md) (immediate) or [Checkbox](../components/checkbox.md) inside FormField (part of a saved form) | Switch for single immediate preferences; Checkbox when the value submits with Save |
|
|
23
24
|
| Actions | [Button](../components/button.md) | Save (primary), Reset/Cancel (secondary) for explicit-save sections |
|
|
24
25
|
| Feedback | [Toast](../components/toast.md), [Alert](../components/alert.md), [ValidationMessage](../components/validation-message.md) | Saved confirmation; section errors; field errors |
|