Color
GDS organizes color so products stay consistent in light and dark mode without hard-coded hex values.
System
Three layers
You'll almost always work with MUI palette slots for components and semantic tokens for custom surfaces. Primitives stay behind the scenes.
Primitives
Raw hex values like gray700 and blue500. Reserved for building tokens — almost never used in product UI.
Token authors onlySemantic tokens
Meaningful names such as surface.default and elements.default that point at primitives and flip with theme mode.
Custom surfaces & textMUI palette slots
Standard slots (primary, success, error, …) wired to GDS. Use these in sx and component color props.
Day-to-day usagePalette
Semantic palette
The six MUI palette slots. Each provides main, dark (hover/active), and light (backgrounds and softer states). Values follow the current theme mode.
primary
palette.primary.main
secondary
palette.secondary.main
success
palette.success.main
warning
palette.warning.main
error
palette.error.main
info
palette.info.main
| Slot | Use it for | Light main | Dark main |
|---|---|---|---|
primary | Dominant action color | blue700 · #1976D2 | blue200 · #90CAF9 |
secondary | Supporting / contrast actions | gray600 · #757575 | gray600 · #757575 |
success | Confirmations, saved states | green500 · #4CAF50 | green300 · #81C784 |
warning | Risky-but-recoverable states | orange500 · #FF9800 | orange100 · #FFE0B2 |
error | Destructive actions, validation | red700 · #D32F2F | red500 · #F44336 |
info | Neutral informational messaging | blue500 · #2196F3 | blue200 · #90CAF9 |
Surfaces
Surfaces & elements
Default, secondary, and low-contrast tokens flip with theme mode. Use low-contrast for subtle chrome — not for critical text.
Default surface
Base page background. Wired to both background.default and background.paper.
#FFFFFF
semantic.surface.defaultSecondary surface
Supporting surface for custom layouts. Read from theme.gds — not an MUI palette slot.
#9E9E9E
semantic.surface.secondaryLow contrast surface
Subtle fills, dividers, and quiet chrome that should stay behind primary content.
#E0E0E0
semantic.surface.lowcontrastDefault
Aa
semantic.elements.default
#000000
Primary text and icons. Maps to text.primary.
Secondary
Aa
semantic.elements.secondary
#757575
Supporting copy and quieter UI. Maps to text.secondary.
Low contrast
Aa
semantic.elements.lowcontrast
#EEEEEE
Decorative lines, placeholders, and low-emphasis marks.
| Token | MUI slot | Light | Dark |
|---|---|---|---|
semantic.surface.default | background.default / paper | white · #FFFFFF | gray900 · #121212 |
semantic.surface.secondary | theme.gds only | gray500 · #9E9E9E | gray300 · #E0E0E0 |
semantic.surface.lowcontrast | theme.gds only | gray300 · #E0E0E0 | gray700 · #616161 |
semantic.elements.default | text.primary | black · #000000 | white · #FFFFFF |
semantic.elements.secondary | text.secondary | gray600 · #757575 | gray600 · #757575 |
semantic.elements.lowcontrast | theme.gds only | gray200 · #EEEEEE | gray700 @ 30% |
Using colors in code
In sx props (most common)
Use MUI palette slot names. They resolve to GDS values automatically.
import { GdsBox } from "@granicus/gds-core/GdsBox";
<GdsBox sx={{ bgcolor: "primary.main", color: "primary.contrastText", p: 2 }}>
Primary surface
</GdsBox>In component color props
Most GDS components accept color="primary" (or any palette slot) directly:
<GdsButton color="error" variant="contained">Delete</GdsButton>
<GdsCheckbox color="success" defaultChecked />From the theme (when sx isn’t enough)
Reach into theme.gds only when you need a semantic value that isn’t covered by the MUI palette:
import { useTheme } from "@mui/material/styles";
function FeedbackBanner() {
const theme = useTheme();
return (
<div
style={{
backgroundColor: theme.gds.semantic.feedbackBackground.info,
color: theme.gds.semantic.feedbackElement.info,
}}
>
Heads up!
</div>
);
}Dark mode
GDS dark mode is fully wired through GDSThemeProvider. Wrap your app with <GDSThemeProvider mode="dark"> and every semantic token, MUI palette slot, and component picks up the dark values shown above.
import { GDSThemeProvider } from "@granicus/gds-core/GDSThemeProvider";
<GDSThemeProvider mode="dark">
<App />
</GDSThemeProvider>