Button System: A Complete Guide to Variants and Dark Mode
Our button system uses semantic CSS variables with CSS Modules for consistent styling across themes. Each button variant maps to specific design tokens that handle colors, hover states, and dark mode automatically.
Design Tokens
Our button system is built on semantic tokens:
/* Primary Button */
--btn-primary-bg: var(--accent-blue)
--btn-primary-text: white
--btn-primary-hover: var(--blue500)
/* Secondary Button */
--btn-secondary-bg: var(--mono2)
--btn-secondary-text: var(--mono12)
--btn-secondary-hover: var(--mono3)
--btn-secondary-border: var(--mono5)
/* Ghost Button */
--btn-ghost-hover: var(--mono2)
--btn-ghost-text: var(--mono11)
--btn-ghost-text-hover: var(--mono12)
/* Common Button Properties */
--btn-disabled-opacity: 0.5
--btn-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)
CSS Module Approach
Our Button component uses CSS Modules for scoped styling:
/* Button.module.css */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: var(--radius);
font-weight: 500;
transition: var(--focus-transition);
cursor: pointer;
position: relative;
white-space: nowrap;
}
.btn--primary {
background-color: var(--btn-primary-bg);
color: var(--btn-primary-text);
box-shadow: var(--btn-shadow);
border: none;
}
/* Additional variant styles... */
The component then imports and uses these styles:
import styles from './Button.module.css';
<button
className={cn(
styles.btn,
styles[`btn--${variant}`],
styles[`btn--${size}`],
className
)}
>
{children}
</button>
Primary Variant
The primary call-to-action button. Uses --btn-primary-*
tokens.
<Button size="sm">Small</Button>
<Button>Default</Button>
<Button size="lg">Large</Button>
<Button isLoading loadingText="Loading">Loading</Button>
<Button disabled>Disabled</Button>
Outline Variant
Secondary actions using --btn-secondary-*
tokens.
<Button variant="outline">Secondary Action</Button>
Ghost Variant
Subtle actions using --btn-ghost-*
tokens.
<Button variant="ghost">Subtle Action</Button>
Common Use Cases
Form Actions
Primary actions use primary variant, secondary actions use ghost:
<div className="btn-row">
<Button>Save changes</Button>
<Button variant="ghost">Discard</Button>
</div>
Dialog Actions
Destructive actions use outline variant for visual distinction:
<div className="btn-row">
<Button variant="outline">Delete Account</Button>
<Button variant="ghost">Cancel</Button>
</div>
Toolbar Groups
Ghost buttons with consistent borders:
<div className="toolbar-container">
<Button variant="ghost" position="left">Copy</Button>
<Button variant="ghost" position="middle">Paste</Button>
<Button variant="ghost" position="right">Delete</Button>
</div>
Multi-Step Forms
Combine outline and primary variants for navigation:
<div className="btn-row btn-row--spaced">
<Button variant="outline">Previous</Button>
<Button>Next Step</Button>
</div>
Loading States
Loading states maintain variant styling:
<div className="btn-row">
<Button isLoading loadingText="Saving...">Save Draft</Button>
<Button variant="outline" isLoading loadingText="Publishing...">
Publish
</Button>
</div>
CSS Module Features
The Button module includes specialized CSS classes:
/* Button modifiers for toolbar positioning */
.btn--left {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.btn--middle {
border-radius: 0;
border-left: 1px solid var(--mono4);
border-right: 1px solid var(--mono4);
}
.btn--right {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
/* Loading animation */
.btn__loader {
margin-right: 0.5rem;
height: 1rem;
width: 1rem;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
Usage Guidelines
Choose variants based on action importance:
-
Primary (
--btn-primary-*
): Primary actions, main calls-to-action- Form submissions
- Confirmation dialogs
- Next steps
- Save/Submit actions
-
Outline (
--btn-secondary-*
): Secondary actions- Alternative options
- Previous/Back buttons
- Cancel options
- Destructive actions
-
Ghost (
--btn-ghost-*
): Subtle actions- Toolbar buttons
- Button groups
- Menu items
- Less prominent options
Accessibility Features
Our button system includes:
- High contrast using semantic color tokens
- Focus rings via global accessibility styles (not component-specific)
- Loading states with visual indicators
- Disabled states using
--btn-disabled-opacity
- Consistent touch targets
- Keyboard navigation support
Try toggling dark mode to see how the semantic tokens automatically adjust!