@kouji-ui/components 0.9.1 → 0.9.3
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/fesm2022/kouji-ui-components.mjs +137 -65
- package/fesm2022/kouji-ui-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/accordion/accordion.css +68 -0
- package/src/action-sheet/action-sheet.css +79 -0
- package/src/alert/alert.css +133 -0
- package/src/avatar/avatar.css +93 -0
- package/src/badge/badge.css +59 -0
- package/src/breadcrumb/breadcrumb.css +145 -0
- package/src/button/button.css +295 -0
- package/src/button-group/button-group.css +114 -0
- package/src/calendar/calendar.css +129 -0
- package/src/card/card.css +120 -0
- package/src/carousel/carousel.css +181 -0
- package/src/cascade-select/cascade-select.css +120 -0
- package/src/chart/chart.css +26 -0
- package/src/chat/chat-ai.css +352 -0
- package/src/chat/chat.css +231 -0
- package/src/checkbox/checkbox.css +74 -0
- package/src/color-picker/color-picker.css +183 -0
- package/src/combobox/combobox.css +101 -0
- package/src/command-palette/command-palette.css +234 -0
- package/src/confirm-popup/confirm-popup.css +47 -0
- package/src/date-picker/date-picker.css +61 -0
- package/src/date-range-presets/date-range-presets.css +49 -0
- package/src/datetime-picker/datetime-picker.css +84 -0
- package/src/dialog/dialog.css +56 -0
- package/src/direction-toggle/direction-toggle.css +55 -0
- package/src/divider/divider.css +93 -0
- package/src/drawer/drawer.css +130 -0
- package/src/dropdown-menu/dropdown-menu.css +108 -0
- package/src/editor/editor.css +140 -0
- package/src/empty-state/empty-state.css +114 -0
- package/src/field/field.css +148 -0
- package/src/file-upload/file-upload.css +207 -0
- package/src/form/form.css +63 -0
- package/src/input/input.css +70 -0
- package/src/input-group/input-group.css +125 -0
- package/src/input-mask/input-mask.css +39 -0
- package/src/input-otp/input-otp.css +87 -0
- package/src/kbd/kbd.css +67 -0
- package/src/link/link.css +104 -0
- package/src/list/list.css +166 -0
- package/src/menubar/menubar.css +62 -0
- package/src/number-input/number-input.css +115 -0
- package/src/overflow/overflow.css +43 -0
- package/src/overlay/overlay.css +45 -0
- package/src/overlay-badge/overlay-badge.css +118 -0
- package/src/pagination/pagination.css +146 -0
- package/src/password-input/password-input.css +155 -0
- package/src/popover/popover.css +71 -0
- package/src/progress-bar/progress-bar.css +113 -0
- package/src/radio/radio.css +58 -0
- package/src/rich-text/rich-text-editor.css +351 -0
- package/src/select/select.css +91 -0
- package/src/sheet/sheet.css +121 -0
- package/src/skeleton/skeleton.css +111 -0
- package/src/skip-link/skip-link.css +58 -0
- package/src/slider/slider.css +172 -0
- package/src/speed-dial/speed-dial.css +137 -0
- package/src/spinner/spinner.css +161 -0
- package/src/stepper/stepper.css +147 -0
- package/src/table/table-filters/filters.css +53 -0
- package/src/table/table.css +509 -0
- package/src/tabs/tabs.css +162 -0
- package/src/tag/tag.css +183 -0
- package/src/textarea/textarea.css +117 -0
- package/src/time-picker/time-picker.css +126 -0
- package/src/toast/toast.css +115 -0
- package/src/toggle/toggle.css +123 -0
- package/src/tooltip/tooltip.css +61 -0
- package/src/tree-select/tree-select.css +220 -0
- package/types/kouji-ui-components.d.ts +17 -1
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/* ─────────────────────────────────────────────────────────────
|
|
2
|
+
kj-rich-text-editor — component-layer tokens + structural CSS.
|
|
3
|
+
Content nodes are rendered by Lexical (ViewEncapsulation.None),
|
|
4
|
+
so every selector is namespaced under `.kj-rte`.
|
|
5
|
+
──────────────────────────────────────────────────────────── */
|
|
6
|
+
|
|
7
|
+
@layer kj.component {
|
|
8
|
+
.kj-rte {
|
|
9
|
+
/* component knobs — themes/users can override any of these */
|
|
10
|
+
--kj-rte-bg: var(--kj-bg-field);
|
|
11
|
+
--kj-rte-fg: var(--kj-fg-default);
|
|
12
|
+
--kj-rte-muted-fg: var(--kj-fg-muted);
|
|
13
|
+
--kj-rte-border-color: var(--kj-border-default);
|
|
14
|
+
--kj-rte-radius: var(--kj-radius-field);
|
|
15
|
+
--kj-rte-toolbar-bg: var(--kj-bg-surface, var(--kj-bg-body));
|
|
16
|
+
--kj-rte-btn-size: 2.75rem; /* 44px — WCAG 2.5.5 AAA touch target */
|
|
17
|
+
--kj-rte-accent: var(--kj-bg-primary);
|
|
18
|
+
--kj-rte-accent-fg: var(--kj-fg-on-primary);
|
|
19
|
+
--kj-rte-min-height: 12rem;
|
|
20
|
+
--kj-rte-pad: var(--kj-space-md);
|
|
21
|
+
--kj-rte-font: var(--kj-font-sans);
|
|
22
|
+
--kj-rte-mono: var(--kj-font-mono, ui-monospace, monospace);
|
|
23
|
+
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
/* Fluid: fill the container and never exceed it. `min-width: 0` lets the
|
|
27
|
+
editor shrink below its content's intrinsic width inside flex/grid
|
|
28
|
+
parents (the docs playground stage, app layouts) so the toolbar scrolls
|
|
29
|
+
instead of the whole editor overflowing its container. */
|
|
30
|
+
width: 100%;
|
|
31
|
+
max-width: 100%;
|
|
32
|
+
min-width: 0;
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
background: var(--kj-rte-bg);
|
|
35
|
+
color: var(--kj-rte-fg);
|
|
36
|
+
border: var(--kj-border) solid var(--kj-rte-border-color);
|
|
37
|
+
border-radius: var(--kj-rte-radius);
|
|
38
|
+
font-family: var(--kj-rte-font);
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.kj-rte:focus-within {
|
|
43
|
+
outline: 2px solid var(--kj-rte-accent);
|
|
44
|
+
outline-offset: 1px;
|
|
45
|
+
border-color: var(--kj-border-focus, var(--kj-rte-accent));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* -- toolbar ----------------------------------------------------------- */
|
|
49
|
+
|
|
50
|
+
.kj-rte__toolbar {
|
|
51
|
+
display: flex;
|
|
52
|
+
/* Single horizontal row that scrolls on overflow rather than wrapping into
|
|
53
|
+
a tall, chunked block in narrow containers. All buttons stay keyboard-
|
|
54
|
+
reachable (roving tabindex) and scroll into view on focus. */
|
|
55
|
+
flex-wrap: nowrap;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: var(--kj-space-xs, 0.25rem);
|
|
58
|
+
padding: var(--kj-space-sm);
|
|
59
|
+
background: var(--kj-rte-toolbar-bg);
|
|
60
|
+
border-bottom: var(--kj-border) solid var(--kj-rte-border-color);
|
|
61
|
+
overflow-x: auto;
|
|
62
|
+
overflow-y: hidden;
|
|
63
|
+
scrollbar-width: thin;
|
|
64
|
+
scrollbar-color: var(--kj-rte-border-color) transparent;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Subtle, unobtrusive scrollbar for the overflowing toolbar (WebKit). */
|
|
68
|
+
.kj-rte__toolbar::-webkit-scrollbar {
|
|
69
|
+
height: 0.5rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.kj-rte__toolbar::-webkit-scrollbar-thumb {
|
|
73
|
+
background: var(--kj-rte-border-color);
|
|
74
|
+
border-radius: 0.25rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.kj-rte__toolbar::-webkit-scrollbar-track {
|
|
78
|
+
background: transparent;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.kj-rte__group {
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: var(--kj-space-xs, 0.25rem);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Keep buttons, groups and separators at their natural size so the row
|
|
88
|
+
scrolls instead of squashing them when space is tight. */
|
|
89
|
+
.kj-rte__group,
|
|
90
|
+
.kj-rte__sep,
|
|
91
|
+
.kj-rte__btn {
|
|
92
|
+
flex: 0 0 auto;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.kj-rte__sep {
|
|
96
|
+
width: 1px;
|
|
97
|
+
align-self: stretch;
|
|
98
|
+
min-height: 1.5rem;
|
|
99
|
+
margin: 0 var(--kj-space-xs, 0.25rem);
|
|
100
|
+
background: var(--kj-rte-border-color);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.kj-rte__btn {
|
|
104
|
+
display: inline-flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
justify-content: center;
|
|
107
|
+
min-width: var(--kj-rte-btn-size);
|
|
108
|
+
min-height: var(--kj-rte-btn-size);
|
|
109
|
+
padding: 0 var(--kj-space-sm);
|
|
110
|
+
color: var(--kj-rte-fg);
|
|
111
|
+
background: transparent;
|
|
112
|
+
border: var(--kj-border) solid transparent;
|
|
113
|
+
border-radius: var(--kj-radius-field);
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
transition: var(--kj-transition, background-color 0.15s ease, color 0.15s ease);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.kj-rte__btn:hover:not(:disabled) {
|
|
119
|
+
background: var(--kj-bg-subtle, var(--kj-bg-body));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.kj-rte__btn:focus-visible {
|
|
123
|
+
outline: 2px solid var(--kj-rte-accent);
|
|
124
|
+
outline-offset: 2px;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.kj-rte__btn--active {
|
|
128
|
+
background: color-mix(in srgb, var(--kj-rte-accent) 16%, transparent);
|
|
129
|
+
border-color: color-mix(in srgb, var(--kj-rte-accent) 40%, transparent);
|
|
130
|
+
color: var(--kj-rte-accent);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.kj-rte__btn:disabled {
|
|
134
|
+
opacity: 0.45;
|
|
135
|
+
cursor: not-allowed;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* -- feature overlays (link / image editors) -------------------------- */
|
|
139
|
+
|
|
140
|
+
.kj-rte__overlay {
|
|
141
|
+
position: relative;
|
|
142
|
+
z-index: 1;
|
|
143
|
+
padding: var(--kj-space-sm) var(--kj-rte-pad);
|
|
144
|
+
background: var(--kj-rte-toolbar-bg);
|
|
145
|
+
border-bottom: var(--kj-border) solid var(--kj-rte-border-color);
|
|
146
|
+
box-shadow: 0 6px 16px rgb(0 0 0 / 12%);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.kj-rte__overlay-form {
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-wrap: wrap;
|
|
152
|
+
align-items: center;
|
|
153
|
+
gap: var(--kj-space-sm);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.kj-rte__overlay-label {
|
|
157
|
+
font-size: var(--kj-text-sm);
|
|
158
|
+
color: var(--kj-rte-muted-fg);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.kj-rte__overlay-input {
|
|
162
|
+
flex: 1 1 12rem;
|
|
163
|
+
min-height: 2.25rem;
|
|
164
|
+
padding: var(--kj-space-sm) var(--kj-space-md);
|
|
165
|
+
color: var(--kj-rte-fg);
|
|
166
|
+
background: var(--kj-bg-field);
|
|
167
|
+
border: var(--kj-border) solid var(--kj-rte-border-color);
|
|
168
|
+
border-radius: var(--kj-radius-field);
|
|
169
|
+
font: var(--kj-text-sm) / 1.4 var(--kj-rte-font);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.kj-rte__overlay-input:focus-visible {
|
|
173
|
+
outline: 2px solid var(--kj-rte-accent);
|
|
174
|
+
outline-offset: 1px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.kj-rte__overlay-actions {
|
|
178
|
+
display: flex;
|
|
179
|
+
gap: var(--kj-space-sm);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.kj-rte__overlay-btn {
|
|
183
|
+
min-height: 2.25rem;
|
|
184
|
+
padding: 0 var(--kj-space-md);
|
|
185
|
+
color: var(--kj-rte-fg);
|
|
186
|
+
background: transparent;
|
|
187
|
+
border: var(--kj-border) solid var(--kj-rte-border-color);
|
|
188
|
+
border-radius: var(--kj-radius-field);
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.kj-rte__overlay-btn:hover:not(:disabled) {
|
|
193
|
+
background: var(--kj-bg-subtle, var(--kj-bg-body));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.kj-rte__overlay-btn:focus-visible {
|
|
197
|
+
outline: 2px solid var(--kj-rte-accent);
|
|
198
|
+
outline-offset: 2px;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.kj-rte__overlay-btn:disabled {
|
|
202
|
+
opacity: 0.45;
|
|
203
|
+
cursor: not-allowed;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* -- editable surface -------------------------------------------------- */
|
|
207
|
+
|
|
208
|
+
.kj-rte__content {
|
|
209
|
+
flex: 1 1 auto;
|
|
210
|
+
min-width: 0;
|
|
211
|
+
min-height: var(--kj-rte-min-height);
|
|
212
|
+
padding: var(--kj-rte-pad);
|
|
213
|
+
outline: none;
|
|
214
|
+
overflow-y: auto;
|
|
215
|
+
overflow-wrap: anywhere;
|
|
216
|
+
line-height: 1.6;
|
|
217
|
+
color: var(--kj-rte-fg);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.kj-rte__content[data-empty][data-placeholder]::before {
|
|
221
|
+
content: attr(data-placeholder);
|
|
222
|
+
color: var(--kj-rte-muted-fg);
|
|
223
|
+
pointer-events: none;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* screen-reader-only live region */
|
|
227
|
+
.kj-rte__sr {
|
|
228
|
+
position: absolute;
|
|
229
|
+
width: 1px;
|
|
230
|
+
height: 1px;
|
|
231
|
+
padding: 0;
|
|
232
|
+
margin: -1px;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
clip: rect(0, 0, 0, 0);
|
|
235
|
+
white-space: nowrap;
|
|
236
|
+
border: 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* -- Lexical content node typography ---------------------------------- */
|
|
240
|
+
|
|
241
|
+
.kj-rte .kj-rte-paragraph {
|
|
242
|
+
margin: 0 0 var(--kj-space-sm);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.kj-rte .kj-rte-h1 {
|
|
246
|
+
margin: 0 0 var(--kj-space-sm);
|
|
247
|
+
font-size: var(--kj-text-2xl, 1.75rem);
|
|
248
|
+
font-weight: 700;
|
|
249
|
+
line-height: 1.25;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.kj-rte .kj-rte-h2 {
|
|
253
|
+
margin: 0 0 var(--kj-space-sm);
|
|
254
|
+
font-size: var(--kj-text-xl, 1.4rem);
|
|
255
|
+
font-weight: 700;
|
|
256
|
+
line-height: 1.3;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.kj-rte .kj-rte-h3 {
|
|
260
|
+
margin: 0 0 var(--kj-space-sm);
|
|
261
|
+
font-size: var(--kj-text-lg, 1.2rem);
|
|
262
|
+
font-weight: 600;
|
|
263
|
+
line-height: 1.35;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.kj-rte .kj-rte-ul,
|
|
267
|
+
.kj-rte .kj-rte-ol {
|
|
268
|
+
margin: 0 0 var(--kj-space-sm);
|
|
269
|
+
padding-left: 1.5rem;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.kj-rte .kj-rte-ul {
|
|
273
|
+
list-style: disc;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.kj-rte .kj-rte-ol {
|
|
277
|
+
list-style: decimal;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.kj-rte .kj-rte-li {
|
|
281
|
+
margin-bottom: 0.25rem;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.kj-rte .kj-rte-quote {
|
|
285
|
+
margin: 0 0 var(--kj-space-sm);
|
|
286
|
+
padding-left: var(--kj-space-md);
|
|
287
|
+
border-left: 3px solid var(--kj-rte-border-color);
|
|
288
|
+
color: var(--kj-rte-muted-fg);
|
|
289
|
+
font-style: italic;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.kj-rte .kj-rte-link {
|
|
293
|
+
color: var(--kj-rte-accent);
|
|
294
|
+
text-decoration: underline;
|
|
295
|
+
text-underline-offset: 2px;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.kj-rte .kj-rte-bold {
|
|
299
|
+
font-weight: 700;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.kj-rte .kj-rte-italic {
|
|
303
|
+
font-style: italic;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.kj-rte .kj-rte-underline {
|
|
307
|
+
text-decoration: underline;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.kj-rte .kj-rte-strikethrough {
|
|
311
|
+
text-decoration: line-through;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.kj-rte .kj-rte-code {
|
|
315
|
+
padding: 0.1em 0.35em;
|
|
316
|
+
background: var(--kj-bg-subtle, var(--kj-bg-body));
|
|
317
|
+
border-radius: 0.25rem;
|
|
318
|
+
font-family: var(--kj-rte-mono);
|
|
319
|
+
font-size: 0.9em;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.kj-rte .kj-rte-code-block {
|
|
323
|
+
display: block;
|
|
324
|
+
margin: 0 0 var(--kj-space-sm);
|
|
325
|
+
padding: var(--kj-space-md);
|
|
326
|
+
background: var(--kj-bg-subtle, var(--kj-bg-body));
|
|
327
|
+
border-radius: var(--kj-radius-field);
|
|
328
|
+
font-family: var(--kj-rte-mono);
|
|
329
|
+
font-size: 0.9em;
|
|
330
|
+
white-space: pre-wrap;
|
|
331
|
+
overflow-x: auto;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.kj-rte .kj-rte-image {
|
|
335
|
+
margin: var(--kj-space-sm) 0;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.kj-rte .kj-rte-image img {
|
|
339
|
+
max-width: 100%;
|
|
340
|
+
height: auto;
|
|
341
|
+
border-radius: var(--kj-radius-field);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* Respect reduced-motion preferences. */
|
|
345
|
+
@media (prefers-reduced-motion: reduce) {
|
|
346
|
+
.kj-rte__btn,
|
|
347
|
+
.kj-rte {
|
|
348
|
+
transition: none;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
@layer kj.component {
|
|
2
|
+
.kj-select { position: relative; display: inline-block; min-width: 12rem; }
|
|
3
|
+
|
|
4
|
+
.kj-select-trigger {
|
|
5
|
+
/* Explicit height contract (matches input.css / button.css) so the
|
|
6
|
+
select trigger lines up with sibling inputs in a form row. */
|
|
7
|
+
--kj-select-trigger-height: 2.25rem;
|
|
8
|
+
width: 100%;
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
gap: var(--kj-space-sm);
|
|
13
|
+
background: var(--kj-bg-field);
|
|
14
|
+
color: var(--kj-fg-default);
|
|
15
|
+
border: var(--kj-border) solid var(--kj-border-default);
|
|
16
|
+
border-radius: var(--kj-radius-field);
|
|
17
|
+
padding: 0 var(--kj-space-md);
|
|
18
|
+
height: var(--kj-select-trigger-height);
|
|
19
|
+
font: 0.875rem var(--kj-font-sans);
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
/* A flex item defaults to min-width:auto, so a long label could push the
|
|
22
|
+
trigger past its column instead of clipping inside it. */
|
|
23
|
+
min-width: 0;
|
|
24
|
+
}
|
|
25
|
+
/* One line, always. The trigger has a fixed height contract, so a label that
|
|
26
|
+
wrapped overflowed the button; a long branch/path name now truncates with
|
|
27
|
+
an ellipsis and the caret keeps its place at the end. */
|
|
28
|
+
.kj-select-trigger-label {
|
|
29
|
+
flex: 1 1 auto;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
text-overflow: ellipsis;
|
|
33
|
+
white-space: nowrap;
|
|
34
|
+
text-align: left;
|
|
35
|
+
}
|
|
36
|
+
.kj-select[data-size="xs"] .kj-select-trigger { --kj-select-trigger-height: 1.75rem; font-size: 0.75rem; padding-inline: var(--kj-space-sm); gap: var(--kj-space-xs); }
|
|
37
|
+
.kj-select[data-size="sm"] .kj-select-trigger { --kj-select-trigger-height: 2rem; font-size: 0.8125rem; }
|
|
38
|
+
.kj-select[data-size="lg"] .kj-select-trigger { --kj-select-trigger-height: 2.75rem; font-size: 1rem; }
|
|
39
|
+
.kj-select[data-size="xs"] { min-width: 0; }
|
|
40
|
+
.kj-select-trigger-caret {
|
|
41
|
+
font-size: 0.875em;
|
|
42
|
+
color: var(--kj-fg-muted);
|
|
43
|
+
flex: none;
|
|
44
|
+
}
|
|
45
|
+
.kj-select-trigger:focus-visible { outline: 2px solid var(--kj-fg-primary); outline-offset: 2px; }
|
|
46
|
+
.kj-select[data-disabled] .kj-select-trigger { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
|
|
47
|
+
|
|
48
|
+
.kj-select-content {
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
min-width: 12rem;
|
|
51
|
+
width: max-content;
|
|
52
|
+
/* max-content alone let one long option make the whole panel as wide as
|
|
53
|
+
that option; the cap keeps the listbox near the trigger's width class
|
|
54
|
+
and inside the viewport, with rows truncating instead. */
|
|
55
|
+
max-width: min(28rem, calc(100vw - 2rem));
|
|
56
|
+
background: var(--kj-bg-elevated);
|
|
57
|
+
border: var(--kj-border) solid var(--kj-border-default);
|
|
58
|
+
border-radius: var(--kj-radius-field);
|
|
59
|
+
box-shadow: var(--kj-shadow-md, 0 8px 24px rgb(0 0 0 / 0.12));
|
|
60
|
+
padding: 4px;
|
|
61
|
+
max-height: 16rem;
|
|
62
|
+
overflow: auto;
|
|
63
|
+
z-index: var(--kj-overlay-z, 100);
|
|
64
|
+
}
|
|
65
|
+
.kj-select-content[hidden] { display: none; }
|
|
66
|
+
|
|
67
|
+
.kj-option {
|
|
68
|
+
padding: var(--kj-space-sm) var(--kj-space-md);
|
|
69
|
+
border-radius: var(--kj-radius-selector);
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
color: var(--kj-fg-default);
|
|
72
|
+
font: 0.875rem var(--kj-font-sans);
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
text-overflow: ellipsis;
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
}
|
|
77
|
+
/* Hover uses bg-field (sunken slot), not bg-surface. In themes where
|
|
78
|
+
elevated == surface (light/corporate, both white) a bg-surface hover would
|
|
79
|
+
be invisible on the bg-elevated panel. */
|
|
80
|
+
.kj-option:hover { background: var(--kj-bg-field); }
|
|
81
|
+
/* Selected option — tinted primary mirrors tree-select / cascade-select so
|
|
82
|
+
the listbox family looks consistent across themes. */
|
|
83
|
+
.kj-option[aria-selected="true"] {
|
|
84
|
+
background: color-mix(in oklab, var(--kj-bg-primary) 12%, transparent);
|
|
85
|
+
color: var(--kj-fg-primary);
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
}
|
|
88
|
+
.kj-option[aria-selected="true"]:hover {
|
|
89
|
+
background: color-mix(in oklab, var(--kj-bg-primary) 18%, transparent);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
@layer kj.component {
|
|
2
|
+
.kj-sheet {
|
|
3
|
+
--kj-sheet-bg: var(--kj-bg-elevated);
|
|
4
|
+
--kj-sheet-fg: var(--kj-fg-default);
|
|
5
|
+
--kj-sheet-shadow: var(--kj-shadow-lg);
|
|
6
|
+
--kj-sheet-radius: var(--kj-radius-box);
|
|
7
|
+
--kj-sheet-max-height: 92vh;
|
|
8
|
+
--kj-sheet-grip-color: var(--kj-border-strong);
|
|
9
|
+
|
|
10
|
+
position: fixed;
|
|
11
|
+
left: 0;
|
|
12
|
+
right: 0;
|
|
13
|
+
bottom: 0;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
width: 100%;
|
|
18
|
+
max-height: var(--kj-sheet-max-height);
|
|
19
|
+
margin-inline: auto;
|
|
20
|
+
background: var(--kj-sheet-bg);
|
|
21
|
+
color: var(--kj-sheet-fg);
|
|
22
|
+
box-shadow: var(--kj-sheet-shadow);
|
|
23
|
+
border-top-left-radius: var(--kj-sheet-radius);
|
|
24
|
+
border-top-right-radius: var(--kj-sheet-radius);
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
z-index: var(--kj-overlay-z, 1000);
|
|
27
|
+
/* Live drag follows the pointer; released via --kj-sheet-drag-offset. */
|
|
28
|
+
transform: translate3d(0, var(--kj-sheet-drag-offset, 0), 0);
|
|
29
|
+
/* Respect the iOS home-indicator / gesture inset. */
|
|
30
|
+
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Constrain width on large viewports so it reads as a sheet, not a bar. */
|
|
34
|
+
@media (min-width: 40rem) {
|
|
35
|
+
.kj-sheet {
|
|
36
|
+
max-width: 32rem;
|
|
37
|
+
border-bottom-left-radius: var(--kj-sheet-radius);
|
|
38
|
+
border-bottom-right-radius: var(--kj-sheet-radius);
|
|
39
|
+
bottom: var(--kj-space-lg);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.kj-sheet[data-kj-detent="half"] { height: 50vh; }
|
|
44
|
+
.kj-sheet[data-kj-detent="full"] { height: var(--kj-sheet-max-height); }
|
|
45
|
+
|
|
46
|
+
/* Grab handle — a ≥44px touch target with a small visible grip pill. */
|
|
47
|
+
.kj-sheet__handle {
|
|
48
|
+
appearance: none;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
justify-content: center;
|
|
52
|
+
width: 100%;
|
|
53
|
+
min-height: 44px;
|
|
54
|
+
padding: 0;
|
|
55
|
+
border: 0;
|
|
56
|
+
background: transparent;
|
|
57
|
+
cursor: grab;
|
|
58
|
+
flex-shrink: 0;
|
|
59
|
+
touch-action: none;
|
|
60
|
+
}
|
|
61
|
+
.kj-sheet[data-kj-dragging] .kj-sheet__handle { cursor: grabbing; }
|
|
62
|
+
|
|
63
|
+
.kj-sheet__grip {
|
|
64
|
+
display: block;
|
|
65
|
+
width: 2.25rem;
|
|
66
|
+
height: 0.3125rem;
|
|
67
|
+
border-radius: 999px;
|
|
68
|
+
background: var(--kj-sheet-grip-color);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.kj-sheet__handle:focus-visible {
|
|
72
|
+
outline: var(--kj-focus-ring-width, 2px) solid var(--kj-focus-ring-color, var(--kj-primary));
|
|
73
|
+
outline-offset: -4px;
|
|
74
|
+
border-radius: var(--kj-radius-sm);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.kj-sheet__content {
|
|
78
|
+
flex: 1;
|
|
79
|
+
min-height: 0;
|
|
80
|
+
overflow-y: auto;
|
|
81
|
+
padding: 0 var(--kj-space-xl) var(--kj-space-xl);
|
|
82
|
+
color: var(--kj-sheet-fg);
|
|
83
|
+
font: 0.9375rem / 1.5 var(--kj-font-sans);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
87
|
+
.kj-sheet:not([data-kj-dragging]) {
|
|
88
|
+
transition: transform 260ms cubic-bezier(0.32, 0.72, 0, 1);
|
|
89
|
+
}
|
|
90
|
+
.kj-sheet[data-state="open"] { transform: translate3d(0, var(--kj-sheet-drag-offset, 0), 0); }
|
|
91
|
+
.kj-sheet[data-state="closed"] { transform: translate3d(0, 100%, 0); }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@media (prefers-reduced-motion: reduce) {
|
|
95
|
+
.kj-sheet:not([data-kj-dragging]) {
|
|
96
|
+
transition: opacity 120ms linear;
|
|
97
|
+
}
|
|
98
|
+
.kj-sheet[data-state="open"] { opacity: 1; }
|
|
99
|
+
.kj-sheet[data-state="closed"] { opacity: 0; }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Header / title / footer helpers for sheet bodies (parallel to drawer). */
|
|
103
|
+
.kj-sheet__title {
|
|
104
|
+
display: block;
|
|
105
|
+
margin: 0 0 var(--kj-space-xs);
|
|
106
|
+
font: 600 1.125rem / 1.3 var(--kj-font-sans);
|
|
107
|
+
color: var(--kj-fg-default);
|
|
108
|
+
}
|
|
109
|
+
.kj-sheet__description {
|
|
110
|
+
display: block;
|
|
111
|
+
margin: 0 0 var(--kj-space-md);
|
|
112
|
+
color: var(--kj-fg-muted);
|
|
113
|
+
font: 0.875rem / 1.4 var(--kj-font-sans);
|
|
114
|
+
}
|
|
115
|
+
.kj-sheet__footer {
|
|
116
|
+
display: flex;
|
|
117
|
+
gap: var(--kj-space-sm);
|
|
118
|
+
padding-top: var(--kj-space-md);
|
|
119
|
+
}
|
|
120
|
+
.kj-sheet__footer[data-align="end"] { justify-content: flex-end; }
|
|
121
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* ─────────────────────────────────────────────────────────────
|
|
2
|
+
.kj-skeleton — component-layer tokens + structural CSS
|
|
3
|
+
The directive lands `aria-hidden="true"` and reflects
|
|
4
|
+
`data-shape` / `data-animation` on the host. CSS owns the
|
|
5
|
+
shimmer / pulse keyframes and the reduced-motion override —
|
|
6
|
+
per `skeleton.md`, the directive deliberately does *not*
|
|
7
|
+
detect `prefers-reduced-motion` itself; theme CSS does, here.
|
|
8
|
+
──────────────────────────────────────────────────────────── */
|
|
9
|
+
|
|
10
|
+
@layer kj.component {
|
|
11
|
+
.kj-skeleton {
|
|
12
|
+
/* component knobs — themes/users can override any of these */
|
|
13
|
+
--kj-skeleton-bg: var(--kj-bg-elevated, color-mix(in oklab, var(--kj-fg-default, #000) 12%, transparent));
|
|
14
|
+
--kj-skeleton-highlight: var(--kj-bg-surface, color-mix(in oklab, var(--kj-fg-default, #000) 5%, transparent));
|
|
15
|
+
--kj-skeleton-radius: var(--kj-radius-field, 0.375rem);
|
|
16
|
+
--kj-skeleton-line-height: 1em;
|
|
17
|
+
--kj-skeleton-line-radius: 0.25rem;
|
|
18
|
+
--kj-skeleton-shimmer-duration: 1.4s;
|
|
19
|
+
--kj-skeleton-pulse-duration: 1.6s;
|
|
20
|
+
|
|
21
|
+
display: block;
|
|
22
|
+
background-color: var(--kj-skeleton-bg);
|
|
23
|
+
border-radius: var(--kj-skeleton-radius);
|
|
24
|
+
/* Sized by consumer-supplied width/height. Default to a sensible
|
|
25
|
+
rectangle so a bare <kj-skeleton/> still paints something. */
|
|
26
|
+
inline-size: 100%;
|
|
27
|
+
block-size: 1rem;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
position: relative;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* ── Shape routing ──────────────────────────────────────────────────── */
|
|
33
|
+
|
|
34
|
+
.kj-skeleton[data-shape="circle"] {
|
|
35
|
+
border-radius: 50%;
|
|
36
|
+
aspect-ratio: 1 / 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.kj-skeleton[data-shape="text"],
|
|
40
|
+
.kj-skeleton.kj-skeleton--line {
|
|
41
|
+
block-size: var(--kj-skeleton-line-height);
|
|
42
|
+
border-radius: var(--kj-skeleton-line-radius);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* The text-block host is a layout container — it doesn't paint itself,
|
|
46
|
+
it stacks N child .kj-skeleton--line elements. Drop the default
|
|
47
|
+
paint chrome so only the lines are visible. */
|
|
48
|
+
.kj-skeleton[data-shape="text-block"] {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
gap: var(--kj-space-sm, 0.5rem);
|
|
52
|
+
background-color: transparent;
|
|
53
|
+
border-radius: 0;
|
|
54
|
+
inline-size: 100%;
|
|
55
|
+
block-size: auto;
|
|
56
|
+
overflow: visible;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ── Animation routing ──────────────────────────────────────────────── */
|
|
60
|
+
|
|
61
|
+
/* Shimmer: a moving highlight gradient sweeps left-to-right. */
|
|
62
|
+
.kj-skeleton[data-animation="shimmer"]::after {
|
|
63
|
+
content: "";
|
|
64
|
+
position: absolute;
|
|
65
|
+
inset: 0;
|
|
66
|
+
background: linear-gradient(
|
|
67
|
+
90deg,
|
|
68
|
+
transparent 0%,
|
|
69
|
+
var(--kj-skeleton-highlight) 50%,
|
|
70
|
+
transparent 100%
|
|
71
|
+
);
|
|
72
|
+
transform: translateX(-100%);
|
|
73
|
+
animation: kj-skeleton-shimmer var(--kj-skeleton-shimmer-duration) linear infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* The text-block host doesn't paint, so its ::after sweep would be a
|
|
77
|
+
ghost. Suppress it; each child line runs its own shimmer. */
|
|
78
|
+
.kj-skeleton[data-shape="text-block"][data-animation="shimmer"]::after {
|
|
79
|
+
content: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.kj-skeleton[data-animation="pulse"] {
|
|
83
|
+
animation: kj-skeleton-pulse var(--kj-skeleton-pulse-duration) ease-in-out infinite;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.kj-skeleton[data-animation="none"]::after {
|
|
87
|
+
content: none;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@keyframes kj-skeleton-shimmer {
|
|
91
|
+
0% { transform: translateX(-100%); }
|
|
92
|
+
100% { transform: translateX(100%); }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@keyframes kj-skeleton-pulse {
|
|
96
|
+
0%, 100% { opacity: 1; }
|
|
97
|
+
50% { opacity: 0.55; }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ── Reduced motion ─────────────────────────────────────────────────── */
|
|
101
|
+
/* Per skeleton.md: when the OS sets the preference, theme CSS pins the
|
|
102
|
+
animation off regardless of `kjSkeletonAnimation`. The directive
|
|
103
|
+
deliberately does not detect the media query — this rule is the
|
|
104
|
+
enforcement layer. */
|
|
105
|
+
@media (prefers-reduced-motion: reduce) {
|
|
106
|
+
.kj-skeleton,
|
|
107
|
+
.kj-skeleton::after {
|
|
108
|
+
animation: none !important;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|