@nationaldesignstudio/react 0.0.16 → 0.0.19
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/component-registry.md +181 -29
- package/dist/components/atoms/background/background.d.ts +135 -0
- package/dist/components/atoms/button/button.d.ts +64 -82
- package/dist/components/atoms/button/icon-button.d.ts +100 -66
- package/dist/components/organisms/card/card.d.ts +130 -4
- package/dist/components/organisms/us-gov-banner/us-gov-banner.d.ts +120 -2
- package/dist/components/sections/hero/hero.d.ts +166 -150
- package/dist/components/sections/quote-block/quote-block.d.ts +152 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +3868 -5978
- package/dist/index.js.map +1 -1
- package/dist/lib/utils.d.ts +1 -2
- package/dist/tokens.css +336 -145
- package/package.json +2 -4
- package/src/components/atoms/background/background.tsx +377 -0
- package/src/components/atoms/background/index.ts +22 -0
- package/src/components/atoms/button/button.stories.tsx +81 -32
- package/src/components/atoms/button/button.tsx +91 -49
- package/src/components/atoms/button/icon-button.stories.tsx +179 -28
- package/src/components/atoms/button/icon-button.tsx +111 -49
- package/src/components/organisms/card/card.tsx +82 -24
- package/src/components/organisms/card/index.ts +7 -0
- package/src/components/organisms/us-gov-banner/index.ts +5 -1
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +72 -16
- package/src/components/sections/hero/hero.stories.tsx +124 -1
- package/src/components/sections/hero/hero.test.tsx +21 -18
- package/src/components/sections/hero/hero.tsx +188 -301
- package/src/components/sections/hero/index.ts +13 -0
- package/src/components/sections/quote-block/index.ts +5 -0
- package/src/components/sections/quote-block/quote-block.tsx +216 -0
- package/src/index.ts +40 -0
- package/src/lib/utils.ts +1 -6
- package/src/stories/ThemeProvider.stories.tsx +5 -5
|
@@ -10,16 +10,14 @@ import { type ButtonTheme, buttonThemeToStyleVars } from "../../../lib/theme";
|
|
|
10
10
|
* Button component based on Figma BaseKit / Interface / Buttons
|
|
11
11
|
*
|
|
12
12
|
* Variants:
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* -
|
|
20
|
-
* -
|
|
21
|
-
* - ivoryOutlineQuiet: Subtle light outlined button (for dark backgrounds)
|
|
22
|
-
* - gray: Gray filled button (for dark backgrounds)
|
|
13
|
+
* - solid: Filled button
|
|
14
|
+
* - outline: Outlined button
|
|
15
|
+
* - ghost: No background/border, just text
|
|
16
|
+
* - subtle: Light background with subtle styling
|
|
17
|
+
*
|
|
18
|
+
* Color Schemes:
|
|
19
|
+
* - dark: Dark colors for use on light backgrounds (default)
|
|
20
|
+
* - light: Light colors for use on dark backgrounds
|
|
23
21
|
*
|
|
24
22
|
* Sizes:
|
|
25
23
|
* - lg: Large buttons
|
|
@@ -30,58 +28,91 @@ import { type ButtonTheme, buttonThemeToStyleVars } from "../../../lib/theme";
|
|
|
30
28
|
*
|
|
31
29
|
* Theme Support:
|
|
32
30
|
* Pass a `theme` prop to override default colors via CSS custom properties.
|
|
33
|
-
* Surface tokens (--radius-surface-button, --surface-button-stroke) control
|
|
34
|
-
* border radius and stroke width across all variants.
|
|
35
31
|
*/
|
|
36
32
|
const buttonVariants = tv({
|
|
37
33
|
base: "inline-flex items-center justify-center gap-spacing-8 whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 rounded-surface-button stroke-surface-button border-solid",
|
|
38
34
|
variants: {
|
|
39
35
|
variant: {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
primaryOutline:
|
|
45
|
-
"bg-transparent text-text-primary border-border-strong hover:bg-alpha-black-5 active:bg-alpha-black-10 focus-visible:ring-button-primary-bg",
|
|
46
|
-
// Secondary - uses semantic color tokens
|
|
47
|
-
secondary:
|
|
48
|
-
"bg-button-secondary-bg text-text-primary border-border-subtle hover:bg-button-secondary-bg-hover focus-visible:ring-button-primary-bg",
|
|
49
|
-
// Charcoal (dark filled) - primary dark (legacy)
|
|
50
|
-
charcoal:
|
|
51
|
-
"bg-gray-1200 text-gray-100 hover:bg-gray-1100 active:bg-gray-1000 border-transparent focus-visible:ring-gray-1000",
|
|
52
|
-
// Charcoal Outline - outlined dark (for light backgrounds)
|
|
53
|
-
charcoalOutline:
|
|
54
|
-
"border-border-strong text-gray-1000 hover:bg-alpha-black-5 active:bg-alpha-black-10 focus-visible:ring-gray-1000",
|
|
55
|
-
// Charcoal Outline Quiet - subtle outlined dark (for light backgrounds)
|
|
56
|
-
charcoalOutlineQuiet:
|
|
57
|
-
"border-border-subtle text-alpha-black-60 hover:border-border-strong hover:text-alpha-black-80 active:bg-alpha-black-5 focus-visible:ring-gray-1000",
|
|
58
|
-
// Ivory (light filled) - primary light (for dark backgrounds)
|
|
59
|
-
ivory:
|
|
60
|
-
"bg-gray-50 text-gray-1000 hover:bg-gray-100 active:bg-gray-200 border-transparent focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
61
|
-
// Ivory Outline - outlined light (for dark backgrounds)
|
|
62
|
-
ivoryOutline:
|
|
63
|
-
"border-gray-50 text-gray-50 hover:bg-alpha-white-10 active:bg-alpha-white-20 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
64
|
-
// Ivory Outline Quiet - subtle light outline (for dark backgrounds)
|
|
65
|
-
ivoryOutlineQuiet:
|
|
66
|
-
"border-alpha-white-20 text-alpha-white-60 hover:border-alpha-white-30 hover:text-alpha-white-80 active:bg-alpha-white-5 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
67
|
-
// Gray - gray filled button (for dark backgrounds)
|
|
68
|
-
gray: "bg-gray-800 text-gray-100 hover:bg-gray-700 active:bg-gray-600 border-transparent focus-visible:ring-gray-700 focus-visible:ring-offset-gray-1000",
|
|
36
|
+
solid: "",
|
|
37
|
+
outline: "bg-transparent border",
|
|
38
|
+
ghost: "bg-transparent border-transparent",
|
|
39
|
+
subtle: "border",
|
|
69
40
|
// Themed - uses CSS custom properties for styling
|
|
70
41
|
themed:
|
|
71
42
|
"[background:var(--btn-bg)] [color:var(--btn-text)] [border-color:var(--btn-border-color,transparent)] hover:[background:var(--btn-bg-hover,var(--btn-bg))] active:[background:var(--btn-bg-active,var(--btn-bg-hover,var(--btn-bg)))]",
|
|
72
43
|
},
|
|
44
|
+
colorScheme: {
|
|
45
|
+
dark: "",
|
|
46
|
+
light: "",
|
|
47
|
+
},
|
|
73
48
|
size: {
|
|
74
|
-
|
|
75
|
-
lg: "px-spacing-24 py-spacing-12 typography-brand-large-button-large h-spacing-48",
|
|
76
|
-
// Medium button (default) - uses primitive spacing tokens
|
|
49
|
+
lg: "px-spacing-24 py-spacing-12 typography-large-button-large h-spacing-48",
|
|
77
50
|
default:
|
|
78
|
-
"px-spacing-20 py-spacing-10 typography-
|
|
79
|
-
|
|
80
|
-
sm: "px-spacing-16 py-spacing-8 typography-brand-small-button-small h-spacing-32",
|
|
51
|
+
"px-spacing-20 py-spacing-10 typography-medium-button-medium h-spacing-40",
|
|
52
|
+
sm: "px-spacing-16 py-spacing-8 typography-small-button-small h-spacing-32",
|
|
81
53
|
},
|
|
82
54
|
},
|
|
55
|
+
compoundVariants: [
|
|
56
|
+
// Solid + Dark (for light backgrounds) - uses semantic button tokens
|
|
57
|
+
{
|
|
58
|
+
variant: "solid",
|
|
59
|
+
colorScheme: "dark",
|
|
60
|
+
class:
|
|
61
|
+
"bg-button-primary-bg text-text-inverted hover:bg-button-primary-bg-hover active:bg-button-primary-bg-hover border-transparent focus-visible:ring-button-primary-bg",
|
|
62
|
+
},
|
|
63
|
+
// Solid + Light (for dark backgrounds)
|
|
64
|
+
{
|
|
65
|
+
variant: "solid",
|
|
66
|
+
colorScheme: "light",
|
|
67
|
+
class:
|
|
68
|
+
"bg-button-secondary-bg text-text-primary hover:bg-button-secondary-bg-hover active:bg-gray-200 border-transparent focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
69
|
+
},
|
|
70
|
+
// Outline + Dark (for light backgrounds)
|
|
71
|
+
{
|
|
72
|
+
variant: "outline",
|
|
73
|
+
colorScheme: "dark",
|
|
74
|
+
class:
|
|
75
|
+
"border-border-strong text-gray-1000 hover:bg-alpha-black-5 active:bg-alpha-black-10 focus-visible:ring-gray-1000",
|
|
76
|
+
},
|
|
77
|
+
// Outline + Light (for dark backgrounds)
|
|
78
|
+
{
|
|
79
|
+
variant: "outline",
|
|
80
|
+
colorScheme: "light",
|
|
81
|
+
class:
|
|
82
|
+
"border-gray-50 text-gray-50 hover:bg-alpha-white-10 active:bg-alpha-white-20 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
83
|
+
},
|
|
84
|
+
// Ghost + Dark (for light backgrounds)
|
|
85
|
+
{
|
|
86
|
+
variant: "ghost",
|
|
87
|
+
colorScheme: "dark",
|
|
88
|
+
class:
|
|
89
|
+
"text-gray-700 hover:text-gray-900 hover:bg-alpha-black-5 active:bg-alpha-black-10 focus-visible:ring-gray-1000",
|
|
90
|
+
},
|
|
91
|
+
// Ghost + Light (for dark backgrounds)
|
|
92
|
+
{
|
|
93
|
+
variant: "ghost",
|
|
94
|
+
colorScheme: "light",
|
|
95
|
+
class:
|
|
96
|
+
"text-gray-300 hover:text-gray-100 hover:bg-alpha-white-10 active:bg-alpha-white-20 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
97
|
+
},
|
|
98
|
+
// Subtle + Dark (for light backgrounds)
|
|
99
|
+
{
|
|
100
|
+
variant: "subtle",
|
|
101
|
+
colorScheme: "dark",
|
|
102
|
+
class:
|
|
103
|
+
"border-border-subtle text-alpha-black-60 hover:border-border-strong hover:text-alpha-black-80 active:bg-alpha-black-5 focus-visible:ring-gray-1000",
|
|
104
|
+
},
|
|
105
|
+
// Subtle + Light (for dark backgrounds)
|
|
106
|
+
{
|
|
107
|
+
variant: "subtle",
|
|
108
|
+
colorScheme: "light",
|
|
109
|
+
class:
|
|
110
|
+
"border-alpha-white-20 text-alpha-white-60 hover:border-alpha-white-30 hover:text-alpha-white-80 active:bg-alpha-white-5 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
111
|
+
},
|
|
112
|
+
],
|
|
83
113
|
defaultVariants: {
|
|
84
|
-
variant: "
|
|
114
|
+
variant: "solid",
|
|
115
|
+
colorScheme: "dark",
|
|
85
116
|
size: "default",
|
|
86
117
|
},
|
|
87
118
|
});
|
|
@@ -106,7 +137,17 @@ function hasThemeValues(theme: ButtonTheme | undefined): boolean {
|
|
|
106
137
|
|
|
107
138
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
108
139
|
(
|
|
109
|
-
{
|
|
140
|
+
{
|
|
141
|
+
className,
|
|
142
|
+
variant,
|
|
143
|
+
colorScheme,
|
|
144
|
+
size,
|
|
145
|
+
render,
|
|
146
|
+
nativeButton,
|
|
147
|
+
theme,
|
|
148
|
+
style,
|
|
149
|
+
...props
|
|
150
|
+
},
|
|
110
151
|
ref,
|
|
111
152
|
) => {
|
|
112
153
|
// When render prop is provided, default nativeButton to false to suppress warnings
|
|
@@ -122,6 +163,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
122
163
|
<BaseButton
|
|
123
164
|
className={buttonVariants({
|
|
124
165
|
variant: effectiveVariant,
|
|
166
|
+
colorScheme,
|
|
125
167
|
size,
|
|
126
168
|
class: className,
|
|
127
169
|
})}
|
|
@@ -77,90 +77,241 @@ Playground.argTypes = {
|
|
|
77
77
|
control: {
|
|
78
78
|
type: "radio",
|
|
79
79
|
},
|
|
80
|
-
options: [
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
options: ["solid", "outline", "ghost", "subtle"],
|
|
81
|
+
},
|
|
82
|
+
colorScheme: {
|
|
83
|
+
control: {
|
|
84
|
+
type: "radio",
|
|
85
|
+
},
|
|
86
|
+
options: ["dark", "light"],
|
|
87
|
+
},
|
|
88
|
+
rounded: {
|
|
89
|
+
control: {
|
|
90
|
+
type: "radio",
|
|
91
|
+
},
|
|
92
|
+
options: ["default", "sm", "full"],
|
|
88
93
|
},
|
|
89
94
|
};
|
|
90
95
|
Playground.args = {
|
|
91
96
|
size: "default",
|
|
92
97
|
disabled: false,
|
|
93
|
-
variant: "
|
|
98
|
+
variant: "solid",
|
|
99
|
+
colorScheme: "dark",
|
|
100
|
+
rounded: "default",
|
|
101
|
+
"aria-label": "Search",
|
|
94
102
|
};
|
|
95
103
|
|
|
96
104
|
// =============================================================================
|
|
97
|
-
// Variants
|
|
105
|
+
// Variants (Dark Color Scheme - for light backgrounds)
|
|
98
106
|
// =============================================================================
|
|
99
107
|
|
|
100
|
-
export const
|
|
101
|
-
<IconButton variant="
|
|
108
|
+
export const Solid = () => (
|
|
109
|
+
<IconButton variant="solid" aria-label="Search">
|
|
102
110
|
<SearchIcon />
|
|
103
111
|
</IconButton>
|
|
104
112
|
);
|
|
105
113
|
|
|
106
|
-
export const
|
|
107
|
-
<IconButton variant="
|
|
114
|
+
export const Outline = () => (
|
|
115
|
+
<IconButton variant="outline" aria-label="Search">
|
|
108
116
|
<SearchIcon />
|
|
109
117
|
</IconButton>
|
|
110
118
|
);
|
|
111
119
|
|
|
112
|
-
export const
|
|
113
|
-
<IconButton variant="
|
|
120
|
+
export const Ghost = () => (
|
|
121
|
+
<IconButton variant="ghost" aria-label="Search">
|
|
114
122
|
<SearchIcon />
|
|
115
123
|
</IconButton>
|
|
116
124
|
);
|
|
117
125
|
|
|
118
|
-
export const
|
|
119
|
-
<IconButton variant="
|
|
126
|
+
export const Subtle = () => (
|
|
127
|
+
<IconButton variant="subtle" aria-label="Search">
|
|
120
128
|
<SearchIcon />
|
|
121
129
|
</IconButton>
|
|
122
130
|
);
|
|
123
131
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Variants (Light Color Scheme - for dark backgrounds)
|
|
134
|
+
// =============================================================================
|
|
135
|
+
|
|
136
|
+
const DarkBackground = ({ children }: { children: React.ReactNode }) => (
|
|
137
|
+
<div className="rounded-radius-12 bg-gray-1200 p-spacing-32">{children}</div>
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
export const SolidLight = () => (
|
|
141
|
+
<DarkBackground>
|
|
142
|
+
<IconButton variant="solid" colorScheme="light" aria-label="Navigate">
|
|
143
|
+
<ArrowRightIcon />
|
|
144
|
+
</IconButton>
|
|
145
|
+
</DarkBackground>
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
export const OutlineLight = () => (
|
|
149
|
+
<DarkBackground>
|
|
150
|
+
<IconButton variant="outline" colorScheme="light" aria-label="Navigate">
|
|
151
|
+
<ArrowRightIcon />
|
|
152
|
+
</IconButton>
|
|
153
|
+
</DarkBackground>
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
export const GhostLight = () => (
|
|
157
|
+
<DarkBackground>
|
|
158
|
+
<IconButton variant="ghost" colorScheme="light" aria-label="Navigate">
|
|
159
|
+
<ArrowRightIcon />
|
|
160
|
+
</IconButton>
|
|
161
|
+
</DarkBackground>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
export const SubtleLight = () => (
|
|
165
|
+
<DarkBackground>
|
|
166
|
+
<IconButton variant="subtle" colorScheme="light" aria-label="Navigate">
|
|
167
|
+
<ArrowRightIcon />
|
|
168
|
+
</IconButton>
|
|
169
|
+
</DarkBackground>
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// All Variants Comparison
|
|
174
|
+
// =============================================================================
|
|
175
|
+
|
|
176
|
+
export const AllVariants = () => (
|
|
177
|
+
<div className="flex flex-col gap-spacing-32">
|
|
178
|
+
<div>
|
|
179
|
+
<h3 className="mb-spacing-16 text-14 font-medium text-text-secondary">
|
|
180
|
+
Dark Color Scheme (for light backgrounds)
|
|
181
|
+
</h3>
|
|
182
|
+
<div className="flex gap-spacing-16">
|
|
183
|
+
<IconButton variant="solid" aria-label="Solid">
|
|
184
|
+
<SearchIcon />
|
|
185
|
+
</IconButton>
|
|
186
|
+
<IconButton variant="outline" aria-label="Outline">
|
|
187
|
+
<SearchIcon />
|
|
188
|
+
</IconButton>
|
|
189
|
+
<IconButton variant="ghost" aria-label="Ghost">
|
|
190
|
+
<SearchIcon />
|
|
191
|
+
</IconButton>
|
|
192
|
+
<IconButton variant="subtle" aria-label="Subtle">
|
|
193
|
+
<SearchIcon />
|
|
194
|
+
</IconButton>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
<DarkBackground>
|
|
198
|
+
<h3 className="mb-spacing-16 text-14 font-medium text-gray-400">
|
|
199
|
+
Light Color Scheme (for dark backgrounds)
|
|
200
|
+
</h3>
|
|
201
|
+
<div className="flex gap-spacing-16">
|
|
202
|
+
<IconButton variant="solid" colorScheme="light" aria-label="Solid">
|
|
203
|
+
<ArrowRightIcon />
|
|
204
|
+
</IconButton>
|
|
205
|
+
<IconButton variant="outline" colorScheme="light" aria-label="Outline">
|
|
206
|
+
<ArrowRightIcon />
|
|
207
|
+
</IconButton>
|
|
208
|
+
<IconButton variant="ghost" colorScheme="light" aria-label="Ghost">
|
|
209
|
+
<ArrowRightIcon />
|
|
210
|
+
</IconButton>
|
|
211
|
+
<IconButton variant="subtle" colorScheme="light" aria-label="Subtle">
|
|
212
|
+
<ArrowRightIcon />
|
|
213
|
+
</IconButton>
|
|
214
|
+
</div>
|
|
215
|
+
</DarkBackground>
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// =============================================================================
|
|
220
|
+
// Rounded Variants
|
|
221
|
+
// =============================================================================
|
|
222
|
+
|
|
223
|
+
export const RoundedDefault = () => (
|
|
224
|
+
<IconButton rounded="default" aria-label="Search">
|
|
225
|
+
<SearchIcon />
|
|
127
226
|
</IconButton>
|
|
128
227
|
);
|
|
129
228
|
|
|
130
|
-
export const
|
|
131
|
-
<IconButton
|
|
132
|
-
<
|
|
229
|
+
export const RoundedSm = () => (
|
|
230
|
+
<IconButton rounded="sm" aria-label="Search">
|
|
231
|
+
<SearchIcon />
|
|
133
232
|
</IconButton>
|
|
134
233
|
);
|
|
135
234
|
|
|
235
|
+
export const RoundedFull = () => (
|
|
236
|
+
<IconButton rounded="full" aria-label="Search">
|
|
237
|
+
<SearchIcon />
|
|
238
|
+
</IconButton>
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
export const AllRounded = () => (
|
|
242
|
+
<div className="flex gap-spacing-16">
|
|
243
|
+
<div className="text-center">
|
|
244
|
+
<IconButton rounded="default" aria-label="Default rounded">
|
|
245
|
+
<SearchIcon />
|
|
246
|
+
</IconButton>
|
|
247
|
+
<p className="mt-spacing-8 text-12 text-text-muted">default</p>
|
|
248
|
+
</div>
|
|
249
|
+
<div className="text-center">
|
|
250
|
+
<IconButton rounded="sm" aria-label="Small rounded">
|
|
251
|
+
<SearchIcon />
|
|
252
|
+
</IconButton>
|
|
253
|
+
<p className="mt-spacing-8 text-12 text-text-muted">sm</p>
|
|
254
|
+
</div>
|
|
255
|
+
<div className="text-center">
|
|
256
|
+
<IconButton rounded="full" aria-label="Full rounded">
|
|
257
|
+
<SearchIcon />
|
|
258
|
+
</IconButton>
|
|
259
|
+
<p className="mt-spacing-8 text-12 text-text-muted">full</p>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
|
|
136
264
|
// =============================================================================
|
|
137
265
|
// Sizes
|
|
138
266
|
// =============================================================================
|
|
139
267
|
|
|
140
268
|
export const Small = () => (
|
|
141
|
-
<IconButton size="sm">
|
|
269
|
+
<IconButton size="sm" aria-label="Search">
|
|
142
270
|
<ArrowRightIcon />
|
|
143
271
|
</IconButton>
|
|
144
272
|
);
|
|
145
273
|
|
|
146
274
|
export const Medium = () => (
|
|
147
|
-
<IconButton size="default">
|
|
275
|
+
<IconButton size="default" aria-label="Search">
|
|
148
276
|
<SearchIcon />
|
|
149
277
|
</IconButton>
|
|
150
278
|
);
|
|
151
279
|
|
|
152
280
|
export const Large = () => (
|
|
153
|
-
<IconButton size="lg">
|
|
281
|
+
<IconButton size="lg" aria-label="Search">
|
|
154
282
|
<SearchIcon />
|
|
155
283
|
</IconButton>
|
|
156
284
|
);
|
|
157
285
|
|
|
286
|
+
export const AllSizes = () => (
|
|
287
|
+
<div className="flex items-center gap-spacing-16">
|
|
288
|
+
<div className="text-center">
|
|
289
|
+
<IconButton size="sm" aria-label="Small">
|
|
290
|
+
<ArrowRightIcon />
|
|
291
|
+
</IconButton>
|
|
292
|
+
<p className="mt-spacing-8 text-12 text-text-muted">sm (32px)</p>
|
|
293
|
+
</div>
|
|
294
|
+
<div className="text-center">
|
|
295
|
+
<IconButton size="default" aria-label="Default">
|
|
296
|
+
<SearchIcon />
|
|
297
|
+
</IconButton>
|
|
298
|
+
<p className="mt-spacing-8 text-12 text-text-muted">default (40px)</p>
|
|
299
|
+
</div>
|
|
300
|
+
<div className="text-center">
|
|
301
|
+
<IconButton size="lg" aria-label="Large">
|
|
302
|
+
<SearchIcon />
|
|
303
|
+
</IconButton>
|
|
304
|
+
<p className="mt-spacing-8 text-12 text-text-muted">lg (48px)</p>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
);
|
|
308
|
+
|
|
158
309
|
// =============================================================================
|
|
159
310
|
// States
|
|
160
311
|
// =============================================================================
|
|
161
312
|
|
|
162
313
|
export const Disabled = () => (
|
|
163
|
-
<IconButton disabled>
|
|
314
|
+
<IconButton disabled aria-label="Search">
|
|
164
315
|
<SearchIcon />
|
|
165
316
|
</IconButton>
|
|
166
317
|
);
|
|
@@ -17,70 +17,115 @@ import { tv, type VariantProps } from "tailwind-variants";
|
|
|
17
17
|
* <IconButton aria-label="Close menu">
|
|
18
18
|
* <CloseIcon />
|
|
19
19
|
* </IconButton>
|
|
20
|
-
*
|
|
21
|
-
* // Correct usage with aria-labelledby
|
|
22
|
-
* <IconButton aria-labelledby="close-label">
|
|
23
|
-
* <CloseIcon />
|
|
24
|
-
* </IconButton>
|
|
25
|
-
* <span id="close-label" className="sr-only">Close menu</span>
|
|
26
20
|
* ```
|
|
27
21
|
*
|
|
28
22
|
* Variants:
|
|
29
|
-
* -
|
|
30
|
-
* -
|
|
31
|
-
* -
|
|
32
|
-
* -
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* -
|
|
36
|
-
* -
|
|
23
|
+
* - solid: Filled button
|
|
24
|
+
* - outline: Outlined button
|
|
25
|
+
* - ghost: No background/border, just icon
|
|
26
|
+
* - subtle: Subtle outlined button
|
|
27
|
+
*
|
|
28
|
+
* Color Schemes:
|
|
29
|
+
* - dark: Dark colors for use on light backgrounds (default)
|
|
30
|
+
* - light: Light colors for use on dark backgrounds
|
|
37
31
|
*
|
|
38
32
|
* Sizes:
|
|
39
|
-
* - lg: Large (
|
|
40
|
-
* - default: Medium (
|
|
41
|
-
* - sm: Small (
|
|
33
|
+
* - lg: Large (48x48)
|
|
34
|
+
* - default: Medium (40x40)
|
|
35
|
+
* - sm: Small (32x32)
|
|
36
|
+
*
|
|
37
|
+
* Rounded:
|
|
38
|
+
* - default: Standard border radius
|
|
39
|
+
* - sm: Smaller border radius
|
|
40
|
+
* - full: Fully circular
|
|
42
41
|
*/
|
|
43
42
|
const iconButtonVariants = tv({
|
|
44
43
|
base: "inline-flex items-center justify-center whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
45
44
|
variants: {
|
|
46
45
|
variant: {
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
solid: "",
|
|
47
|
+
outline: "border",
|
|
48
|
+
ghost: "",
|
|
49
|
+
subtle: "border",
|
|
50
|
+
},
|
|
51
|
+
colorScheme: {
|
|
52
|
+
dark: "",
|
|
53
|
+
light: "",
|
|
54
|
+
},
|
|
55
|
+
size: {
|
|
56
|
+
lg: "size-48",
|
|
57
|
+
default: "size-40",
|
|
58
|
+
sm: "size-32",
|
|
59
|
+
},
|
|
60
|
+
rounded: {
|
|
61
|
+
default: "rounded-radius-12",
|
|
62
|
+
sm: "rounded-radius-10",
|
|
63
|
+
full: "rounded-full",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
compoundVariants: [
|
|
67
|
+
// Solid + Dark (for light backgrounds)
|
|
68
|
+
{
|
|
69
|
+
variant: "solid",
|
|
70
|
+
colorScheme: "dark",
|
|
71
|
+
class:
|
|
49
72
|
"bg-gray-1200 text-gray-100 hover:bg-gray-1100 active:bg-gray-1000 focus-visible:ring-gray-1000",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
},
|
|
74
|
+
// Solid + Light (for dark backgrounds)
|
|
75
|
+
{
|
|
76
|
+
variant: "solid",
|
|
77
|
+
colorScheme: "light",
|
|
78
|
+
class:
|
|
79
|
+
"bg-gray-50 text-gray-1000 hover:bg-gray-100 active:bg-gray-200 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
80
|
+
},
|
|
81
|
+
// Outline + Dark (for light backgrounds)
|
|
82
|
+
{
|
|
83
|
+
variant: "outline",
|
|
84
|
+
colorScheme: "dark",
|
|
85
|
+
class:
|
|
86
|
+
"border-alpha-black-30 text-gray-1000 hover:bg-alpha-black-5 active:bg-alpha-black-10 focus-visible:ring-gray-1000",
|
|
87
|
+
},
|
|
88
|
+
// Outline + Light (for dark backgrounds)
|
|
89
|
+
{
|
|
90
|
+
variant: "outline",
|
|
91
|
+
colorScheme: "light",
|
|
92
|
+
class:
|
|
93
|
+
"border-gray-50 text-gray-50 hover:bg-alpha-white-10 active:bg-alpha-white-20 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
94
|
+
},
|
|
95
|
+
// Ghost + Dark (for light backgrounds)
|
|
96
|
+
{
|
|
97
|
+
variant: "ghost",
|
|
98
|
+
colorScheme: "dark",
|
|
99
|
+
class:
|
|
58
100
|
"text-gray-700 hover:text-gray-900 hover:bg-alpha-black-5 active:bg-alpha-black-10 focus-visible:ring-gray-1000",
|
|
59
|
-
|
|
60
|
-
|
|
101
|
+
},
|
|
102
|
+
// Ghost + Light (for dark backgrounds)
|
|
103
|
+
{
|
|
104
|
+
variant: "ghost",
|
|
105
|
+
colorScheme: "light",
|
|
106
|
+
class:
|
|
61
107
|
"text-gray-300 hover:text-gray-100 hover:bg-alpha-white-10 active:bg-alpha-white-20 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
62
|
-
// Ivory (light filled) - primary light (for dark backgrounds)
|
|
63
|
-
ivory:
|
|
64
|
-
"bg-gray-50 text-gray-1000 hover:bg-gray-100 active:bg-gray-200 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
65
|
-
// Ivory Outline - outlined light (for dark backgrounds)
|
|
66
|
-
ivoryOutline:
|
|
67
|
-
"border border-gray-50 text-gray-50 hover:bg-alpha-white-10 active:bg-alpha-white-20 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
68
|
-
// Ivory Outline Quiet - subtle light outline (for dark backgrounds)
|
|
69
|
-
ivoryOutlineQuiet:
|
|
70
|
-
"border border-alpha-white-20 text-alpha-white-60 hover:border-alpha-white-30 hover:text-alpha-white-80 active:bg-alpha-white-5 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
71
108
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
sm: "rounded-radius-10 size-spacing-32",
|
|
109
|
+
// Subtle + Dark (for light backgrounds)
|
|
110
|
+
{
|
|
111
|
+
variant: "subtle",
|
|
112
|
+
colorScheme: "dark",
|
|
113
|
+
class:
|
|
114
|
+
"border-alpha-black-20 text-alpha-black-60 hover:border-alpha-black-30 hover:text-alpha-black-80 active:bg-alpha-black-5 focus-visible:ring-gray-1000",
|
|
79
115
|
},
|
|
80
|
-
|
|
116
|
+
// Subtle + Light (for dark backgrounds)
|
|
117
|
+
{
|
|
118
|
+
variant: "subtle",
|
|
119
|
+
colorScheme: "light",
|
|
120
|
+
class:
|
|
121
|
+
"border-alpha-white-20 text-alpha-white-60 hover:border-alpha-white-30 hover:text-alpha-white-80 active:bg-alpha-white-5 focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
81
124
|
defaultVariants: {
|
|
82
|
-
variant: "
|
|
125
|
+
variant: "solid",
|
|
126
|
+
colorScheme: "dark",
|
|
83
127
|
size: "default",
|
|
128
|
+
rounded: "default",
|
|
84
129
|
},
|
|
85
130
|
});
|
|
86
131
|
|
|
@@ -91,7 +136,18 @@ export interface IconButtonProps
|
|
|
91
136
|
}
|
|
92
137
|
|
|
93
138
|
const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
|
|
94
|
-
(
|
|
139
|
+
(
|
|
140
|
+
{
|
|
141
|
+
className,
|
|
142
|
+
variant,
|
|
143
|
+
colorScheme,
|
|
144
|
+
size,
|
|
145
|
+
rounded,
|
|
146
|
+
asChild = false,
|
|
147
|
+
...props
|
|
148
|
+
},
|
|
149
|
+
ref,
|
|
150
|
+
) => {
|
|
95
151
|
// Development warning for missing accessible label
|
|
96
152
|
React.useEffect(() => {
|
|
97
153
|
if (import.meta.env?.DEV) {
|
|
@@ -108,7 +164,13 @@ const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
|
|
|
108
164
|
const Comp = asChild ? Slot : "button";
|
|
109
165
|
return (
|
|
110
166
|
<Comp
|
|
111
|
-
className={iconButtonVariants({
|
|
167
|
+
className={iconButtonVariants({
|
|
168
|
+
variant,
|
|
169
|
+
colorScheme,
|
|
170
|
+
size,
|
|
171
|
+
rounded,
|
|
172
|
+
class: className,
|
|
173
|
+
})}
|
|
112
174
|
ref={ref}
|
|
113
175
|
{...props}
|
|
114
176
|
/>
|