@kolkrabbi/kol-theme 0.5.0 → 0.6.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/kol-color.css CHANGED
@@ -32,7 +32,7 @@
32
32
 
33
33
  --kol-accent-primary: var(--kol-surface-on-primary);
34
34
  --kol-accent-on-primary: var(--kol-surface-primary);
35
- --kol-accent-primary-strong: color-mix(in srgb, var(--kol-surface-on-primary) 80%, transparent);
35
+ --kol-accent-primary-strong: color-mix(in srgb, var(--kol-accent-primary) 80%, var(--kol-surface-primary));
36
36
 
37
37
  /* ---------------------------------------------------------------------------
38
38
  * UI STATE — error / warning / info / success.
@@ -34,7 +34,7 @@
34
34
  * [aria-disabled="true"] /
35
35
  * :disabled auto opacity-50 + pointer-events-none
36
36
  *
37
- * Single visible-border tone (--kol-fg-16). No hover/focus escalation,
37
+ * Single visible-border tone (--kol-oq-16, opaque — chrome law). No hover/focus escalation,
38
38
  * no `--bordered` modifier. When you want a ghost field to render
39
39
  * persistently visible (e.g. "is-edited" indicator), switch the variant
40
40
  * to `--outline` — same visual, clearer semantic.
@@ -68,12 +68,10 @@
68
68
  }
69
69
 
70
70
  .kol-control--ghost {
71
- /* Subtle resting bg so the input bounds are discoverable without
72
- * hovering. Theme-independent darken via fg-absolute-04 (4% black);
73
- * on dark theme this reads as a softly sunken input area, on light
74
- * theme as a faintly tinted field. Hover/focus then reveals the
75
- * border for affordance. */
76
- background-color: var(--kol-fg-absolute-04);
71
+ /* DEPRECATED (2026-07-08 chrome law) ghost folded into outline; Input
72
+ * aliases variant="ghost" → outline. Rule kept only for stray direct
73
+ * class consumers; do not use in new code. Opaque 4% black on surface. */
74
+ background-color: color-mix(in srgb, var(--kol-color-absolute-black) 4%, var(--kol-surface-primary));
77
75
  color: var(--kol-fg-meta);
78
76
  }
79
77
  .kol-control--ghost:hover,
@@ -85,14 +83,17 @@
85
83
  .kol-control--outline {
86
84
  background-color: transparent;
87
85
  color: var(--kol-surface-on-primary);
88
- border-color: var(--kol-fg-16);
86
+ border-color: var(--kol-oq-16);
89
87
  }
90
88
 
91
89
  /* Textarea modifier — flips the inline-flex shell to block so a multi-line
92
- * textarea takes full width. Resize is disabled (textarea is fixed at its
93
- * `rows` count, content scrolls). The kol resize-corner icon in the
94
- * bottom-right is decorative. Used by atoms/Textarea.jsx. */
95
- .kol-control--textarea {
90
+ * textarea takes full width. Vertical resize is real (2026-07-08): the
91
+ * resize-grip icon is the JS drag handle (both axes); native resize is
92
+ * off (a Firefox grip can't be hidden otherwise). atoms/Textarea.jsx. */
93
+ .kol-control.kol-control--textarea {
94
+ /* compound selector: must out-specify the .kol-control-{size} padding
95
+ * rules below, which otherwise win by order and double the padding
96
+ * (shell 6/16 + inner textarea 6/16). */
96
97
  display: block;
97
98
  position: relative;
98
99
  padding: 0; /* outer label is purely a wrapper; inner textarea owns padding */
@@ -102,13 +103,27 @@
102
103
  }
103
104
  .kol-control--textarea.kol-control-md > textarea { padding: 6px 16px; }
104
105
  .kol-control--textarea.kol-control-lg > textarea { padding: 8px 20px; }
106
+ /* The grip — kol-icon-set-v1 `resize-grip` at the native placement. It IS
107
+ * the resize handle (JS corner drag, both axes, in Textarea.jsx); native
108
+ * resize is OFF because Firefox's grip can't be hidden any other way. */
105
109
  .kol-textarea-resize-icon {
106
110
  position: absolute;
107
111
  bottom: 4px;
108
112
  right: 4px;
109
113
  display: inline-flex;
114
+ color: var(--kol-fg-48);
115
+ cursor: nwse-resize;
116
+ touch-action: none;
117
+ }
118
+ /* Micro-glyph: at size 10 the set's 1.5 keyline scales to 0.625px — mush.
119
+ * non-scaling-stroke pins the stroke at a crisp 1.5 CSS px regardless of
120
+ * render size; the set asset stays keyline-conform. */
121
+ .kol-textarea-resize-icon svg {
122
+ overflow: visible; /* round caps sit on the viewBox edge — don't clip them */
123
+ }
124
+ .kol-textarea-resize-icon svg path {
125
+ vector-effect: non-scaling-stroke;
110
126
  }
111
-
112
127
  /* ─── Sizes (padding only — type class applied via JSX) ─── */
113
128
 
114
129
  .kol-control-sm { padding: 4px 12px; }
@@ -160,14 +175,21 @@
160
175
  cursor: pointer;
161
176
  }
162
177
 
178
+ .kol-btn:focus-visible {
179
+ outline: 2px solid var(--kol-focus-ring);
180
+ outline-offset: 2px;
181
+ }
182
+
163
183
  .kol-btn-primary {
164
184
  background: var(--kol-surface-secondary);
165
185
  color: var(--kol-surface-on-primary);
166
186
  border: 1px solid transparent;
167
187
  }
168
- .kol-btn-primary:not(.kol-btn-animate):hover {
169
- background-color: var(--kol-fg-08);
170
- color: var(--kol-surface-on-primary);
188
+ @media (hover: hover) {
189
+ .kol-btn-primary:not(.kol-btn-animate):hover {
190
+ background-color: var(--kol-oq-08);
191
+ color: var(--kol-surface-on-primary);
192
+ }
171
193
  }
172
194
 
173
195
  .kol-btn-secondary {
@@ -176,10 +198,12 @@
176
198
  border: 1px solid transparent;
177
199
  font-weight: 500;
178
200
  }
179
- .kol-btn-secondary:not(.kol-btn-animate):hover {
180
- background-color: color-mix(in srgb, var(--kol-surface-on-primary) 20%, transparent);
181
- color: var(--kol-surface-on-primary);
182
- border-color: transparent;
201
+ @media (hover: hover) {
202
+ .kol-btn-secondary:not(.kol-btn-animate):hover {
203
+ background-color: var(--kol-oq-inverse-40);
204
+ color: var(--kol-surface-primary);
205
+ border-color: transparent;
206
+ }
183
207
  }
184
208
 
185
209
  .kol-btn-accent {
@@ -188,36 +212,50 @@
188
212
  border: 1px solid transparent;
189
213
  font-weight: 500;
190
214
  }
191
- .kol-btn-accent:not(.kol-btn-animate):hover {
192
- background-color: var(--kol-accent-primary-strong);
193
- color: var(--kol-accent-on-primary);
215
+ @media (hover: hover) {
216
+ .kol-btn-accent:not(.kol-btn-animate):hover {
217
+ background-color: var(--kol-accent-primary-strong);
218
+ color: var(--kol-accent-on-primary);
219
+ }
194
220
  }
195
221
 
196
222
  .kol-btn-outline {
197
223
  background: transparent;
198
224
  color: var(--kol-surface-on-primary);
199
- border: 1px solid var(--kol-fg-16);
225
+ border: 1px solid var(--kol-oq-16);
200
226
  }
201
- .kol-btn-outline:not(.kol-btn-animate):hover {
202
- background-color: var(--kol-fg-08);
203
- border-color: color-mix(in srgb, var(--kol-surface-on-primary) 25%, transparent);
204
- color: var(--kol-surface-on-primary);
227
+ @media (hover: hover) {
228
+ .kol-btn-outline:not(.kol-btn-animate):hover {
229
+ background-color: var(--kol-oq-02);
230
+ border-color: color-mix(in srgb, var(--kol-surface-on-primary) 25%, var(--kol-surface-primary));
231
+ color: var(--kol-surface-on-primary);
232
+ }
205
233
  }
206
234
 
207
235
  .kol-btn-ghost {
208
236
  background: transparent;
209
- color: var(--kol-fg-48);
237
+ color: var(--kol-oq-48);
210
238
  border: 1px solid transparent;
211
239
  }
212
- .kol-btn-ghost:not(.kol-btn-animate):hover {
213
- background-color: var(--kol-fg-08);
214
- color: var(--kol-surface-on-primary);
240
+ @media (hover: hover) {
241
+ .kol-btn-ghost:not(.kol-btn-animate):hover {
242
+ background-color: var(--kol-oq-04);
243
+ color: var(--kol-surface-on-primary);
244
+ }
215
245
  }
216
246
 
217
247
  .kol-btn-sm { padding: 4px 12px; }
218
248
  .kol-btn-md { padding: 6px 16px; }
219
249
  .kol-btn-lg { padding: 8px 20px; }
220
250
 
251
+ /* Press feedback — one stop past hover (2026-07-08 button-states spec).
252
+ * Outside the hover guard: :active fires on touch too. */
253
+ .kol-btn-primary:not(.kol-btn-animate):not(:disabled):active { background-color: var(--kol-oq-16); }
254
+ .kol-btn-secondary:not(.kol-btn-animate):not(:disabled):active { background-color: var(--kol-oq-inverse-48); color: var(--kol-surface-primary); }
255
+ .kol-btn-accent:not(.kol-btn-animate):not(:disabled):active { background-color: color-mix(in srgb, var(--kol-accent-primary) 70%, var(--kol-surface-primary)); }
256
+ .kol-btn-outline:not(.kol-btn-animate):not(:disabled):active,
257
+ .kol-btn-ghost:not(.kol-btn-animate):not(:disabled):active { background-color: var(--kol-oq-08); }
258
+
221
259
  .kol-btn:disabled,
222
260
  .kol-btn[disabled] {
223
261
  opacity: var(--kol-opacity-disabled);
@@ -236,8 +274,10 @@
236
274
  opacity: var(--kol-opacity-disabled);
237
275
  transition: opacity 150ms ease;
238
276
  }
239
- .kol-btn-quiet:not(:disabled):hover {
240
- opacity: 1;
277
+ @media (hover: hover) {
278
+ .kol-btn-quiet:not(:disabled):hover {
279
+ opacity: 1;
280
+ }
241
281
  }
242
282
 
243
283
  /* Icon-swap animation (Button.jsx iconLeft / iconRight with hover icon) */
@@ -262,14 +302,16 @@
262
302
  transform var(--kol-transition-base);
263
303
  }
264
304
 
265
- .kol-btn:hover .kol-icon-default {
266
- opacity: 0;
267
- transform: translateY(-4px);
268
- }
305
+ @media (hover: hover) {
306
+ .kol-btn:hover .kol-icon-default {
307
+ opacity: 0;
308
+ transform: translateY(-4px);
309
+ }
269
310
 
270
- .kol-btn:hover .kol-icon-hover {
271
- opacity: 1;
272
- transform: translateY(0);
311
+ .kol-btn:hover .kol-icon-hover {
312
+ opacity: 1;
313
+ transform: translateY(0);
314
+ }
273
315
  }
274
316
 
275
317
 
@@ -296,38 +338,49 @@
296
338
  * absolute-white, indicator absolute-black (theme-invariant).
297
339
  * ═══════════════════════════════════════════════════════════════ */
298
340
 
341
+ /* Toggle Switch — atoms/ToggleSwitch.jsx (2026-07-08 chrome law rewrite).
342
+ * Bare by default: label + track, no box. Shell variants (primary/outline)
343
+ * only when the switch must read as chrome — button geometry exactly
344
+ * (pad 4/12 · 6/16 · 8/20 → heights 26/32/40). Type via kol-mono-{12,14,16}
345
+ * in JSX; the old auto-uppercase label died with the box (no-auto-casing).
346
+ * On = inverted ink (surface-on-primary track / surface-primary thumb) —
347
+ * same identity as .kol-btn-pressed. JSX aliases: plain→bare, default→outline. */
348
+
299
349
  .toggle-switch {
300
- display: flex;
350
+ display: inline-flex;
301
351
  align-items: center;
302
352
  justify-content: space-between;
303
353
  gap: 12px;
304
- padding: 10px 14px;
305
- border-radius: 4px;
306
- border: 1px solid color-mix(in srgb, var(--kol-surface-on-primary) 12%, transparent);
354
+ padding: 0;
355
+ border: 1px solid transparent;
356
+ border-radius: var(--kol-radius-sm);
307
357
  background-color: transparent;
308
358
  color: var(--kol-surface-on-primary);
359
+ cursor: pointer;
309
360
  transition: background-color var(--kol-transition-base),
310
361
  border-color var(--kol-transition-base);
311
362
  }
312
363
 
313
- .toggle-switch:hover,
314
364
  .toggle-switch:focus-visible {
315
- border-color: color-mix(in srgb, var(--kol-surface-on-primary) 18%, transparent);
365
+ outline: 2px solid var(--kol-focus-ring);
366
+ outline-offset: 2px;
316
367
  }
317
368
 
318
- .toggle-switch-label {
319
- font-family: var(--kol-font-family-mono);
320
- font-size: 11px;
321
- text-transform: uppercase;
322
- letter-spacing: 0.08em;
323
- }
369
+ .toggle-switch--primary { background-color: var(--kol-surface-secondary); }
370
+ .toggle-switch--outline { border-color: var(--kol-oq-16); }
371
+
372
+ /* shells take button padding; bare stays padding 0 */
373
+ .toggle-switch--primary.toggle-switch--sm, .toggle-switch--outline.toggle-switch--sm { padding: 4px 12px; }
374
+ .toggle-switch--primary.toggle-switch--md, .toggle-switch--outline.toggle-switch--md { padding: 6px 16px; }
375
+ .toggle-switch--primary.toggle-switch--lg, .toggle-switch--outline.toggle-switch--lg { padding: 8px 20px; }
324
376
 
325
377
  .toggle-switch-indicator {
326
378
  width: 20px;
327
379
  height: 12px;
328
380
  border-radius: var(--kol-radius-full);
329
- background-color: color-mix(in srgb, var(--kol-surface-on-primary) 16%, transparent);
381
+ background-color: var(--kol-oq-16);
330
382
  position: relative;
383
+ flex-shrink: 0;
331
384
  transition: background-color var(--kol-transition-base);
332
385
  }
333
386
 
@@ -344,26 +397,23 @@
344
397
  background-color var(--kol-transition-base);
345
398
  }
346
399
 
400
+ /* track scales with size */
401
+ .toggle-switch--sm .toggle-switch-indicator { width: 16px; height: 10px; }
402
+ .toggle-switch--sm .toggle-switch-indicator::after { width: 6px; height: 6px; }
403
+ .toggle-switch--lg .toggle-switch-indicator { width: 24px; height: 14px; }
404
+ .toggle-switch--lg .toggle-switch-indicator::after { width: 10px; height: 10px; }
405
+
347
406
  .toggle-switch[data-state='on'] .toggle-switch-indicator {
348
- background-color: var(--kol-color-absolute-white);
407
+ background-color: var(--kol-surface-on-primary);
349
408
  }
350
409
 
351
410
  .toggle-switch[data-state='on'] .toggle-switch-indicator::after {
352
411
  transform: translateX(8px);
353
- background-color: var(--kol-color-absolute-black);
354
- }
355
-
356
- .toggle-switch--plain {
357
- padding: 0;
358
- border: none;
359
- background-color: transparent;
412
+ background-color: var(--kol-surface-primary);
360
413
  }
361
414
 
362
- .toggle-switch--plain:hover,
363
- .toggle-switch--plain:focus-visible {
364
- background-color: transparent;
365
- border-color: transparent;
366
- }
415
+ .toggle-switch--sm[data-state='on'] .toggle-switch-indicator::after { transform: translateX(6px); }
416
+ .toggle-switch--lg[data-state='on'] .toggle-switch-indicator::after { transform: translateX(10px); }
367
417
 
368
418
 
369
419
  /* ═══════════════════════════════════════════════════════════════
@@ -416,18 +466,18 @@
416
466
 
417
467
 
418
468
  /* ═══════════════════════════════════════════════════════════════
419
- * Slider — atoms/Slider.jsx
420
- *
421
- * Range-input with custom track + thumb pseudo-elements. Two variants
422
- * post-2026-04-30 restructure (bordered `default` retired):
469
+ * Slider — molecules/Slider.jsx
423
470
  *
424
- * default bare track + boxed value (was `minimal`). Most-used
425
- * Pattern Lab cols/rows/gap, Compositor sliders, Type Lab.
426
- * subtle filled rounded chip for inspector-style controls.
471
+ * Range-input with custom track + thumb pseudo-elements. One bare
472
+ * inline-flex row (label · track · editable readout) — the sole slider
473
+ * look. Bordered `default` and chip `subtle` variants retired
474
+ * (2026-07-08); the phantom `.control-slider-minimal` the JSX used to
475
+ * emit never had a rule, so real `variant="minimal"` call sites now
476
+ * resolve here and finally get their intended layout.
427
477
  *
428
478
  * Track color is exposed as `--kol-slider-track` (set on `.slider-black`)
429
- * so consumers can override per-instance via inline style or className.
430
- * Default = fg-64 (dim grey); subtle overrides to full ink.
479
+ * so consumers can override per-instance via inline style or className;
480
+ * default = fg-64 (dim grey).
431
481
  * ═══════════════════════════════════════════════════════════════ */
432
482
 
433
483
  .control-slider {
@@ -453,26 +503,6 @@
453
503
  flex-shrink: 0;
454
504
  }
455
505
 
456
- .control-slider-subtle {
457
- display: inline-flex;
458
- flex-direction: row;
459
- align-items: center;
460
- padding: 6px 16px;
461
- border-radius: 4px;
462
- background-color: var(--kol-surface-secondary);
463
- border: none;
464
- transition: background-color var(--kol-transition-base),
465
- color var(--kol-transition-base);
466
- }
467
-
468
- .control-slider-subtle label {
469
- color: var(--kol-fg-48);
470
- }
471
-
472
- .control-slider-subtle .slider-black {
473
- --kol-slider-track: var(--kol-surface-on-primary);
474
- }
475
-
476
506
  .slider-black {
477
507
  --kol-slider-track: var(--kol-fg-64);
478
508
  -webkit-appearance: none;
@@ -772,12 +802,14 @@ a:hover .icon-hover,
772
802
  * (after `.kol-btn-quiet:not(:disabled):hover { opacity: 1; }`). */
773
803
 
774
804
  /* .kol-btn-pressed — toggle-on state (Button `pressed` prop, aria-pressed).
775
- * Hover-level fill held at rest so a pressed toggle reads as lit. Pairs
776
- * with `quiet`: Button drops kol-btn-quiet while pressed, giving the
777
- * tool-palette dim-at-rest → lit-when-active idiom. */
805
+ * Solid inverted ink so a pressed toggle reads as unmistakably lit
806
+ * (2026-07-08 button-states spec). Pairs with `quiet`: Button drops
807
+ * kol-btn-quiet while pressed, giving the tool-palette dim-at-rest →
808
+ * lit-when-active idiom. Always composes with a variant class — the
809
+ * variant supplies the border that suppresses UA button chrome. */
778
810
  .kol-btn-pressed {
779
- background-color: var(--kol-fg-08);
780
- color: var(--kol-surface-on-primary);
811
+ background-color: var(--kol-surface-on-primary);
812
+ color: var(--kol-surface-primary);
781
813
  }
782
814
 
783
815
  /* ─────────────────────────────────────────────────────────────────────
@@ -132,16 +132,55 @@
132
132
  color var(--kol-transition-base);
133
133
  }
134
134
 
135
+ /* ─────────────────────────────────────────────────────────────────────
136
+ * Dropdown — molecules/Dropdown.jsx (2026-07-08 chrome law).
137
+ * The trigger IS button chrome: JSX emits kol-btn kol-btn-{primary|
138
+ * outline} kol-btn-{size}, so fills/hover/active/focus come from the
139
+ * button rules. Classes below only add trigger layout + the open/panel
140
+ * fusion: primary = one piece (same fill, no border, no gap, hairline
141
+ * divider inside); outline = bordered pair, trigger's bottom edge is
142
+ * the divider.
143
+ * ───────────────────────────────────────────────────────────────────── */
144
+
145
+ .kol-dd-trigger {
146
+ width: 100%;
147
+ justify-content: space-between;
148
+ }
149
+
150
+ .kol-dd-trigger--open {
151
+ border-radius: var(--kol-radius-sm) var(--kol-radius-sm) 0 0;
152
+ }
153
+
154
+ .kol-dd-panel {
155
+ color: var(--kol-surface-on-primary);
156
+ border-radius: 0 0 var(--kol-radius-sm) var(--kol-radius-sm);
157
+ padding-bottom: 4px;
158
+ }
159
+
160
+ .kol-dd-panel--primary { background-color: var(--kol-surface-secondary); }
161
+
162
+ .kol-dd-panel--outline {
163
+ background-color: var(--kol-surface-primary);
164
+ border: 1px solid var(--kol-oq-16);
165
+ border-top: none;
166
+ }
167
+
168
+ .kol-dd-div {
169
+ height: 1px;
170
+ background-color: var(--kol-border-default);
171
+ margin: 0 12px;
172
+ }
173
+
135
174
  .tag-control:hover {
136
- background-color: color-mix(in srgb, var(--kol-surface-on-primary) 24%, transparent);
175
+ background-color: var(--kol-oq-12);
137
176
  color: var(--kol-surface-on-primary);
138
177
  border-color: transparent;
139
178
  }
140
179
 
141
180
  .tag-control.is-active {
142
- background-color: color-mix(in srgb, var(--kol-surface-on-primary) 24%, transparent);
181
+ background-color: var(--kol-oq-12);
143
182
  color: var(--kol-surface-on-primary);
144
- border-color: color-mix(in srgb, var(--kol-surface-on-primary) 24%, transparent);
183
+ border-color: var(--kol-oq-12);
145
184
  }
146
185
 
147
186
  .tag-sm { font-size: 10px; font-weight: 470; padding: 4px 10px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-theme",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "KOL (Kolkrabbi) design-system tokens + base CSS — brand-neutral. The canonical token/cascade layer every other KOL package and consumer builds on.",
5
5
  "license": "MIT",
6
6
  "type": "module",