@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,94 @@
1
+ # Onboarding
2
+
3
+ Onboarding guides a new person through the minimum setup required to reach a
4
+ useful product state. It is a finite task flow, not a product tour, marketing
5
+ carousel, or substitute for clear everyday UI.
6
+
7
+ ## Component composition
8
+
9
+ - [Card](../components/card.md) groups each setup task or the active task.
10
+ - [Button](../components/button.md) performs setup actions; at most one primary
11
+ Button advances the current step.
12
+ - [Link](../components/link.md) opens optional documentation or a route that
13
+ must remain navigable.
14
+ - [FormField](../components/form-field.md), [Input](../components/input.md),
15
+ [Select](../components/select.md), and their existing validation composition
16
+ collect setup data.
17
+ - [Alert](../components/alert.md) and
18
+ [ValidationMessage](../components/validation-message.md) handle failures at
19
+ their proper scope. Skeleton or a loading Button handles pending work.
20
+
21
+ The issue calls the progress composition “Steps”, but Kiso has no Steps
22
+ component. Represent progress as a semantic ordered list with current and
23
+ completed text states; do not invent a new component in this pattern.
24
+
25
+ Cards use `--color-surface`, `--color-border`, `--radius-lg`, and semantic
26
+ spacing. Current-step emphasis uses `--color-primary`; completed status may use
27
+ `--color-success`; primary and secondary copy use `--color-foreground` and
28
+ `--color-muted-foreground`. Keyboard focus uses `--color-focus`.
29
+
30
+ ## Flow
31
+
32
+ 1. Define the first useful outcome and include only setup steps required to
33
+ reach it.
34
+ 2. Show the total sequence and identify the current step in text.
35
+ 3. Explain why the current input or permission is needed before asking for it.
36
+ 4. Preserve valid input while an action loads or fails. Advance only after the
37
+ step succeeds.
38
+ 5. Let the person go back without losing completed work. Offer **Skip** only
39
+ for genuinely optional steps and state the consequence.
40
+ 6. Finish in the useful product view, not a celebration screen that blocks the
41
+ next task. Keep a way to resume incomplete setup later.
42
+
43
+ Do not force onboarding when the required state already exists. Returning
44
+ people resume at the first incomplete required step; they do not replay the
45
+ whole flow.
46
+
47
+ ## States
48
+
49
+ | State | Treatment |
50
+ | --- | --- |
51
+ | New / incomplete | Show ordered progress, the current Card, and one primary advance action. |
52
+ | Loading | Preserve fields and progress; the active Button shows Spinner or known Card content uses Skeleton. |
53
+ | Empty dependency | Explain the missing prerequisite and offer the action that creates or connects it; do not show a generic empty dashboard. |
54
+ | Validation error | Keep the step open and show ValidationMessage at the field. |
55
+ | Operation error | Keep the step open, show an actionable Alert using what/why/now, and allow retry. |
56
+ | Permission denied | Explain the blocked setup task and access path with the permission-denied pattern; do not call the whole onboarding failed. |
57
+ | Complete | Mark required steps complete and navigate to the useful destination. |
58
+
59
+ ## Layout sketch
60
+
61
+ ```text
62
+ PageHeader: Set up your workspace
63
+
64
+ 1. Workspace details Complete
65
+ 2. Connect database Current
66
+ 3. Invite team Optional
67
+
68
+ ┌─ Card: Connect database ────────────────────────────────┐
69
+ │ Add a connection to run your first query. │
70
+ │ │
71
+ │ Connection name │
72
+ │ [production___________________________________________] │
73
+ │ Database URL │
74
+ │ [postgres://__________________________________________] │
75
+ │ │
76
+ │ Docs [Back] [Test connection]│
77
+ └─────────────────────────────────────────────────────────┘
78
+ ```
79
+
80
+ `Docs` is a Link. `Back` and `Test connection` are Buttons; only **Test
81
+ connection** is primary. The ordered list communicates “2 of 3” and current,
82
+ completed, and optional states in text rather than color alone.
83
+
84
+ ## Accessibility and copy
85
+
86
+ - Use an ordered list for progress and expose the current item with text and
87
+ `aria-current="step"`. Announce step changes politely and move focus to the
88
+ new step heading.
89
+ - Keep native form order and visible labels. Back navigation must not discard
90
+ input without warning.
91
+ - Do not use disabled future steps as the only explanation of prerequisites.
92
+ State what is needed beside the active task.
93
+ - Keep copy direct and task-oriented. Avoid tours, jargon without context,
94
+ playful filler, and terminal flourish in functional text.
@@ -0,0 +1,121 @@
1
+ # Pagination
2
+
3
+ Explicit page navigation for known or meaningfully bounded datasets. This
4
+ pattern composes the [Pagination](../components/pagination.md) component below a
5
+ [Table / DataTable](../components/table.md) and governs when paging is the right
6
+ model versus [large-data-tables](large-data-tables.md) virtualization or
7
+ incremental loading.
8
+
9
+ User story #3.
10
+
11
+ ## Purpose
12
+
13
+ Pagination exposes position and direct page navigation when page numbers help
14
+ the person reason about a dataset: "page 7 of 40", "show me the next 20
15
+ replicas". It composes with [Search](search.md), [filtering](filtering.md), and
16
+ [sorting](sorting.md) — those narrow and order the set; Pagination pages it.
17
+
18
+ **Canonical rule:** A list screen must not reinvent search, filters,
19
+ pagination, and empty state. Compose [Pagination](../components/pagination.md)
20
+ — do not invent a per-list pager.
21
+
22
+ ## Component composition
23
+
24
+ | Region | Compose with | Role |
25
+ | --- | --- | --- |
26
+ | Page navigation | [Pagination](../components/pagination.md) | Previous / page numbers / Next below the table |
27
+ | Position summary | text line or Pagination result summary | "Page 3 of 12 · 41–60 of 230 replicas" |
28
+ | Page size control | [Select](../components/select.md) (optional) | "20 / 50 / 100 per page" |
29
+ | Loading state | [Skeleton](../components/skeleton.md) rows in the body | While a page fetch runs |
30
+
31
+ Pagination is a sibling below the table, not a table row. It is a navigation
32
+ region: `<nav aria-label="Pagination">`.
33
+
34
+ ## Flow
35
+
36
+ 1. Person views page 1 of the result set (already narrowed by Search/filters
37
+ and ordered by sort).
38
+ 2. Person activates a page number, Previous, or Next.
39
+ 3. The results region enters loading: [Skeleton](../components/skeleton.md)
40
+ rows replace the body, `aria-busy` on the region. Pagination controls stay
41
+ visible and the current page is still indicated.
42
+ 4. The new page arrives; body swaps to populated rows.
43
+ 5. If the page is empty (e.g. filters changed and the set shrank), show
44
+ [EmptyState](../components/empty-state.md) `no-results` and reset to page 1.
45
+
46
+ Changing [Search](search.md) query, [filters](filtering.md), or
47
+ [sort](sorting.md) resets to page 1 — the person expects the top of the new
48
+ result set.
49
+
50
+ ## States
51
+
52
+ | State | Behavior |
53
+ | --- | --- |
54
+ | idle | Page loaded; Pagination shows current position. |
55
+ | loading | Body shows Skeleton rows; `aria-busy` on the region. Controls remain; duplicate page requests are prevented. Previous/Next at a boundary are disabled with `--color-disabled`. |
56
+ | boundary | Previous disabled on page 1; Next disabled on last page. Page Links are never disabled — they are omitted or replaced by ellipsis. |
57
+ | empty | Set shrank to zero → [EmptyState](../components/empty-state.md) `no-results`; reset to page 1. |
58
+ | error | Page fetch failed → [Alert](../components/alert.md) with retry. Do not leave Skeleton rows up after failure. |
59
+
60
+ ## Layout sketch
61
+
62
+ ```text
63
+ ┌──────────────────────────────────────────────────────────────────────┐
64
+ │ Toolbar │
65
+ │ [🔍 Search replicas...] [Status▾] │
66
+ │ 230 replicas · Page 3 of 12 │
67
+ ├──────────────────────────────────────────────────────────────────────┤
68
+ │ Table / DataTable │
69
+ │ ┌────────────────────────────────────────────────────────────────┐ │
70
+ │ │ Name ▲ Host Status Lag ▼ Region │ │
71
+ │ │ replica-41 db.eu.example ok 12 ms eu │ │
72
+ │ │ replica-42 db.eu.backup ok 8 ms eu │ │
73
+ │ │ ... │ │
74
+ │ └────────────────────────────────────────────────────────────────┘ │
75
+ │ │
76
+ │ Page 3 of 12 · 41–60 of 230 [20 / page ▾] │
77
+ │ ← 1 2 [3] 4 5 … 12 → │
78
+ └──────────────────────────────────────────────────────────────────────┘
79
+ ```
80
+
81
+ ## Rules
82
+
83
+ - Use Pagination when the dataset size is **known or meaningfully bounded** and
84
+ page position helps the person. For unbounded activity streams, use
85
+ incremental loading instead.
86
+ - Do not use Pagination to hide a missing [Search](search.md) or
87
+ [filtering](filtering.md) capability. If the person must page through 200
88
+ pages to find one row, the list is missing Search.
89
+ - Changing Search, filters, or sort resets to page 1.
90
+ - Selection policy across pages must be explicit: either clear selection on
91
+ page change, or keep a cross-page selection model with a visible count
92
+ ("3 selected across pages"). Pick one per surface and document it.
93
+ - Page size, if configurable, is a [Select](../components/select.md) with a
94
+ small set of sensible options. Changing page size resets to page 1.
95
+ - For very large client-side sets (thousands of rows) where paging is not the
96
+ task, prefer virtualization — see [large-data-tables](large-data-tables.md).
97
+ Pagination is for known, page-shaped sets; virtualization is for scrolling
98
+ one large loaded set.
99
+ - Do not combine Pagination and infinite scroll on the same table. Pick one
100
+ model per surface.
101
+
102
+ ## Accessibility
103
+
104
+ - Use `<nav aria-label="Pagination">`. Give controls names such as "Go to
105
+ page 4", "Previous page", "Next page"; the visible numeral alone is not
106
+ enough.
107
+ - Current page has `aria-current="page"`. Ellipses are not focusable.
108
+ - After a page change, move focus to the results heading or announce the
109
+ updated range in a polite live region ("Showing 41 to 60 of 230 replicas").
110
+ - Previous/Next at a boundary use `--color-disabled` and `aria-disabled`;
111
+ page Links are never disabled.
112
+ - Native Link or Button keyboard behavior applies; `Tab` moves through
113
+ controls.
114
+
115
+ ## Related patterns
116
+
117
+ - [Search](search.md) — narrows the set before paging.
118
+ - [Filtering](filtering.md) — narrows the set before paging.
119
+ - [Sorting](sorting.md) — orders the set before paging.
120
+ - [Large data tables](large-data-tables.md) — when virtualization is the
121
+ better model than paging.
@@ -0,0 +1,84 @@
1
+ # Permission denied
2
+
3
+ Permission denied is a distinct product state: the requested resource or action
4
+ exists, and the system has determined that the current person cannot access it.
5
+ It is not a generic error, an empty collection, or a failed request.
6
+
7
+ ## Component composition
8
+
9
+ - Use [Alert](../components/alert.md) with `info` semantics when a denied action
10
+ sits inside otherwise available content.
11
+ - Use [EmptyState](../components/empty-state.md) as a dedicated locked state
12
+ when denial replaces an entire bounded region or page. Its title and
13
+ description explain access, never emptiness.
14
+ - Use [Link](../components/link.md) to open access documentation, workspace
15
+ settings, or a request-access route.
16
+ - Use [Button](../components/button.md) only when the product can submit an
17
+ access request directly. Do not present a retry action unless permissions may
18
+ genuinely have changed.
19
+
20
+ Permission-denied content uses `--color-foreground` and
21
+ `--color-muted-foreground` on `--color-surface` or `--color-background`.
22
+ An informational Alert may use `--color-info`. **Do not use
23
+ `--color-danger`: denied access is not an error severity.** Link and focus
24
+ treatments use `--color-primary` and `--color-focus`.
25
+
26
+ ## Flow
27
+
28
+ 1. Identify exactly which resource or action is blocked.
29
+ 2. State the applicable access rule or required role when known.
30
+ 3. Offer the shortest real route to access: request access, contact a named
31
+ role, or open access settings/documentation.
32
+ 4. Preserve navigation and any safe surrounding context. Hide protected data
33
+ rather than rendering redacted fragments that reveal its shape.
34
+ 5. If access is granted, re-check authorization and replace the denied state
35
+ with loading, then the authorized result.
36
+
37
+ Copy follows **blocked / why / access path**:
38
+
39
+ > You can't edit this query. Only the query author and workspace admins can
40
+ > edit it. Ask a workspace admin to grant you access.
41
+
42
+ This resembles the clarity of what/why/now, but it does not label the condition
43
+ as a failure and does not use error severity.
44
+
45
+ ## States
46
+
47
+ | State | Treatment |
48
+ | --- | --- |
49
+ | Checking access | Show the loading pattern without protected content. Do not flash a denied state before authorization resolves. |
50
+ | Action denied | Keep available content and place an informational Alert beside the blocked action. Explain why the control is absent or unavailable. |
51
+ | Region/page denied | Replace protected content with a dedicated locked EmptyState composition and one access path when available. |
52
+ | Requesting access | The request Button shows its loading state; keep the explanation visible and prevent duplicates. |
53
+ | Request sent | Confirm in context with the expected next step; do not imply access is already granted. |
54
+ | Access granted | Re-enter loading, then show authorized content. |
55
+ | Access check failed | Use the errors pattern. An inability to determine permission is a system failure, not a denial. |
56
+
57
+ ## Layout sketch
58
+
59
+ ```text
60
+ PageHeader: Production workspace
61
+ Breadcrumb and safe workspace navigation remain visible
62
+ ┌─────────────────────────────────────────────────────────┐
63
+ │ Restricted query │
64
+ │ You can't view this query. It is limited to members │
65
+ │ of the Production Operators group. │
66
+ │ │
67
+ │ [Request access] Access policy │
68
+ └─────────────────────────────────────────────────────────┘
69
+ ```
70
+
71
+ `Request access` is a Button because it submits an action. `Access policy` is a
72
+ Link because it navigates. If neither path exists, say whom to contact and omit
73
+ the action rather than showing a disabled control.
74
+
75
+ ## Accessibility and copy
76
+
77
+ - Name the denied region from its title. Use a polite status for a denial that
78
+ appears after interaction; reserve assertive alerts for actual urgent errors.
79
+ - Move focus to the explanation when denial follows activation and the person
80
+ would otherwise land on removed content.
81
+ - Do not disclose protected names, counts, field values, or membership beyond
82
+ what the person is allowed to know.
83
+ - Avoid “Access denied,” “unauthorized,” blame, corporate language, and false
84
+ retry actions. State what is blocked, why, and how to request access.
@@ -0,0 +1,150 @@
1
+ # Search
2
+
3
+ Search-driven interaction for narrowing a visible collection by query. This
4
+ pattern composes the [Search](../components/search.md) component into a list or
5
+ table toolbar and governs when, how, and where filtering by query happens.
6
+
7
+ User story #6.
8
+
9
+ ## Purpose
10
+
11
+ Search lets the person type a query to narrow what they already see — rows in a
12
+ [Table / DataTable](../components/table.md), items in a list-detail master, or
13
+ entries in a panel. The collection owns the data; Search only supplies the query
14
+ string. It is **not** global navigation or command execution — that is the
15
+ [command palette](command-palette.md).
16
+
17
+ **Canonical rule:** A list screen must not reinvent search, filters,
18
+ pagination, and empty state. Compose [Search](../components/search.md) — do not
19
+ invent a parallel query field per list.
20
+
21
+ ## Component composition
22
+
23
+ | Region | Compose with | Role |
24
+ | --- | --- | --- |
25
+ | Query field | [Search](../components/search.md) | Live-filter or submit-filter the collection; scoped to this list |
26
+ | Clear control | Search's built-in Clear [IconButton](../components/icon-button.md) | Resets query; keeps focus in the field |
27
+ | Pending indicator | [Spinner](../components/spinner.md) inside Search | Results are resolving (server-side or slow index) |
28
+ | Result count | polite live region or toolbar text | "12 of 40 replicas" after results settle |
29
+ | No matches | [EmptyState](../components/empty-state.md) `no-results` | Replaces the collection body, not a Search error |
30
+ | Invalid query | [ValidationMessage](../components/validation-message.md) via [FormField](../components/form-field.md) | Only when the query syntax itself is invalid (regex, etc.) |
31
+
32
+ Search sits in the list toolbar, beside [filtering](filtering.md) controls and
33
+ above [Pagination](../components/pagination.md). It is a sibling of those
34
+ controls, not nested inside the table.
35
+
36
+ ## Flow
37
+
38
+ ### Instant (client-side or fast index)
39
+
40
+ 1. Person types into the Search field.
41
+ 2. Query is debounced (product-defined; short enough to feel live).
42
+ 3. Collection re-filters. Result count updates in a polite live region.
43
+ 4. If the query matches nothing, the collection body shows
44
+ [EmptyState](../components/empty-state.md) `no-results` with a
45
+ "Clear search" action.
46
+ 5. Clearing the query restores the full collection; focus stays in the field.
47
+
48
+ ### Submit (expensive server query)
49
+
50
+ 1. Person types the query. Results do not change yet.
51
+ 2. Person presses `Enter` or activates a visible "Search" [Button](../components/button.md).
52
+ 3. The results region enters loading ([Skeleton](../components/skeleton.md)
53
+ rows), and `aria-busy` is set on the region.
54
+ 4. Results arrive; region swaps to populated or no-matches EmptyState.
55
+
56
+ Do not mix instant and submit behavior on the same Search field without making
57
+ the mode obvious. Pick one per surface and keep it consistent.
58
+
59
+ ## States
60
+
61
+ | State | Behavior |
62
+ | --- | --- |
63
+ | idle | Empty query; full collection visible. Field is ready to type. |
64
+ | typing (instant) | Debounced; results update without focus leaving the field. |
65
+ | resolving | Query submitted; [Spinner](../components/spinner.md) inside Search; region `aria-busy`. Query value stays visible — do not clear it. |
66
+ | no matches | Collection body → [EmptyState](../components/empty-state.md) `no-results`. Offer "Clear search". The Search field keeps the query so the person can edit it. |
67
+ | error | Results failed to load. Show [Alert](../components/alert.md) (what/why/now) above or in place of the body with a retry [Button](../components/button.md). Do not show an EmptyState for a failure. |
68
+ | invalid query | Rare. Only when the query syntax is invalid (e.g. malformed regex). `aria-invalid` + [ValidationMessage](../components/validation-message.md). Zero hits is **not** invalid. |
69
+
70
+ Loading, empty, and error must never be ambiguous: Skeleton rows ≠ EmptyState
71
+ no-results ≠ Alert.
72
+
73
+ ## Layout sketch
74
+
75
+ ```text
76
+ ┌──────────────────────────────────────────────────────────────────────┐
77
+ │ PageHeader: Queries [Create query] │
78
+ ├──────────────────────────────────────────────────────────────────────┤
79
+ │ Toolbar │
80
+ │ [🔍 Search queries..............] [Status▾] [Clear filters] │
81
+ │ │
82
+ │ 12 of 40 queries │
83
+ ├──────────────────────────────────────────────────────────────────────┤
84
+ │ Table / DataTable │
85
+ │ ┌────────────────────────────────────────────────────────────────┐ │
86
+ │ │ Name Author Status Modified │ │
87
+ │ │ slow-join ada warn 2d ago │ │
88
+ │ │ index-health sam ok 5d ago │ │
89
+ │ │ ... │ │
90
+ │ └────────────────────────────────────────────────────────────────┘ │
91
+ │ ← 1 2 3 … → │
92
+ └──────────────────────────────────────────────────────────────────────┘
93
+ ```
94
+
95
+ When the query matches nothing:
96
+
97
+ ```text
98
+ ┌──────────────────────────────────────────────────────────────────────┐
99
+ │ Toolbar │
100
+ │ [🔍 no matching..............] [Clear filters] │
101
+ ├──────────────────────────────────────────────────────────────────────┤
102
+ │ │
103
+ │ No queries match "no matching" │
104
+ │ Try a different search or clear filters. │
105
+ │ [Clear search] │
106
+ │ │
107
+ └──────────────────────────────────────────────────────────────────────┘
108
+ ```
109
+
110
+ ## Rules
111
+
112
+ - Search is scoped to **one collection**. Do not make a single Search field
113
+ filter two independent tables on the same screen; give each its own field.
114
+ - Zero hits is an empty state, not a Search error. Never show
115
+ [ValidationMessage](../components/validation-message.md) for "no results".
116
+ - Keep the query visible during loading and no-matches. Clearing the query is
117
+ the person's action, not a side effect of a failed fetch.
118
+ - `Escape` clears the query if non-empty (pick one behavior per surface and
119
+ keep it consistent). `Escape` does **not** open the
120
+ [command palette](command-palette.md) — that is `⌘K` / `Ctrl+K`.
121
+ - After clearing, keep focus in the Search field and refresh results.
122
+ - Do not move focus into the table on every keystroke.
123
+ - Highlight matched query terms in results by default. Highlighting is
124
+ reinforcement; the filtered set is the real signal. A product may opt out
125
+ only when highlighting is impractical (e.g. server-side search with no
126
+ match offsets) and must document the opt-out. Keep highlighted text readable
127
+ — use `--color-primary` as a background tint, never as text color that
128
+ drops below contrast.
129
+ - Search composes with [filtering](filtering.md): the active query and active
130
+ filters narrow the collection together. Clearing Search does not clear
131
+ filters unless the surface defines that as its single clear action.
132
+
133
+ ## Accessibility
134
+
135
+ - Accessible name always present (`label` / `aria-label` / `aria-labelledby`).
136
+ Placeholder is not the name.
137
+ - Use native `type="search"` so platform clear and semantics work.
138
+ - Debounced instant search updates results without trapping focus. Use a polite
139
+ status region ("12 of 40 replicas") only when the change would otherwise be
140
+ silent.
141
+ - No-matches EmptyState must be announced or discoverable; do not leave the
142
+ region visually and accessibly empty.
143
+
144
+ ## Related patterns
145
+
146
+ - [Filtering](filtering.md) — structured filters that compose with Search.
147
+ - [Sorting](sorting.md) — order of the filtered set.
148
+ - [Pagination](pagination.md) — paging the filtered set.
149
+ - [Command palette](command-palette.md) — global actions and navigation, not
150
+ list filtering.
@@ -0,0 +1,100 @@
1
+ # Settings
2
+
3
+ Configuration pages with predictable form layout and explicit save behavior.
4
+ Settings are preferences and product configuration — not entity CRUD for a
5
+ list of resources.
6
+
7
+ User story #31.
8
+
9
+ ## Purpose
10
+
11
+ Let the person change durable product or workspace options with clear grouping,
12
+ immediate vs deferred persistence, and unambiguous save feedback.
13
+
14
+ ## Component composition
15
+
16
+ | Region | Compose with | Role |
17
+ | --- | --- | --- |
18
+ | Page framing | [PageHeader](../components/page-header.md) | "Settings" or section title; optional save actions when the page uses explicit save |
19
+ | Section nav | [Tabs](../components/tabs.md) or Sidebar sub-nav [Link](../components/link.md)s | Split General / Notifications / API, etc. |
20
+ | Groups | [Card](../components/card.md) | One settings group per Card |
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
+ | 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
+ | Actions | [Button](../components/button.md) | Save (primary), Reset/Cancel (secondary) for explicit-save sections |
24
+ | Feedback | [Toast](../components/toast.md), [Alert](../components/alert.md), [ValidationMessage](../components/validation-message.md) | Saved confirmation; section errors; field errors |
25
+ | Shell | [Application shell](application-shell.md) | Authenticated framing |
26
+
27
+ Tokens: `--color-background` canvas, `--color-surface` Cards, `--color-border`
28
+ separators, `--color-foreground` / `--color-muted-foreground` copy,
29
+ `--color-primary` for current section/nav, `--color-focus` on controls.
30
+
31
+ ## Flow
32
+
33
+ ### Explicit save (default for multi-field sections)
34
+
35
+ 1. Person opens Settings and optionally a section Tab.
36
+ 2. Edits FormFields; Save stays disabled until dirty (recommended) or remains
37
+ available — pick one rule per product and keep it.
38
+ 3. Save validates; ValidationMessage on fields; Alert for section-level
39
+ failures (what / why / now).
40
+ 4. On success: Toast "Settings saved" (or equivalent); clear dirty state.
41
+ 5. Leaving a dirty section prompts only when unsaved loss is material.
42
+
43
+ ### Immediate Switch
44
+
45
+ 1. Person toggles Switch.
46
+ 2. Value persists immediately; failure reverts the Switch and shows Toast or
47
+ inline Alert with recovery.
48
+ 3. Do not also require Save for that same boolean.
49
+
50
+ Do not mix both persistence models inside one Card without labeling which
51
+ controls save immediately.
52
+
53
+ ## States
54
+
55
+ | State | Behavior |
56
+ | --- | --- |
57
+ | loading | Skeleton for known Card/field layout; keep section nav visible. |
58
+ | empty | Rare; if a section has no configurable options yet, short explanatory copy — not a collection EmptyState. |
59
+ | dirty | Visual cue that Save applies; warn on navigate-away when appropriate. |
60
+ | invalid | Field ValidationMessage; focus first error; preserve other values. |
61
+ | saving | Save Button loading; prevent duplicate submits. |
62
+ | error | Alert with what / why / now; values preserved; Switch failures revert. |
63
+ | success | Toast or quiet confirmation; do not block the page. |
64
+
65
+ ## Layout sketch
66
+
67
+ ```text
68
+ ┌──────────────────────────────────────────────────────────────────────┐
69
+ │ PageHeader: Settings │
70
+ │ Tabs: [General] Notifications API │
71
+ ├──────────────────────────────────────────────────────────────────────┤
72
+ │ Card: Workspace │
73
+ │ FormField Display name │
74
+ │ FormField Default region [Select] │
75
+ │ │
76
+ │ Card: Query defaults │
77
+ │ Switch Persist query history │
78
+ │ FormField Statement timeout │
79
+ │ │
80
+ │ [Reset] [Save changes] │
81
+ └──────────────────────────────────────────────────────────────────────┘
82
+ ```
83
+
84
+ ## When to use
85
+
86
+ - Product, workspace, or user preference screens.
87
+ - Multi-section configuration that is not a resource list.
88
+
89
+ ## When NOT to use
90
+
91
+ - Creating/editing listed resources (connections, users as entities) —
92
+ [CRUD](crud.md) + [List-detail](list-detail.md).
93
+ - One-off destructive operations — confirmation / destructive-action patterns.
94
+
95
+ ## Related patterns
96
+
97
+ - [CRUD](crud.md) — entity forms vs preference forms.
98
+ - [Application shell](application-shell.md)
99
+ - [Login / authentication](login-authentication.md) — account recovery and
100
+ auth entry, not general settings.