@kolkrabbi/kol-component 0.159.0 → 0.161.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-component",
3
- "version": "0.159.0",
3
+ "version": "0.161.0",
4
4
  "description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,12 +23,13 @@ const VARIANT_MAP = {
23
23
  }
24
24
 
25
25
  const SIZE_MAP = {
26
+ xs: 'kol-badge-xs',
26
27
  sm: 'kol-badge-sm',
27
28
  md: 'kol-badge-md',
28
29
  lg: 'kol-badge-lg'
29
30
  }
30
31
 
31
- const ICON_SIZES = { sm: 12, md: 14, lg: 16 }
32
+ const ICON_SIZES = { xs: 10, sm: 12, md: 14, lg: 16 }
32
33
 
33
34
  const Badge = ({
34
35
  children,
@@ -61,7 +61,7 @@ import { toneClass } from '../utilities/tone.js'
61
61
  *
62
62
  * @param {string} name icon name (kol-icons)
63
63
  * @param {string} variant primary|secondary|accent|outline|ghost|nav|grey|danger
64
- * @param {string} size sm|md|lg — square + glyph together
64
+ * @param {string} size xs|sm|md|lg — square + glyph together (xs = 20px square, 12 glyph; the panel rung, 2026-09-01)
65
65
  * @param {string} radius sm (default, the system's 4px) | full (9999px).
66
66
  * Two values, nothing between: a round frame is its
67
67
  * own chrome idiom (edge-straddling controls, avatars),
@@ -47,7 +47,7 @@
47
47
  * className — additional classes on the outer shell
48
48
  */
49
49
  export default function SegmentedToggle({ value, onChange, options = [], variant = 'default', size = 'md', ariaLabel, className = '' }) {
50
- const cellType = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }[size]
50
+ const cellType = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }[size]
51
51
  const stateless = value == null
52
52
  const focusIdx = Math.max(0, options.findIndex((opt) => opt.value === value))
53
53
 
package/src/atoms/Tag.jsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Icon } from '@kolkrabbi/kol-icons'
2
2
 
3
- const ICON_SIZES = { sm: 10, md: 12, lg: 14 }
3
+ const ICON_SIZES = { xs: 8, sm: 10, md: 12, lg: 14 }
4
4
 
5
5
  /**
6
6
  * Tag — the INTERACTIVE chip: filterable, selectable, removable.
@@ -35,7 +35,7 @@ const ICON_SIZES = { sm: 10, md: 12, lg: 14 }
35
35
  * @param {ReactNode} children label content
36
36
  * @param {string} text content fallback when no children (brand SwatchControls)
37
37
  * @param {string} variant 'primary' | 'secondary' | 'inverse' · `tertiary` = secondary's fill, no outline, mono at fg-80 (2026-08-27)
38
- * @param {string} size 'sm' | 'md' | 'lg' — `sm` is the default and
38
+ * @param {string} size 'xs' | 'sm' | 'md' | 'lg' — `sm` is the default and
39
39
  * should stay the answer; `lg` needs a reason
40
40
  * @param {boolean} active selected state (filter chips)
41
41
  * @param {boolean} hash prepend `#` (default true); false for plain labels
@@ -31,7 +31,7 @@ import { Icon } from '@kolkrabbi/kol-icons'
31
31
  * Pairs with `<Input>` for single-line.
32
32
  */
33
33
 
34
- const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
34
+ const SIZE_TYPE = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
35
35
 
36
36
  export default function Textarea({
37
37
  value,
@@ -7,15 +7,16 @@ import React from 'react'
7
7
  * variant="primary" — filled shell (surface-secondary), button geometry
8
8
  * variant="outline" — bordered shell (border-oq-16), button geometry
9
9
  *
10
- * size="sm" / "md" (default) / "lg" — shells match button heights
11
- * (26/32/40); the track scales with size in all variants.
10
+ * size="xs" / "sm" / "md" (default) / "lg" — shells match button heights
11
+ * (22/26/32/40); the track scales with size in all variants. xs is the
12
+ * panel rung (2026-09-01).
12
13
  *
13
14
  * Legacy aliases: 'plain' → bare · 'default' (the old boxed look) → outline.
14
15
  * Type via kol-mono-{12,14,16}; no auto-uppercase — casing is authored at
15
16
  * the call site (KOL no-auto-casing rule).
16
17
  */
17
18
 
18
- const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
19
+ const SIZE_TYPE = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
19
20
  const LEGACY_VARIANTS = { plain: 'bare', default: 'outline' }
20
21
 
21
22
  const ToggleSwitch = ({
@@ -184,6 +184,8 @@ const Dropdown = ({
184
184
  className={`kol-dd-panel kol-dd-panel--${resolvedVariant} ${toneClass(tone)}`.trim()}
185
185
  style={{
186
186
  '--kol-dd-max-rows': maxRows ?? 10,
187
+ /* xs rows are 20px; the panel's max-height reads the row height */
188
+ ...(resolvedSize === 'xs' && rowHeight == null ? { '--kol-dd-row-h': '20px' } : null),
187
189
  ...(rowHeight != null ? { '--kol-dd-row-h': typeof rowHeight === 'number' ? `${rowHeight}px` : rowHeight } : null),
188
190
  }}
189
191
  >
@@ -202,11 +204,12 @@ const Dropdown = ({
202
204
  * all — and this was the last one left, the option row's ink
203
205
  * brighten. The check mark is what marks the current value. */
204
206
  hover={false}
207
+ size={resolvedSize}
205
208
  height={rowHeight}
206
209
  onPointerEnter={onOptionHover ? () => reportHover(option.value) : undefined}
207
210
  onPointerLeave={onOptionHover ? () => reportHover(null) : undefined}
208
211
  onClick={() => handleSelect(option)}
209
- shortcut={isActive ? <Icon name="check" size={11} /> : undefined}
212
+ shortcut={isActive ? <Icon name="check" size={resolvedSize === 'xs' ? indicatorSize('xs') : 11} /> : undefined}
210
213
  >
211
214
  {option.label}
212
215
  </MenuDropdownItem>
@@ -99,7 +99,14 @@ export function MenuItem({
99
99
  * - children — main label, flex-1.
100
100
  * - shortcut — trailing content (text shortcut hint, ✓ marker, or icon).
101
101
  */
102
- export function MenuDropdownItem({ onClick, onPointerEnter, onPointerLeave, disabled, prefix, iconLeft, shortcut, hover = true, height, children }) {
102
+ /* the row follows the trigger's rung (DropdownXsList, kol-monitor 2026-09-01):
103
+ * at xs the list kept sm rows — kol-helper-12 in a 32px pitch — inside a panel
104
+ * fused to an 8px-type trigger, so the options truncated. One map, both ends. */
105
+ /* xs wears the TRIGGER's face (kol-mono-8), not the helper: the trigger's ghost
106
+ * stack reserves the widest label in its own face, and a row set in a different
107
+ * one measured 3px wider and clipped the selected row beside its check. */
108
+ const ROW_BY_SIZE = { xs: 'kol-mono-8 px-2 h-5', sm: 'kol-helper-12 px-3 h-8', md: 'kol-helper-12 px-3 h-8', lg: 'kol-helper-12 px-3 h-8' }
109
+ export function MenuDropdownItem({ onClick, onPointerEnter, onPointerLeave, disabled, prefix, iconLeft, shortcut, hover = true, height, size = 'sm', children }) {
103
110
  return (
104
111
  <button
105
112
  type="button"
@@ -110,7 +117,7 @@ export function MenuDropdownItem({ onClick, onPointerEnter, onPointerLeave, disa
110
117
  disabled={disabled}
111
118
  role="menuitem"
112
119
  style={height != null ? { height } : undefined}
113
- className={`w-full kol-helper-12 px-3 h-8 shrink-0 inline-flex items-center gap-2 text-body ${hover ? 'hover:text-emphasis' : ''} disabled:opacity-40 disabled:cursor-not-allowed text-left`}
120
+ className={`w-full ${ROW_BY_SIZE[size] ?? ROW_BY_SIZE.sm} shrink-0 inline-flex items-center gap-2 text-body ${hover ? 'hover:text-emphasis' : ''} disabled:opacity-40 disabled:cursor-not-allowed text-left`}
114
121
  >
115
122
  {prefix && <span className="shrink-0 inline-flex items-center">{prefix}</span>}
116
123
  {iconLeft && <span className="shrink-0 w-4 inline-flex items-center justify-center">{iconLeft}</span>}
@@ -33,7 +33,7 @@ import { glyphSize } from '../hooks/glyphLadders.js'
33
33
  * @param {Function} onClear () => void — trailing × click; × renders only when set and value is non-empty
34
34
  * @param {string} shortcutHint caller-authored kbd chip (e.g. "⌘K"); shown while the value is empty
35
35
  * @param {Function} onFocus (event) => void — input focus
36
- * @param {string} size 'sm' | 'md' — kol-control size + matched mono type class
36
+ * @param {string} size 'xs' | 'sm' | 'md' — kol-control size + matched mono type class (xs = the panel rung, 2026-09-01)
37
37
  * @param {string} variant 'filled' | 'ghost' | 'outline' — kol-control variant, same chrome as Input (ignored when bare/expanding)
38
38
  * @param {string} tone 'default' | 'inverse' — the dark chip for a washed plane (ControlToneInverse, 2026-08-27); on the expanding pill it is the OPEN fill
39
39
  * @param {boolean} bare borderless inline field for overlay panels (full width, no shell chrome; keeps icon/clear/chip slots)
@@ -67,7 +67,7 @@ import { glyphSize } from '../hooks/glyphLadders.js'
67
67
  *
68
68
  * The flat `{ sm: 14, md: 14 }` it used to carry was the ADJACENT sm rung
69
69
  * frozen for both sizes — right ladder, no size. */
70
- const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14' }
70
+ const SIZE_TYPE = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14' }
71
71
 
72
72
  export default function SearchInput({
73
73
  value = '',
@@ -12,6 +12,15 @@
12
12
  * at both ends, and `onChange` reports the option in the same event shape
13
13
  * as the number path — `{ target: { value: option } }`.
14
14
  *
15
+ * layout="inline" — `‹ value ›` on ONE line (StepperInlineVariant, kol-monitor
16
+ * 2026-09-02; the rack's Selector shape, the other half of its collapse onto
17
+ * this): the chevrons flank the value as left/right hit targets, the value is
18
+ * centred in a 3ch floor so the row does not jitter as it steps, and there is
19
+ * NO `.kol-control` field chrome — it is inline text on the panel. Value
20
+ * `text-fg-64`, chevrons `text-fg-40`, type from the size ladder; casing is the
21
+ * caller's. Works for `options` and for the number range alike. Default
22
+ * `stacked` is the field with the chevron stack, exactly as before.
23
+ *
15
24
  * Loaded from `00-kol/chevron-{up,down}.svg` via the Icon registry — first
16
25
  * stroke icons in the kol curated set. For a plain number input without
17
26
  * bump affordance, use `<Input type="number" />`.
@@ -28,6 +37,7 @@ export default function Stepper({
28
37
  max,
29
38
  step = 1,
30
39
  options,
40
+ layout = 'stacked',
31
41
  size = 'sm',
32
42
  className = '',
33
43
  style = {},
@@ -63,6 +73,23 @@ export default function Stepper({
63
73
  onChange?.(e)
64
74
  }
65
75
 
76
+ /* INLINE — ‹ value › with no field: the chevrons are the hit targets, the
77
+ * value is text on the panel. `min-w-[3ch]` holds the width across steps. */
78
+ if (layout === 'inline') {
79
+ const chev = CHEVRON_SIZE[size]
80
+ return (
81
+ <div className={`inline-flex items-center justify-center gap-0.5 select-none ${SIZE_TYPE[size]} ${className}`.trim()} style={style}>
82
+ <button type="button" onClick={handleDecrement} className="inline-flex items-center justify-center px-0.5 text-fg-40 hover:text-emphasis transition-colors" aria-label="Previous">
83
+ <Icon name="chevron-left" size={chev} />
84
+ </button>
85
+ <span className="min-w-[3ch] text-center text-fg-64" {...props}>{value ?? ''}</span>
86
+ <button type="button" onClick={handleIncrement} className="inline-flex items-center justify-center px-0.5 text-fg-40 hover:text-emphasis transition-colors" aria-label="Next">
87
+ <Icon name="chevron-right" size={chev} />
88
+ </button>
89
+ </div>
90
+ )
91
+ }
92
+
66
93
  const shellCls = [
67
94
  'kol-control',
68
95
  'kol-control--filled',