@momoi-labs/kiso 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/kiso/AGENTS.md +50 -0
  4. package/kiso/README.md +62 -0
  5. package/kiso/docs/accessibility.md +87 -0
  6. package/kiso/docs/brand.md +95 -0
  7. package/kiso/docs/components/README.md +58 -0
  8. package/kiso/docs/components/alert.md +158 -0
  9. package/kiso/docs/components/badge.md +135 -0
  10. package/kiso/docs/components/breadcrumb.md +66 -0
  11. package/kiso/docs/components/button.md +168 -0
  12. package/kiso/docs/components/card.md +154 -0
  13. package/kiso/docs/components/checkbox.md +91 -0
  14. package/kiso/docs/components/command-palette.md +165 -0
  15. package/kiso/docs/components/drawer.md +79 -0
  16. package/kiso/docs/components/dropdown-menu.md +178 -0
  17. package/kiso/docs/components/empty-state.md +142 -0
  18. package/kiso/docs/components/form-field.md +115 -0
  19. package/kiso/docs/components/header.md +79 -0
  20. package/kiso/docs/components/helper-text.md +86 -0
  21. package/kiso/docs/components/icon-button.md +161 -0
  22. package/kiso/docs/components/input.md +99 -0
  23. package/kiso/docs/components/label.md +88 -0
  24. package/kiso/docs/components/link.md +152 -0
  25. package/kiso/docs/components/modal-dialog.md +82 -0
  26. package/kiso/docs/components/navigation.md +68 -0
  27. package/kiso/docs/components/page-header.md +70 -0
  28. package/kiso/docs/components/pagination.md +129 -0
  29. package/kiso/docs/components/popover.md +74 -0
  30. package/kiso/docs/components/search.md +147 -0
  31. package/kiso/docs/components/select.md +105 -0
  32. package/kiso/docs/components/sidebar.md +74 -0
  33. package/kiso/docs/components/skeleton.md +140 -0
  34. package/kiso/docs/components/spinner.md +125 -0
  35. package/kiso/docs/components/switch.md +92 -0
  36. package/kiso/docs/components/table.md +255 -0
  37. package/kiso/docs/components/tabs.md +69 -0
  38. package/kiso/docs/components/textarea.md +91 -0
  39. package/kiso/docs/components/toast.md +80 -0
  40. package/kiso/docs/components/tooltip.md +162 -0
  41. package/kiso/docs/components/validation-message.md +96 -0
  42. package/kiso/docs/data-interfaces.md +309 -0
  43. package/kiso/docs/evolution.md +35 -0
  44. package/kiso/docs/patterns/README.md +40 -0
  45. package/kiso/docs/patterns/application-shell.md +106 -0
  46. package/kiso/docs/patterns/command-palette.md +142 -0
  47. package/kiso/docs/patterns/confirmations.md +158 -0
  48. package/kiso/docs/patterns/crud.md +139 -0
  49. package/kiso/docs/patterns/dashboard.md +102 -0
  50. package/kiso/docs/patterns/destructive-actions.md +137 -0
  51. package/kiso/docs/patterns/developer-oriented-interfaces.md +162 -0
  52. package/kiso/docs/patterns/empty-states.md +76 -0
  53. package/kiso/docs/patterns/errors.md +93 -0
  54. package/kiso/docs/patterns/filtering.md +147 -0
  55. package/kiso/docs/patterns/keyboard-shortcuts.md +155 -0
  56. package/kiso/docs/patterns/large-data-tables.md +182 -0
  57. package/kiso/docs/patterns/list-detail.md +118 -0
  58. package/kiso/docs/patterns/loading.md +80 -0
  59. package/kiso/docs/patterns/login-authentication.md +101 -0
  60. package/kiso/docs/patterns/onboarding.md +94 -0
  61. package/kiso/docs/patterns/pagination.md +121 -0
  62. package/kiso/docs/patterns/permission-denied.md +84 -0
  63. package/kiso/docs/patterns/search.md +150 -0
  64. package/kiso/docs/patterns/settings.md +100 -0
  65. package/kiso/docs/patterns/sorting.md +121 -0
  66. package/kiso/docs/principles.md +122 -0
  67. package/kiso/docs/tokens.md +95 -0
  68. package/kiso/docs/voice-and-tone.md +154 -0
  69. package/package.json +42 -0
  70. package/tokens/build/tokens.css +143 -0
  71. package/tokens/build/tokens.d.ts +160 -0
  72. package/tokens/build/tokens.json +88 -0
  73. package/tokens/build/tokens.scss +89 -0
@@ -0,0 +1,121 @@
1
+ # Sorting
2
+
3
+ Column sorting with visible indicators and consistent cycle. This pattern
4
+ governs how a [Table / DataTable](../components/table.md) orders its rows by a
5
+ sortable column, and how that order is communicated.
6
+
7
+ User story #7.
8
+
9
+ ## Purpose
10
+
11
+ Sorting lets the person reorder the current result set by a comparable column —
12
+ name, lag, modified date, row count. It answers "which of these is most/least
13
+ X?" without the person scrolling to compare manually. Sorting orders the set
14
+ that [Search](search.md) and [filtering](filtering.md) have already narrowed.
15
+
16
+ ## Component composition
17
+
18
+ | Region | Compose with | Role |
19
+ | --- | --- | --- |
20
+ | Sortable header | [Table / DataTable](../components/table.md) ColumnHeaderCell with sort control | Button inside the header that toggles sort |
21
+ | Sort indicator | icon in the header (▲ / ▼) | Visual + accessible direction; not color alone |
22
+ | Unsortable header | plain `<th>` | No sort control, no `aria-sort` |
23
+ | Sort announcement | polite live region | "Sorted by lag, descending" when not obvious from focus |
24
+
25
+ The sort control is a button (or a nested button) inside the `<th>`. It is not
26
+ a separate "Sort" dropdown outside the table.
27
+
28
+ ## Flow
29
+
30
+ 1. Person views the table with its default sort (or unsorted).
31
+ 2. Person activates a sortable header (click or keyboard `Enter`/`Space` on
32
+ the header button).
33
+ 3. Sort cycles: unsorted → ascending → descending → unsorted (or
34
+ ascending → descending → ascending if a default sort is required and
35
+ "unsorted" is not meaningful).
36
+ 4. The rows reorder. If server-side, the region shows
37
+ [Skeleton](../components/skeleton.md) rows during the fetch; if client-side,
38
+ the set reorders immediately.
39
+ 5. The header's `aria-sort` and visible indicator update.
40
+ 6. The sort interacts with [pagination](pagination.md): changing sort on a
41
+ paged set resets to page 1 (the person should see the top of the new order).
42
+
43
+ ## States
44
+
45
+ | State | Header behavior | Tokens |
46
+ | --- | --- | --- |
47
+ | unsorted | No indicator. `aria-sort="none"`. Sort control still present on sortable headers. | Header default tokens. |
48
+ | ascending | ▲ indicator. `aria-sort="ascending"`. | Indicator `--color-foreground` or `--color-primary`. |
49
+ | descending | ▼ indicator. `aria-sort="descending"`. | Same as ascending. |
50
+ | applying | Server-side: body shows Skeleton rows; `aria-busy` on the region. Header indicator already shows the new direction. | |
51
+ | error | Sort query failed → [Alert](../components/alert.md). Do not leave a stale sort indicator with mismatched rows. | |
52
+
53
+ The sort indicator must be visible without relying on color alone. Use an icon
54
+ (▲ / ▼ or an equivalent) plus `aria-sort`.
55
+
56
+ ## Layout sketch
57
+
58
+ ```text
59
+ ┌──────────────────────────────────────────────────────────────────────┐
60
+ │ Toolbar │
61
+ │ [🔍 Search replicas...] [Status▾] │
62
+ │ 18 replicas │
63
+ ├──────────────────────────────────────────────────────────────────────┤
64
+ │ Table / DataTable │
65
+ │ ┌────────────────────────────────────────────────────────────────┐ │
66
+ │ │ Name ▲ Host Status Lag ▼ Region │ │
67
+ │ │ analytics db.an.example ok 1.2 s ap │ │
68
+ │ │ replica-01 db.eu.example degraded 820 ms eu │ │
69
+ │ │ replica-03 db.eu.backup degraded 410 ms eu │ │
70
+ │ │ replica-07 db.eu.dr ok — eu │ │
71
+ │ │ ... │ │
72
+ │ └────────────────────────────────────────────────────────────────┘ │
73
+ │ ← 1 2 3 … → │
74
+ └──────────────────────────────────────────────────────────────────────┘
75
+ ```
76
+
77
+ `Lag ▼` is the active sort (descending): highest lag first. `Name ▲` shows a
78
+ previous or secondary direction indicator only if the surface supports
79
+ multi-sort; otherwise only one column carries an indicator at a time.
80
+
81
+ ## Rules
82
+
83
+ - One primary sort column at a time unless the product explicitly supports
84
+ multi-sort. Multi-sort is rare; if supported, document the precedence order
85
+ and show secondary indicators subtly.
86
+ - Default sort is allowed and often sensible (e.g. "Modified, descending").
87
+ The default-sorted column shows its indicator on first render.
88
+ - The sort cycle is consistent across the product. Pick one —
89
+ unsorted → asc → desc → unsorted, or asc → desc → asc — and keep it.
90
+ - Sort changes the **order of the current result set** (client) or the
91
+ **query** (server). Document which on the screen or in its notes.
92
+ - Changing sort on a paged set resets to page 1. The person expects to see the
93
+ top of the new order, not page 3 of the old one.
94
+ - Do not sort by a column the person cannot see. If a column is hidden by
95
+ responsive collapse ([large-data-tables](large-data-tables.md)), it should
96
+ not be the active sort — or its sort must transfer to a visible column.
97
+ - Unsortable columns (e.g. row actions, a Badge-only status that has no natural
98
+ order) have no sort control and no `aria-sort`.
99
+ - Sort interacts with [filtering](filtering.md) and [Search](search.md): the
100
+ filtered/searched set is what gets sorted. Sorting does not widen the set.
101
+
102
+ ## Accessibility
103
+
104
+ - Sortable headers use a button with `aria-sort` on the `<th>`:
105
+ `ascending`, `descending`, or `none`.
106
+ - The sort direction is announced when it changes, especially when focus does
107
+ not remain on the header (e.g. server-side sort that swaps rows). Use a
108
+ polite live region: "Sorted by lag, descending."
109
+ - The header button has an accessible name that includes the column: "Sort by
110
+ lag". The visible label plus icon is the name; do not rely on the icon alone.
111
+ - Keyboard: `Tab` reaches sortable headers; `Enter` / `Space` toggles sort.
112
+ Arrow-key grid navigation, if implemented, follows the
113
+ [Table](../components/table.md) keyboard model.
114
+
115
+ ## Related patterns
116
+
117
+ - [Search](search.md) — narrows the set before sorting.
118
+ - [Filtering](filtering.md) — narrows the set before sorting.
119
+ - [Pagination](pagination.md) — pages the sorted set.
120
+ - [Large data tables](large-data-tables.md) — tables where sort + scroll is the
121
+ primary navigation.
@@ -0,0 +1,122 @@
1
+ # Design Principles
2
+
3
+ These principles govern *how Momoi Labs product interfaces are designed*. They
4
+ are the design analog to the repository's
5
+ [engineering principles](../../docs/PRINCIPLES.md) — separate in domain, but
6
+ philosophically coherent with them.
7
+
8
+ Use them when a design choice has more than one correct answer. When a
9
+ principle and a convenience conflict, the principle wins. When two principles
10
+ conflict, the order below is the tie-breaker.
11
+
12
+ ## Relationship to engineering principles
13
+
14
+ The engineering principles (`docs/PRINCIPLES.md`) govern *how this repository
15
+ is built*: smallest viable change, commit history tells a story, interfaces are
16
+ a compatibility contract, no silent defaults, decisions are recorded
17
+ append-only.
18
+
19
+ These design principles govern *how product interfaces look and feel*. They do
20
+ not duplicate the engineering principles. They are coherent with them in
21
+ spirit:
22
+
23
+ - Engineering's "no silent defaults" has a design analog — "no ad-hoc
24
+ tokens/components when an equivalent exists" — which will live in
25
+ `kiso/AGENTS.md` (a later epic), not here.
26
+ - Engineering's "smallest viable change" echoes in design's "useful over
27
+ decorative": both ask *does this need to be here?*
28
+ - Engineering's "decisions are recorded" echoes in design's "opinionated, not
29
+ restrictive": have a position, and make it findable.
30
+
31
+ Do not conflate the two. When an engineering question arises in design work
32
+ (e.g. "should this be a new component?"), defer to the engineering principles.
33
+
34
+ ## 1. Useful over decorative
35
+
36
+ Every element in a product interface must serve a purpose. Decoration that does
37
+ not help the person do something is removed — not minimized, removed.
38
+
39
+ When choosing between two directions, prefer the one that:
40
+
41
+ - helps the person understand or act, rather than merely impresses;
42
+ - uses whitespace and hierarchy instead of ornament to create structure;
43
+ - defers any visual flourish that the task does not require.
44
+
45
+ This is the first principle because it is the most common question: *should
46
+ this be here?* If it does not help, it does not belong.
47
+
48
+ ## 2. Clarity over cleverness
49
+
50
+ A clear interface is better than a clever one. Novelty for its own sake makes a
51
+ product harder to learn and harder to maintain.
52
+
53
+ When choosing between two directions, prefer the one that:
54
+
55
+ - uses familiar patterns where a familiar pattern already works;
56
+ - makes the current state and available actions obvious;
57
+ - resists a novel solution unless the novel solution is materially better — and
58
+ if it is, records why.
59
+
60
+ ## 3. Technical, not intimidating
61
+
62
+ Momoi builds developer tools, DB/infra tooling, and data-heavy interfaces.
63
+ These can be dense and precise without being hostile.
64
+
65
+ When choosing between two directions, prefer the one that:
66
+
67
+ - trusts the person's competence — show exact values, raw data, and technical
68
+ terms where they are the clearest language;
69
+ - adds context and affordance rather than dumbing down — explain, don't hide;
70
+ - keeps density high where density aids understanding, and uses hierarchy to
71
+ keep it navigable.
72
+
73
+ ## 4. Quiet interfaces, strong hierarchy
74
+
75
+ Calm surfaces with strong information hierarchy. Visual noise is low; the
76
+ structure does the guiding.
77
+
78
+ When choosing between two directions, prefer the one that:
79
+
80
+ - uses one accent for attention, not a palette competing for it;
81
+ - lets typography and spacing establish rank, rather than color or weight
82
+ escalation;
83
+ - reserves emphasis for the thing that matters most on the screen right now.
84
+
85
+ ## 5. Opinionated, not restrictive
86
+
87
+ Momoi has a position on how things should look and sound. That position makes
88
+ decisions faster and products more consistent. But it should never block a
89
+ legitimate need.
90
+
91
+ When choosing between two directions, prefer the one that:
92
+
93
+ - has a clear default and a documented reason for it;
94
+ - allows escape hatches when a genuine case demands it — and records the
95
+ escape;
96
+ - does not invent a rule just to forbid something; every restriction should
97
+ trace back to a principle above.
98
+
99
+ ## Conflict resolution (tie-breaker)
100
+
101
+ When two principles conflict, resolve in this order:
102
+
103
+ 1. **Useful over decorative** — if one option serves the person and the other
104
+ decorates, the useful one wins. This is almost always the first cut.
105
+ 2. **Clarity over cleverness** — if both are useful, the clearer one wins.
106
+ 3. **Technical, not intimidating** — if both are clear, prefer the one that
107
+ respects and engages the person's competence.
108
+ 4. **Quiet interfaces, strong hierarchy** — if both are technical, prefer the
109
+ calmer, more hierarchically structured one.
110
+ 5. **Opinionated, not restrictive** — apply defaults and positions, but yield
111
+ to a documented, genuine exception.
112
+
113
+ The order moves from *does it belong?* through *is it clear?* through *is it
114
+ respectful?* through *is it calm?* to *is it consistent?* — a funnel from
115
+ existence to polish. If a conflict is not resolved after walking this list, the
116
+ design principle is missing and should be added (in a later epic, through real
117
+ product need — not speculatively here).
118
+
119
+ ---
120
+
121
+ *Design principles are separate from engineering principles but philosophically
122
+ coherent. Read both. Do not duplicate.*
@@ -0,0 +1,95 @@
1
+ # Kiso design tokens
2
+
3
+ Kiso tokens are a two-layer interface. `tokens/tokens.json` is the single DTCG
4
+ 2025.10 source: `color.*` contains raw palette primitives, while `semantic.*`
5
+ names the roles a product needs. Components consume semantic colors only; they
6
+ must not use a primitive, a generated primitive variable, or a raw hex value.
7
+ If no semantic role fits, propose a role instead of bypassing this interface.
8
+
9
+ Install dependencies and build with Style Dictionary v5:
10
+
11
+ ```sh
12
+ npm ci
13
+ npm run build
14
+ ```
15
+
16
+ The build emits `tokens/build/tokens.css`, `tokens.json`, `tokens.d.ts`, and
17
+ `tokens.scss`. CSS contains the dark default in `:root`, light overrides in
18
+ `[data-theme="light"]`, and the reduced-motion override. Import `tokens.css`,
19
+ then toggle the light theme on an ancestor; application CSS should need no raw
20
+ color values.
21
+
22
+ ```css
23
+ @import "../../tokens/build/tokens.css";
24
+
25
+ .panel {
26
+ color: var(--color-foreground);
27
+ background: var(--color-surface);
28
+ border: 1px solid var(--color-border);
29
+ }
30
+ ```
31
+
32
+ ## Semantic colors
33
+
34
+ | Role | Meaning and use | Dark primitive | Light primitive |
35
+ | --- | --- | --- | --- |
36
+ | `background` | Application canvas; never text. | `neutral.900` | `neutral.200` |
37
+ | `surface` | Cards, panels, and table rows. | `neutral.800` | `neutral.100` |
38
+ | `elevated-surface` | Menus, popovers, and dialogs. | `neutral.700` | `white` |
39
+ | `foreground` | Primary text and content that must carry the strongest hierarchy. | `neutral.100` | `neutral.900` |
40
+ | `muted-foreground` | Secondary text and labels. It remains normal-text eligible. | `neutral.400` | `neutral.600` |
41
+ | `subtle-foreground` | Placeholders, timestamps, and non-essential hints; large text only, never body copy. | `neutral.500` | `neutral.500` |
42
+ | `border` | Dividers and control outlines; never text. | `neutral.600` | `neutral.300` |
43
+ | `primary` | The main interactive action: links, active states, and primary controls. | `accent.base` | `accent.base` |
44
+ | `accent` | Secondary emphasis and highlights, not the page's main action. | `accent.300` | `accent.800` |
45
+ | `success` | Positive or completed state. | `status.success` | `status.success` |
46
+ | `warning` | Caution or a condition needing attention. | `status.warning` | `status.warning` |
47
+ | `danger` | Error or destructive action. | `status.danger` | `status.danger` |
48
+ | `info` | Neutral informational state. | `status.info` | `status.info` |
49
+ | `focus` | Keyboard focus ring; never text. | `accent.300` | `accent.base` |
50
+ | `disabled` | Disabled text and controls only. | `neutral.600` | `neutral.400` |
51
+
52
+ Use `foreground` for default reading, `muted-foreground` when content is
53
+ secondary but still needs normal-text contrast, and `subtle-foreground` only
54
+ for large or non-essential supporting copy. Use `primary` for the action that
55
+ drives the current task; use `accent` to draw secondary attention without
56
+ creating another primary action.
57
+
58
+ The semantic aliases deliberately point at different primitives by theme.
59
+ Status primitives and `accent.base` are themselves mode-aware, so the same
60
+ semantic role preserves its meaning and contrast rather than preserving a
61
+ literal color.
62
+
63
+ ## AA gate
64
+
65
+ `scripts/check-contrast.mjs` is the build-time AA gate. In both dark and light
66
+ themes it resolves the semantic aliases and checks:
67
+
68
+ - `foreground`, `muted-foreground`, `primary`, `accent`, `success`, `warning`,
69
+ `danger`, and `info` at **4.5:1** or better against `background`, `surface`,
70
+ and `elevated-surface`;
71
+ - `subtle-foreground` at **3:1** or better against those surfaces, restricting
72
+ it to large text and non-essential metadata;
73
+ - `focus` at **3:1** or better against `background` for visible focus rings.
74
+
75
+ `disabled` is intentionally outside the gate because inactive controls are
76
+ exempt from WCAG 1.4.3. `background`, `surface`, `elevated-surface`, and `border`
77
+ are not text roles. Run the gate with:
78
+
79
+ ```sh
80
+ node scripts/check-contrast.mjs tokens/tokens.json
81
+ ```
82
+
83
+ ## Generated files
84
+
85
+ All four files in `tokens/build/` are committed. This makes the published
86
+ artifacts directly consumable without requiring downstream projects to install
87
+ Style Dictionary. Do not edit them: change `tokens/tokens.json` or the build
88
+ configuration and regenerate. CI rebuilds the artifacts and fails if the
89
+ committed output has drifted, so `tokens/build/` is intentionally not ignored.
90
+
91
+ Published releases expose the artifacts as `@momoi-labs/kiso/tokens.css`,
92
+ `@momoi-labs/kiso/tokens.json`, `@momoi-labs/kiso/tokens.scss`, and
93
+ `@momoi-labs/kiso/tokens.d.ts`. Kiso's Markdown contracts are available below
94
+ `@momoi-labs/kiso/contracts/` so consumers can pin the contracts and generated
95
+ tokens to the same version.
@@ -0,0 +1,154 @@
1
+ # Voice and Tone — Product UI Microcopy
2
+
3
+ This document governs how product UI copy **sounds**. It is the voice layer of
4
+ the [brand](./brand.md), made concrete for the words on screen.
5
+
6
+ It covers **product UI** — labels, buttons, states, errors, empty states,
7
+ confirmations. It does not cover marketing copy, documentation prose, or the
8
+ marketing site's voice. The marketing site is more experimental; products are
9
+ lived in.
10
+
11
+ ## The core decision: universal directness vs reserved terminal flourish
12
+
13
+ Momoi's marketing site has a terminal-inspired voice. For products, that voice
14
+ splits into two layers with different rules:
15
+
16
+ ### Universal directness (applies everywhere)
17
+
18
+ All product UI copy is **direct, brief, and structured**. This is not a style
19
+ preference — it is a baseline requirement for every word on screen.
20
+
21
+ - **Direct.** Say what the thing is and what it does. No hedging, no
22
+ filler, no "please" or "sorry" in functional copy.
23
+ - **Brief.** The fewest words that are still clear. If a label works at two
24
+ words, it does not need three.
25
+ - **Structured.** Prefer scannable structure (labels, lists, known patterns)
26
+ over prose paragraphs. Dense data gets a table, not a sentence.
27
+
28
+ ### Reserved terminal flourish (chrome and metadata only)
29
+
30
+ The site's literal terminal flourishes — "~/ABOUT" navigation prefixes, "$
31
+ export THEME=light" command-style actions — are **reserved for chrome and
32
+ metadata**. They do not appear in:
33
+
34
+ - **Body copy.** Explanatory text is plain and direct.
35
+ - **Error messages.** Errors follow their own mandatory structure (below).
36
+ - **Functional UI text.** Buttons, form labels, status indicators, and
37
+ navigation use plain language.
38
+
39
+ Terminal flourish is appropriate in chrome where it reinforces identity without
40
+ costing clarity — e.g. a subtle metadata label, a decorative section eyebrow.
41
+ When in doubt, leave it out. Directness always works; flourish is optional.
42
+
43
+ ## Error messages (hard rule)
44
+
45
+ Error messages **must** follow this three-part structure:
46
+
47
+ 1. **What happened.** State the problem in plain language.
48
+ 2. **Why, when possible.** If the cause is knowable, explain it briefly. If it
49
+ is not, skip this part rather than guess.
50
+ 3. **What the person can do now.** Offer a concrete next step or recovery
51
+ action.
52
+
53
+ This is a **hard rule**, not a suggestion. An error without a recovery path is
54
+ incomplete.
55
+
56
+ **Examples:**
57
+
58
+ > Connection to the database failed. The server at `db.example.com:5432` did
59
+ > not respond within 5 seconds. Check that the database is running and
60
+ > reachable, then retry.
61
+
62
+ > This query was canceled. It exceeded the 30-second timeout. Narrow the query
63
+ > scope or increase the timeout in Settings.
64
+
65
+ > You don't have permission to delete this record. Only workspace admins can
66
+ > delete records. Ask an admin to perform this action or to grant you the
67
+ > role.
68
+
69
+ **Anti-patterns** (never do these):
70
+
71
+ - Cryptic codes without context: `Error: ECONNREFUSED`
72
+ - Accusatory tone: "You entered an invalid value."
73
+ - Apologetic filler: "Oops! We're sorry, but something went wrong."
74
+ - No recovery path: "Operation failed."
75
+
76
+ ## Specific UI states
77
+
78
+ ### Empty states
79
+
80
+ Helpful and actionable, not apologetic or cute. State what is empty, why it
81
+ might be, and what to do next.
82
+
83
+ > No queries yet. Create your first query to see it here.
84
+
85
+ > This workspace has no members. Invite people to collaborate.
86
+
87
+ Do not say "Nothing to see here" or "It's lonely in here." Empty states are a
88
+ signpost, not a joke.
89
+
90
+ ### Loading states
91
+
92
+ Informative without being chatty. State what is happening; do not narrate
93
+ enthusiasm.
94
+
95
+ > Loading queries…
96
+
97
+ > Connecting to database…
98
+
99
+ Do not say "Hang tight!" or "Cooking something up…". A loading state that takes
100
+ longer than a moment may add a reason ("Loading 1,240 rows…").
101
+
102
+ ### Destructive actions and confirmations
103
+
104
+ Clear without being alarming. Name the action and its consequence; let the
105
+ person decide.
106
+
107
+ > Delete "production-db"? This cannot be undone.
108
+
109
+ > Disconnect from `staging-db`? Active queries will be terminated.
110
+
111
+ Do not say "Are you absolutely sure?" or wrap confirmations in warning colors
112
+ that imply danger beyond the actual stakes. Match the confirmation's weight to
113
+ the action's irreversibility.
114
+
115
+ ### Permission denied
116
+
117
+ Explain clearly without sounding accusatory or corporate. State what was
118
+ denied, why, and what to do.
119
+
120
+ > You can't edit this query. Only the query author and workspace admins can
121
+ > edit it. Ask an admin to grant you access.
122
+
123
+ Do not say "Access Denied" in red with no context. Do not say "You are not
124
+ authorized to perform this action" — it is corporate and unhelpful.
125
+
126
+ ## Product UI voice vs documentation tone
127
+
128
+ Product UI copy is **terse and structural**. Documentation is **prose that
129
+ explains**. They share directness but differ in form:
130
+
131
+ - **Product UI:** "Delete query" / "3 of 12 rows selected" / "Reconnect"
132
+ - **Documentation:** "To delete a query, open its menu and choose Delete. The
133
+ query is removed from the workspace; its results are not exported anywhere."
134
+
135
+ Do not write product UI copy as documentation, and do not write documentation
136
+ as a series of labels. If a UI element needs more explanation than a label
137
+ allows, link to documentation rather than inflating the label.
138
+
139
+ ## Quick reference
140
+
141
+ | Situation | Rule |
142
+ | --- | --- |
143
+ | Any copy | Direct, brief, structured |
144
+ | Error | What happened → why (if possible) → what to do now |
145
+ | Empty state | What's empty → why → what to do |
146
+ | Loading | What's happening, no chatter |
147
+ | Destructive | Name action + consequence, match weight to stakes |
148
+ | Permission denied | What was denied → why → what to do |
149
+ | Terminal flourish | Chrome/metadata only, never body/errors/functional text |
150
+
151
+ ---
152
+
153
+ *Directness is universal. Terminal flourish is reserved. Errors always have a
154
+ recovery path.*
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@momoi-labs/kiso",
3
+ "version": "0.1.0",
4
+ "description": "Kiso design-system contracts and generated design tokens",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/momoi-labs/blueprint.git"
9
+ },
10
+ "type": "module",
11
+ "files": [
12
+ "kiso/",
13
+ "tokens/build/"
14
+ ],
15
+ "exports": {
16
+ "./tokens.css": "./tokens/build/tokens.css",
17
+ "./tokens.json": "./tokens/build/tokens.json",
18
+ "./tokens.scss": "./tokens/build/tokens.scss",
19
+ "./tokens.d.ts": "./tokens/build/tokens.d.ts",
20
+ "./contracts/*": "./kiso/*"
21
+ },
22
+ "scripts": {
23
+ "build": "style-dictionary build --config style-dictionary.config.mjs",
24
+ "check": "npm run build && npm run check:generated && npm run check:tokens",
25
+ "check:generated": "git diff --exit-code -- tokens/build/",
26
+ "check:tokens": "node scripts/validate-dtcg.mjs && node scripts/check-contrast.mjs tokens/tokens.json && node scripts/check-component-token-refs.mjs",
27
+ "changeset": "changeset",
28
+ "release": "changeset publish"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "provenance": true
33
+ },
34
+ "devDependencies": {
35
+ "@changesets/cli": "^2.29.7",
36
+ "ajv": "^8.17.1",
37
+ "style-dictionary": "^5.0.0"
38
+ },
39
+ "engines": {
40
+ "node": ">=24"
41
+ }
42
+ }
@@ -0,0 +1,143 @@
1
+ /* Kiso design tokens — generated from tokens/tokens.json. Do not edit. */
2
+
3
+ :root {
4
+ --color-white: #ffffff;
5
+ --color-black: #000000;
6
+ --color-neutral-100: #faf9f7;
7
+ --color-neutral-200: #f4f2ee;
8
+ --color-neutral-300: #e0dcd6;
9
+ --color-neutral-400: #b6b2ae;
10
+ --color-neutral-500: #8a857f;
11
+ --color-neutral-600: #5f5b57;
12
+ --color-neutral-700: #312f37;
13
+ --color-neutral-800: #26252b;
14
+ --color-neutral-900: #1b1a1e;
15
+ --color-accent-200: #ede8fd;
16
+ --color-accent-300: #ded5fb;
17
+ --color-accent-400: #c4b4f5;
18
+ --color-accent-600: #7d68d1;
19
+ --color-accent-700: #6552b8;
20
+ --color-accent-800: #46309e;
21
+ --color-accent-900: #2e2170;
22
+ --color-accent-base: #cbbdf7;
23
+ --color-status-success: #4ed69a;
24
+ --color-status-warning: #e8c05a;
25
+ --color-status-danger: #f4867f;
26
+ --color-status-info: #8fc9f5;
27
+ --color-background: var(--color-neutral-900);
28
+ --color-surface: var(--color-neutral-800);
29
+ --color-elevated-surface: var(--color-neutral-700);
30
+ --color-foreground: var(--color-neutral-100);
31
+ --color-muted-foreground: var(--color-neutral-400);
32
+ --color-subtle-foreground: var(--color-neutral-500);
33
+ --color-border: var(--color-neutral-600);
34
+ --color-primary: var(--color-accent-base);
35
+ --color-accent: var(--color-accent-300);
36
+ --color-success: var(--color-status-success);
37
+ --color-warning: var(--color-status-warning);
38
+ --color-danger: var(--color-status-danger);
39
+ --color-info: var(--color-status-info);
40
+ --color-focus: var(--color-accent-300);
41
+ --color-disabled: var(--color-neutral-600);
42
+ --font-heading: Inter, system-ui, sans-serif;
43
+ --font-body: Inter, system-ui, sans-serif;
44
+ --font-mono: JetBrains Mono, ui-monospace, monospace;
45
+ --type-size-metadata: 11px;
46
+ --type-size-label: 13px;
47
+ --type-size-body: 16px;
48
+ --type-size-h3: 19px;
49
+ --type-size-h2: 23px;
50
+ --type-size-h1: 28px;
51
+ --type-size-display: 33px;
52
+ --type-weight-regular: 400;
53
+ --type-weight-medium: 500;
54
+ --type-weight-semibold: 600;
55
+ --type-weight-bold: 700;
56
+ --type-line-height-tight: 1.2;
57
+ --type-line-height-normal: 1.5;
58
+ --type-line-height-relaxed: 1.6;
59
+ --type-letter-spacing-heading: -0.01rem;
60
+ --type-letter-spacing-normal: 0rem;
61
+ --type-letter-spacing-label: 0.02rem;
62
+ --type-role-display-font-family: var(--font-heading);
63
+ --type-role-display-font-size: var(--type-size-display);
64
+ --type-role-display-font-weight: var(--type-weight-medium);
65
+ --type-role-display-letter-spacing: var(--type-letter-spacing-heading);
66
+ --type-role-display-line-height: var(--type-line-height-tight);
67
+ --type-role-heading-font-family: var(--font-heading);
68
+ --type-role-heading-font-size: var(--type-size-h1);
69
+ --type-role-heading-font-weight: var(--type-weight-semibold);
70
+ --type-role-heading-letter-spacing: var(--type-letter-spacing-heading);
71
+ --type-role-heading-line-height: var(--type-line-height-tight);
72
+ --type-role-body-font-family: var(--font-body);
73
+ --type-role-body-font-size: var(--type-size-body);
74
+ --type-role-body-font-weight: var(--type-weight-regular);
75
+ --type-role-body-letter-spacing: var(--type-letter-spacing-normal);
76
+ --type-role-body-line-height: var(--type-line-height-normal);
77
+ --type-role-label-font-family: var(--font-body);
78
+ --type-role-label-font-size: var(--type-size-label);
79
+ --type-role-label-font-weight: var(--type-weight-medium);
80
+ --type-role-label-letter-spacing: var(--type-letter-spacing-label);
81
+ --type-role-label-line-height: var(--type-line-height-normal);
82
+ --type-role-metadata-font-family: var(--font-body);
83
+ --type-role-metadata-font-size: var(--type-size-metadata);
84
+ --type-role-metadata-font-weight: var(--type-weight-regular);
85
+ --type-role-metadata-letter-spacing: var(--type-letter-spacing-normal);
86
+ --type-role-metadata-line-height: var(--type-line-height-normal);
87
+ --type-role-code-font-family: var(--font-mono);
88
+ --type-role-code-font-size: var(--type-size-label);
89
+ --type-role-code-font-weight: var(--type-weight-regular);
90
+ --type-role-code-letter-spacing: var(--type-letter-spacing-normal);
91
+ --type-role-code-line-height: var(--type-line-height-normal);
92
+ --type-role-numeric-font-family: var(--font-body);
93
+ --type-role-numeric-font-size: var(--type-size-body);
94
+ --type-role-numeric-font-weight: var(--type-weight-regular);
95
+ --type-role-numeric-letter-spacing: var(--type-letter-spacing-normal);
96
+ --type-role-numeric-line-height: var(--type-line-height-normal);
97
+ --spacing-xs: 4px;
98
+ --spacing-sm: 8px;
99
+ --spacing-md: 12px;
100
+ --spacing-lg: 16px;
101
+ --spacing-xl: 24px;
102
+ --spacing-2xl: 32px;
103
+ --spacing-3xl: 48px;
104
+ --spacing-4xl: 64px;
105
+ --radius-sm: 4px;
106
+ --radius-md: 8px;
107
+ --radius-lg: 12px;
108
+ --radius-full: 9999px;
109
+ --shadow-sm: 0px 1px 2px 0px rgba(0, 0, 0, 0.24);
110
+ --shadow-md: 0px 2px 8px 0px rgba(0, 0, 0, 0.32);
111
+ --motion-duration-fast: 120ms;
112
+ --motion-duration-normal: 200ms;
113
+ --motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);
114
+ --breakpoint-sm: 640px;
115
+ --breakpoint-md: 768px;
116
+ --breakpoint-lg: 1024px;
117
+ --breakpoint-xl: 1280px;
118
+ --font-variant-numeric: tabular-nums;
119
+ }
120
+
121
+ [data-theme="light"] {
122
+ --color-accent-base: #5b3fc4;
123
+ --color-status-success: #0c6b43;
124
+ --color-status-warning: #7a5000;
125
+ --color-status-danger: #ad2f29;
126
+ --color-status-info: #15559e;
127
+ --color-background: var(--color-neutral-200);
128
+ --color-surface: var(--color-neutral-100);
129
+ --color-elevated-surface: var(--color-white);
130
+ --color-foreground: var(--color-neutral-900);
131
+ --color-muted-foreground: var(--color-neutral-600);
132
+ --color-border: var(--color-neutral-300);
133
+ --color-accent: var(--color-accent-800);
134
+ --color-focus: var(--color-accent-base);
135
+ --color-disabled: var(--color-neutral-400);
136
+ }
137
+
138
+ @media (prefers-reduced-motion: reduce) {
139
+ :root {
140
+ --motion-duration-fast: 0s;
141
+ --motion-duration-normal: 0s;
142
+ }
143
+ }