Skip to content

Commit 4c083ae

Browse files
committed
feat: add shadow prop to Button component
1 parent 1f189fe commit 4c083ae

File tree

10 files changed

+131
-57
lines changed

10 files changed

+131
-57
lines changed

src/components/Button/Button.stories.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ const meta = {
4343
},
4444
description: 'Whether the button should take full width',
4545
},
46+
shadow: {
47+
control: {
48+
type: 'boolean',
49+
},
50+
description: 'Whether to apply a box shadow to the button',
51+
},
4652
loading: {
4753
control: {
4854
type: 'boolean',
@@ -170,6 +176,17 @@ export const FullWidth: Story = {
170176
},
171177
}
172178

179+
/**
180+
* Button with shadow applied.
181+
*/
182+
export const WithShadow: Story = {
183+
args: {
184+
children: 'Button with Shadow',
185+
variant: 'primary',
186+
shadow: true,
187+
},
188+
}
189+
173190
/**
174191
* Disabled button state.
175192
*/

src/components/Button/Button.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ export interface ButtonProps
5252
* @default 'default'
5353
*/
5454
shape?: 'default' | 'round' | 'circle'
55+
/**
56+
* Whether to apply a box shadow to the button
57+
* @default false
58+
*/
59+
shadow?: boolean
5560
/**
5661
* Whether the button is in an active/pressed state (for toggle buttons)
5762
*/
@@ -91,6 +96,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
9196
icon,
9297
iconPosition = 'left',
9398
shape = 'default',
99+
shadow = false,
94100
active = false,
95101
danger = false,
96102
className,
@@ -151,6 +157,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
151157
sizeClass,
152158
shapeClass,
153159
fullWidth && 'memori-button--full-width',
160+
shadow && 'memori-button--shadow',
154161
isDisabled && 'memori-button--disabled',
155162
loading && 'memori-button--loading',
156163
active && 'memori-button--active',

src/components/Button/styles.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,24 @@
310310
STATES
311311
============================================ */
312312

313+
.memori-button--shadow {
314+
box-shadow:
315+
0 4px 6px -1px rgb(0 0 0 / 10%),
316+
0 2px 4px -2px rgb(0 0 0 / 10%);
317+
}
318+
319+
.memori-button--shadow:hover:not(:disabled) {
320+
box-shadow:
321+
0 6px 10px -2px rgb(0 0 0 / 12%),
322+
0 4px 6px -3px rgb(0 0 0 / 10%);
323+
}
324+
325+
.memori-button--shadow:active:not(:disabled) {
326+
box-shadow:
327+
0 2px 4px -2px rgb(0 0 0 / 8%),
328+
0 1px 2px -1px rgb(0 0 0 / 6%);
329+
}
330+
313331
.memori-button--full-width {
314332
width: 100%;
315333
}

src/components/Card/Card.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface CardProps extends Omit<
2626
* Visual variant of the card
2727
* @default 'elevated'
2828
*/
29-
variant?: 'elevated' | 'outlined'
29+
variant?: 'elevated' | 'outlined' | 'flat'
3030

3131
/**
3232
* Controls the padding inside the card body
@@ -96,11 +96,15 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
9696
const isInteractive = !!onClick || focusable === true || hoverable === true
9797
const tabIndex = isInteractive || focusable ? 0 : undefined
9898

99-
const rootClassName = cx(
100-
'memori-card',
99+
const variantClass =
101100
variant === 'elevated'
102101
? 'memori-card--elevated'
103-
: 'memori-card--outlined',
102+
: variant === 'flat'
103+
? 'memori-card--flat'
104+
: 'memori-card--outlined'
105+
const rootClassName = cx(
106+
'memori-card',
107+
variantClass,
104108
isInteractive && 'memori-card--interactive',
105109
className,
106110
)

src/components/Card/styles.css

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
width: 100%;
1111
box-sizing: border-box;
1212
flex-direction: column;
13-
padding: 0;
13+
padding: var(--memori-spacing-lg);
1414
border-radius: var(--memori-radius-box);
1515
margin: 0;
1616
background-color: var(--memori-main-background);
17+
box-shadow: var(--memori-shadow-md);
1718
color: var(--memori-text-color);
1819
font-family: var(--memori-font-family);
1920
text-align: left;
@@ -65,12 +66,10 @@
6566
VARIANTS
6667
============================================ */
6768

68-
/* Elevated: Shadow, no border (usually) */
69+
/* Elevated: stronger shadow */
6970
.memori-card--elevated {
7071
border: 1px solid transparent;
71-
72-
/* Keeps sizing consistent with outlined */
73-
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
72+
box-shadow: var(--memori-shadow-xl);
7473
}
7574

7675
/* Dark mode: add subtle border for better visibility */
@@ -79,6 +78,12 @@
7978
border-color: var(--memori-border-color);
8079
}
8180

81+
/* Flat: no shadow, border only */
82+
.memori-card--flat {
83+
border: var(--memori-border) solid var(--memori-border-color);
84+
box-shadow: none;
85+
}
86+
8287
/* Outlined: Border, no shadow */
8388
.memori-card--outlined {
8489
border: 1px solid var(--memori-border-color);

src/components/Drawer/styles.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
.memori-drawer__backdrop {
2626
position: fixed;
27-
z-index: 1200;
2827
animation: memori-drawer-fade-in var(--memori-motion-duration-slow) var(--memori-motion-ease-in);
2928
backdrop-filter: blur(2px);
3029
background-color: rgb(0 0 0 / 45%);
@@ -37,7 +36,7 @@
3736

3837
.memori-drawer {
3938
position: fixed;
40-
z-index: 1300;
39+
z-index: 1001;
4140
display: flex;
4241
box-sizing: border-box;
4342
flex-direction: column;

src/components/Dropdown/styles.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139

140140
.memori-dropdown__item:hover:not([data-disabled]),
141141
.memori-dropdown__item[data-highlighted]:not([data-disabled]) {
142-
background-color: var(--memori-primary-subtle);
142+
background-color: var(--memori-border-color);
143143
color: var(--memori-text-color);
144144
}
145145

src/components/Input/styles.css

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,36 @@
1111
font-weight: var(--memori-text-weight-normal);
1212
line-height: var(--memori-text-line-normal);
1313
outline: none;
14-
transition:
15-
border-color var(--memori-motion-duration-normal) var(--memori-motion-ease-in-out),
16-
box-shadow var(--memori-motion-duration-normal) var(--memori-motion-ease-in-out),
17-
background-color var(--memori-motion-duration-normal) var(--memori-motion-ease-in-out),
18-
color var(--memori-motion-duration-normal) var(--memori-motion-ease-in-out),
19-
outline-color var(--memori-motion-duration-normal) var(--memori-motion-ease-in-out);
20-
}
21-
22-
.memori-input::placeholder {
23-
color: var(--memori-neutral);
14+
transition: all var(--memori-motion-duration-normal) var(--memori-motion-ease);
2415
}
2516

2617
.memori-input:focus,
2718
.memori-input:focus-within {
2819
border-color: var(--memori-primary);
29-
box-shadow: 0 0 0 2px oklch(from var(--memori-primary) l c h / 20%);
20+
box-shadow: var(--memori-focus-ring);
3021
isolation: isolate;
31-
outline: 2px solid var(--memori-primary);
32-
outline-offset: 2px;
22+
}
23+
24+
.memori-input::placeholder {
25+
color: var(--memori-neutral);
26+
}
27+
28+
.memori-input--disabled,
29+
.memori-input:disabled {
30+
border-color: var(--memori-border-color);
31+
background-color: var(--memori-primary-subtle);
32+
color: var(--memori-neutral);
33+
cursor: not-allowed;
34+
opacity: 0.6;
35+
}
36+
37+
.memori-input--disabled::placeholder,
38+
.memori-input:disabled::placeholder {
39+
color: var(--memori-neutral);
40+
}
41+
42+
.memori-input:hover:not(:disabled) {
43+
border-color: var(--memori-primary-hover);
3344
}
3445

3546
/* ============================================
@@ -61,21 +72,6 @@
6172
color: var(--memori-error);
6273
}
6374

64-
/* Disabled Variant */
65-
.memori-input--disabled,
66-
.memori-input:disabled {
67-
border-color: var(--memori-border-color);
68-
background-color: var(--memori-primary-subtle);
69-
color: var(--memori-neutral);
70-
cursor: not-allowed;
71-
opacity: 0.6;
72-
}
73-
74-
.memori-input--disabled::placeholder,
75-
.memori-input:disabled::placeholder {
76-
color: var(--memori-neutral);
77-
}
78-
7975
/* ============================================
8076
SIZES
8177
============================================ */

src/components/SelectBox/styles.css

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,13 @@
3131
font-size: var(--memori-text-size-base);
3232
line-height: var(--memori-text-line-normal);
3333
outline: none;
34-
transition:
35-
background-color var(--memori-motion-duration-normal) var(--memori-motion-ease-in-out),
36-
border-color var(--memori-motion-duration-normal) var(--memori-motion-ease-in-out),
37-
box-shadow var(--memori-motion-duration-normal) var(--memori-motion-ease),
38-
transform var(--memori-motion-duration-fast) var(--memori-motion-ease);
34+
transition: all var(--memori-motion-duration-normal) var(--memori-motion-ease);
3935
}
4036

37+
.memori-select__trigger:focus,
4138
.memori-select__trigger:focus-visible {
4239
border-color: var(--memori-primary);
43-
box-shadow: 0 0 0 2px oklch(from var(--memori-primary) l c h / 20%);
40+
box-shadow: var(--memori-focus-ring);
4441
}
4542

4643
.memori-select__trigger:disabled {
@@ -50,6 +47,10 @@
5047
cursor: not-allowed;
5148
}
5249

50+
.memori-select__trigger:hover:not(:disabled) {
51+
border-color: var(--memori-primary-hover);
52+
}
53+
5354
.memori-select__portal {
5455
z-index: 999;
5556
}
@@ -63,11 +64,6 @@
6364
transform: scale(0.98);
6465
}
6566

66-
.memori-select__trigger:hover:not(:disabled) {
67-
border-color: var(--memori-primary);
68-
background-color: var(--memori-main-background);
69-
}
70-
7167
.memori-select__trigger[data-popup-open] {
7268
border-color: var(--memori-primary);
7369
}
@@ -110,6 +106,7 @@
110106
border-radius: 0.375rem;
111107
background-clip: padding-box;
112108
background-color: var(--memori-main-background);
109+
box-shadow: var(--memori-shadow-lg);
113110
color: var(--memori-text-color);
114111
font-family: var(--memori-font-family);
115112
transform-origin: var(--transform-origin);

src/theme/variables.css

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@
1212

1313
/* Base colors - used for backgrounds and surfaces */
1414
--memori-main-background: oklch(100% 0 0deg); /* Main background color (white) */
15-
--memori-primary-subtle: oklch(
16-
68.7% 0.213 297deg / 25%
15+
16+
/* Primary subtle is computed as a translucent/lightened version of the primary color */
17+
--memori-primary-subtle: color-mix(
18+
in oklch,
19+
var(--memori-primary) 40%,
20+
white 60%
1721
); /* Primary subtle – secondary/tertiary backgrounds, hover */
1822

1923
--memori-text-color: oklch(20% 0.05 240deg); /* Text color on base backgrounds (dark gray) */
@@ -22,6 +26,11 @@
2226
--memori-primary: var(--memori-primary-color, oklch(48% 0.19 297deg)); /* Primary actions, links, highlights */
2327
--memori-primary-content: oklch(98% 0.01 240deg); /* Text/icon color on primary background */
2428

29+
/* Primary interactive states */
30+
--memori-primary-hover: color-mix(in oklch, var(--memori-primary), black 15%);
31+
--memori-primary-active: color-mix(in oklch, var(--memori-primary), black 25%);
32+
--memori-primary-disabled: color-mix(in oklch, var(--memori-primary), transparent 60%);
33+
2534
/* Secondary colors - secondary brand color */
2635
--memori-secondary: var(--memori-secondary-color, oklch(70% 0.15 200deg)); /* Secondary actions, accents */
2736
--memori-secondary-content: oklch(98% 0.01 200deg); /* Text/icon color on secondary background */
@@ -35,6 +44,10 @@
3544
--memori-neutral-content: oklch(98% 0.01 240deg); /* Text/icon color on neutral background */
3645
--memori-border-color: rgb(73 103 125 / 16%); /* Component borders and dividers */
3746

47+
/* Focus states */
48+
--memori-focus-ring: 0 0 0 3px color-mix(in oklch, var(--memori-primary), transparent 80%);
49+
--memori-focus-ring-offset: 2px;
50+
3851
/* Semantic colors - Info */
3952
--memori-info: oklch(70% 0.2 220deg); /* Informational messages, badges */
4053
--memori-info-content: oklch(98% 0.01 220deg); /* Text/icon color on info background */
@@ -57,8 +70,10 @@
5770
--memori-spacing-md: 16px;
5871
--memori-spacing-lg: 24px;
5972
--memori-spacing-xl: 32px;
60-
--memori-spacing-2xl: 48px;
61-
--memori-spacing-3xl: 64px;
73+
--memori-spacing-2xl: 40px;
74+
--memori-spacing-3xl: 48px;
75+
--memori-spacing-4xl: 64px;
76+
--memori-spacing-5xl: 80px;
6277

6378
/* Typography - Font Sizes */
6479
--memori-text-size-small: 0.875rem; /* 14px - Small text, labels */
@@ -97,8 +112,16 @@
97112
--memori-modal-size-md: 600px;
98113
--memori-modal-size-lg: 800px;
99114

100-
/* Effects */
101-
--memori-depth: 1; /* Shadow depth/elevation level (for box-shadow calculations) */
115+
/* Effects - Shadows */
116+
--memori-shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 5%);
117+
--memori-shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 10%), 0 1px 2px -1px rgb(0 0 0 / 10%);
118+
--memori-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 10%), 0 2px 4px -2px rgb(0 0 0 / 10%);
119+
--memori-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 10%), 0 4px 6px -4px rgb(0 0 0 / 10%);
120+
--memori-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 10%), 0 8px 10px -6px rgb(0 0 0 / 10%);
121+
--memori-shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 25%);
122+
123+
/* Brand-colored shadow - adapts to primary color overrides */
124+
--memori-shadow-primary: 0 8px 16px -4px color-mix(in oklch, var(--memori-primary), transparent 70%);
102125
--memori-noise: 0; /* Noise/grain effect intensity (0 = no noise) */
103126

104127
/* Motion - for hover/focus and micro-interactions (under 300ms) */
@@ -125,4 +148,12 @@
125148
--memori-neutral: oklch(50% 0.05 240deg);
126149
--memori-neutral-content: oklch(98% 0.01 240deg);
127150
--memori-border-color: rgb(73 103 125 / 16%);
151+
152+
/* Dark theme shadows */
153+
--memori-shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 30%);
154+
--memori-shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 40%), 0 1px 2px -1px rgb(0 0 0 / 30%);
155+
--memori-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 50%), 0 2px 4px -2px rgb(0 0 0 / 40%);
156+
--memori-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 60%), 0 4px 6px -4px rgb(0 0 0 / 50%);
157+
--memori-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 70%), 0 8px 10px -6px rgb(0 0 0 / 60%);
158+
--memori-shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 80%);
128159
}

0 commit comments

Comments
 (0)