@momoi-labs/kiso 0.2.0 → 0.4.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/AGENTS.md +7 -5
- package/kiso/README.md +23 -3
- package/kiso/ui.css +983 -0
- package/package.json +2 -1
package/kiso/AGENTS.md
CHANGED
|
@@ -45,11 +45,13 @@ documented; it does not become Kiso by repetition or copy-paste.
|
|
|
45
45
|
6. If no contract fits, describe the gap and propose an addition. Do not
|
|
46
46
|
create a parallel local system.
|
|
47
47
|
|
|
48
|
-
Kiso is spec-first
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
Kiso is spec-first: its Markdown files are contracts and carry no
|
|
49
|
+
implementation code. Two files carry implementation instead —
|
|
50
|
+
[`ui.css`](ui.css), the component layer, published as
|
|
51
|
+
`@momoi-labs/kiso/ui.css`; and [`blocks/`](blocks/README.md), reference
|
|
52
|
+
screens built from it, meant to be copied and not published. Neither is a
|
|
53
|
+
contract: when one disagrees with a contract, the contract is right and the
|
|
54
|
+
implementation is the bug.
|
|
53
55
|
|
|
54
56
|
Deliberate omissions and the evidence-based growth model are recorded in
|
|
55
57
|
[`docs/evolution.md`](docs/evolution.md).
|
package/kiso/README.md
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
Kiso is the Momoi Labs product design system. It combines product identity,
|
|
4
4
|
semantic tokens, component contracts, and reusable screen patterns so products
|
|
5
5
|
with different purposes still belong to the same family. Kiso is spec-first:
|
|
6
|
-
|
|
7
|
-
implementation
|
|
8
|
-
|
|
6
|
+
its Markdown files document what to build and how it behaves, and they carry
|
|
7
|
+
no implementation. Two files are the exception: [`ui.css`](ui.css), the
|
|
8
|
+
component layer every contract's visual values resolve through, and
|
|
9
|
+
[`blocks/`](blocks/README.md), reference screens built from it to copy from.
|
|
10
|
+
Neither overrides a contract — if a block and a contract disagree, the
|
|
9
11
|
contract is right.
|
|
10
12
|
|
|
11
13
|
## For agents
|
|
@@ -41,6 +43,7 @@ keyboard flow where relevant.
|
|
|
41
43
|
| Path | Purpose |
|
|
42
44
|
| --- | --- |
|
|
43
45
|
| [`AGENTS.md`](AGENTS.md) | Consumption contract and decision boundaries |
|
|
46
|
+
| [`ui.css`](ui.css) | Component layer — published as `@momoi-labs/kiso/ui.css` |
|
|
44
47
|
| [`docs/brand.md`](docs/brand.md) | Product personality and visual direction |
|
|
45
48
|
| [`docs/principles.md`](docs/principles.md) | Design principles and tie-breakers |
|
|
46
49
|
| [`docs/voice-and-tone.md`](docs/voice-and-tone.md) | Product UI copy rules |
|
|
@@ -50,10 +53,27 @@ keyboard flow where relevant.
|
|
|
50
53
|
| [`docs/data-interfaces.md`](docs/data-interfaces.md) | Data-heavy interface rules |
|
|
51
54
|
| [`docs/accessibility.md`](docs/accessibility.md) | Accessibility requirements across all layers |
|
|
52
55
|
| [`docs/evolution.md`](docs/evolution.md) | Deliberate deferrals and evidence-based growth |
|
|
56
|
+
| [`blocks/`](blocks/README.md) | Reference screens built from `ui.css` (not published) |
|
|
53
57
|
|
|
54
58
|
Token sources and generated artifacts live in the repository-level `tokens/`
|
|
55
59
|
directory; validation scripts live in `scripts/`.
|
|
56
60
|
|
|
61
|
+
## Installing
|
|
62
|
+
|
|
63
|
+
Contracts, tokens, and the component layer ship in one versioned package, so a
|
|
64
|
+
project pins the whole system to a single version:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install @momoi-labs/kiso
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```css
|
|
71
|
+
@import "@momoi-labs/kiso/tokens.css";
|
|
72
|
+
@import "@momoi-labs/kiso/ui.css";
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`ui.css` reads tokens as custom properties, so import it after `tokens.css`.
|
|
76
|
+
|
|
57
77
|
## Proposing a change
|
|
58
78
|
|
|
59
79
|
First confirm that an existing token, component, or pattern cannot express the
|
package/kiso/ui.css
ADDED
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
/* ===========================================================================
|
|
2
|
+
Kiso v2 — component layer (prototype)
|
|
3
|
+
Every value here resolves to a token in tokens.css. No raw hex, no raw px
|
|
4
|
+
for anything a token covers.
|
|
5
|
+
=========================================================================== */
|
|
6
|
+
|
|
7
|
+
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap");
|
|
8
|
+
|
|
9
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
10
|
+
|
|
11
|
+
html { -webkit-text-size-adjust: 100%; }
|
|
12
|
+
|
|
13
|
+
body {
|
|
14
|
+
margin: 0;
|
|
15
|
+
min-height: 100vh;
|
|
16
|
+
background: var(--color-background);
|
|
17
|
+
color: var(--color-foreground);
|
|
18
|
+
font-family: var(--font-body);
|
|
19
|
+
font-size: var(--type-size-body);
|
|
20
|
+
font-weight: var(--type-weight-regular);
|
|
21
|
+
line-height: var(--type-line-height-normal);
|
|
22
|
+
letter-spacing: var(--type-letter-spacing-normal);
|
|
23
|
+
-webkit-font-smoothing: antialiased;
|
|
24
|
+
font-feature-settings: "cv11", "ss01";
|
|
25
|
+
font-variant-numeric: tabular-nums;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
::selection { background: light-dark(var(--color-accent-200), var(--color-accent-800)); color: light-dark(var(--color-accent-900), var(--color-accent-50)); }
|
|
29
|
+
|
|
30
|
+
/* Focus: one ring, everywhere, violet. This is the accent's main job now. */
|
|
31
|
+
:focus-visible {
|
|
32
|
+
outline: 2px solid var(--color-ring);
|
|
33
|
+
outline-offset: 2px;
|
|
34
|
+
border-radius: var(--radius-sm);
|
|
35
|
+
}
|
|
36
|
+
:focus:not(:focus-visible) { outline: none; }
|
|
37
|
+
|
|
38
|
+
/* --- typography roles ---------------------------------------------------- */
|
|
39
|
+
h1, h2, h3, h4, p, figure { margin: 0; }
|
|
40
|
+
|
|
41
|
+
.t-display {
|
|
42
|
+
font-family: var(--font-heading);
|
|
43
|
+
font-size: var(--type-size-display);
|
|
44
|
+
font-weight: var(--type-weight-semibold);
|
|
45
|
+
line-height: var(--type-line-height-tight);
|
|
46
|
+
letter-spacing: var(--type-letter-spacing-display);
|
|
47
|
+
}
|
|
48
|
+
.t-h1 {
|
|
49
|
+
font-family: var(--font-heading);
|
|
50
|
+
font-size: var(--type-size-h1);
|
|
51
|
+
font-weight: var(--type-weight-semibold);
|
|
52
|
+
line-height: var(--type-line-height-tight);
|
|
53
|
+
letter-spacing: var(--type-letter-spacing-heading);
|
|
54
|
+
}
|
|
55
|
+
.t-h2 {
|
|
56
|
+
font-family: var(--font-heading);
|
|
57
|
+
font-size: var(--type-size-h2);
|
|
58
|
+
font-weight: var(--type-weight-semibold);
|
|
59
|
+
line-height: var(--type-line-height-snug);
|
|
60
|
+
letter-spacing: var(--type-letter-spacing-heading);
|
|
61
|
+
}
|
|
62
|
+
.t-h3 {
|
|
63
|
+
font-family: var(--font-heading);
|
|
64
|
+
font-size: var(--type-size-h3);
|
|
65
|
+
font-weight: var(--type-weight-semibold);
|
|
66
|
+
line-height: var(--type-line-height-snug);
|
|
67
|
+
letter-spacing: var(--type-letter-spacing-heading);
|
|
68
|
+
}
|
|
69
|
+
.t-body { font-size: var(--type-size-body); }
|
|
70
|
+
.t-label { font-size: var(--type-size-label); font-weight: var(--type-weight-medium); }
|
|
71
|
+
.t-metadata { font-size: var(--type-size-metadata); line-height: var(--type-line-height-normal); }
|
|
72
|
+
.t-caps {
|
|
73
|
+
font-size: var(--type-size-metadata);
|
|
74
|
+
font-weight: var(--type-weight-semibold);
|
|
75
|
+
letter-spacing: var(--type-letter-spacing-caps);
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
color: var(--color-subtle-foreground);
|
|
78
|
+
}
|
|
79
|
+
.t-mono, .mono, code, pre, kbd {
|
|
80
|
+
font-family: var(--font-mono);
|
|
81
|
+
font-size: 0.925em;
|
|
82
|
+
font-variant-ligatures: none;
|
|
83
|
+
}
|
|
84
|
+
.t-numeric {
|
|
85
|
+
font-variant-numeric: tabular-nums;
|
|
86
|
+
font-feature-settings: "tnum";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.muted { color: var(--color-muted-foreground); }
|
|
90
|
+
.subtle { color: var(--color-subtle-foreground); }
|
|
91
|
+
.fg { color: var(--color-foreground); }
|
|
92
|
+
.success { color: var(--color-success); }
|
|
93
|
+
.warning { color: var(--color-warning); }
|
|
94
|
+
.danger { color: var(--color-danger); }
|
|
95
|
+
.info { color: var(--color-info); }
|
|
96
|
+
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
97
|
+
.hidden { display: none !important; }
|
|
98
|
+
|
|
99
|
+
a, .link {
|
|
100
|
+
color: var(--color-link);
|
|
101
|
+
text-decoration: none;
|
|
102
|
+
text-underline-offset: 3px;
|
|
103
|
+
text-decoration-thickness: 1px;
|
|
104
|
+
}
|
|
105
|
+
a:hover, .link:hover { text-decoration: underline; }
|
|
106
|
+
|
|
107
|
+
/* --- layout helpers ------------------------------------------------------ */
|
|
108
|
+
.stack { display: grid; gap: var(--spacing-lg); }
|
|
109
|
+
.stack-sm{ display: grid; gap: var(--spacing-sm); }
|
|
110
|
+
.stack-xs{ display: grid; gap: var(--spacing-xs); }
|
|
111
|
+
.row { display: flex; align-items: center; gap: var(--spacing-sm); }
|
|
112
|
+
.row-md { display: flex; align-items: center; gap: var(--spacing-md); }
|
|
113
|
+
.row-wrap{ display: flex; align-items: center; gap: var(--spacing-sm); flex-wrap: wrap; }
|
|
114
|
+
.between { display: flex; align-items: center; justify-content: space-between; gap: var(--spacing-md); }
|
|
115
|
+
.grow { flex: 1; min-width: 0; }
|
|
116
|
+
.separator { height: 1px; background: var(--color-border); border: 0; margin: 0; }
|
|
117
|
+
.separator-v { width: 1px; align-self: stretch; background: var(--color-border); }
|
|
118
|
+
/* A configuration row: name on the left, control flushed right. One rule, so a
|
|
119
|
+
settings row cannot drift between screens. Inline padding is left to the
|
|
120
|
+
container — inside a Card the row aligns with the body, and the sidebar
|
|
121
|
+
footer below re-adds the nav item's inset so labels line up. */
|
|
122
|
+
.settings-row { display: flex; align-items: center; justify-content: space-between; gap: var(--spacing-md); padding-block: var(--spacing-xs); }
|
|
123
|
+
|
|
124
|
+
.icon { width: var(--size-icon-md); height: var(--size-icon-md); flex: none; stroke: currentColor; fill: none; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
|
|
125
|
+
.icon-sm { width: var(--size-icon-sm); height: var(--size-icon-sm); }
|
|
126
|
+
.icon-lg { width: var(--size-icon-lg); height: var(--size-icon-lg); }
|
|
127
|
+
|
|
128
|
+
/* ===========================================================================
|
|
129
|
+
Button
|
|
130
|
+
=========================================================================== */
|
|
131
|
+
.btn {
|
|
132
|
+
--btn-height: var(--size-control-md);
|
|
133
|
+
--btn-padding-x: var(--spacing-md);
|
|
134
|
+
display: inline-flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: center;
|
|
137
|
+
gap: var(--spacing-sm);
|
|
138
|
+
height: var(--btn-height);
|
|
139
|
+
padding-inline: var(--btn-padding-x);
|
|
140
|
+
border: 1px solid transparent;
|
|
141
|
+
border-radius: var(--radius-md);
|
|
142
|
+
background: transparent;
|
|
143
|
+
color: var(--color-foreground);
|
|
144
|
+
font-family: var(--font-body);
|
|
145
|
+
font-size: var(--type-size-body);
|
|
146
|
+
font-weight: var(--type-weight-medium);
|
|
147
|
+
line-height: 1;
|
|
148
|
+
white-space: nowrap;
|
|
149
|
+
cursor: pointer;
|
|
150
|
+
user-select: none;
|
|
151
|
+
text-decoration: none;
|
|
152
|
+
transition:
|
|
153
|
+
background var(--motion-duration-fast) var(--motion-easing-standard),
|
|
154
|
+
border-color var(--motion-duration-fast) var(--motion-easing-standard),
|
|
155
|
+
color var(--motion-duration-fast) var(--motion-easing-standard);
|
|
156
|
+
}
|
|
157
|
+
.btn:disabled, .btn[aria-disabled="true"] {
|
|
158
|
+
cursor: not-allowed;
|
|
159
|
+
opacity: 0.5;
|
|
160
|
+
pointer-events: none;
|
|
161
|
+
}
|
|
162
|
+
.btn:hover { text-decoration: none; }
|
|
163
|
+
|
|
164
|
+
.btn-primary {
|
|
165
|
+
background: var(--color-primary);
|
|
166
|
+
color: var(--color-primary-foreground);
|
|
167
|
+
box-shadow: var(--shadow-xs);
|
|
168
|
+
}
|
|
169
|
+
.btn-primary:hover { background: var(--color-primary-hover); }
|
|
170
|
+
|
|
171
|
+
.btn-secondary {
|
|
172
|
+
background: var(--color-secondary);
|
|
173
|
+
color: var(--color-secondary-foreground);
|
|
174
|
+
}
|
|
175
|
+
.btn-secondary:hover { background: var(--color-secondary-hover); }
|
|
176
|
+
|
|
177
|
+
.btn-outline {
|
|
178
|
+
background: var(--color-card);
|
|
179
|
+
border-color: var(--color-input);
|
|
180
|
+
color: var(--color-foreground);
|
|
181
|
+
box-shadow: var(--shadow-xs);
|
|
182
|
+
}
|
|
183
|
+
.btn-outline:hover { background: var(--color-accent-surface); }
|
|
184
|
+
|
|
185
|
+
.btn-ghost { color: var(--color-muted-foreground); }
|
|
186
|
+
.btn-ghost:hover { background: var(--color-accent-surface-hover); color: var(--color-foreground); }
|
|
187
|
+
|
|
188
|
+
.btn-danger {
|
|
189
|
+
background: var(--color-danger);
|
|
190
|
+
color: var(--color-danger-foreground);
|
|
191
|
+
box-shadow: var(--shadow-xs);
|
|
192
|
+
}
|
|
193
|
+
.btn-danger:hover { filter: brightness(0.93); }
|
|
194
|
+
|
|
195
|
+
.btn-danger-ghost { color: var(--color-danger); }
|
|
196
|
+
.btn-danger-ghost:hover { background: var(--color-danger-surface); }
|
|
197
|
+
|
|
198
|
+
.btn-link { color: var(--color-link); padding-inline: 0; height: auto; }
|
|
199
|
+
.btn-link:hover { text-decoration: underline; }
|
|
200
|
+
|
|
201
|
+
.btn-xs { --btn-height: var(--size-control-xs); --btn-padding-x: var(--spacing-sm); font-size: var(--type-size-label); border-radius: var(--radius-sm); }
|
|
202
|
+
.btn-sm { --btn-height: var(--size-control-sm); --btn-padding-x: var(--spacing-md); font-size: var(--type-size-label); }
|
|
203
|
+
.btn-lg { --btn-height: var(--size-control-lg); --btn-padding-x: var(--spacing-lg); }
|
|
204
|
+
|
|
205
|
+
.btn-icon { --btn-padding-x: 0; width: var(--btn-height); }
|
|
206
|
+
.btn-block { width: 100%; }
|
|
207
|
+
|
|
208
|
+
/* Loading: the label stays in flow so the button never resizes. */
|
|
209
|
+
.btn[data-loading="true"] { position: relative; color: transparent; pointer-events: none; }
|
|
210
|
+
.btn[data-loading="true"]::after {
|
|
211
|
+
content: "";
|
|
212
|
+
position: absolute;
|
|
213
|
+
width: var(--size-icon-md); height: var(--size-icon-md);
|
|
214
|
+
border: 2px solid currentColor;
|
|
215
|
+
border-radius: var(--radius-full);
|
|
216
|
+
border-top-color: transparent;
|
|
217
|
+
animation: spin 600ms linear infinite;
|
|
218
|
+
}
|
|
219
|
+
.btn-primary[data-loading="true"]::after { border-color: var(--color-primary-foreground); border-top-color: transparent; }
|
|
220
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
221
|
+
|
|
222
|
+
/* Button group / segmented control */
|
|
223
|
+
.btn-group { display: inline-flex; }
|
|
224
|
+
.btn-group .btn { border-radius: 0; border-color: var(--color-input); background: var(--color-card); }
|
|
225
|
+
.btn-group .btn:first-child { border-start-start-radius: var(--radius-md); border-end-start-radius: var(--radius-md); }
|
|
226
|
+
.btn-group .btn:last-child { border-start-end-radius: var(--radius-md); border-end-end-radius: var(--radius-md); }
|
|
227
|
+
.btn-group .btn + .btn { margin-inline-start: -1px; }
|
|
228
|
+
.btn-group .btn:hover { background: var(--color-accent-surface); }
|
|
229
|
+
.btn-group .btn[aria-pressed="true"] { background: var(--color-secondary); z-index: 1; }
|
|
230
|
+
|
|
231
|
+
.segmented {
|
|
232
|
+
display: inline-flex;
|
|
233
|
+
gap: var(--spacing-2xs);
|
|
234
|
+
padding: var(--spacing-2xs);
|
|
235
|
+
background: var(--color-muted);
|
|
236
|
+
border: 1px solid var(--color-border);
|
|
237
|
+
border-radius: var(--radius-lg);
|
|
238
|
+
}
|
|
239
|
+
.segmented button {
|
|
240
|
+
height: var(--size-control-sm);
|
|
241
|
+
padding-inline: var(--spacing-md);
|
|
242
|
+
border: 0;
|
|
243
|
+
border-radius: var(--radius-md);
|
|
244
|
+
background: transparent;
|
|
245
|
+
color: var(--color-muted-foreground);
|
|
246
|
+
font: inherit;
|
|
247
|
+
font-size: var(--type-size-label);
|
|
248
|
+
font-weight: var(--type-weight-medium);
|
|
249
|
+
cursor: pointer;
|
|
250
|
+
transition: background var(--motion-duration-fast) var(--motion-easing-standard);
|
|
251
|
+
}
|
|
252
|
+
.segmented button:hover { color: var(--color-foreground); }
|
|
253
|
+
.segmented button[aria-selected="true"] {
|
|
254
|
+
background: var(--color-card);
|
|
255
|
+
color: var(--color-foreground);
|
|
256
|
+
box-shadow: var(--shadow-xs);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* ===========================================================================
|
|
260
|
+
Badge / pill / status
|
|
261
|
+
=========================================================================== */
|
|
262
|
+
.badge {
|
|
263
|
+
display: inline-flex;
|
|
264
|
+
align-items: center;
|
|
265
|
+
gap: var(--spacing-xs);
|
|
266
|
+
height: 20px;
|
|
267
|
+
padding-inline: var(--spacing-sm);
|
|
268
|
+
border: 1px solid transparent;
|
|
269
|
+
border-radius: var(--radius-sm);
|
|
270
|
+
font-size: var(--type-size-metadata);
|
|
271
|
+
font-weight: var(--type-weight-medium);
|
|
272
|
+
line-height: 1;
|
|
273
|
+
white-space: nowrap;
|
|
274
|
+
}
|
|
275
|
+
.badge-solid { background: var(--color-primary); color: var(--color-primary-foreground); }
|
|
276
|
+
.badge-neutral { background: var(--color-secondary); color: var(--color-secondary-foreground); }
|
|
277
|
+
.badge-outline { border-color: var(--color-border-strong); color: var(--color-muted-foreground); }
|
|
278
|
+
.badge-success { background: var(--color-success-surface); border-color: var(--color-success-border); color: var(--color-success); }
|
|
279
|
+
.badge-warning { background: var(--color-warning-surface); border-color: var(--color-warning-border); color: var(--color-warning); }
|
|
280
|
+
.badge-danger { background: var(--color-danger-surface); border-color: var(--color-danger-border); color: var(--color-danger); }
|
|
281
|
+
.badge-info { background: var(--color-info-surface); border-color: var(--color-info-border); color: var(--color-info); }
|
|
282
|
+
.badge-pill { border-radius: var(--radius-full); padding-inline: var(--spacing-md); }
|
|
283
|
+
.badge-count { min-width: 20px; justify-content: center; padding-inline: var(--spacing-xs); border-radius: var(--radius-full); }
|
|
284
|
+
.badge .mono { font-size: inherit; }
|
|
285
|
+
|
|
286
|
+
.dot { width: 6px; height: 6px; border-radius: var(--radius-full); background: currentColor; flex: none; }
|
|
287
|
+
.dot-lg { width: 8px; height: 8px; }
|
|
288
|
+
.dot-pulse { position: relative; }
|
|
289
|
+
.dot-pulse::after {
|
|
290
|
+
content: ""; position: absolute; inset: -3px;
|
|
291
|
+
border-radius: var(--radius-full);
|
|
292
|
+
background: currentColor; opacity: 0.25;
|
|
293
|
+
animation: pulse 2s var(--motion-easing-standard) infinite;
|
|
294
|
+
}
|
|
295
|
+
@keyframes pulse { 0%,100% { transform: scale(1); opacity: .25 } 50% { transform: scale(1.4); opacity: 0 } }
|
|
296
|
+
|
|
297
|
+
/* ===========================================================================
|
|
298
|
+
Form controls
|
|
299
|
+
=========================================================================== */
|
|
300
|
+
.field { display: grid; gap: var(--spacing-xs); }
|
|
301
|
+
.field > label, .label {
|
|
302
|
+
font-size: var(--type-size-label);
|
|
303
|
+
font-weight: var(--type-weight-medium);
|
|
304
|
+
color: var(--color-foreground);
|
|
305
|
+
letter-spacing: var(--type-letter-spacing-label);
|
|
306
|
+
}
|
|
307
|
+
.field-hint { font-size: var(--type-size-label); color: var(--color-muted-foreground); }
|
|
308
|
+
.field-error { font-size: var(--type-size-label); color: var(--color-danger); display: flex; align-items: center; gap: var(--spacing-xs); }
|
|
309
|
+
.field-required { color: var(--color-danger); }
|
|
310
|
+
|
|
311
|
+
.input, .select, .textarea {
|
|
312
|
+
width: 100%;
|
|
313
|
+
min-width: 0;
|
|
314
|
+
height: var(--size-control-md);
|
|
315
|
+
padding-inline: var(--spacing-md);
|
|
316
|
+
border: 1px solid var(--color-input);
|
|
317
|
+
border-radius: var(--radius-md);
|
|
318
|
+
background: var(--color-card);
|
|
319
|
+
color: var(--color-foreground);
|
|
320
|
+
font-family: var(--font-body);
|
|
321
|
+
font-size: var(--type-size-body);
|
|
322
|
+
line-height: 1;
|
|
323
|
+
box-shadow: var(--shadow-xs);
|
|
324
|
+
transition: border-color var(--motion-duration-fast) var(--motion-easing-standard);
|
|
325
|
+
}
|
|
326
|
+
.textarea { height: auto; min-height: 76px; padding-block: var(--spacing-sm); line-height: var(--type-line-height-normal); resize: vertical; }
|
|
327
|
+
.input::placeholder, .textarea::placeholder { color: var(--color-subtle-foreground); }
|
|
328
|
+
.input:hover, .select:hover, .textarea:hover { border-color: var(--color-border-strong); }
|
|
329
|
+
.input:disabled, .select:disabled, .textarea:disabled {
|
|
330
|
+
background: var(--color-disabled-surface);
|
|
331
|
+
color: var(--color-disabled);
|
|
332
|
+
cursor: not-allowed;
|
|
333
|
+
}
|
|
334
|
+
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--color-danger); }
|
|
335
|
+
.input[aria-invalid="true"]:focus-visible { outline-color: var(--color-danger); }
|
|
336
|
+
.input-sm { height: var(--size-control-sm); font-size: var(--type-size-label); }
|
|
337
|
+
.input.mono, .textarea.mono { font-size: var(--type-size-label); }
|
|
338
|
+
|
|
339
|
+
.select {
|
|
340
|
+
appearance: none;
|
|
341
|
+
padding-inline-end: var(--spacing-2xl);
|
|
342
|
+
/* neutral-500 sits between both themes, so the chevron needs no fork */
|
|
343
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238a857f' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
|
|
344
|
+
background-repeat: no-repeat;
|
|
345
|
+
background-position: right var(--spacing-md) center;
|
|
346
|
+
background-size: var(--size-icon-md);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* input with a leading icon or a trailing affix */
|
|
350
|
+
.input-group { position: relative; display: flex; align-items: center; }
|
|
351
|
+
.input-group .icon {
|
|
352
|
+
position: absolute;
|
|
353
|
+
inset-inline-start: var(--spacing-md);
|
|
354
|
+
color: var(--color-subtle-foreground);
|
|
355
|
+
pointer-events: none;
|
|
356
|
+
}
|
|
357
|
+
.input-group .icon ~ .input { padding-inline-start: calc(var(--spacing-md) * 2 + var(--size-icon-md)); }
|
|
358
|
+
.input-group .affix {
|
|
359
|
+
position: absolute;
|
|
360
|
+
inset-inline-end: var(--spacing-sm);
|
|
361
|
+
display: flex; align-items: center; gap: var(--spacing-xs);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* checkbox / radio */
|
|
365
|
+
.check {
|
|
366
|
+
display: inline-flex;
|
|
367
|
+
align-items: flex-start;
|
|
368
|
+
gap: var(--spacing-sm);
|
|
369
|
+
cursor: pointer;
|
|
370
|
+
font-size: var(--type-size-body);
|
|
371
|
+
}
|
|
372
|
+
.check input[type="checkbox"], .check input[type="radio"] {
|
|
373
|
+
appearance: none;
|
|
374
|
+
margin: 0;
|
|
375
|
+
width: var(--size-icon-md);
|
|
376
|
+
height: var(--size-icon-md);
|
|
377
|
+
flex: none;
|
|
378
|
+
margin-block-start: 3px;
|
|
379
|
+
border: 1px solid var(--color-input);
|
|
380
|
+
border-radius: var(--radius-xs);
|
|
381
|
+
background: var(--color-card);
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
transition: background var(--motion-duration-fast) var(--motion-easing-standard);
|
|
384
|
+
}
|
|
385
|
+
.check input[type="radio"] { border-radius: var(--radius-full); }
|
|
386
|
+
.check input:checked {
|
|
387
|
+
background: var(--color-primary);
|
|
388
|
+
border-color: var(--color-primary);
|
|
389
|
+
}
|
|
390
|
+
/* The tick is a mask painted with `primary-foreground`, so it inverts with
|
|
391
|
+
the fill through a token. Two per-theme data URIs used to do this job and
|
|
392
|
+
they could not follow `color-scheme` when the theme is left to the OS. */
|
|
393
|
+
.check input[type="checkbox"] { display: inline-grid; place-content: center; }
|
|
394
|
+
.check input[type="checkbox"]::after {
|
|
395
|
+
content: "";
|
|
396
|
+
width: 10px; height: 10px;
|
|
397
|
+
background: var(--color-primary-foreground);
|
|
398
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6.5'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
399
|
+
scale: 0;
|
|
400
|
+
transition: scale var(--motion-duration-fast) var(--motion-easing-standard);
|
|
401
|
+
}
|
|
402
|
+
.check input[type="checkbox"]:checked::after { scale: 1; }
|
|
403
|
+
.check input[type="radio"]:checked {
|
|
404
|
+
box-shadow: inset 0 0 0 3px var(--color-card);
|
|
405
|
+
}
|
|
406
|
+
.check input:disabled { background: var(--color-disabled-surface); border-color: var(--color-border); cursor: not-allowed; }
|
|
407
|
+
.check-text { display: grid; gap: var(--spacing-2xs); }
|
|
408
|
+
|
|
409
|
+
/* switch */
|
|
410
|
+
.switch { display: inline-flex; align-items: center; gap: var(--spacing-sm); cursor: pointer; }
|
|
411
|
+
.switch input {
|
|
412
|
+
appearance: none;
|
|
413
|
+
margin: 0;
|
|
414
|
+
position: relative;
|
|
415
|
+
width: 32px; height: 18px; flex: none;
|
|
416
|
+
border-radius: var(--radius-full);
|
|
417
|
+
background: var(--color-border-strong);
|
|
418
|
+
cursor: pointer;
|
|
419
|
+
transition: background var(--motion-duration-fast) var(--motion-easing-standard);
|
|
420
|
+
}
|
|
421
|
+
.switch input::after {
|
|
422
|
+
content: "";
|
|
423
|
+
position: absolute;
|
|
424
|
+
top: 2px; inset-inline-start: 2px;
|
|
425
|
+
width: 14px; height: 14px;
|
|
426
|
+
border-radius: var(--radius-full);
|
|
427
|
+
background: var(--color-white);
|
|
428
|
+
box-shadow: var(--shadow-xs);
|
|
429
|
+
transition: translate var(--motion-duration-fast) var(--motion-easing-standard);
|
|
430
|
+
}
|
|
431
|
+
.switch input:checked { background: var(--color-primary); }
|
|
432
|
+
.switch input:checked::after { translate: 14px 0; background: var(--color-primary-foreground); }
|
|
433
|
+
.switch input:disabled { opacity: .5; cursor: not-allowed; }
|
|
434
|
+
|
|
435
|
+
/* range */
|
|
436
|
+
.range { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; border-radius: var(--radius-full); background: var(--color-secondary); }
|
|
437
|
+
.range::-webkit-slider-thumb {
|
|
438
|
+
-webkit-appearance: none;
|
|
439
|
+
width: var(--size-icon-md); height: var(--size-icon-md);
|
|
440
|
+
border-radius: var(--radius-full);
|
|
441
|
+
background: var(--color-card);
|
|
442
|
+
border: 1px solid var(--color-border-strong);
|
|
443
|
+
box-shadow: var(--shadow-sm);
|
|
444
|
+
cursor: grab;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* ===========================================================================
|
|
448
|
+
Card
|
|
449
|
+
=========================================================================== */
|
|
450
|
+
.card {
|
|
451
|
+
position: relative;
|
|
452
|
+
background: var(--color-card);
|
|
453
|
+
color: var(--color-card-foreground);
|
|
454
|
+
border: 1px solid var(--color-border);
|
|
455
|
+
border-radius: var(--radius-surface);
|
|
456
|
+
box-shadow: var(--shadow-xs);
|
|
457
|
+
}
|
|
458
|
+
.card-header { padding: var(--spacing-lg) var(--spacing-lg) 0; display: grid; gap: var(--spacing-2xs); }
|
|
459
|
+
.card-body { padding: var(--spacing-lg); display: grid; gap: var(--spacing-md); }
|
|
460
|
+
.card-footer {
|
|
461
|
+
padding: var(--spacing-md) var(--spacing-lg);
|
|
462
|
+
border-top: 1px solid var(--color-border);
|
|
463
|
+
display: flex; align-items: center; justify-content: flex-end; gap: var(--spacing-sm);
|
|
464
|
+
background: var(--color-muted);
|
|
465
|
+
border-end-start-radius: var(--radius-surface);
|
|
466
|
+
border-end-end-radius: var(--radius-surface);
|
|
467
|
+
}
|
|
468
|
+
.card-header + .card-body { padding-block-start: var(--spacing-md); }
|
|
469
|
+
.card-interactive { cursor: pointer; transition: border-color var(--motion-duration-fast) var(--motion-easing-standard); }
|
|
470
|
+
.card-interactive:hover { border-color: var(--color-border-strong); box-shadow: var(--shadow-sm); }
|
|
471
|
+
|
|
472
|
+
/* stat / KPI tile */
|
|
473
|
+
.stat { display: grid; gap: var(--spacing-2xs); padding: var(--spacing-lg); }
|
|
474
|
+
.stat-label { font-size: var(--type-size-label); color: var(--color-muted-foreground); font-weight: var(--type-weight-regular); }
|
|
475
|
+
.stat-value {
|
|
476
|
+
font-size: var(--type-size-display);
|
|
477
|
+
font-weight: var(--type-weight-semibold);
|
|
478
|
+
line-height: var(--type-line-height-tight);
|
|
479
|
+
letter-spacing: var(--type-letter-spacing-display);
|
|
480
|
+
font-variant-numeric: tabular-nums;
|
|
481
|
+
}
|
|
482
|
+
.stat-foot { font-size: var(--type-size-label); color: var(--color-muted-foreground); margin-block-start: var(--spacing-sm); }
|
|
483
|
+
.stat-delta { display: inline-flex; align-items: center; gap: var(--spacing-xs); font-size: var(--type-size-metadata); font-weight: var(--type-weight-medium); }
|
|
484
|
+
|
|
485
|
+
/* ===========================================================================
|
|
486
|
+
Table
|
|
487
|
+
=========================================================================== */
|
|
488
|
+
.table-wrap {
|
|
489
|
+
position: relative;
|
|
490
|
+
border: 1px solid var(--color-border);
|
|
491
|
+
border-radius: var(--radius-surface);
|
|
492
|
+
background: var(--color-card);
|
|
493
|
+
}
|
|
494
|
+
/* Scrolling moves to the inner element so the corner marks, which sit just
|
|
495
|
+
outside the frame, are not clipped away. */
|
|
496
|
+
.table-wrap > .table-scroll { overflow: auto; border-radius: inherit; }
|
|
497
|
+
table.table { width: 100%; border-collapse: collapse; font-size: var(--type-size-body); }
|
|
498
|
+
.table thead th {
|
|
499
|
+
position: sticky; top: 0; z-index: 1;
|
|
500
|
+
background: var(--color-muted);
|
|
501
|
+
padding: var(--spacing-sm) var(--spacing-md);
|
|
502
|
+
border-bottom: 1px solid var(--color-border);
|
|
503
|
+
text-align: start;
|
|
504
|
+
font-size: var(--type-size-label);
|
|
505
|
+
font-weight: var(--type-weight-medium);
|
|
506
|
+
color: var(--color-muted-foreground);
|
|
507
|
+
white-space: nowrap;
|
|
508
|
+
}
|
|
509
|
+
.table tbody td {
|
|
510
|
+
padding: var(--spacing-sm) var(--spacing-md);
|
|
511
|
+
border-bottom: 1px solid var(--color-border);
|
|
512
|
+
vertical-align: middle;
|
|
513
|
+
height: var(--size-control-lg);
|
|
514
|
+
}
|
|
515
|
+
.table tbody tr:last-child td { border-bottom: 0; }
|
|
516
|
+
.table tbody tr:hover { background: var(--color-accent-surface); }
|
|
517
|
+
.table tbody tr[aria-selected="true"] { background: var(--color-selected); }
|
|
518
|
+
.table .num { text-align: end; font-variant-numeric: tabular-nums; }
|
|
519
|
+
.table .col-tight { width: 1%; white-space: nowrap; }
|
|
520
|
+
.th-sort { display: inline-flex; align-items: center; gap: var(--spacing-xs); cursor: pointer; background: none; border: 0; font: inherit; color: inherit; padding: 0; }
|
|
521
|
+
.table-toolbar { display: flex; align-items: center; gap: var(--spacing-sm); padding: var(--spacing-md); border-bottom: 1px solid var(--color-border); }
|
|
522
|
+
.table-footer { display: flex; align-items: center; justify-content: space-between; gap: var(--spacing-md); padding: var(--spacing-sm) var(--spacing-md); border-top: 1px solid var(--color-border); font-size: var(--type-size-label); color: var(--color-muted-foreground); }
|
|
523
|
+
|
|
524
|
+
/* ===========================================================================
|
|
525
|
+
Tabs
|
|
526
|
+
=========================================================================== */
|
|
527
|
+
.tabs { display: flex; gap: var(--spacing-lg); border-bottom: 1px solid var(--color-border); }
|
|
528
|
+
.tabs button {
|
|
529
|
+
position: relative;
|
|
530
|
+
height: var(--size-control-lg);
|
|
531
|
+
padding: 0;
|
|
532
|
+
border: 0; background: none;
|
|
533
|
+
font: inherit;
|
|
534
|
+
font-size: var(--type-size-body);
|
|
535
|
+
font-weight: var(--type-weight-medium);
|
|
536
|
+
color: var(--color-muted-foreground);
|
|
537
|
+
cursor: pointer;
|
|
538
|
+
}
|
|
539
|
+
.tabs button:hover { color: var(--color-foreground); }
|
|
540
|
+
.tabs button[aria-selected="true"] { color: var(--color-foreground); }
|
|
541
|
+
.tabs button[aria-selected="true"]::after {
|
|
542
|
+
content: ""; position: absolute; inset-inline: 0; bottom: -1px; height: 2px;
|
|
543
|
+
background: var(--color-primary);
|
|
544
|
+
border-radius: var(--radius-full);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* ===========================================================================
|
|
548
|
+
Alert / callout
|
|
549
|
+
=========================================================================== */
|
|
550
|
+
.alert {
|
|
551
|
+
display: flex;
|
|
552
|
+
gap: var(--spacing-md);
|
|
553
|
+
padding: var(--spacing-md);
|
|
554
|
+
border: 1px solid var(--color-border);
|
|
555
|
+
border-radius: var(--radius-lg);
|
|
556
|
+
background: var(--color-card);
|
|
557
|
+
font-size: var(--type-size-body);
|
|
558
|
+
}
|
|
559
|
+
.alert .icon { margin-block-start: 2px; }
|
|
560
|
+
.alert-title { font-weight: var(--type-weight-medium); }
|
|
561
|
+
.alert-body { color: var(--color-muted-foreground); font-size: var(--type-size-label); }
|
|
562
|
+
.alert-info { background: var(--color-info-surface); border-color: var(--color-info-border); }
|
|
563
|
+
.alert-info > .icon { color: var(--color-info); }
|
|
564
|
+
.alert-success { background: var(--color-success-surface); border-color: var(--color-success-border); }
|
|
565
|
+
.alert-success > .icon { color: var(--color-success); }
|
|
566
|
+
.alert-warning { background: var(--color-warning-surface); border-color: var(--color-warning-border); }
|
|
567
|
+
.alert-warning > .icon { color: var(--color-warning); }
|
|
568
|
+
.alert-danger { background: var(--color-danger-surface); border-color: var(--color-danger-border); }
|
|
569
|
+
.alert-danger > .icon { color: var(--color-danger); }
|
|
570
|
+
|
|
571
|
+
/* ===========================================================================
|
|
572
|
+
Dialog / drawer / overlay
|
|
573
|
+
=========================================================================== */
|
|
574
|
+
.overlay {
|
|
575
|
+
position: fixed; inset: 0; z-index: 50;
|
|
576
|
+
display: grid; place-items: center;
|
|
577
|
+
padding: var(--spacing-lg);
|
|
578
|
+
background: var(--color-overlay);
|
|
579
|
+
backdrop-filter: blur(2px);
|
|
580
|
+
}
|
|
581
|
+
.dialog {
|
|
582
|
+
position: relative;
|
|
583
|
+
width: 100%; max-width: 440px;
|
|
584
|
+
background: var(--color-popover);
|
|
585
|
+
color: var(--color-popover-foreground);
|
|
586
|
+
border: 1px solid var(--color-border);
|
|
587
|
+
border-radius: var(--radius-surface);
|
|
588
|
+
box-shadow: var(--shadow-lg);
|
|
589
|
+
animation: dialog-in var(--motion-duration-normal) var(--motion-easing-standard);
|
|
590
|
+
}
|
|
591
|
+
@keyframes dialog-in { from { opacity: 0; translate: 0 8px; scale: .98 } }
|
|
592
|
+
.dialog-header { padding: var(--spacing-lg) var(--spacing-lg) 0; display: grid; gap: var(--spacing-2xs); }
|
|
593
|
+
.dialog-body { padding: var(--spacing-lg); display: grid; gap: var(--spacing-lg); }
|
|
594
|
+
.dialog-header + .dialog-body { padding-block-start: var(--spacing-md); }
|
|
595
|
+
.dialog-footer { padding: var(--spacing-md) var(--spacing-lg); border-top: 1px solid var(--color-border); display: flex; justify-content: flex-end; gap: var(--spacing-sm); background: var(--color-muted); border-end-start-radius: var(--radius-surface); border-end-end-radius: var(--radius-surface); }
|
|
596
|
+
|
|
597
|
+
/* ===========================================================================
|
|
598
|
+
Menu / popover / tooltip / command palette
|
|
599
|
+
=========================================================================== */
|
|
600
|
+
.menu {
|
|
601
|
+
min-width: 200px;
|
|
602
|
+
padding: var(--spacing-xs);
|
|
603
|
+
background: var(--color-popover);
|
|
604
|
+
border: 1px solid var(--color-border);
|
|
605
|
+
border-radius: var(--radius-lg);
|
|
606
|
+
box-shadow: var(--shadow-lg);
|
|
607
|
+
}
|
|
608
|
+
.menu-item {
|
|
609
|
+
display: flex; align-items: center; gap: var(--spacing-sm);
|
|
610
|
+
width: 100%;
|
|
611
|
+
height: var(--size-control-sm);
|
|
612
|
+
padding-inline: var(--spacing-sm);
|
|
613
|
+
border: 0; border-radius: var(--radius-md);
|
|
614
|
+
background: none; color: var(--color-foreground);
|
|
615
|
+
font: inherit; font-size: var(--type-size-body);
|
|
616
|
+
text-align: start; text-decoration: none;
|
|
617
|
+
cursor: pointer;
|
|
618
|
+
}
|
|
619
|
+
.menu-item:hover { background: var(--color-accent-surface-hover); }
|
|
620
|
+
.menu-item .icon { color: var(--color-muted-foreground); }
|
|
621
|
+
.menu-item.is-danger { color: var(--color-danger); }
|
|
622
|
+
.menu-item.is-danger:hover { background: var(--color-danger-surface); }
|
|
623
|
+
.menu-item .kbd { margin-inline-start: auto; }
|
|
624
|
+
.menu-label { padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-xs); }
|
|
625
|
+
.menu-separator { height: 1px; background: var(--color-border); margin: var(--spacing-xs) calc(var(--spacing-xs) * -1); }
|
|
626
|
+
|
|
627
|
+
.kbd, kbd {
|
|
628
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
629
|
+
min-width: 18px; height: 18px;
|
|
630
|
+
padding-inline: var(--spacing-xs);
|
|
631
|
+
border: 1px solid var(--color-border);
|
|
632
|
+
border-bottom-width: 2px;
|
|
633
|
+
border-radius: var(--radius-xs);
|
|
634
|
+
background: var(--color-muted);
|
|
635
|
+
color: var(--color-muted-foreground);
|
|
636
|
+
font-family: var(--font-mono);
|
|
637
|
+
font-size: var(--type-size-metadata);
|
|
638
|
+
line-height: 1;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.tooltip {
|
|
642
|
+
display: inline-block;
|
|
643
|
+
padding: var(--spacing-xs) var(--spacing-sm);
|
|
644
|
+
border-radius: var(--radius-md);
|
|
645
|
+
background: var(--color-primary);
|
|
646
|
+
color: var(--color-primary-foreground);
|
|
647
|
+
font-size: var(--type-size-label);
|
|
648
|
+
line-height: var(--type-line-height-tight);
|
|
649
|
+
box-shadow: var(--shadow-md);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.palette {
|
|
653
|
+
width: 100%; max-width: 560px;
|
|
654
|
+
background: var(--color-popover);
|
|
655
|
+
border: 1px solid var(--color-border);
|
|
656
|
+
border-radius: var(--radius-surface);
|
|
657
|
+
box-shadow: var(--shadow-lg);
|
|
658
|
+
overflow: hidden;
|
|
659
|
+
}
|
|
660
|
+
.palette-input {
|
|
661
|
+
display: flex; align-items: center; gap: var(--spacing-sm);
|
|
662
|
+
padding: var(--spacing-md) var(--spacing-lg);
|
|
663
|
+
border-bottom: 1px solid var(--color-border);
|
|
664
|
+
}
|
|
665
|
+
.palette-input input { flex: 1; border: 0; background: none; color: inherit; font: inherit; outline: none; }
|
|
666
|
+
.palette-list { padding: var(--spacing-xs); max-height: 280px; overflow-y: auto; }
|
|
667
|
+
.palette-list .menu-item[aria-selected="true"] { background: var(--color-selected); color: var(--color-selected-foreground); }
|
|
668
|
+
|
|
669
|
+
/* ===========================================================================
|
|
670
|
+
Toast
|
|
671
|
+
=========================================================================== */
|
|
672
|
+
.toast-region { position: fixed; inset-block-end: var(--spacing-lg); inset-inline-end: var(--spacing-lg); z-index: 60; display: grid; gap: var(--spacing-sm); }
|
|
673
|
+
.toast {
|
|
674
|
+
display: flex; align-items: flex-start; gap: var(--spacing-md);
|
|
675
|
+
width: 340px;
|
|
676
|
+
padding: var(--spacing-md);
|
|
677
|
+
background: var(--color-popover);
|
|
678
|
+
border: 1px solid var(--color-border);
|
|
679
|
+
border-radius: var(--radius-lg);
|
|
680
|
+
box-shadow: var(--shadow-lg);
|
|
681
|
+
animation: toast-in var(--motion-duration-normal) var(--motion-easing-standard);
|
|
682
|
+
}
|
|
683
|
+
@keyframes toast-in { from { opacity: 0; translate: 0 12px } }
|
|
684
|
+
|
|
685
|
+
/* ===========================================================================
|
|
686
|
+
Feedback: spinner, progress, skeleton, empty state
|
|
687
|
+
=========================================================================== */
|
|
688
|
+
.spinner {
|
|
689
|
+
width: var(--size-icon-md); height: var(--size-icon-md);
|
|
690
|
+
border: 2px solid var(--color-border-strong);
|
|
691
|
+
border-top-color: var(--color-foreground);
|
|
692
|
+
border-radius: var(--radius-full);
|
|
693
|
+
animation: spin 600ms linear infinite;
|
|
694
|
+
}
|
|
695
|
+
.spinner-lg { width: var(--size-icon-lg); height: var(--size-icon-lg); }
|
|
696
|
+
|
|
697
|
+
.progress { height: 6px; border-radius: var(--radius-full); background: var(--color-secondary); overflow: hidden; }
|
|
698
|
+
.progress > span { display: block; height: 100%; border-radius: inherit; background: var(--color-primary); }
|
|
699
|
+
.progress-accent > span { background: var(--color-accent-600); }
|
|
700
|
+
|
|
701
|
+
.skeleton { border-radius: var(--radius-md); background: var(--color-skeleton); animation: shimmer 1.4s ease-in-out infinite; }
|
|
702
|
+
@keyframes shimmer { 50% { opacity: .55 } }
|
|
703
|
+
|
|
704
|
+
.empty {
|
|
705
|
+
display: grid; justify-items: center; gap: var(--spacing-sm);
|
|
706
|
+
padding: var(--spacing-4xl) var(--spacing-xl);
|
|
707
|
+
text-align: center;
|
|
708
|
+
}
|
|
709
|
+
.empty-icon {
|
|
710
|
+
display: grid; place-items: center;
|
|
711
|
+
width: var(--size-control-lg); height: var(--size-control-lg);
|
|
712
|
+
border: 1px solid var(--color-border);
|
|
713
|
+
border-radius: var(--radius-lg);
|
|
714
|
+
background: var(--color-muted);
|
|
715
|
+
color: var(--color-muted-foreground);
|
|
716
|
+
margin-block-end: var(--spacing-xs);
|
|
717
|
+
}
|
|
718
|
+
.empty p { color: var(--color-muted-foreground); font-size: var(--type-size-label); max-width: 42ch; }
|
|
719
|
+
|
|
720
|
+
/* ===========================================================================
|
|
721
|
+
Navigation: breadcrumb, pagination, sidebar, header
|
|
722
|
+
=========================================================================== */
|
|
723
|
+
.breadcrumb { display: flex; align-items: center; gap: var(--spacing-sm); font-size: var(--type-size-label); color: var(--color-muted-foreground); }
|
|
724
|
+
.breadcrumb a { color: var(--color-muted-foreground); }
|
|
725
|
+
.breadcrumb a:hover { color: var(--color-foreground); text-decoration: none; }
|
|
726
|
+
.breadcrumb [aria-current="page"] { color: var(--color-foreground); font-weight: var(--type-weight-medium); }
|
|
727
|
+
.breadcrumb .sep { color: var(--color-border-strong); }
|
|
728
|
+
|
|
729
|
+
.pagination { display: flex; align-items: center; gap: var(--spacing-xs); }
|
|
730
|
+
.pagination .btn[aria-current="page"] { background: var(--color-secondary); }
|
|
731
|
+
|
|
732
|
+
.app-shell { display: grid; grid-template-columns: var(--size-sidebar) minmax(0, 1fr); min-height: 100vh; }
|
|
733
|
+
.sidebar {
|
|
734
|
+
display: flex; flex-direction: column;
|
|
735
|
+
min-width: 0;
|
|
736
|
+
position: sticky; top: 0;
|
|
737
|
+
height: 100vh;
|
|
738
|
+
background: var(--color-sidebar);
|
|
739
|
+
border-inline-end: 1px solid var(--color-sidebar-border);
|
|
740
|
+
}
|
|
741
|
+
.sidebar-header { padding: var(--spacing-md); display: grid; gap: var(--spacing-md); }
|
|
742
|
+
.sidebar-body { flex: 1; overflow-y: auto; padding: 0 var(--spacing-sm) var(--spacing-sm); display: grid; gap: var(--spacing-lg); align-content: start; }
|
|
743
|
+
.sidebar-footer { padding: var(--spacing-sm); border-top: 1px solid var(--color-sidebar-border); display: grid; gap: var(--spacing-2xs); }
|
|
744
|
+
.sidebar-footer .settings-row { padding-inline: var(--spacing-sm); } /* match .nav-item's inset */
|
|
745
|
+
.nav-group { display: grid; gap: var(--spacing-2xs); }
|
|
746
|
+
.nav-group > .t-caps { padding-inline: var(--spacing-sm); margin-block-end: var(--spacing-2xs); }
|
|
747
|
+
.nav-item {
|
|
748
|
+
display: flex; align-items: center; gap: var(--spacing-sm);
|
|
749
|
+
height: var(--size-control-sm);
|
|
750
|
+
padding-inline: var(--spacing-sm);
|
|
751
|
+
border: 0; border-radius: var(--radius-md);
|
|
752
|
+
background: none;
|
|
753
|
+
color: var(--color-muted-foreground);
|
|
754
|
+
font: inherit; font-size: var(--type-size-body);
|
|
755
|
+
text-align: start; text-decoration: none;
|
|
756
|
+
cursor: pointer;
|
|
757
|
+
width: 100%;
|
|
758
|
+
}
|
|
759
|
+
.nav-item .icon { color: var(--color-subtle-foreground); }
|
|
760
|
+
.nav-item:hover { background: var(--color-accent-surface-hover); color: var(--color-foreground); text-decoration: none; }
|
|
761
|
+
.nav-item[aria-current="page"], .nav-item.active {
|
|
762
|
+
background: var(--color-selected);
|
|
763
|
+
box-shadow: inset 2px 0 0 var(--color-primary);
|
|
764
|
+
color: var(--color-selected-foreground);
|
|
765
|
+
font-weight: var(--type-weight-medium);
|
|
766
|
+
}
|
|
767
|
+
.nav-item[aria-current="page"] .icon, .nav-item.active .icon { color: currentColor; }
|
|
768
|
+
.nav-item .badge { margin-inline-start: auto; }
|
|
769
|
+
|
|
770
|
+
.brand { display: flex; align-items: center; gap: var(--spacing-sm); min-width: 0; }
|
|
771
|
+
.brand-mark {
|
|
772
|
+
display: grid; place-items: center;
|
|
773
|
+
width: var(--size-control-sm); height: var(--size-control-sm);
|
|
774
|
+
flex: none;
|
|
775
|
+
border-radius: var(--radius-lg);
|
|
776
|
+
background: var(--color-primary);
|
|
777
|
+
color: var(--color-primary-foreground);
|
|
778
|
+
font-weight: var(--type-weight-bold);
|
|
779
|
+
font-size: var(--type-size-label);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.topbar {
|
|
783
|
+
display: flex; align-items: center; gap: var(--spacing-md);
|
|
784
|
+
height: var(--size-control-lg);
|
|
785
|
+
min-height: 52px;
|
|
786
|
+
padding-inline: var(--spacing-lg);
|
|
787
|
+
border-bottom: 1px solid var(--color-border);
|
|
788
|
+
background: var(--color-background);
|
|
789
|
+
position: sticky; top: 0; z-index: 20;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.avatar {
|
|
793
|
+
display: grid; place-items: center;
|
|
794
|
+
width: var(--size-control-sm); height: var(--size-control-sm);
|
|
795
|
+
flex: none;
|
|
796
|
+
border-radius: var(--radius-full);
|
|
797
|
+
background: var(--color-secondary);
|
|
798
|
+
color: var(--color-secondary-foreground);
|
|
799
|
+
font-size: var(--type-size-metadata);
|
|
800
|
+
font-weight: var(--type-weight-medium);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.page { padding: var(--spacing-xl); display: grid; gap: var(--spacing-xl); align-content: start; max-width: var(--size-content-max); }
|
|
804
|
+
.page-header { display: grid; gap: var(--spacing-2xs); }
|
|
805
|
+
|
|
806
|
+
/* ===========================================================================
|
|
807
|
+
Developer-oriented surfaces (Kiso: developer-oriented-interfaces pattern)
|
|
808
|
+
=========================================================================== */
|
|
809
|
+
code {
|
|
810
|
+
padding: 1px var(--spacing-xs);
|
|
811
|
+
border-radius: var(--radius-xs);
|
|
812
|
+
background: var(--color-muted);
|
|
813
|
+
border: 1px solid var(--color-border);
|
|
814
|
+
}
|
|
815
|
+
pre {
|
|
816
|
+
position: relative;
|
|
817
|
+
margin: 0;
|
|
818
|
+
padding: var(--spacing-md);
|
|
819
|
+
border: 1px solid var(--color-border);
|
|
820
|
+
border-radius: var(--radius-surface);
|
|
821
|
+
background: var(--color-muted);
|
|
822
|
+
/* The frame no longer scrolls, so it needs an explicit floor to stay
|
|
823
|
+
inside a grid or flex parent when a code line is long. */
|
|
824
|
+
min-inline-size: 0;
|
|
825
|
+
font-size: var(--type-size-label);
|
|
826
|
+
line-height: var(--type-line-height-relaxed);
|
|
827
|
+
}
|
|
828
|
+
/* Scrolling sits on the inner element, not on `pre`. A scroll container
|
|
829
|
+
scrolls for its own corner marks, which are absolutely positioned outside
|
|
830
|
+
the frame — the same reason `.table-scroll` exists. Write `<pre><code>`. */
|
|
831
|
+
pre code { display: block; overflow-x: auto; padding: 0; background: none; border: 0; }
|
|
832
|
+
|
|
833
|
+
.kv { display: grid; grid-template-columns: minmax(96px, auto) minmax(0, 1fr); gap: var(--spacing-sm) var(--spacing-lg); font-size: var(--type-size-body); }
|
|
834
|
+
.kv dt { color: var(--color-muted-foreground); font-size: var(--type-size-label); }
|
|
835
|
+
.kv dd { margin: 0; font-family: var(--font-mono); font-size: var(--type-size-label); overflow-wrap: anywhere; }
|
|
836
|
+
|
|
837
|
+
.logview {
|
|
838
|
+
position: relative;
|
|
839
|
+
display: flex;
|
|
840
|
+
flex-direction: column;
|
|
841
|
+
background: var(--color-neutral-950);
|
|
842
|
+
color: var(--color-neutral-300);
|
|
843
|
+
border: 1px solid var(--color-border);
|
|
844
|
+
border-radius: var(--radius-surface);
|
|
845
|
+
padding: var(--spacing-md);
|
|
846
|
+
min-inline-size: 0;
|
|
847
|
+
font-family: var(--font-mono);
|
|
848
|
+
font-size: var(--type-size-label);
|
|
849
|
+
line-height: var(--type-line-height-relaxed);
|
|
850
|
+
}
|
|
851
|
+
/* The frame is `.logview`, the scroller is `.log-scroll` inside it — same
|
|
852
|
+
split as `.table-wrap` / `.table-scroll`, and for the same reason: a scroll
|
|
853
|
+
container scrolls for the corner marks it draws just outside its own frame.
|
|
854
|
+
Height goes on `.logview`; the scroller takes what is left. */
|
|
855
|
+
.logview > .log-scroll { flex: 1 1 auto; min-block-size: 0; overflow: auto; }
|
|
856
|
+
.logview .log-time { color: var(--color-neutral-600); margin-inline-end: var(--spacing-sm); }
|
|
857
|
+
.logview .log-warn { color: var(--color-warning-on-dark); }
|
|
858
|
+
.logview .log-error{ color: var(--color-danger-on-dark); }
|
|
859
|
+
.logview .log-info { color: var(--color-info-on-dark); }
|
|
860
|
+
|
|
861
|
+
/* Sparkline / bar chart placeholders, neutral-only per the brand rules. */
|
|
862
|
+
.bars { display: flex; align-items: flex-end; gap: 3px; height: 40px; }
|
|
863
|
+
.bars i { flex: 1; background: var(--color-border-strong); border-radius: var(--radius-xs); }
|
|
864
|
+
.bars i.on { background: var(--color-primary); }
|
|
865
|
+
|
|
866
|
+
/* ===========================================================================
|
|
867
|
+
Touch targets: the 44px rule applies to coarse pointers only.
|
|
868
|
+
This is the whole reason the desktop UI can be 36px.
|
|
869
|
+
=========================================================================== */
|
|
870
|
+
@media (pointer: coarse) {
|
|
871
|
+
.btn, .input, .select, .nav-item, .menu-item {
|
|
872
|
+
min-height: var(--size-touch-min);
|
|
873
|
+
}
|
|
874
|
+
.btn-icon { min-width: var(--size-touch-min); }
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
@media (max-width: 1023px) {
|
|
878
|
+
.app-shell { grid-template-columns: 1fr; }
|
|
879
|
+
.sidebar { display: none; }
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
/* ===========================================================================
|
|
884
|
+
Corner marks — the "blueprint" corner treatment
|
|
885
|
+
---------------------------------------------------------------------------
|
|
886
|
+
An open registration mark on each corner: two 1px ticks, each lying along
|
|
887
|
+
the frame line it extends and stopping `--corner-mark-gap` short of it, so
|
|
888
|
+
the mark points at the corner without touching it.
|
|
889
|
+
|
|
890
|
+
Deliberately two ticks and not four. A full cross puts its other two arms
|
|
891
|
+
directly on top of the panel border, where they are invisible — the mark
|
|
892
|
+
read as a bracket anyway. Dropping them halves the paint and makes the
|
|
893
|
+
hollow centre explicit instead of accidental.
|
|
894
|
+
|
|
895
|
+
Eight 1px gradient bars on one pseudo-element. No markup, no images, and
|
|
896
|
+
`--corner-mark: 0` removes them entirely.
|
|
897
|
+
=========================================================================== */
|
|
898
|
+
.card::after,
|
|
899
|
+
.dialog::after,
|
|
900
|
+
.table-wrap::after,
|
|
901
|
+
.logview::after,
|
|
902
|
+
.marked::after,
|
|
903
|
+
pre::after {
|
|
904
|
+
content: "";
|
|
905
|
+
position: absolute;
|
|
906
|
+
/* Reach far enough out that a tick clears the frame by the gap. */
|
|
907
|
+
inset: calc(-1 * (var(--corner-mark-tick) + var(--corner-mark-gap)));
|
|
908
|
+
pointer-events: none;
|
|
909
|
+
opacity: var(--corner-mark);
|
|
910
|
+
background-repeat: no-repeat;
|
|
911
|
+
background-image:
|
|
912
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark)),
|
|
913
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark)),
|
|
914
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark)),
|
|
915
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark)),
|
|
916
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark)),
|
|
917
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark)),
|
|
918
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark)),
|
|
919
|
+
linear-gradient(var(--color-corner-mark), var(--color-corner-mark));
|
|
920
|
+
background-size:
|
|
921
|
+
var(--corner-mark-tick) 1px, 1px var(--corner-mark-tick),
|
|
922
|
+
var(--corner-mark-tick) 1px, 1px var(--corner-mark-tick),
|
|
923
|
+
var(--corner-mark-tick) 1px, 1px var(--corner-mark-tick),
|
|
924
|
+
var(--corner-mark-tick) 1px, 1px var(--corner-mark-tick);
|
|
925
|
+
/* The offset lands each tick exactly on the 1px frame line it extends. */
|
|
926
|
+
background-position:
|
|
927
|
+
left 0 top calc(var(--corner-mark-tick) + var(--corner-mark-gap)),
|
|
928
|
+
left calc(var(--corner-mark-tick) + var(--corner-mark-gap)) top 0,
|
|
929
|
+
right 0 top calc(var(--corner-mark-tick) + var(--corner-mark-gap)),
|
|
930
|
+
right calc(var(--corner-mark-tick) + var(--corner-mark-gap)) top 0,
|
|
931
|
+
left 0 bottom calc(var(--corner-mark-tick) + var(--corner-mark-gap)),
|
|
932
|
+
left calc(var(--corner-mark-tick) + var(--corner-mark-gap)) bottom 0,
|
|
933
|
+
right 0 bottom calc(var(--corner-mark-tick) + var(--corner-mark-gap)),
|
|
934
|
+
right calc(var(--corner-mark-tick) + var(--corner-mark-gap)) bottom 0;
|
|
935
|
+
}
|
|
936
|
+
.marked { position: relative; }
|
|
937
|
+
|
|
938
|
+
/* ===========================================================================
|
|
939
|
+
Hatch — "this region is not data"
|
|
940
|
+
---------------------------------------------------------------------------
|
|
941
|
+
Companion to the corner marks. Three sanctioned uses, and no others:
|
|
942
|
+
.hatch on an empty state nothing here yet
|
|
943
|
+
.hatch on a chrome band this strip is title/controls, not content
|
|
944
|
+
.hatch on an unavailable pane the data does not exist right now
|
|
945
|
+
|
|
946
|
+
The stripe colour is one step off the surface it sits on, so text stays
|
|
947
|
+
legible on top of it. Do not raise the contrast to make it "read better" —
|
|
948
|
+
if it is loud enough to notice while reading, it is in the wrong place.
|
|
949
|
+
=========================================================================== */
|
|
950
|
+
.hatch {
|
|
951
|
+
background-image: repeating-linear-gradient(
|
|
952
|
+
var(--hatch-angle),
|
|
953
|
+
var(--color-hatch) 0,
|
|
954
|
+
var(--color-hatch) var(--hatch-line),
|
|
955
|
+
transparent var(--hatch-line),
|
|
956
|
+
transparent var(--hatch-period)
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
/* An empty region *is* the message, so empty states are the primary use. */
|
|
960
|
+
.empty.hatch { background-color: var(--color-card); }
|
|
961
|
+
|
|
962
|
+
/* A chrome band inside a panel — title, breadcrumb, controls. */
|
|
963
|
+
.band {
|
|
964
|
+
padding: var(--spacing-lg);
|
|
965
|
+
border-bottom: 1px solid var(--color-border);
|
|
966
|
+
}
|
|
967
|
+
.band > * { position: relative; }
|
|
968
|
+
|
|
969
|
+
/* ===========================================================================
|
|
970
|
+
Split pane (list-detail with a resizable divider)
|
|
971
|
+
=========================================================================== */
|
|
972
|
+
.split { display: flex; min-width: 0; }
|
|
973
|
+
.split > .pane { min-width: 0; overflow: auto; }
|
|
974
|
+
.splitter { flex: none; width: 1px; background: var(--color-border); cursor: col-resize; position: relative; }
|
|
975
|
+
.splitter::after { content: ""; position: absolute; inset-block: 0; inset-inline: -3px; }
|
|
976
|
+
.splitter:hover, .splitter.dragging { background: var(--color-ring); }
|
|
977
|
+
|
|
978
|
+
/* Chart frame: neutral only, gridlines at border strength. */
|
|
979
|
+
.chart { width: 100%; height: 200px; display: block; overflow: visible; color: var(--color-primary); }
|
|
980
|
+
.chart .grid { stroke: var(--color-border); stroke-width: 1; }
|
|
981
|
+
.chart .line { fill: none; stroke: var(--color-primary); stroke-width: 1.5; }
|
|
982
|
+
.chart .area { fill: url(#chart-fill); }
|
|
983
|
+
.chart-axis { display: flex; justify-content: space-between; font-size: var(--type-size-metadata); color: var(--color-subtle-foreground); margin-block-start: var(--spacing-sm); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momoi-labs/kiso",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Kiso design-system contracts and generated design tokens",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"exports": {
|
|
17
17
|
"./tokens.css": "./tokens/build/tokens.css",
|
|
18
|
+
"./ui.css": "./kiso/ui.css",
|
|
18
19
|
"./tokens.json": "./tokens/build/tokens.json",
|
|
19
20
|
"./tokens.scss": "./tokens/build/tokens.scss",
|
|
20
21
|
"./tokens.d.ts": "./tokens/build/tokens.d.ts",
|