Skip to content

Commit 67337fd

Browse files
authored
chore: migrate card, panel, and elevation
1 parent 6f326ec commit 67337fd

26 files changed

+1046
-5
lines changed

colors/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import {
2424
} from '@radix-ui/colors';
2525

2626
import { badgeDarkTheme, badgeLightTheme } from '../components/Badge/Badge.theme';
27+
import { cardDarkTheme, cardLightTheme } from '../components/Card/Card.theme';
28+
import { panelDarkTheme, panelLightTheme } from '../components/Panel/Panel.theme';
2729
import * as deepBlue from './deepBlue';
2830
import * as elevation from './elevation';
2931
import * as grayBlue from './grayBlue';
@@ -67,6 +69,8 @@ export const lightColors: ColorMap = Object.entries(customColors)
6769
...blackA,
6870
// component-specific theme tokens
6971
...badgeLightTheme,
72+
...cardLightTheme,
73+
...panelLightTheme,
7074
},
7175
);
7276

@@ -93,6 +97,8 @@ export const darkColors: ColorMap = Object.entries(customColors)
9397
...blackA,
9498
// component-specific theme tokens
9599
...badgeDarkTheme,
100+
...cardDarkTheme,
101+
...panelDarkTheme,
96102
},
97103
);
98104

components/Card/Card.stories.tsx

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Flex } from '../Flex';
66
import { H2 } from '../Heading';
77
import { Text } from '../Text';
88
import { Card } from './Card';
9+
import { CardVanilla } from './Card.vanilla';
910

1011
const Component: Meta<typeof Card> = {
1112
title: 'Components/Card',
@@ -125,4 +126,106 @@ export const Elevation: StoryFn<typeof Card> = () => (
125126

126127
Elevation.args = {};
127128

129+
export const Comparison: StoryFn = () => {
130+
const cardContent = (
131+
<>
132+
<H2 css={{ mb: '$3' }}>Card Title</H2>
133+
<Text>
134+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
135+
labore et dolore magna aliqua.
136+
</Text>
137+
</>
138+
);
139+
140+
return (
141+
<Flex css={{ gap: '$3' }}>
142+
{/* Stitches Column */}
143+
<Flex css={{ flex: 1, flexDirection: 'column', gap: '$3' }}>
144+
<Text css={{ fontWeight: 'bold' }}>Stitches</Text>
145+
146+
<div>
147+
<Text css={{ mb: '$2' }}>Basic</Text>
148+
<Card>{cardContent}</Card>
149+
</div>
150+
151+
<div>
152+
<Text css={{ mb: '$2' }}>Ghost</Text>
153+
<Card variant="ghost">{cardContent}</Card>
154+
</div>
155+
156+
<div>
157+
<Text css={{ mb: '$2' }}>Inner</Text>
158+
<Card>
159+
<H2 css={{ mb: '$3' }}>Outer Card</H2>
160+
<Card variant="inner">{cardContent}</Card>
161+
</Card>
162+
</div>
163+
164+
<div>
165+
<Text css={{ mb: '$2' }}>Active</Text>
166+
<Card active>{cardContent}</Card>
167+
</div>
168+
169+
<div>
170+
<Text css={{ mb: '$2' }}>Interactive</Text>
171+
<Card interactive>{cardContent}</Card>
172+
</div>
173+
174+
<div>
175+
<Text css={{ mb: '$2' }}>Elevation 2</Text>
176+
<Card elevation={2}>{cardContent}</Card>
177+
</div>
178+
179+
<div>
180+
<Text css={{ mb: '$2' }}>Elevation 5</Text>
181+
<Card elevation={5}>{cardContent}</Card>
182+
</div>
183+
</Flex>
184+
185+
{/* Vanilla Extract Column */}
186+
<Flex css={{ flex: 1, flexDirection: 'column', gap: '$3' }}>
187+
<Text css={{ fontWeight: 'bold' }}>Vanilla Extract</Text>
188+
189+
<div>
190+
<Text css={{ mb: '$2' }}>Basic</Text>
191+
<CardVanilla>{cardContent}</CardVanilla>
192+
</div>
193+
194+
<div>
195+
<Text css={{ mb: '$2' }}>Ghost</Text>
196+
<CardVanilla variant="ghost">{cardContent}</CardVanilla>
197+
</div>
198+
199+
<div>
200+
<Text css={{ mb: '$2' }}>Inner</Text>
201+
<CardVanilla>
202+
<H2 css={{ mb: '$3' }}>Outer Card</H2>
203+
<CardVanilla variant="inner">{cardContent}</CardVanilla>
204+
</CardVanilla>
205+
</div>
206+
207+
<div>
208+
<Text css={{ mb: '$2' }}>Active</Text>
209+
<CardVanilla active>{cardContent}</CardVanilla>
210+
</div>
211+
212+
<div>
213+
<Text css={{ mb: '$2' }}>Interactive</Text>
214+
<CardVanilla interactive>{cardContent}</CardVanilla>
215+
</div>
216+
217+
<div>
218+
<Text css={{ mb: '$2' }}>Elevation 2</Text>
219+
<CardVanilla elevation={2}>{cardContent}</CardVanilla>
220+
</div>
221+
222+
<div>
223+
<Text css={{ mb: '$2' }}>Elevation 5</Text>
224+
<CardVanilla elevation={5}>{cardContent}</CardVanilla>
225+
</div>
226+
</Flex>
227+
</Flex>
228+
);
229+
};
230+
128231
export default Component;

components/Card/Card.theme.css.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Re-export from plain TypeScript file to avoid circular dependencies
2+
// The source of truth is Card.theme.ts
3+
export { cardDarkTheme, cardLightTheme } from './Card.theme';

components/Card/Card.theme.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Plain TypeScript color tokens (no vanilla-extract)
2+
// Can be safely imported by colors/index.ts for Stitches
3+
export const cardLightTheme = {
4+
cardBackground: 'white',
5+
cardHoverBackground: 'rgba(0,0,0,.05)',
6+
cardHoverBorder: 'hsla(211, 100%, 50%, 0.6)', // Will be overridden with primary color
7+
cardActiveBackground: 'rgba(0,0,0,.03)',
8+
cardActiveBorder: 'hsl(211, 100%, 50%)', // Will be overridden with primary color
9+
innerCardBgColor: 'hsla(0, 0%, 0%, 0.04)',
10+
};
11+
12+
export const cardDarkTheme = {
13+
cardBackground: 'hsl(209, 29%, 14%)', // deepBlue2
14+
cardHoverBackground: 'rgba(255,255,255,.12)',
15+
cardHoverBorder: 'hsla(211, 100%, 50%, 0.6)', // Will be overridden with primary color
16+
cardActiveBackground: 'rgba(255,255,255,.07)',
17+
cardActiveBorder: 'hsla(211, 100%, 50%, 0.4)', // Will be overridden with primary color
18+
innerCardBgColor: 'hsla(0, 0%, 100%, 0.07)',
19+
};
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { style } from '@vanilla-extract/css';
2+
import { recipe } from '@vanilla-extract/recipes';
3+
4+
import { breakpoints } from '../../styles/breakpoints.css';
5+
import { tokens } from '../../styles/tokens.css';
6+
7+
// Elevation variants matching Elevation component
8+
const elevationVariants = {
9+
0: {
10+
boxShadow: 'none',
11+
},
12+
1: {
13+
backgroundColor: tokens.colors['01dp'],
14+
boxShadow:
15+
'0 1px 5px 0 hsla(0, 0%, 0%, 0.2), 0 3px 1px -2px hsla(0, 0%, 0%, 0.12), 0 2px 2px 0 hsla(0, 0%, 0%, 0.14)',
16+
},
17+
2: {
18+
backgroundColor: tokens.colors['02dp'],
19+
boxShadow:
20+
'0 2px 4px -1px hsla(0, 0%, 0%, 0.2), 0 1px 10px 0 hsla(0, 0%, 0%, 0.12), 0 4px 5px 0 hsla(0, 0%, 0%, 0.14)',
21+
},
22+
3: {
23+
backgroundColor: tokens.colors['03dp'],
24+
boxShadow:
25+
'0 3px 5px -1px hsla(0, 0%, 0%, 0.2), 0 1px 18px 0 hsla(0, 0%, 0%, 0.12), 0 6px 10px 0 hsla(0, 0%, 0%, 0.14)',
26+
},
27+
4: {
28+
backgroundColor: tokens.colors['04dp'],
29+
boxShadow:
30+
'0 7px 8px -4px hsla(0, 0%, 0%, 0.2), 0 5px 22px 4px hsla(0, 0%, 0%, 0.12), 0 12px 17px 2px hsla(0, 0%, 0%, 0.14)',
31+
},
32+
5: {
33+
backgroundColor: tokens.colors['05dp'],
34+
boxShadow:
35+
'0 11px 15px -7px hsla(0, 0%, 0%, 0.2), 0 9px 46px 8px hsla(0, 0%, 0%, 0.12), 0 24px 38px 3px hsla(0, 0%, 0%, 0.14)',
36+
},
37+
} as const;
38+
39+
// Base card styles
40+
const cardBase = style({
41+
appearance: 'none',
42+
border: 'none',
43+
boxSizing: 'border-box',
44+
font: 'inherit',
45+
lineHeight: '1',
46+
outline: 'none',
47+
padding: tokens.space['3'],
48+
textAlign: 'inherit',
49+
verticalAlign: 'middle',
50+
WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)',
51+
backgroundColor: tokens.colors.cardBackground,
52+
display: 'block',
53+
textDecoration: 'none',
54+
color: 'inherit',
55+
borderRadius: tokens.radii['3'],
56+
position: 'relative',
57+
58+
'::before': {
59+
boxSizing: 'border-box',
60+
content: '""',
61+
position: 'absolute',
62+
top: 0,
63+
right: 0,
64+
bottom: 0,
65+
left: 0,
66+
borderRadius: tokens.radii['3'],
67+
pointerEvents: 'none',
68+
},
69+
});
70+
71+
export const cardRecipe = recipe({
72+
base: cardBase,
73+
74+
variants: {
75+
elevation: elevationVariants,
76+
variant: {
77+
inner: {
78+
backgroundColor: tokens.colors.innerCardBgColor,
79+
},
80+
ghost: {
81+
backgroundColor: 'transparent',
82+
boxShadow: 'none',
83+
},
84+
},
85+
active: {
86+
true: {
87+
'::before': {
88+
outline: `1px solid ${tokens.colors.cardActiveBorder}`,
89+
backgroundColor: tokens.colors.cardActiveBackground,
90+
},
91+
},
92+
},
93+
},
94+
95+
defaultVariants: {
96+
elevation: 1,
97+
},
98+
});
99+
100+
// Interactive card styles (for button element)
101+
const interactiveCardBase = style([
102+
cardBase,
103+
{
104+
'@media': {
105+
[breakpoints.hover]: {
106+
selectors: {
107+
'&:hover': {
108+
cursor: 'pointer',
109+
},
110+
'&:hover::before': {
111+
outline: `1px solid ${tokens.colors.cardHoverBorder}`,
112+
backgroundColor: tokens.colors.cardHoverBackground,
113+
},
114+
},
115+
},
116+
},
117+
selectors: {
118+
'&:focus': {
119+
outline: `2px solid ${tokens.colors.primary}`,
120+
},
121+
'&:active::before': {
122+
outline: `1px solid ${tokens.colors.cardActiveBorder}`,
123+
backgroundColor: tokens.colors.cardActiveBackground,
124+
},
125+
},
126+
},
127+
]);
128+
129+
export const interactiveCardRecipe = recipe({
130+
base: interactiveCardBase,
131+
132+
variants: {
133+
elevation: elevationVariants,
134+
variant: {
135+
inner: {
136+
backgroundColor: tokens.colors.innerCardBgColor,
137+
},
138+
ghost: {
139+
backgroundColor: 'transparent',
140+
boxShadow: 'none',
141+
},
142+
},
143+
active: {
144+
true: {
145+
'::before': {
146+
outline: `1px solid ${tokens.colors.cardActiveBorder}`,
147+
backgroundColor: tokens.colors.cardActiveBackground,
148+
},
149+
},
150+
},
151+
},
152+
153+
defaultVariants: {
154+
elevation: 1,
155+
},
156+
});

0 commit comments

Comments
 (0)