@discourser/design-system 0.15.0 → 0.15.1

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.
Files changed (88) hide show
  1. package/dist/{chunk-QC44JPCA.cjs → chunk-ABC7N32K.cjs} +316 -10
  2. package/dist/chunk-ABC7N32K.cjs.map +1 -0
  3. package/dist/{chunk-F7LHARS4.js → chunk-GD6Q2FUE.js} +446 -6
  4. package/dist/chunk-GD6Q2FUE.js.map +1 -0
  5. package/dist/{chunk-M7J7WKJY.js → chunk-SBKRSXSZ.js} +317 -11
  6. package/dist/chunk-SBKRSXSZ.js.map +1 -0
  7. package/dist/{chunk-QP4EJI3G.cjs → chunk-UNWXE6UB.cjs} +450 -2
  8. package/dist/chunk-UNWXE6UB.cjs.map +1 -0
  9. package/dist/components/Breadcrumb.d.ts +9 -0
  10. package/dist/components/Breadcrumb.d.ts.map +1 -0
  11. package/dist/components/Checkbox.d.ts +6 -6
  12. package/dist/components/Icons/ClockIcon.d.ts +6 -0
  13. package/dist/components/Icons/ClockIcon.d.ts.map +1 -0
  14. package/dist/components/Icons/GripDotsVerticalIcon.d.ts +6 -0
  15. package/dist/components/Icons/GripDotsVerticalIcon.d.ts.map +1 -0
  16. package/dist/components/Icons/index.d.ts +3 -0
  17. package/dist/components/Icons/index.d.ts.map +1 -0
  18. package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts +16 -0
  19. package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts.map +1 -0
  20. package/dist/components/ScenarioQueue/ScenarioCard.d.ts +10 -0
  21. package/dist/components/ScenarioQueue/ScenarioCard.d.ts.map +1 -0
  22. package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts +15 -0
  23. package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts.map +1 -0
  24. package/dist/components/ScenarioQueue/ScenarioQueue.d.ts +3 -0
  25. package/dist/components/ScenarioQueue/ScenarioQueue.d.ts.map +1 -0
  26. package/dist/components/ScenarioQueue/index.d.ts +6 -0
  27. package/dist/components/ScenarioQueue/index.d.ts.map +1 -0
  28. package/dist/components/ScenarioQueue/types.d.ts +56 -0
  29. package/dist/components/ScenarioQueue/types.d.ts.map +1 -0
  30. package/dist/components/index.cjs +65 -33
  31. package/dist/components/index.d.ts +4 -0
  32. package/dist/components/index.d.ts.map +1 -1
  33. package/dist/components/index.js +1 -1
  34. package/dist/index.cjs +69 -37
  35. package/dist/index.js +2 -2
  36. package/dist/preset/index.cjs +2 -2
  37. package/dist/preset/index.d.ts.map +1 -1
  38. package/dist/preset/index.js +1 -1
  39. package/dist/preset/recipes/avatar.d.ts.map +1 -1
  40. package/dist/preset/recipes/breadcrumb.d.ts +2 -0
  41. package/dist/preset/recipes/breadcrumb.d.ts.map +1 -0
  42. package/dist/preset/recipes/checkbox.d.ts.map +1 -1
  43. package/dist/preset/recipes/field.d.ts.map +1 -1
  44. package/dist/preset/recipes/index.d.ts +3 -0
  45. package/dist/preset/recipes/index.d.ts.map +1 -1
  46. package/dist/preset/recipes/progress.d.ts.map +1 -1
  47. package/dist/preset/recipes/radio-group.d.ts.map +1 -1
  48. package/dist/preset/recipes/scenario-card.d.ts +2 -0
  49. package/dist/preset/recipes/scenario-card.d.ts.map +1 -0
  50. package/dist/preset/recipes/scenario-queue.d.ts +2 -0
  51. package/dist/preset/recipes/scenario-queue.d.ts.map +1 -0
  52. package/dist/preset/recipes/steps.d.ts.map +1 -1
  53. package/dist/preset/recipes/toast.d.ts.map +1 -1
  54. package/dist/preset/recipes/tooltip.d.ts.map +1 -1
  55. package/dist/preset/semantic-tokens.d.ts +12 -0
  56. package/dist/preset/semantic-tokens.d.ts.map +1 -1
  57. package/package.json +10 -1
  58. package/src/components/Breadcrumb.tsx +34 -0
  59. package/src/components/Icons/ClockIcon.tsx +40 -0
  60. package/src/components/Icons/GripDotsVerticalIcon.tsx +26 -0
  61. package/src/components/Icons/index.ts +2 -0
  62. package/src/components/ScenarioQueue/AddScenarioDialog.tsx +137 -0
  63. package/src/components/ScenarioQueue/ScenarioCard.tsx +120 -0
  64. package/src/components/ScenarioQueue/ScenarioCardDraggable.tsx +41 -0
  65. package/src/components/ScenarioQueue/ScenarioQueue.test.tsx +398 -0
  66. package/src/components/ScenarioQueue/ScenarioQueue.tsx +162 -0
  67. package/src/components/ScenarioQueue/index.ts +11 -0
  68. package/src/components/ScenarioQueue/types.ts +86 -0
  69. package/src/components/index.ts +19 -0
  70. package/src/preset/index.ts +9 -0
  71. package/src/preset/recipes/avatar.ts +1 -2
  72. package/src/preset/recipes/breadcrumb.ts +77 -0
  73. package/src/preset/recipes/checkbox.ts +1 -2
  74. package/src/preset/recipes/field.ts +1 -2
  75. package/src/preset/recipes/index.ts +7 -0
  76. package/src/preset/recipes/progress.ts +1 -2
  77. package/src/preset/recipes/radio-group.ts +1 -2
  78. package/src/preset/recipes/scenario-card.ts +151 -0
  79. package/src/preset/recipes/scenario-queue.ts +99 -0
  80. package/src/preset/recipes/steps.ts +1 -2
  81. package/src/preset/recipes/toast.ts +1 -2
  82. package/src/preset/recipes/tooltip.ts +1 -2
  83. package/src/preset/semantic-tokens.ts +4 -0
  84. package/src/test/setup.ts +12 -0
  85. package/dist/chunk-F7LHARS4.js.map +0 -1
  86. package/dist/chunk-M7J7WKJY.js.map +0 -1
  87. package/dist/chunk-QC44JPCA.cjs.map +0 -1
  88. package/dist/chunk-QP4EJI3G.cjs.map +0 -1
@@ -19,6 +19,9 @@ import { accordion } from './recipes/accordion';
19
19
  import { drawer } from './recipes/drawer';
20
20
  import { tabs } from './recipes/tabs';
21
21
 
22
+ // Park UI recipes - Navigation
23
+ import { breadcrumb } from './recipes/breadcrumb';
24
+
22
25
  // Park UI recipes - Form Elements
23
26
  import { switchRecipe } from './recipes/switch';
24
27
  import { checkbox } from './recipes/checkbox';
@@ -44,6 +47,8 @@ import { heading } from './recipes/heading';
44
47
 
45
48
  // Custom Components
46
49
  import { stepper } from './recipes/stepper';
50
+ import { scenarioCard } from './recipes/scenario-card';
51
+ import { scenarioQueue } from './recipes/scenario-queue';
47
52
 
48
53
  // Park UI theme extensions
49
54
  import { layerStyles } from './layer-styles';
@@ -155,6 +160,8 @@ export const discourserPandaPreset = definePreset({
155
160
  accordion,
156
161
  drawer,
157
162
  tabs,
163
+ // Navigation
164
+ breadcrumb,
158
165
  // Form Elements
159
166
  switchComponent: switchRecipe,
160
167
  checkbox,
@@ -171,6 +178,8 @@ export const discourserPandaPreset = definePreset({
171
178
  tooltip,
172
179
  // Custom Components
173
180
  stepper,
181
+ scenarioCard,
182
+ scenarioQueue,
174
183
  },
175
184
  },
176
185
  },
@@ -1,9 +1,8 @@
1
- import { avatarAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const avatar = defineSlotRecipe({
5
4
  className: 'avatar',
6
- slots: avatarAnatomy.keys(),
5
+ slots: ['root', 'image', 'fallback'],
7
6
  base: {
8
7
  root: {
9
8
  display: 'inline-flex',
@@ -0,0 +1,77 @@
1
+ import { defineSlotRecipe } from '@pandacss/dev'
2
+
3
+ export const breadcrumb = defineSlotRecipe({
4
+ className: 'breadcrumb',
5
+ slots: ['root', 'list', 'link', 'item', 'separator', 'ellipsis'],
6
+ base: {
7
+ list: {
8
+ alignItems: 'center',
9
+ display: 'flex',
10
+ listStyle: 'none',
11
+ wordBreak: 'break-word',
12
+ },
13
+ link: {
14
+ alignItems: 'center',
15
+ borderRadius: 'l1',
16
+ display: 'inline-flex',
17
+ focusRing: 'outside',
18
+ gap: '2',
19
+ outline: '0',
20
+ textDecoration: 'none',
21
+ transition: 'color',
22
+ _icon: { boxSize: '1em' },
23
+ },
24
+ item: {
25
+ display: 'inline-flex',
26
+ alignItems: 'center',
27
+ color: 'fg.muted',
28
+ _last: {
29
+ color: 'fg.default',
30
+ },
31
+ },
32
+ separator: {
33
+ color: 'fg.subtle',
34
+ _icon: { boxSize: '1em' },
35
+ _rtl: { rotate: '180deg' },
36
+ },
37
+ ellipsis: {
38
+ alignItems: 'center',
39
+ color: 'fg.muted',
40
+ display: 'inline-flex',
41
+ justifyContent: 'center',
42
+ _icon: { boxSize: '1em' },
43
+ },
44
+ },
45
+
46
+ variants: {
47
+ variant: {
48
+ underline: {
49
+ link: {
50
+ textDecoration: 'underline',
51
+ textDecorationThickness: '0.1em',
52
+ textUnderlineOffset: '0.125em',
53
+ textDecorationColor: 'fg.subtle',
54
+ _hover: { textDecorationColor: 'fg.default' },
55
+ },
56
+ },
57
+ plain: {
58
+ link: {
59
+ color: 'fg.muted',
60
+ _hover: { color: 'fg.default' },
61
+ _currentPage: { color: 'fg.default' },
62
+ },
63
+ },
64
+ },
65
+ size: {
66
+ xs: { list: { gap: '1', textStyle: 'xs' } },
67
+ sm: { list: { gap: '1', textStyle: 'sm' } },
68
+ md: { list: { gap: '1.5', textStyle: 'md' } },
69
+ lg: { list: { gap: '2', textStyle: 'lg' } },
70
+ },
71
+ },
72
+
73
+ defaultVariants: {
74
+ variant: 'plain',
75
+ size: 'md',
76
+ },
77
+ })
@@ -1,8 +1,7 @@
1
- import { checkboxAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const checkbox = defineSlotRecipe({
5
- slots: checkboxAnatomy.keys(),
4
+ slots: ['root', 'label', 'control', 'indicator', 'group'],
6
5
  className: 'checkbox',
7
6
  base: {
8
7
  root: {
@@ -1,9 +1,8 @@
1
- import { fieldAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const field = defineSlotRecipe({
5
4
  className: 'field',
6
- slots: fieldAnatomy.keys(),
5
+ slots: ['root', 'errorText', 'helperText', 'input', 'label', 'select', 'textarea', 'requiredIndicator'],
7
6
  base: {
8
7
  root: {
9
8
  display: 'flex',
@@ -11,6 +11,9 @@ export * from './accordion';
11
11
  export * from './drawer';
12
12
  export * from './tabs';
13
13
 
14
+ // Navigation
15
+ export * from './breadcrumb';
16
+
14
17
  // Form elements
15
18
  export * from './switch';
16
19
  export * from './checkbox';
@@ -38,3 +41,7 @@ export * from './heading';
38
41
  // Utilities
39
42
  export * from './absolute-center';
40
43
  export * from './group';
44
+
45
+ // Custom Components
46
+ export * from './scenario-card';
47
+ export * from './scenario-queue';
@@ -1,8 +1,7 @@
1
- import { progressAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const progress = defineSlotRecipe({
5
- slots: progressAnatomy.keys(),
4
+ slots: ['root', 'label', 'track', 'range', 'valueText', 'view', 'circle', 'circleTrack', 'circleRange'],
6
5
  className: 'progress',
7
6
  base: {
8
7
  root: {
@@ -1,9 +1,8 @@
1
- import { radioGroupAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const radioGroup = defineSlotRecipe({
5
4
  className: 'radio-group',
6
- slots: radioGroupAnatomy.keys(),
5
+ slots: ['root', 'label', 'item', 'itemText', 'itemControl', 'indicator'],
7
6
  base: {
8
7
  root: {
9
8
  display: 'flex',
@@ -0,0 +1,151 @@
1
+ import { defineSlotRecipe } from '@pandacss/dev'
2
+
3
+ export const scenarioCard = defineSlotRecipe({
4
+ className: 'scenario-card',
5
+ // Slots cover only scenario-specific styling.
6
+ // Base card appearance (bg, border, borderRadius, overflow) comes from
7
+ // Card.Root variant="outline" via the card slot recipe.
8
+ // Layout structure (flex, gap, alignment) comes from Panda CSS JSX primitives.
9
+ slots: ['root', 'positionBadge', 'dragHandle', 'title', 'switchRow', 'switchLabel', 'difficultyBadge', 'durationBadge'],
10
+ base: {
11
+ root: {
12
+ // Left accent border — inactive cards use transparent to preserve card width.
13
+ borderLeftWidth: '3px',
14
+ borderLeftStyle: 'solid',
15
+ borderLeftColor: 'transparent',
16
+ // Custom purple-tinted shadow from Figma spec (0px 2px 8px 0px rgba(167,139,250,0.15)).
17
+ // This overrides Card.Root variant="elevated" (M3 level4) which is too heavy.
18
+ // Raw value intentional: this non-standard shadow has no M3 elevation equivalent.
19
+ boxShadow: '0px 2px 8px 0px rgba(167, 139, 250, 0.15)',
20
+ // Figma cards use neutral/99 (#FDFCF5) — a warm off-white.
21
+ // neutral.surface.bg resolves to white (#FFFFFF) in light mode, so we override.
22
+ bg: 'neutral.1',
23
+ transition: 'border-color',
24
+ transitionDuration: 'normal',
25
+ },
26
+
27
+ // Circle primitive handles: w/h (size="12"), borderRadius:full, flex centering.
28
+ // Recipe handles: visual appearance (border, color, font).
29
+ positionBadge: {
30
+ fontSize: 'md',
31
+ fontWeight: 'semibold',
32
+ flexShrink: 0,
33
+ borderWidth: '1px',
34
+ borderStyle: 'solid',
35
+ borderColor: 'neutral.6',
36
+ color: 'fg.default',
37
+ bg: 'transparent',
38
+ transition: 'all',
39
+ transitionDuration: 'normal',
40
+ },
41
+
42
+ dragHandle: {
43
+ display: 'flex',
44
+ alignItems: 'center',
45
+ justifyContent: 'center',
46
+ // TODO: Create semantic token (e.g. colors.icon.subdued) that maps to secondary.6
47
+ // Using palette bridge value directly until semantic token is defined
48
+ color: 'secondary.6',
49
+ cursor: 'grab',
50
+ flexShrink: 0,
51
+ width: '9',
52
+ height: '10',
53
+ _active: { cursor: 'grabbing' },
54
+ },
55
+
56
+ title: {
57
+ width: 'full',
58
+ fontSize: 'sm',
59
+ fontWeight: 'semibold',
60
+ lineHeight: '1.4',
61
+ color: 'fg.default',
62
+ },
63
+
64
+ // HStack handles: display:flex, alignItems:center, justifyContent:space-between.
65
+ switchRow: {
66
+ width: 'full',
67
+ pt: '1',
68
+ mt: '1',
69
+ },
70
+
71
+ switchLabel: {
72
+ fontSize: 'sm',
73
+ color: 'fg.muted',
74
+ fontWeight: 'medium',
75
+ },
76
+
77
+ // Difficulty pill: M3 container tokens selected by data-difficulty attribute.
78
+ // Default (beginner) = primaryContainer; overridden for intermediate/advanced.
79
+ difficultyBadge: {
80
+ display: 'inline-flex',
81
+ alignItems: 'center',
82
+ borderRadius: 'l2',
83
+ px: '2',
84
+ h: '5',
85
+ fontSize: 'xs',
86
+ fontWeight: 'medium',
87
+ bg: 'm3Primary.container',
88
+ color: 'onM3Primary.container',
89
+ '&[data-difficulty="intermediate"]': {
90
+ bg: 'm3Secondary.container',
91
+ color: 'onM3Secondary.container',
92
+ },
93
+ '&[data-difficulty="advanced"]': {
94
+ bg: 'm3Tertiary.container',
95
+ color: 'onM3Tertiary.container',
96
+ },
97
+ },
98
+
99
+ // Duration pill: per-difficulty inverse palette colors.
100
+ // Default (beginner): inversePrimary → semanticDark.primary (#B1D18A light, #4C662B dark)
101
+ // Intermediate: inverseSecondary → semanticDark.secondary (#BFCBAD light, #586249 dark)
102
+ // Advanced: inverseTertiary → semanticDark.tertiary (#A0D0CB light, #386663 dark)
103
+ durationBadge: {
104
+ display: 'inline-flex',
105
+ alignItems: 'center',
106
+ gap: '1',
107
+ borderRadius: 'l2',
108
+ px: '2',
109
+ h: '5',
110
+ fontSize: 'xs',
111
+ fontWeight: 'medium',
112
+ bg: 'inversePrimary',
113
+ color: 'onSurface',
114
+ '&[data-difficulty="intermediate"]': {
115
+ bg: 'inverseSecondary',
116
+ },
117
+ '&[data-difficulty="advanced"]': {
118
+ bg: 'inverseTertiary',
119
+ },
120
+ },
121
+ },
122
+
123
+ variants: {
124
+ isActive: {
125
+ true: {
126
+ root: {
127
+ borderLeftColor: 'primary.6',
128
+ },
129
+ positionBadge: {
130
+ bg: 'primary.6',
131
+ color: 'white',
132
+ borderColor: 'primary.6',
133
+ },
134
+ },
135
+ false: {},
136
+ },
137
+ isDragging: {
138
+ true: {
139
+ root: {
140
+ opacity: '0.4',
141
+ },
142
+ },
143
+ false: {},
144
+ },
145
+ },
146
+
147
+ defaultVariants: {
148
+ isActive: false,
149
+ isDragging: false,
150
+ },
151
+ })
@@ -0,0 +1,99 @@
1
+ import { defineSlotRecipe } from '@pandacss/dev'
2
+
3
+ export const scenarioQueue = defineSlotRecipe({
4
+ className: 'scenario-queue',
5
+ description: 'Panel component with tabs and draggable scenario cards',
6
+
7
+ slots: [
8
+ 'root',
9
+ 'header',
10
+ 'title',
11
+ 'count',
12
+ 'tabsInner',
13
+ 'tabList',
14
+ 'tabsContent',
15
+ 'scrollArea',
16
+ 'emptyState',
17
+ 'addButtonArea',
18
+ 'addButton',
19
+ ],
20
+
21
+ base: {
22
+ root: {
23
+ display: 'flex',
24
+ flexDirection: 'column',
25
+ h: 'full',
26
+ minH: '320px',
27
+ minW: '250px',
28
+ maxW: '300px',
29
+ overflow: 'hidden',
30
+ bg: 'surface',
31
+ },
32
+
33
+ header: {
34
+ px: '4',
35
+ pt: '4',
36
+ pb: '2',
37
+ flexShrink: 0,
38
+ },
39
+
40
+ title: {
41
+ fontWeight: 'semibold',
42
+ fontSize: 'md',
43
+ color: 'fg.default',
44
+ },
45
+
46
+ count: {
47
+ fontSize: 'xs',
48
+ color: 'fg.muted',
49
+ mt: '0.5',
50
+ },
51
+
52
+ // Applied directly to Tabs.Root className — handles the flex column growth
53
+ // and scroll containment without a redundant wrapper div.
54
+ tabsInner: {
55
+ flex: '1',
56
+ minHeight: '0',
57
+ overflow: 'hidden',
58
+ display: 'flex',
59
+ flexDirection: 'column',
60
+ },
61
+
62
+ tabList: {
63
+ px: '4',
64
+ flexShrink: 0,
65
+ },
66
+
67
+ // Applied to each Tabs.Content — allows independent scrolling per tab.
68
+ tabsContent: {
69
+ flex: '1',
70
+ minHeight: '0',
71
+ overflowY: 'auto',
72
+ },
73
+
74
+ // Block layout so cards size to natural height and the container scrolls.
75
+ scrollArea: {
76
+ px: '4',
77
+ py: '3',
78
+ '& > * + *': { marginTop: '6' },
79
+ },
80
+
81
+ // Center primitive handles the flex centering; recipe provides spacing/text.
82
+ emptyState: {
83
+ py: '12',
84
+ fontSize: 'sm',
85
+ color: 'fg.muted',
86
+ },
87
+
88
+ addButtonArea: {
89
+ px: '4',
90
+ pb: '4',
91
+ pt: '2',
92
+ flexShrink: 0,
93
+ },
94
+
95
+ addButton: {
96
+ width: 'full',
97
+ },
98
+ },
99
+ })
@@ -1,8 +1,7 @@
1
- import { stepsAnatomy } from '@ark-ui/react/anatomy';
2
1
  import { defineSlotRecipe } from '@pandacss/dev';
3
2
 
4
3
  export const steps = defineSlotRecipe({
5
4
  className: 'steps',
6
- slots: stepsAnatomy.keys(),
5
+ slots: ['root', 'list', 'item', 'trigger', 'indicator', 'separator', 'content', 'nextTrigger', 'prevTrigger', 'progress'],
7
6
  base: {},
8
7
  });
@@ -1,9 +1,8 @@
1
- import { toastAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const toast = defineSlotRecipe({
5
4
  className: 'toast',
6
- slots: toastAnatomy.keys(),
5
+ slots: ['group', 'root', 'title', 'description', 'actionTrigger', 'closeTrigger'],
7
6
  base: {
8
7
  root: {
9
8
  alignItems: 'start',
@@ -1,9 +1,8 @@
1
- import { tooltipAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const tooltip = defineSlotRecipe({
5
4
  className: 'tooltip',
6
- slots: tooltipAnatomy.keys(),
5
+ slots: ['trigger', 'arrow', 'arrowTip', 'positioner', 'content'],
7
6
  base: {
8
7
  content: {
9
8
  '--tooltip-bg': 'colors.gray.solid.bg',
@@ -69,6 +69,10 @@ export const m3SemanticTokens = defineSemanticTokens.colors({
69
69
  inverseSurface: { value: { base: semantic.inverseSurface, _dark: semanticDark.inverseSurface } },
70
70
  inverseOnSurface: { value: { base: semantic.inverseOnSurface, _dark: semanticDark.inverseOnSurface } },
71
71
  inversePrimary: { value: { base: semantic.inversePrimary, _dark: semanticDark.inversePrimary } },
72
+ // Not standard M3 tokens, but follow inversePrimary's pattern:
73
+ // light mode = dark-palette value, dark mode = light-palette value.
74
+ inverseSecondary: { value: { base: semanticDark.secondary, _dark: semantic.secondary } },
75
+ inverseTertiary: { value: { base: semanticDark.tertiary, _dark: semantic.tertiary } },
72
76
 
73
77
  // Scrim/Shadow
74
78
  scrim: { value: { base: semantic.scrim, _dark: semanticDark.scrim } },
package/src/test/setup.ts CHANGED
@@ -6,6 +6,18 @@ import { toHaveNoViolations } from 'jest-axe';
6
6
  // Extend Vitest's expect with jest-axe matchers
7
7
  expect.extend(toHaveNoViolations);
8
8
 
9
+ // Mock ResizeObserver — JSDOM does not implement it, but @zag-js/tabs requires
10
+ // it to sync the active tab indicator rectangle position.
11
+ Object.defineProperty(window, 'ResizeObserver', {
12
+ writable: true,
13
+ configurable: true,
14
+ value: class ResizeObserver {
15
+ observe() {}
16
+ unobserve() {}
17
+ disconnect() {}
18
+ },
19
+ });
20
+
9
21
  // Cleanup after each test
10
22
  afterEach(() => {
11
23
  cleanup();