@ds-mo/ui 0.1.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.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/dist/index.css +1 -0
  4. package/dist/index.d.ts +969 -0
  5. package/dist/index.js +2623 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/r/accordion.json +79 -0
  8. package/dist/r/badge.json +69 -0
  9. package/dist/r/banner.json +90 -0
  10. package/dist/r/breadcrumb.json +70 -0
  11. package/dist/r/button-group.json +65 -0
  12. package/dist/r/button.json +137 -0
  13. package/dist/r/card.json +82 -0
  14. package/dist/r/checkbox.json +91 -0
  15. package/dist/r/divider.json +57 -0
  16. package/dist/r/empty-state.json +69 -0
  17. package/dist/r/error-boundary.json +66 -0
  18. package/dist/r/fade.json +65 -0
  19. package/dist/r/field.json +71 -0
  20. package/dist/r/header.json +67 -0
  21. package/dist/r/input.json +77 -0
  22. package/dist/r/loader.json +54 -0
  23. package/dist/r/menu.json +130 -0
  24. package/dist/r/modal.json +93 -0
  25. package/dist/r/pagination.json +80 -0
  26. package/dist/r/radio.json +85 -0
  27. package/dist/r/registry.json +1600 -0
  28. package/dist/r/scrollbar.json +68 -0
  29. package/dist/r/select.json +93 -0
  30. package/dist/r/sidebar.json +105 -0
  31. package/dist/r/skeleton.json +72 -0
  32. package/dist/r/slider.json +87 -0
  33. package/dist/r/surface.json +97 -0
  34. package/dist/r/tab-group.json +72 -0
  35. package/dist/r/tab.json +71 -0
  36. package/dist/r/table.json +110 -0
  37. package/dist/r/tag.json +110 -0
  38. package/dist/r/text.json +94 -0
  39. package/dist/r/toast.json +77 -0
  40. package/dist/r/toggle-button-group.json +65 -0
  41. package/dist/r/toggle-button.json +94 -0
  42. package/dist/r/toggle.json +65 -0
  43. package/dist/r/tooltip.json +86 -0
  44. package/package.json +80 -0
  45. package/src/components/Accordion/Accordion.module.css +66 -0
  46. package/src/components/Accordion/Accordion.stories.tsx +95 -0
  47. package/src/components/Accordion/Accordion.tsx +122 -0
  48. package/src/components/Accordion/index.ts +2 -0
  49. package/src/components/Badge/Badge.module.css +6 -0
  50. package/src/components/Badge/Badge.stories.tsx +99 -0
  51. package/src/components/Badge/Badge.tsx +23 -0
  52. package/src/components/Badge/index.ts +2 -0
  53. package/src/components/Banner/Banner.module.css +67 -0
  54. package/src/components/Banner/Banner.stories.tsx +81 -0
  55. package/src/components/Banner/Banner.tsx +109 -0
  56. package/src/components/Banner/index.ts +2 -0
  57. package/src/components/Breadcrumb/Breadcrumb.module.css +46 -0
  58. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +72 -0
  59. package/src/components/Breadcrumb/Breadcrumb.tsx +68 -0
  60. package/src/components/Breadcrumb/index.ts +2 -0
  61. package/src/components/Button/Button.module.css +343 -0
  62. package/src/components/Button/Button.stories.tsx +575 -0
  63. package/src/components/Button/Button.tsx +201 -0
  64. package/src/components/Button/index.ts +10 -0
  65. package/src/components/ButtonGroup/ButtonGroup.module.css +157 -0
  66. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +384 -0
  67. package/src/components/ButtonGroup/ButtonGroup.tsx +122 -0
  68. package/src/components/ButtonGroup/index.ts +2 -0
  69. package/src/components/Card/Card.module.css +36 -0
  70. package/src/components/Card/Card.stories.tsx +78 -0
  71. package/src/components/Card/Card.tsx +53 -0
  72. package/src/components/Card/index.ts +2 -0
  73. package/src/components/Checkbox/Checkbox.module.css +47 -0
  74. package/src/components/Checkbox/Checkbox.stories.tsx +129 -0
  75. package/src/components/Checkbox/Checkbox.tsx +68 -0
  76. package/src/components/Checkbox/index.ts +2 -0
  77. package/src/components/Divider/Divider.module.css +19 -0
  78. package/src/components/Divider/Divider.stories.tsx +55 -0
  79. package/src/components/Divider/Divider.tsx +28 -0
  80. package/src/components/Divider/index.ts +2 -0
  81. package/src/components/EmptyState/EmptyState.module.css +8 -0
  82. package/src/components/EmptyState/EmptyState.stories.tsx +53 -0
  83. package/src/components/EmptyState/EmptyState.tsx +31 -0
  84. package/src/components/EmptyState/index.ts +2 -0
  85. package/src/components/ErrorBoundary/ErrorBoundary.module.css +18 -0
  86. package/src/components/ErrorBoundary/ErrorBoundary.stories.tsx +38 -0
  87. package/src/components/ErrorBoundary/ErrorBoundary.tsx +50 -0
  88. package/src/components/ErrorBoundary/index.ts +2 -0
  89. package/src/components/Fade/Fade.module.css +31 -0
  90. package/src/components/Fade/Fade.stories.tsx +35 -0
  91. package/src/components/Fade/Fade.tsx +33 -0
  92. package/src/components/Fade/index.ts +2 -0
  93. package/src/components/Field/Field.module.css +5 -0
  94. package/src/components/Field/Field.stories.tsx +111 -0
  95. package/src/components/Field/Field.tsx +22 -0
  96. package/src/components/Field/index.ts +2 -0
  97. package/src/components/Header/Header.module.css +33 -0
  98. package/src/components/Header/Header.stories.tsx +52 -0
  99. package/src/components/Header/Header.tsx +42 -0
  100. package/src/components/Header/index.ts +2 -0
  101. package/src/components/Input/Input.module.css +71 -0
  102. package/src/components/Input/Input.stories.tsx +135 -0
  103. package/src/components/Input/Input.tsx +76 -0
  104. package/src/components/Input/index.ts +2 -0
  105. package/src/components/LabelWrap/LabelWrap.module.css +34 -0
  106. package/src/components/LabelWrap/LabelWrap.tsx +32 -0
  107. package/src/components/LabelWrap/index.ts +1 -0
  108. package/src/components/Loader/Loader.module.css +9 -0
  109. package/src/components/Loader/Loader.stories.tsx +44 -0
  110. package/src/components/Loader/Loader.tsx +43 -0
  111. package/src/components/Loader/index.ts +2 -0
  112. package/src/components/Menu/DestructiveMenuItem.module.css +57 -0
  113. package/src/components/Menu/DestructiveMenuItem.tsx +64 -0
  114. package/src/components/Menu/Menu.module.css +49 -0
  115. package/src/components/Menu/Menu.stories.tsx +129 -0
  116. package/src/components/Menu/Menu.tsx +269 -0
  117. package/src/components/Menu/MenuItem.module.css +84 -0
  118. package/src/components/Menu/MenuItem.tsx +101 -0
  119. package/src/components/Menu/index.ts +6 -0
  120. package/src/components/Modal/Modal.module.css +67 -0
  121. package/src/components/Modal/Modal.stories.tsx +71 -0
  122. package/src/components/Modal/Modal.tsx +122 -0
  123. package/src/components/Modal/index.ts +2 -0
  124. package/src/components/Pagination/Pagination.module.css +64 -0
  125. package/src/components/Pagination/Pagination.stories.tsx +51 -0
  126. package/src/components/Pagination/Pagination.tsx +103 -0
  127. package/src/components/Pagination/index.ts +2 -0
  128. package/src/components/Radio/Radio.module.css +61 -0
  129. package/src/components/Radio/Radio.stories.tsx +101 -0
  130. package/src/components/Radio/Radio.tsx +108 -0
  131. package/src/components/Radio/index.ts +2 -0
  132. package/src/components/Scrollbar/Scrollbar.module.css +81 -0
  133. package/src/components/Scrollbar/Scrollbar.stories.tsx +46 -0
  134. package/src/components/Scrollbar/Scrollbar.tsx +187 -0
  135. package/src/components/Scrollbar/index.ts +2 -0
  136. package/src/components/Select/Select.module.css +31 -0
  137. package/src/components/Select/Select.stories.tsx +59 -0
  138. package/src/components/Select/Select.tsx +92 -0
  139. package/src/components/Select/index.ts +2 -0
  140. package/src/components/Sidebar/Sidebar.module.css +161 -0
  141. package/src/components/Sidebar/Sidebar.stories.tsx +125 -0
  142. package/src/components/Sidebar/Sidebar.tsx +129 -0
  143. package/src/components/Sidebar/SidebarItem.tsx +57 -0
  144. package/src/components/Sidebar/SidebarSection.tsx +42 -0
  145. package/src/components/Sidebar/index.ts +6 -0
  146. package/src/components/Skeleton/Skeleton.module.css +53 -0
  147. package/src/components/Skeleton/Skeleton.stories.tsx +89 -0
  148. package/src/components/Skeleton/Skeleton.tsx +66 -0
  149. package/src/components/Skeleton/index.ts +2 -0
  150. package/src/components/Slider/Slider.module.css +62 -0
  151. package/src/components/Slider/Slider.stories.tsx +120 -0
  152. package/src/components/Slider/Slider.tsx +74 -0
  153. package/src/components/Slider/index.ts +2 -0
  154. package/src/components/Surface/Surface.module.css +222 -0
  155. package/src/components/Surface/Surface.stories.tsx +213 -0
  156. package/src/components/Surface/Surface.tsx +219 -0
  157. package/src/components/Surface/index.ts +12 -0
  158. package/src/components/Tab/Tab.module.css +66 -0
  159. package/src/components/Tab/Tab.stories.tsx +103 -0
  160. package/src/components/Tab/Tab.tsx +41 -0
  161. package/src/components/Tab/index.ts +2 -0
  162. package/src/components/TabGroup/TabGroup.module.css +26 -0
  163. package/src/components/TabGroup/TabGroup.stories.tsx +60 -0
  164. package/src/components/TabGroup/TabGroup.tsx +83 -0
  165. package/src/components/TabGroup/index.ts +2 -0
  166. package/src/components/Table/Table.module.css +130 -0
  167. package/src/components/Table/Table.stories.tsx +111 -0
  168. package/src/components/Table/Table.tsx +226 -0
  169. package/src/components/Table/index.ts +11 -0
  170. package/src/components/Tag/Tag.module.css +270 -0
  171. package/src/components/Tag/Tag.stories.tsx +399 -0
  172. package/src/components/Tag/Tag.tsx +162 -0
  173. package/src/components/Tag/index.ts +2 -0
  174. package/src/components/Text/Text.module.css +166 -0
  175. package/src/components/Text/Text.stories.tsx +212 -0
  176. package/src/components/Text/Text.tsx +181 -0
  177. package/src/components/Text/index.ts +13 -0
  178. package/src/components/Toast/Toast.module.css +91 -0
  179. package/src/components/Toast/Toast.stories.tsx +66 -0
  180. package/src/components/Toast/Toast.tsx +182 -0
  181. package/src/components/Toast/index.ts +8 -0
  182. package/src/components/Toggle/Toggle.module.css +87 -0
  183. package/src/components/Toggle/Toggle.stories.tsx +85 -0
  184. package/src/components/Toggle/Toggle.tsx +52 -0
  185. package/src/components/Toggle/index.ts +2 -0
  186. package/src/components/ToggleButton/ToggleButton.module.css +172 -0
  187. package/src/components/ToggleButton/ToggleButton.stories.tsx +299 -0
  188. package/src/components/ToggleButton/ToggleButton.tsx +118 -0
  189. package/src/components/ToggleButton/index.ts +2 -0
  190. package/src/components/ToggleButtonGroup/ToggleButtonGroup.module.css +163 -0
  191. package/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx +341 -0
  192. package/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx +111 -0
  193. package/src/components/ToggleButtonGroup/index.ts +2 -0
  194. package/src/components/Tooltip/Tooltip.module.css +45 -0
  195. package/src/components/Tooltip/Tooltip.stories.tsx +46 -0
  196. package/src/components/Tooltip/Tooltip.tsx +224 -0
  197. package/src/components/Tooltip/index.ts +2 -0
  198. package/src/css-modules.d.ts +4 -0
  199. package/src/docs/ColorUsage.mdx +195 -0
  200. package/src/docs/ElevationUsage.mdx +95 -0
  201. package/src/docs/Introduction.mdx +97 -0
  202. package/src/docs/OpticalSizing.mdx +178 -0
  203. package/src/docs/TypographyUsage.mdx +121 -0
  204. package/src/index.ts +154 -0
  205. package/src/stories/Colors.stories.tsx +528 -0
  206. package/src/stories/Dimensions.stories.tsx +200 -0
  207. package/src/stories/Effects.stories.tsx +231 -0
  208. package/src/stories/Icons.stories.tsx +159 -0
  209. package/src/stories/Typography.stories.tsx +157 -0
  210. package/src/types/icons.ts +15 -0
  211. package/src/types/index.ts +1 -0
  212. package/src/utils/cn.ts +6 -0
  213. package/src/utils/css-tokens.ts +22 -0
@@ -0,0 +1,201 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { cn } from '@/utils/cn';
3
+ import { Text } from '@/components/Text';
4
+ import { LabelWrap } from '@/components/LabelWrap';
5
+ import { Loader } from '@/components/Loader';
6
+ import { ChevronDown } from '@ds-mo/icons';
7
+ import type { IconComponent } from '@/types/icons';
8
+ import styles from './Button.module.css';
9
+
10
+ export type ButtonVariant = 'primary' | 'secondary';
11
+ export type ButtonElevation = 'none' | 'flat' | 'elevated' | 'floating';
12
+ export type ButtonIntent = 'none' | 'neutral' | 'brand' | 'ai' | 'negative' | 'warning' | 'caution' | 'positive';
13
+ export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg';
14
+ export type ButtonContrast = 'strong' | 'bold' | 'medium' | 'faint';
15
+ export type ButtonBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'always-dark';
16
+
17
+ export interface ButtonProps {
18
+ /** Polymorphic element type. Defaults to 'button'. */
19
+ as?: React.ElementType;
20
+ /** Visual variant. Defaults to 'primary'. */
21
+ variant?: ButtonVariant;
22
+ /** Label text. */
23
+ label?: string;
24
+ /** Leading icon component from @ds-mo/icons. */
25
+ icon?: IconComponent;
26
+ /** Semantic intent. Defaults to 'brand'. */
27
+ intent?: ButtonIntent;
28
+ /** Size. Defaults to 'md'. */
29
+ size?: ButtonSize;
30
+ /** Pill shape with extra padding. */
31
+ rounded?: boolean;
32
+ /** Fill container width. Label truncates if space is tight. */
33
+ fullWidth?: boolean;
34
+ /** Fixed width. Omit for content width. */
35
+ width?: React.CSSProperties['width'];
36
+ /** Contrast level (primary variant only). Defaults to 'bold'. */
37
+ contrast?: ButtonContrast;
38
+ /**
39
+ * Parent surface context. Adjusts hover tokens and secondary fg colors
40
+ * for buttons placed on colored backgrounds.
41
+ * faint — default, no adjustment
42
+ * medium — use on-medium-background interaction tokens
43
+ * bold — use on-bold-background interaction + inverted fg
44
+ * strong — use on-strong-background interaction + inverted fg
45
+ * always-dark — dark surface (no theme flip), on-bold-background tokens
46
+ */
47
+ background?: ButtonBackground;
48
+ /** Show trailing ChevronDown dropdown indicator. */
49
+ dropdown?: boolean;
50
+ /** Badge count. */
51
+ badgeCount?: number;
52
+ /**
53
+ * Chrome level for secondary; shadow level for primary.
54
+ * none — ghost (transparent, no border, no shadow)
55
+ * flat — border only, transparent bg [default for secondary]
56
+ * elevated — bg-primary + shadow [default for primary]
57
+ * floating — bg-primary + FAB-strength shadow
58
+ */
59
+ elevation?: ButtonElevation;
60
+ /** Show loading spinner in place of icon. Prevents interaction. */
61
+ loading?: boolean;
62
+
63
+ onClick?: (e: React.MouseEvent<HTMLElement>) => void;
64
+ onMouseEnter?: React.MouseEventHandler<HTMLElement>;
65
+ onMouseLeave?: React.MouseEventHandler<HTMLElement>;
66
+ className?: string;
67
+ inactive?: boolean;
68
+ type?: 'button' | 'submit' | 'reset';
69
+ id?: string;
70
+ 'aria-label'?: string;
71
+ href?: string;
72
+ target?: string;
73
+ rel?: string;
74
+ }
75
+
76
+ const ICON_SIZE_MAP: Record<ButtonSize, number> = { xs: 12, sm: 16, md: 20, lg: 24 };
77
+
78
+ const TEXT_STYLE_MAP: Record<ButtonSize, string> = {
79
+ xs: 'text-caption-emphasis',
80
+ sm: 'text-body-small-emphasis',
81
+ md: 'text-body-medium-emphasis',
82
+ lg: 'text-body-large-emphasis',
83
+ };
84
+
85
+
86
+ export const Button = forwardRef<HTMLElement, ButtonProps>(
87
+ (
88
+ {
89
+ as,
90
+ variant = 'primary',
91
+ label,
92
+ icon: Icon,
93
+ intent = 'brand',
94
+ size = 'md',
95
+ rounded = false,
96
+ fullWidth = false,
97
+ width,
98
+ contrast = 'bold',
99
+ background,
100
+ dropdown = false,
101
+ badgeCount,
102
+ elevation,
103
+ loading = false,
104
+ onClick,
105
+ onMouseEnter,
106
+ onMouseLeave,
107
+ className,
108
+ inactive = false,
109
+ type = 'button',
110
+ id,
111
+ 'aria-label': ariaLabel,
112
+ href,
113
+ target,
114
+ rel,
115
+ },
116
+ ref
117
+ ) => {
118
+ const Component = (as ?? 'button') as React.ElementType;
119
+ const isButton = !as || as === 'button';
120
+ const isInteractive = !inactive && !loading;
121
+
122
+ const hasIcon = !!Icon;
123
+ const hasLabel = !!label;
124
+ const showLeading = hasIcon || loading; // spinner fills the icon slot
125
+ const isIconOnly = showLeading && !hasLabel;
126
+ const isLabelOnly = hasLabel && !showLeading;
127
+ const isIconAndLabel = showLeading && hasLabel;
128
+ const iconSize = ICON_SIZE_MAP[size];
129
+ const textStyle = TEXT_STYLE_MAP[size];
130
+
131
+ // Derive default elevation per variant: primary→none, secondary→flat
132
+ const effectiveElevation: ButtonElevation = elevation ?? (variant === 'primary' ? 'none' : 'flat');
133
+ const elevKey = effectiveElevation.charAt(0).toUpperCase() + effectiveElevation.slice(1);
134
+
135
+ const classes = cn(
136
+ styles.button,
137
+ styles[variant],
138
+ styles[`intent${intent.charAt(0).toUpperCase() + intent.slice(1)}`],
139
+ size !== 'md' && styles[`size${size.toUpperCase()}`],
140
+ rounded && styles.rounded,
141
+ (inactive || loading) && styles.inactive,
142
+ isIconOnly && styles.iconOnly,
143
+ isLabelOnly && styles.labelOnly,
144
+ isIconAndLabel && styles.iconAndLabel,
145
+ dropdown && styles.dropdown,
146
+ variant === 'primary' && intent !== 'none' && contrast !== 'bold' && styles[`contrast${contrast.charAt(0).toUpperCase() + contrast.slice(1)}`],
147
+ styles[`elevation${elevKey}`],
148
+ background && background !== 'faint' && styles[
149
+ background === 'always-dark' ? 'onAlwaysDark'
150
+ : `on${background.charAt(0).toUpperCase() + background.slice(1)}`
151
+ ],
152
+ fullWidth && styles.fullWidth,
153
+ className,
154
+ );
155
+
156
+ const resolvedAriaLabel = ariaLabel || label || 'button';
157
+
158
+ const extraProps: Record<string, unknown> = {};
159
+ if (isButton) {
160
+ extraProps.type = type;
161
+ extraProps.disabled = inactive || loading;
162
+ }
163
+ if (href) extraProps.href = href;
164
+ if (target) extraProps.target = target;
165
+ if (rel) extraProps.rel = rel;
166
+
167
+ return (
168
+ <Component
169
+ ref={ref}
170
+ id={id}
171
+ className={classes}
172
+ style={width ? { width } : undefined}
173
+ onClick={isInteractive ? onClick : undefined}
174
+ onMouseEnter={isInteractive ? onMouseEnter : undefined}
175
+ onMouseLeave={isInteractive ? onMouseLeave : undefined}
176
+ aria-label={resolvedAriaLabel}
177
+ aria-busy={loading || undefined}
178
+ {...extraProps}
179
+ >
180
+ {loading ? (
181
+ <Loader size={iconSize} />
182
+ ) : (
183
+ Icon && <Icon size={iconSize} />
184
+ )}
185
+ {label && (
186
+ <LabelWrap truncate={fullWidth}>
187
+ <Text variant={textStyle as never} as="span" color="inherit">
188
+ {label}
189
+ </Text>
190
+ </LabelWrap>
191
+ )}
192
+ {dropdown && <ChevronDown size={iconSize} />}
193
+ {badgeCount != null && badgeCount > 0 && (
194
+ <span className={styles.badge}>{badgeCount > 9 ? '+' : badgeCount}</span>
195
+ )}
196
+ </Component>
197
+ );
198
+ }
199
+ );
200
+
201
+ Button.displayName = 'Button';
@@ -0,0 +1,10 @@
1
+ export { Button } from './Button';
2
+ export type {
3
+ ButtonProps,
4
+ ButtonVariant,
5
+ ButtonElevation,
6
+ ButtonIntent,
7
+ ButtonSize,
8
+ ButtonContrast,
9
+ ButtonBackground,
10
+ } from './Button';
@@ -0,0 +1,157 @@
1
+ /* ==========================================================================
2
+ ButtonGroup — flush row of Button components
3
+ ========================================================================== */
4
+
5
+ .group {
6
+ display: inline-flex;
7
+ align-items: stretch;
8
+ border-radius: var(--dimension-radius-050);
9
+ position: relative;
10
+ }
11
+
12
+ /* Inset ring / highlight overlay — sits above button children so it isn't
13
+ covered by their backgrounds. Outset shadow stays on the element itself. */
14
+ .group::after {
15
+ content: '';
16
+ position: absolute;
17
+ inset: 0;
18
+ border-radius: inherit;
19
+ pointer-events: none;
20
+ }
21
+
22
+ /* ── Group-level elevation ──────────────────────────────────────────────── */
23
+ /* Individual item chrome is suppressed via .item — the group container
24
+ carries the elevation so it wraps the whole control as one visual unit. */
25
+
26
+ /* Outset shadow on element; inset highlight on ::after (children would cover it otherwise) */
27
+ .groupElevated { box-shadow: var(--effect-shadow-elevated-sm); }
28
+ .groupElevated::after { box-shadow: var(--effect-highlight-elevated-sm); }
29
+
30
+ /* Flat ring lives entirely on ::after — same reasoning as highlight */
31
+ .groupFlat::after { box-shadow: inset 0 0 0 1px var(--color-border-tertiary); }
32
+
33
+ .groupFloating { box-shadow: var(--effect-shadow-elevated-floating); }
34
+ .groupFloating::after { box-shadow: var(--effect-highlight-elevated-floating); }
35
+ /* groupNone = ghost / primary — no shadow, no border */
36
+
37
+ .groupRounded { border-radius: var(--dimension-radius-half); }
38
+
39
+ /* ── Base item ─────────────────────────────────────────────────────────────── */
40
+
41
+ .item {
42
+ flex-shrink: 0;
43
+ /* Suppress individual elevation so the group reads as one unit */
44
+ box-shadow: none !important;
45
+ /* Suppress noElevation individual border — divider handles separation */
46
+ border: none !important;
47
+ /* Suppress scale-on-press: transform would detach the divider from the
48
+ adjacent button, creating a visible gap. Pressed state is still
49
+ communicated via the ::after overlay colour. */
50
+ }
51
+ .item:active {
52
+ transform: none !important;
53
+ }
54
+
55
+ /* ── Divider ────────────────────────────────────────────────────────────────
56
+ * Rendered as a separate <div> between items so it is never part of any
57
+ * button's stacking context — disabled (opacity: 0.5) or other state changes
58
+ * on adjacent buttons cannot bleed through to the divider.
59
+ * ─────────────────────────────────────────────────────────────────────────── */
60
+
61
+ /* Full-height divider (secondary + primary).
62
+ * Overlaps each neighbour by 0.5px and paints on top so the alpha token
63
+ * composites over the button fill rather than the page background. */
64
+ .divider {
65
+ width: 1px;
66
+ flex-shrink: 0;
67
+ background-color: var(--color-border-tertiary);
68
+ position: relative;
69
+ z-index: 1;
70
+ margin: 0 -0.5px;
71
+ pointer-events: none;
72
+ }
73
+
74
+ /* Ghost (elevation=none): short centred divider at icon height */
75
+ .dividerGhost { height: 20px; align-self: center; } /* md default */
76
+ .dividerGhostXS { height: 12px; }
77
+ .dividerGhostSM { height: 16px; }
78
+ .dividerGhostLG { height: 24px; }
79
+
80
+ /* Primary intent: on-background divider colour */
81
+ .dividerBold { background-color: var(--color-border-on-bold-background-tertiary); }
82
+ .dividerMedium { background-color: var(--color-border-on-medium-background-tertiary); }
83
+ .dividerStrong { background-color: var(--color-border-on-strong-background-tertiary); }
84
+
85
+ /* Ghost hover-fade: hide the divider on either side of a hovered ghost button */
86
+ .item:hover + .dividerGhost,
87
+ .dividerGhost:has(+ .item:hover) { opacity: 0; }
88
+
89
+ /* ── Ghost interaction states: rounded chips ──────────────────────────────────
90
+ * Ghost items have no group chrome — hover/press overlay should appear as
91
+ * fully rounded chips (4px all corners). Rounded groups: outer corners of
92
+ * first/last become pill; inner corners stay 4px.
93
+ *
94
+ * Uses .group ancestor (2-class specificity) to reliably beat .first / .last /
95
+ * .middle (1-class) regardless of load order. Explicit longhands throughout to
96
+ * avoid shorthand/longhand cascade ambiguity with the !important rules above.
97
+ * ─────────────────────────────────────────────────────────────────────────── */
98
+
99
+ /* ::after hover/press overlay — all positions: 4px all corners */
100
+ .group .itemShort::after {
101
+ border-top-left-radius: var(--dimension-radius-050) !important;
102
+ border-top-right-radius: var(--dimension-radius-050) !important;
103
+ border-bottom-right-radius: var(--dimension-radius-050) !important;
104
+ border-bottom-left-radius: var(--dimension-radius-050) !important;
105
+ }
106
+
107
+ /* ── Border radius: flatten only the inner-facing corners ──────────────────── */
108
+ /* Outer corners keep the button's own radius — works for default + rounded */
109
+
110
+ .first {
111
+ border-top-right-radius: 0 !important;
112
+ border-bottom-right-radius: 0 !important;
113
+ }
114
+
115
+ .last {
116
+ border-top-left-radius: 0 !important;
117
+ border-bottom-left-radius: 0 !important;
118
+ }
119
+
120
+ .middle {
121
+ border-radius: 0 !important;
122
+ }
123
+
124
+ /* Rounded group: first — left corners become pill (3-class specificity) */
125
+ .groupRounded .first.itemShort::after {
126
+ border-top-left-radius: var(--dimension-radius-half) !important;
127
+ border-bottom-left-radius: var(--dimension-radius-half) !important;
128
+ }
129
+
130
+ /* Rounded group: last — right corners become pill (3-class specificity) */
131
+ .groupRounded .last.itemShort::after {
132
+ border-top-right-radius: var(--dimension-radius-half) !important;
133
+ border-bottom-right-radius: var(--dimension-radius-half) !important;
134
+ }
135
+
136
+ /* ── Rounded: inner-edge padding correction ────────────────────────────────── */
137
+ /*
138
+ * Rounded buttons add `space-050` extra padding on each side.
139
+ * In a group the inner-facing edge should use base (non-pill) padding instead.
140
+ * These classes are injected by ButtonGroup for first/last/mid × size.
141
+ * XS rounded has no extra padding so needs no correction.
142
+ */
143
+
144
+ /* MD — base padding: space-100 */
145
+ .roundedFirstMD { padding-right: var(--dimension-space-100) !important; }
146
+ .roundedLastMD { padding-left: var(--dimension-space-100) !important; }
147
+ .roundedMidMD { padding-left: var(--dimension-space-100) !important; padding-right: var(--dimension-space-100) !important; }
148
+
149
+ /* SM — base padding: space-075 */
150
+ .roundedFirstSM { padding-right: var(--dimension-space-075) !important; }
151
+ .roundedLastSM { padding-left: var(--dimension-space-075) !important; }
152
+ .roundedMidSM { padding-left: var(--dimension-space-075) !important; padding-right: var(--dimension-space-075) !important; }
153
+
154
+ /* LG — base padding: space-150 */
155
+ .roundedFirstLG { padding-right: var(--dimension-space-150) !important; }
156
+ .roundedLastLG { padding-left: var(--dimension-space-150) !important; }
157
+ .roundedMidLG { padding-left: var(--dimension-space-150) !important; padding-right: var(--dimension-space-150) !important; }