gridsmith-ui 0.6.4 → 0.6.6

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/dist/kanban.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Badge } from './chunk-KXNJJKIV.js';
1
+ import { Badge } from './chunk-6UZZYWTY.js';
2
2
  import { useTextOverflow, Tooltip } from './chunk-DDKCQVKO.js';
3
3
  import { Icon } from './chunk-GX6O72IV.js';
4
4
  import { cn } from './chunk-TTZSZNJ6.js';
package/dist/roots.css CHANGED
@@ -318,6 +318,11 @@
318
318
  --ds-status-error-bg: var(--ds-destructive-100);
319
319
  --ds-status-info: var(--ds-info-500);
320
320
  --ds-status-info-bg: var(--ds-info-100);
321
+ /* Text on the filled status surfaces — computed per palette by the theme;
322
+ defaults for the scale above (the warning fill is too light for white) */
323
+ --ds-text-on-success: oklch(1 0 0);
324
+ --ds-text-on-warning: var(--ds-neutral-950);
325
+ --ds-text-on-error: oklch(1 0 0);
321
326
 
322
327
  --ds-sidebar-bg: var(--ds-neutral-100);
323
328
  --ds-sidebar-border: var(--ds-neutral-200);
@@ -328,6 +333,10 @@
328
333
  --ds-sidebar-text-muted: var(--ds-text-muted);
329
334
  --ds-sidebar-accent: var(--ds-accent-primary);
330
335
  --ds-sidebar-accent-subtle: color-mix(in srgb, var(--ds-sidebar-accent) 10%, transparent);
336
+ /* Text on the sidebar / header accent — computed from the target palette in
337
+ independent modes; in match mode they follow the page */
338
+ --ds-sidebar-text-on-accent: var(--ds-text-on-primary);
339
+ --ds-header-text-on-accent: var(--ds-text-on-primary);
331
340
 
332
341
  /* Tab strip — presets can override for stronger contrast (e.g. Modern Dark) */
333
342
  --ds-tab-hover-bg: var(--ds-bg-muted);
@@ -344,6 +353,47 @@
344
353
  --ds-check-border: var(--ds-accent-primary);
345
354
  }
346
355
 
356
+ /* ── Navigation active styles ───────────────────────────────────────
357
+ Defaults for the theme-level active-item shape (sidebar rail, in-page
358
+ underline, in-page rail). Generated themes and the theme writers emit the
359
+ same set for the chosen styles; these literals reproduce today's rendering
360
+ for a consumer with only roots.css. Kept in lockstep with
361
+ getNavStyleTokens() by showcase/src/test/nav-style-defaults.test.ts.
362
+
363
+ Declared for the root, every nested mode wrapper and every overlay portal:
364
+ var() inside a custom property is substituted where it is declared, so a
365
+ root-only declaration would hand a dark wrapper the light-resolved values. */
366
+
367
+ :root, [data-mode], [data-ds-overlay] {
368
+ --ds-nav-h-active-bg: transparent;
369
+ --ds-nav-h-active-text: var(--ds-text-primary);
370
+ --ds-nav-h-inactive-text: var(--ds-text-muted);
371
+ --ds-nav-h-indicator: calc(var(--ds-border-width) * 3);
372
+ --ds-nav-h-item-radius: 0px;
373
+ --ds-nav-h-hover-bg: transparent;
374
+ --ds-nav-h-track-border: var(--ds-border-width);
375
+ --ds-nav-h-track-gap: 0px;
376
+ --ds-nav-h-badge-bg: var(--ds-accent-primary);
377
+
378
+ --ds-nav-v-active-bg: var(--ds-tab-active-bg);
379
+ --ds-nav-v-active-text: var(--ds-accent-primary);
380
+ --ds-nav-v-active-rail: var(--ds-border-width-thick);
381
+ --ds-nav-v-item-radius: var(--ds-radius-sm);
382
+ --ds-nav-v-track-border: var(--ds-border-width);
383
+ --ds-nav-v-track-padding: var(--ds-space-1);
384
+ --ds-nav-v-badge-bg: var(--ds-accent-primary);
385
+
386
+ --ds-tree-active-bg: var(--ds-tab-active-bg);
387
+ --ds-tree-active-text: var(--ds-accent-primary);
388
+ --ds-tree-active-muted: var(--ds-text-muted);
389
+ --ds-tree-active-rail: var(--ds-border-width-thick);
390
+
391
+ --ds-sidebar-active-bg: var(--ds-sidebar-accent-subtle);
392
+ --ds-sidebar-active-text: var(--ds-sidebar-accent);
393
+ --ds-sidebar-active-accent: var(--ds-sidebar-accent);
394
+ --ds-sidebar-active-rail: var(--ds-border-width-thick);
395
+ }
396
+
347
397
  /* ── Dark Mode Semantic Overrides ─────────────────────────────────── */
348
398
  /* Layered surface hierarchy: sidebar(darkest) < canvas < surface < elevated.
349
399
  This gives cards/panels visible lift and keeps the sidebar anchored. */
@@ -362,7 +412,6 @@
362
412
  --ds-text-primary: var(--ds-neutral-50);
363
413
  --ds-text-secondary: var(--ds-neutral-300);
364
414
  --ds-text-muted: var(--ds-neutral-400);
365
- --ds-text-on-primary: oklch(1 0 0);
366
415
  color: var(--ds-text-primary);
367
416
 
368
417
  /* Borders — bumped one step lighter for visibility against 800/900 surfaces */
@@ -381,6 +430,10 @@
381
430
  --ds-status-error-bg: var(--ds-destructive-950);
382
431
  --ds-status-info: var(--ds-info-400);
383
432
  --ds-status-info-bg: var(--ds-info-950);
433
+ /* The 400 fills above are too light for white at every hue */
434
+ --ds-text-on-success: var(--ds-neutral-950);
435
+ --ds-text-on-warning: var(--ds-neutral-950);
436
+ --ds-text-on-error: var(--ds-neutral-950);
384
437
 
385
438
  /* Sidebar — sits at canvas level, border provides separation */
386
439
  --ds-sidebar-bg: var(--ds-neutral-800);
package/dist/tokens.css CHANGED
@@ -41,6 +41,59 @@
41
41
  --ds-ring-active: var(--ds-accent-soft);
42
42
  --ds-input-focus-ring: var(--ds-ring-active);
43
43
  --ds-shadow-focus: 0 0 0 2px var(--ds-primary-900);
44
+ --ds-text-on-primary: oklch(1 0 0);
45
+ --ds-nav-h-active-bg: transparent;
46
+ --ds-nav-h-active-text: var(--ds-text-primary);
47
+ --ds-nav-h-inactive-text: var(--ds-text-muted);
48
+ --ds-nav-h-indicator: calc(var(--ds-border-width) * 3);
49
+ --ds-nav-h-item-radius: 0px;
50
+ --ds-nav-h-hover-bg: transparent;
51
+ --ds-nav-h-track-border: var(--ds-border-width);
52
+ --ds-nav-h-track-gap: 0px;
53
+ --ds-nav-h-badge-bg: var(--ds-accent-primary);
54
+ --ds-nav-v-active-bg: var(--ds-tab-active-bg);
55
+ --ds-nav-v-active-text: var(--ds-accent-primary);
56
+ --ds-nav-v-active-rail: var(--ds-border-width-thick);
57
+ --ds-nav-v-item-radius: var(--ds-radius-sm);
58
+ --ds-nav-v-track-border: var(--ds-border-width);
59
+ --ds-nav-v-track-padding: var(--ds-space-1);
60
+ --ds-nav-v-badge-bg: var(--ds-accent-primary);
61
+ --ds-tree-active-bg: var(--ds-tab-active-bg);
62
+ --ds-tree-active-text: var(--ds-accent-primary);
63
+ --ds-tree-active-muted: var(--ds-text-muted);
64
+ --ds-tree-active-rail: var(--ds-border-width-thick);
65
+ --ds-sidebar-active-bg: var(--ds-sidebar-accent-subtle);
66
+ --ds-sidebar-active-text: var(--ds-sidebar-accent);
67
+ --ds-sidebar-active-accent: var(--ds-sidebar-accent);
68
+ --ds-sidebar-active-rail: var(--ds-border-width-thick);
69
+ }
70
+
71
+ /* Navigation active styles — redeclared for nested mode wrappers and overlay portals */
72
+ :root, [data-mode], [data-ds-overlay] {
73
+ --ds-nav-h-active-bg: transparent;
74
+ --ds-nav-h-active-text: var(--ds-text-primary);
75
+ --ds-nav-h-inactive-text: var(--ds-text-muted);
76
+ --ds-nav-h-indicator: calc(var(--ds-border-width) * 3);
77
+ --ds-nav-h-item-radius: 0px;
78
+ --ds-nav-h-hover-bg: transparent;
79
+ --ds-nav-h-track-border: var(--ds-border-width);
80
+ --ds-nav-h-track-gap: 0px;
81
+ --ds-nav-h-badge-bg: var(--ds-accent-primary);
82
+ --ds-nav-v-active-bg: var(--ds-tab-active-bg);
83
+ --ds-nav-v-active-text: var(--ds-accent-primary);
84
+ --ds-nav-v-active-rail: var(--ds-border-width-thick);
85
+ --ds-nav-v-item-radius: var(--ds-radius-sm);
86
+ --ds-nav-v-track-border: var(--ds-border-width);
87
+ --ds-nav-v-track-padding: var(--ds-space-1);
88
+ --ds-nav-v-badge-bg: var(--ds-accent-primary);
89
+ --ds-tree-active-bg: var(--ds-tab-active-bg);
90
+ --ds-tree-active-text: var(--ds-accent-primary);
91
+ --ds-tree-active-muted: var(--ds-text-muted);
92
+ --ds-tree-active-rail: var(--ds-border-width-thick);
93
+ --ds-sidebar-active-bg: var(--ds-sidebar-accent-subtle);
94
+ --ds-sidebar-active-text: var(--ds-sidebar-accent);
95
+ --ds-sidebar-active-accent: var(--ds-sidebar-accent);
96
+ --ds-sidebar-active-rail: var(--ds-border-width-thick);
44
97
  }
45
98
 
46
99
  /* Preset overrides — light mode */
@@ -41,6 +41,59 @@
41
41
  --ds-ring-active: var(--ds-accent-soft);
42
42
  --ds-input-focus-ring: var(--ds-ring-active);
43
43
  --ds-shadow-focus: 0 0 0 2px var(--ds-primary-900);
44
+ --ds-text-on-primary: oklch(1 0 0);
45
+ --ds-nav-h-active-bg: transparent;
46
+ --ds-nav-h-active-text: var(--ds-text-primary);
47
+ --ds-nav-h-inactive-text: var(--ds-text-muted);
48
+ --ds-nav-h-indicator: calc(var(--ds-border-width) * 3);
49
+ --ds-nav-h-item-radius: 0px;
50
+ --ds-nav-h-hover-bg: transparent;
51
+ --ds-nav-h-track-border: var(--ds-border-width);
52
+ --ds-nav-h-track-gap: 0px;
53
+ --ds-nav-h-badge-bg: var(--ds-accent-primary);
54
+ --ds-nav-v-active-bg: var(--ds-tab-active-bg);
55
+ --ds-nav-v-active-text: var(--ds-accent-primary);
56
+ --ds-nav-v-active-rail: var(--ds-border-width-thick);
57
+ --ds-nav-v-item-radius: var(--ds-radius-sm);
58
+ --ds-nav-v-track-border: var(--ds-border-width);
59
+ --ds-nav-v-track-padding: var(--ds-space-1);
60
+ --ds-nav-v-badge-bg: var(--ds-accent-primary);
61
+ --ds-tree-active-bg: var(--ds-tab-active-bg);
62
+ --ds-tree-active-text: var(--ds-accent-primary);
63
+ --ds-tree-active-muted: var(--ds-text-muted);
64
+ --ds-tree-active-rail: var(--ds-border-width-thick);
65
+ --ds-sidebar-active-bg: var(--ds-sidebar-accent-subtle);
66
+ --ds-sidebar-active-text: var(--ds-sidebar-accent);
67
+ --ds-sidebar-active-accent: var(--ds-sidebar-accent);
68
+ --ds-sidebar-active-rail: var(--ds-border-width-thick);
69
+ }
70
+
71
+ /* Navigation active styles — redeclared for nested mode wrappers and overlay portals */
72
+ :root, [data-mode], [data-ds-overlay] {
73
+ --ds-nav-h-active-bg: transparent;
74
+ --ds-nav-h-active-text: var(--ds-text-primary);
75
+ --ds-nav-h-inactive-text: var(--ds-text-muted);
76
+ --ds-nav-h-indicator: calc(var(--ds-border-width) * 3);
77
+ --ds-nav-h-item-radius: 0px;
78
+ --ds-nav-h-hover-bg: transparent;
79
+ --ds-nav-h-track-border: var(--ds-border-width);
80
+ --ds-nav-h-track-gap: 0px;
81
+ --ds-nav-h-badge-bg: var(--ds-accent-primary);
82
+ --ds-nav-v-active-bg: var(--ds-tab-active-bg);
83
+ --ds-nav-v-active-text: var(--ds-accent-primary);
84
+ --ds-nav-v-active-rail: var(--ds-border-width-thick);
85
+ --ds-nav-v-item-radius: var(--ds-radius-sm);
86
+ --ds-nav-v-track-border: var(--ds-border-width);
87
+ --ds-nav-v-track-padding: var(--ds-space-1);
88
+ --ds-nav-v-badge-bg: var(--ds-accent-primary);
89
+ --ds-tree-active-bg: var(--ds-tab-active-bg);
90
+ --ds-tree-active-text: var(--ds-accent-primary);
91
+ --ds-tree-active-muted: var(--ds-text-muted);
92
+ --ds-tree-active-rail: var(--ds-border-width-thick);
93
+ --ds-sidebar-active-bg: var(--ds-sidebar-accent-subtle);
94
+ --ds-sidebar-active-text: var(--ds-sidebar-accent);
95
+ --ds-sidebar-active-accent: var(--ds-sidebar-accent);
96
+ --ds-sidebar-active-rail: var(--ds-border-width-thick);
44
97
  }
45
98
 
46
99
  /* Preset overrides — light mode */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gridsmith-ui",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "Gridsmith component library \u2014 98 React components with design tokens and a CSS custom property theming engine",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../showcase/src/components/Badge.tsx"],"names":[],"mappings":"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","file":"chunk-KXNJJKIV.js","sourcesContent":["import { cn } from \"../lib/utils\";\nimport { Icon, type IconName } from \"../lib/icons\";\nimport { Tooltip } from \"./Tooltip\";\nimport { useTextOverflow } from \"../lib/use-text-overflow\";\nimport type { ReactNode } from \"react\";\n\nexport type BadgeVariant =\n | \"default\"\n | \"neutral\"\n | \"subtle\"\n | \"success\"\n | \"success-soft\"\n | \"warning\"\n | \"warning-soft\"\n | \"error\"\n | \"error-soft\"\n | \"outline\";\n\nexport type BadgeSize = \"xs\" | \"sm\" | \"md\";\nexport type BadgePaletteColor = 1 | 2 | 3 | 4 | 5;\n\nconst variantStyles: Record<BadgeVariant, string> = {\n default: \"bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)]\",\n neutral: \"bg-[var(--ds-text-muted)]/[0.12] text-[var(--ds-text-secondary)]\",\n subtle: \"bg-[var(--ds-bg-muted)] text-[var(--ds-text-secondary)]\",\n success: \"bg-[var(--ds-status-success)] text-[var(--ds-text-on-primary)]\",\n \"success-soft\": \"bg-[var(--ds-status-success-bg)] text-[var(--ds-status-success)] border-[length:var(--ds-border-width)] border-[var(--ds-status-success)]/[0.2]\",\n warning: \"bg-[var(--ds-status-warning)] text-[var(--ds-text-on-primary)]\",\n \"warning-soft\": \"bg-[var(--ds-status-warning-bg)] text-[var(--ds-status-warning-text)] border-[length:var(--ds-border-width)] border-[var(--ds-status-warning)]/[0.2]\",\n error: \"bg-[var(--ds-status-error)] text-[var(--ds-text-on-primary)]\",\n \"error-soft\": \"bg-[var(--ds-status-error-bg)] text-[var(--ds-status-error)] border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]/[0.2]\",\n outline: \"bg-transparent text-[var(--ds-text-primary)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\",\n};\n\nconst sizeStyles: Record<BadgeSize, string> = {\n xs: \"px-ds-1.5 py-[1px] text-[length:10px] gap-[3px]\",\n sm: \"px-ds-2 py-ds-0.5 text-[length:var(--ds-font-size-xs)] gap-ds-1\",\n md: \"px-ds-2.5 py-ds-0.5 text-[length:var(--ds-font-size-xs)] gap-ds-1\",\n};\n\nconst iconSizes: Record<BadgeSize, number> = {\n xs: 10,\n sm: 12,\n md: 12,\n};\n\ntype PaletteStyleFn = (n: BadgePaletteColor) => string;\n\nconst paletteByVariant: Record<string, PaletteStyleFn> = {\n default: (n) => paletteDefault[n],\n neutral: (n) => paletteSubtle[n],\n subtle: (n) => paletteSubtle[n],\n outline: (n) => paletteOutline[n],\n};\n\nconst paletteDefault: Record<BadgePaletteColor, string> = {\n 1: \"bg-[var(--ds-palette-1-bold)] text-[var(--ds-text-on-primary)]\",\n 2: \"bg-[var(--ds-palette-2-bold)] text-[var(--ds-text-on-primary)]\",\n 3: \"bg-[var(--ds-palette-3-bold)] text-[var(--ds-text-on-primary)]\",\n 4: \"bg-[var(--ds-palette-4-bold)] text-[var(--ds-text-on-primary)]\",\n 5: \"bg-[var(--ds-palette-5-bold)] text-[var(--ds-text-on-primary)]\",\n};\n\nconst paletteSubtle: Record<BadgePaletteColor, string> = {\n 1: \"bg-[var(--ds-palette-1-bg)] text-[var(--ds-palette-1-text)]\",\n 2: \"bg-[var(--ds-palette-2-bg)] text-[var(--ds-palette-2-text)]\",\n 3: \"bg-[var(--ds-palette-3-bg)] text-[var(--ds-palette-3-text)]\",\n 4: \"bg-[var(--ds-palette-4-bg)] text-[var(--ds-palette-4-text)]\",\n 5: \"bg-[var(--ds-palette-5-bg)] text-[var(--ds-palette-5-text)]\",\n};\n\nconst paletteOutline: Record<BadgePaletteColor, string> = {\n 1: \"bg-transparent text-[var(--ds-palette-1-bold)] border-[length:var(--ds-border-width)] border-[var(--ds-palette-1-bold)]\",\n 2: \"bg-transparent text-[var(--ds-palette-2-bold)] border-[length:var(--ds-border-width)] border-[var(--ds-palette-2-bold)]\",\n 3: \"bg-transparent text-[var(--ds-palette-3-bold)] border-[length:var(--ds-border-width)] border-[var(--ds-palette-3-bold)]\",\n 4: \"bg-transparent text-[var(--ds-palette-4-bold)] border-[length:var(--ds-border-width)] border-[var(--ds-palette-4-bold)]\",\n 5: \"bg-transparent text-[var(--ds-palette-5-bold)] border-[length:var(--ds-border-width)] border-[var(--ds-palette-5-bold)]\",\n};\n\nfunction getPaletteClasses(color: BadgePaletteColor, variant: BadgeVariant): string {\n const styleFn = paletteByVariant[variant] ?? paletteByVariant.default;\n return styleFn(color);\n}\n\ninterface BadgeProps {\n variant?: BadgeVariant;\n size?: BadgeSize;\n /** Palette colour (1-5). Overrides variant colour with decorative palette tokens. */\n color?: BadgePaletteColor;\n /** Icon to show before the label */\n iconLeft?: IconName;\n /** Icon to show after the label */\n iconRight?: IconName;\n /** Max width before truncation (e.g. \"120px\", \"16ch\"). Shows tooltip when truncated. */\n maxWidth?: string;\n className?: string;\n children?: ReactNode;\n}\n\nexport function Badge({ variant = \"default\", size = \"md\", color, iconLeft, iconRight, maxWidth, className, children }: BadgeProps) {\n const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1, disabled: !maxWidth });\n const iconSize = iconSizes[size];\n const isIconOnly = (iconLeft || iconRight) && !children;\n\n const colorClasses = color\n ? getPaletteClasses(color, variant)\n : variantStyles[variant];\n\n const badge = (\n <span\n className={cn(\n \"inline-flex items-center font-medium max-w-full whitespace-nowrap\",\n \"rounded-[var(--ds-radius-full)]\",\n sizeStyles[size],\n isIconOnly && size === \"xs\" && \"px-[3px]\",\n isIconOnly && size === \"sm\" && \"px-ds-1\",\n isIconOnly && size === \"md\" && \"px-ds-1.5\",\n colorClasses,\n className\n )}\n style={maxWidth ? { maxWidth } : undefined}\n >\n {iconLeft && <Icon name={iconLeft} size={iconSize} className=\"shrink-0\" />}\n {maxWidth ? (\n <span ref={truncRef} className=\"truncate\">{children}</span>\n ) : (\n children\n )}\n {iconRight && <Icon name={iconRight} size={iconSize} className=\"shrink-0\" />}\n </span>\n );\n\n if (isTruncated) {\n return <Tooltip content={children}>{badge}</Tooltip>;\n }\n return badge;\n}\n"]}