Skip to content

Commit fc92140

Browse files
committed
Refactor Button component to use CSS modules instead of string literals for CSS, add typings, add unit tests
1 parent e785d2e commit fc92140

6 files changed

Lines changed: 371 additions & 252 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
.button {
2+
display: inline-flex;
3+
align-items: center;
4+
justify-content: center;
5+
text-align: center;
6+
vertical-align: middle;
7+
font-weight: 600;
8+
font-size: 0.875rem;
9+
line-height: 1.25rem;
10+
text-transform: uppercase;
11+
letter-spacing: 0.08em;
12+
white-space: nowrap;
13+
border-radius: 0.375rem;
14+
border-width: 2px;
15+
border-style: solid;
16+
border-color: transparent;
17+
transition-property: color, background-color, border-color, box-shadow, transform;
18+
transition-duration: 200ms;
19+
transition-timing-function: ease-in-out;
20+
user-select: none;
21+
text-shadow: var(--shadow-text);
22+
box-shadow: var(--shadow-sm);
23+
}
24+
25+
.button:disabled {
26+
opacity: 0.65;
27+
cursor: not-allowed;
28+
pointer-events: none;
29+
}
30+
31+
.sizeSmall {
32+
padding: 0.5rem 1rem;
33+
font-size: 0.75rem;
34+
line-height: 1rem;
35+
}
36+
37+
.sizeMedium {
38+
padding: 0.75rem 1.5rem;
39+
}
40+
41+
.sizeLarge {
42+
padding: 1rem 2rem;
43+
font-size: 1rem;
44+
line-height: 1.5rem;
45+
}
46+
47+
.variantPrimary {
48+
background-color: var(--color-primary);
49+
color: var(--color-text);
50+
box-shadow: var(--shadow-sm);
51+
}
52+
53+
.variantPrimary:hover,
54+
.variantPrimary:focus-visible {
55+
background-color: var(--color-primary-offset);
56+
color: var(--color-text);
57+
box-shadow: var(--shadow-hover);
58+
transform: translateY(-1px);
59+
}
60+
61+
.variantPrimary:active {
62+
background-color: var(--color-primary-offset);
63+
box-shadow: var(--shadow-active);
64+
transform: translateY(1px);
65+
}
66+
67+
.variantSecondary {
68+
background-color: var(--color-secondary);
69+
color: var(--color-text);
70+
}
71+
72+
.variantSecondary:hover,
73+
.variantSecondary:focus-visible {
74+
background-color: var(--color-secondary-offset);
75+
color: var(--color-text);
76+
box-shadow: var(--shadow-hover);
77+
transform: translateY(-1px);
78+
}
79+
80+
.variantSecondary:active {
81+
background-color: var(--color-secondary-offset);
82+
box-shadow: var(--shadow-active);
83+
transform: translateY(1px);
84+
}
85+
86+
.variantTwitter {
87+
background-color: var(--color-twitter);
88+
border-color: var(--color-twitter);
89+
color: var(--color-text);
90+
}
91+
92+
.variantTwitter:hover,
93+
.variantTwitter:focus-visible {
94+
background-color: transparent;
95+
color: var(--color-twitter);
96+
}
97+
98+
.variantSuccess {
99+
background-color: var(--color-success);
100+
color: #fff;
101+
}
102+
103+
.variantSuccess:hover,
104+
.variantSuccess:focus-visible {
105+
background-color: var(--color-success-offset);
106+
box-shadow: var(--shadow-hover);
107+
transform: translateY(-1px);
108+
}
109+
110+
.variantSuccess:active {
111+
background-color: var(--color-success-offset);
112+
box-shadow: var(--shadow-active);
113+
transform: translateY(1px);
114+
}
115+
116+
.variantWarning {
117+
background-color: var(--color-warning);
118+
color: var(--color-text);
119+
}
120+
121+
.variantWarning:hover,
122+
.variantWarning:focus-visible {
123+
background-color: var(--color-warning-offset);
124+
box-shadow: var(--shadow-hover);
125+
transform: translateY(-1px);
126+
}
127+
128+
.variantWarning:active {
129+
background-color: var(--color-warning-offset);
130+
box-shadow: var(--shadow-active);
131+
transform: translateY(1px);
132+
}
133+
134+
.variantIcon {
135+
background-color: transparent;
136+
color: var(--color-text);
137+
box-shadow: none;
138+
aspect-ratio: 1 / 1;
139+
padding: 0.5rem;
140+
}
141+
142+
.variantIcon:hover,
143+
.variantIcon:focus-visible {
144+
background-color: var(--color-bg-offset);
145+
color: var(--color-primary);
146+
}
147+
148+
.variantIcon:active {
149+
background-color: var(--color-bg-offset);
150+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
declare const styles: {
2+
readonly button: string
3+
readonly sizeSmall: string
4+
readonly sizeMedium: string
5+
readonly sizeLarge: string
6+
readonly variantPrimary: string
7+
readonly variantSecondary: string
8+
readonly variantTwitter: string
9+
readonly variantSuccess: string
10+
readonly variantWarning: string
11+
readonly variantIcon: string
12+
}
13+
14+
export default styles

‎src/components/Button/index.astro‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
---
22
import type { ButtonProps } from '@components/Button/server'
3-
import { baseClasses, sizeClasses, variantClasses } from '@components/Button/server'
3+
import { buildButtonClassList, resolveAriaLabel } from '@components/Button/server'
44
import Sprite from '@components/Sprite/index.astro'
5+
import styles from './button.module.css'
56
67
type Props = ButtonProps
78
@@ -22,21 +23,27 @@ const {
2223
} = Astro.props
2324
2425
// Combine all classes
25-
const buttonClasses = [baseClasses, sizeClasses[size], variantClasses[variant], additionalClasses]
26-
.filter(Boolean)
27-
.join(' ')
26+
const buttonClassList = buildButtonClassList({
27+
styles,
28+
variant,
29+
size,
30+
additionalClasses,
31+
})
2832
2933
// Determine icon size based on button size if not specified
3034
const defaultIconSize = iconSize || (size === 'small' ? 16 : size === 'large' ? 24 : 20)
35+
36+
const computedAriaLabel = resolveAriaLabel({ text, ariaLabel, icon, iconPosition })
37+
const ariaAttributes = computedAriaLabel ? { 'aria-label': computedAriaLabel } : {}
3138
---
3239

3340
<button
3441
type={type}
35-
class={buttonClasses}
42+
class:list={buttonClassList}
3643
disabled={disabled}
3744
id={id}
38-
aria-label={ariaLabel}
3945
onclick={onClick}
46+
{...ariaAttributes}
4047
{...rest}
4148
>
4249
{

0 commit comments

Comments
 (0)