Getting Started
Welcome to the GfG Design System. This is the single source of truth for all design decisions across the Gifts for Good Dashboard.
Overview
The GfG Design System provides a comprehensive set of colors, typography, components, and patterns built on Material-UI (MUI v6). It ensures consistency across the dashboard and streamlines development by providing pre-built, battle-tested components.
All colors are defined as CSS custom properties and MUI palette values. Typography uses Crimson Text for headings and Lato for body text. Components are fully customizable through the theme file.
Font Imports
Add this to your HTML head or import in your CSS:
<link href="https://fonts.googleapis.com/css2?family=Crimson+Text:wght@400&family=Lato:wght@400;700&display=swap" rel="stylesheet"> Theme Provider Setup
Wrap your application with ThemeProvider:
import { ThemeProvider } from '@mui/material/styles';
import gfgTheme from './theme/gfgTheme';
function App() {
return (
<ThemeProvider theme={gfgTheme}>
{/* Your application */}
</ThemeProvider>
);
} Component Details
Click any swatch to copy the hex code. All colors are available as CSS custom properties and MUI palette values.
Primary Brand Colors (Dust Family)
Neutrals
Semantic / Status Colors
Good Sage (Success)
Good Wine (Error)
Good Ochre (Warning)
Good Night (Info)
Component Details
Typography is built on Crimson Text for headings (elegant, serif) and Lato for body text and UI (clean, sans-serif).
Font Families
Crimson Text (400) for headings • Lato (400, 700) for body/UI
Typography Variants
Usage Guidelines
Use Crimson Text (serif) for headings to create visual hierarchy and elegance. Use Lato (sans-serif) for body text and UI controls for clarity and readability. Never mix serif and sans-serif within a single line or element.
Component Details
CSS custom properties available for use throughout your application. All tokens are defined in the :root selector.
Complete Token Reference
:root {
/* Brand Colors - Dust Family */
--color-dark-dust: #AF9577;
--color-rodeo-dust: #C8B298;
--color-light-dust: #F4EFEA;
--color-clear-dust: #FBF9F7;
/* Neutrals */
--color-rodeo-black: #333333;
--color-graphite: #6D6D6D;
--color-smoke: #E0E0E0;
--color-wax: #F5F5F5;
--color-white: #FFFFFF;
--color-card-border: #F0ECE8;
--color-background: #FAFAFA;
/* Semantic Colors */
--color-good-sage: #637058;
--color-good-sage-2: #8C9583;
--color-good-sage-3: #B5BBAF;
--color-good-sage-4: #E0E3DD;
--color-good-sage-5: #F5F6F4;
--color-good-wine: #A34740;
--color-good-wine-2: #BB7570;
--color-good-wine-3: #D4A4A1;
--color-good-wine-4: #EED4D3;
--color-good-wine-bg: #F9F1F0;
--color-good-ochre: #8A5D1F;
--color-good-ochre-2: #AC8958;
--color-good-ochre-3: #CFB591;
--color-good-ochre-4: #F3E3CD;
--color-good-ochre-bg: #FBF6EF;
--color-good-night: #2B4A60;
--color-good-night-2: #5E798C;
--color-good-night-3: #91A8B8;
--color-good-night-4: #C7D9E6;
--color-good-night-bg: #F1F5F9;
/* Typography */
--font-lato: "Lato", sans-serif;
--font-crimson: "Crimson Text", Georgia, serif;
/* Spacing & Sizing */
--sidebar-width: 240px;
--topbar-height: 76px;
--radius-card: 10px;
--radius-pill: 20px;
--radius-btn: 6px;
--radius-sm: 4px;
--grid-gap: 20px;
} How to Use Tokens
In CSS, reference tokens using the var() function:
.my-element {
color: var(--color-dark-dust);
background-color: var(--color-clear-dust);
border-radius: var(--radius-card);
font-family: var(--font-crimson);
padding: var(--grid-gap);
} In JavaScript, access tokens from computed styles:
const color = getComputedStyle(document.documentElement)
.getPropertyValue('--color-dark-dust')
.trim(); // Returns: #AF9577 Component Details
Four border radius values create visual consistency across components.
Radius Scale
Usage by Component
| Component | Radius Value | CSS Variable |
|---|---|---|
| Cards, Containers | 10px | var(--radius-card) |
| Pills, Chips, Badges | 20px | var(--radius-pill) |
| Buttons, Inputs | 8px | var(--radius-btn) |
| Small UI Elements | 4px | var(--radius-sm) |
Component Details
Cards are fundamental containers for content. All cards use 10px border radius, 1px border with smoke color, and 20px padding.
Default Card
This is a typical card component with a title, subtitle, and content area. Cards are used to group related information and create visual separation.
.card {
background-color: var(--color-white);
border-radius: var(--radius-card);
border: 1px solid var(--color-smoke);
padding: 20px;
transition: box-shadow 0.2s ease;
}
.card:hover {
box-shadow: 0 4px 16px rgba(51, 51, 51, 0.06);
} Stat Card
function StatCard({ label, value, subtitle }) {
return (
<div className="stat-card">
<div className="stat-card-overline">{label}</div>
<div className="stat-card-value">{value}</div>
<div className="stat-card-subtitle">{subtitle}</div>
</div>
);
} Use stat-card--white when stat cards sit on a page with a #fafafa (clear dust) background. The pure white card surface creates the necessary contrast separation.
{/* White variant — use on #fafafa / clear-dust page backgrounds */}
function StatCard({ label, value, subtitle, variant = 'default' }) {
return (
<div className={`stat-card ${variant === 'white' ? 'stat-card--white' : ''}`}>
<div className="stat-card-overline">{label}</div>
<div className="stat-card-value">{value}</div>
<div className="stat-card-subtitle">{subtitle}</div>
</div>
);
}
// Usage on a #fafafa background page:
<StatCard variant="white" label="Total Donations" value="24,850" subtitle="↑ 12% from last month" /> Campaign Card
Help us deliver gifts to families in need this holiday season.
Metric Card Grid
Media Card
A card with a top image area and content below. Use for product tiles, blog previews, or any content that leads with a visual.
/* Media card — image top, content below */
.media-card {
background: var(--color-white);
border: 1px solid var(--color-smoke);
border-radius: var(--radius-card);
overflow: hidden;
transition: box-shadow 0.2s ease;
}
.media-card:hover {
box-shadow: 0 4px 16px rgba(51, 51, 51, 0.06);
}
.media-card__image {
height: 160px;
background: var(--color-light-dust);
display: flex;
align-items: center;
justify-content: center;
}
.media-card__image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.media-card__body {
padding: 16px 20px;
}
.media-card__title {
font-family: var(--font-crimson);
font-size: 18px;
font-weight: 400;
color: var(--color-rodeo-black);
margin-bottom: 8px;
}
.media-card__subtitle {
font-size: 13px;
color: var(--color-graphite);
margin-bottom: 12px;
}
.media-card__footer {
display: flex;
align-items: center;
justify-content: space-between;
} Campaign Card with Separator
The primary card pattern in the campaigns list. A horizontal row with an image area, body content, and a right-side status panel separated by a 1px border. The card uses --color-card-border (#F0ECE8) and gains a subtle shadow on hover.
/* Campaign card — split layout with separator */
.campaign-card {
display: flex;
flex-direction: column;
border: 1px solid var(--color-smoke);
border-radius: var(--radius-card);
background: var(--color-white);
overflow: hidden;
transition: box-shadow 0.2s ease;
}
.campaign-card:hover {
box-shadow: 0 4px 16px rgba(51, 51, 51, 0.06);
}
.campaign-card__row {
display: flex;
align-items: stretch;
}
/* Left image area */
.campaign-card__image {
width: 140px;
min-height: 140px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
/* Center body */
.campaign-card__body {
flex: 1;
padding: 18px 20px;
display: flex;
flex-direction: column;
gap: 8px;
}
/* Right status panel — border-left is the separator */
.campaign-card__right {
width: 320px;
flex-shrink: 0;
padding: 18px 20px;
display: flex;
flex-direction: column;
gap: 8px;
border-left: 1px solid var(--color-card-border);
}
/* Card border token */
/* --color-card-border: #F0ECE8 (warm-tinted) */
/* Action icon buttons inside cards */
.card-action-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--color-smoke);
border-radius: var(--radius-sm);
background: none;
color: var(--color-graphite);
cursor: pointer;
transition: border-color 0.15s;
}
.card-action-btn:hover {
border-color: var(--color-dark-dust);
color: var(--color-dark-dust);
}
/* Meta dot separator */
.meta-dot {
width: 3px;
height: 3px;
background: var(--color-rodeo-dust);
border-radius: 50%;
} Component Details
Buttons use 6px border radius, 600 font weight in Lato, and come in multiple variants for different contexts. All buttons include a Material-style ink ripple on click, tinted to brand colors.
Interaction: Click Ripple
All buttons use a JS-driven ink ripple identical to MUI's TouchRipple. The ripple originates from the click point, expands to fill the button, then fades on release. Contained buttons (dark surfaces) use a white 35% ripple. Outlined and text buttons use a Dark Dust 30% ripple. Click and hold any button below to see it.
Contained Buttons
button.btn-contained {
background-color: var(--color-dark-dust);
color: var(--color-white);
padding: 10px 20px;
border-radius: var(--radius-btn);
font-family: var(--font-lato);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 2px;
border: none;
cursor: pointer;
}
button.btn-contained:hover {
background-color: #9B7D63;
transform: translateY(-1px);
} Outlined Buttons
button.btn-outlined {
background-color: transparent;
border: 1px solid var(--color-dark-dust);
color: var(--color-dark-dust);
padding: 10px 20px;
border-radius: var(--radius-btn);
font-family: var(--font-lato);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 2px;
cursor: pointer;
} Text Buttons
button.btn-text {
background-color: transparent;
color: var(--color-dark-dust);
padding: 10px 20px;
border: 1px solid transparent;
border-radius: var(--radius-btn);
font-family: var(--font-lato);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 2px;
cursor: pointer;
}
button.btn-text:hover {
background-color: rgba(175, 149, 119, 0.05);
border-color: var(--color-dark-dust);
} Icon Buttons
Small 32×32 icon buttons used inside cards for contextual actions. Default state has a light border; hover transitions to Dark Dust.
When an icon button is toggled on (e.g. analytics drawer open), apply the active state with Dark Dust border and color.
/* Icon button — default */
.icon-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--color-smoke);
border-radius: var(--radius-sm);
background: none;
color: var(--color-graphite);
cursor: pointer;
transition: border-color 0.15s, color 0.15s;
padding: 0;
}
/* Icon button — hover */
.icon-btn:hover {
border-color: var(--color-dark-dust);
color: var(--color-dark-dust);
}
/* Icon button — active / toggled-on */
.icon-btn--active {
border-color: var(--color-dark-dust);
color: var(--color-dark-dust);
}
/* SVG icon sizing inside buttons */
.icon-btn svg {
width: 14px;
height: 14px;
} Button Best Practices
Component Details
Eight distinct status pill styles for campaign states and order statuses. Use 20px border radius and 600 font weight.
All Status States
Each status indicates a specific state. Use consistently across your application.
Status Definitions
| Status | Color | Background | Use Case |
|---|---|---|---|
| Active | #637058 | #E0E3DD | Campaign is live and accepting gifts |
| Fulfilled | #637058 | #E0E3DD | Gift delivery completed successfully |
| Pending | #8A5D1F | #F3E3CD | Awaiting action or approval |
| Processing | #2B4A60 | #C7D9E6 | In progress, e.g., sending, packaging |
| Cancelled | #A34740 | #EED4D3 | Campaign cancelled or gift declined |
| Draft | #6D6D6D | #F4EFEA | Unsaved or incomplete campaign |
| Closed | #6D6D6D | #F4EFEA | Campaign ended, no longer accepting gifts |
| Archived | #FFFFFF | #333333 | Historical campaign, not active |
CSS Styles
.chip {
display: inline-block;
padding: 6px 12px;
border-radius: var(--radius-pill);
font-family: var(--font-lato);
font-size: 12px;
font-weight: 600;
}
.chip-active { background-color: #E0E3DD; color: #637058; }
.chip-fulfilled { background-color: #E0E3DD; color: #637058; }
.chip-pending { background-color: #F3E3CD; color: #8A5D1F; }
.chip-processing { background-color: #C7D9E6; color: #2B4A60; }
.chip-cancelled { background-color: #EED4D3; color: #A34740; }
.chip-draft { background-color: #F4EFEA; color: #6D6D6D; }
.chip-closed { background-color: #F4EFEA; color: #6D6D6D; }
.chip-archived { background-color: #333333; color: #FFFFFF; } Alert Button
Used on campaign cards when there are delivery failures or issues requiring attention. The button is filled Good Wine with a count badge.
/* Alert action button — filled Good Wine */
.action-btn--alert {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-good-wine);
border: 1px solid var(--color-good-wine);
border-radius: var(--radius-sm);
color: var(--color-white);
cursor: pointer;
position: relative;
}
.action-btn--alert:hover {
background: #8B2E28;
border-color: #8B2E28;
}
/* Count badge — positioned top-right of button */
.action-badge {
position: absolute;
top: -5px;
right: -5px;
width: 16px;
height: 16px;
background: var(--color-good-wine-bg);
color: var(--color-good-wine);
border: 1px solid var(--color-good-wine);
border-radius: 50%;
font-family: var(--font-lato);
font-size: 10px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
} Notification Badge
Used in navigation items to indicate unread counts. Dark Dust background with white text, fully rounded pill.
/* Notification badge — nav items, sidebar counts */
.badge {
background: var(--color-dark-dust);
color: var(--color-white);
font-family: var(--font-lato);
font-size: 12px;
font-weight: 700;
border-radius: 999px;
width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* Urgent variant */
.badge--urgent {
background: var(--color-good-wine);
} Alert & Notification Dialogs
Full-width tinted boxes for contextual alerts, confirmations, warnings, and informational messages. Each uses the Level 5 background tint, a 3px left accent border in the main semantic color, and Level 1 text color for the title.
Your campaign has been published and gifts are now being delivered to all 24 recipients.
3 gift deliveries failed due to invalid email addresses. Please review the recipient list and retry.
Your campaign budget is 90% spent with 15 days remaining. Consider adding funds or closing the campaign early.
Your Earth Day campaign is scheduled to launch on April 22. Recipients will receive invitations at 9:00 AM ET.
/* Base alert dialog */
.alert-dialog {
border-radius: var(--radius-btn);
padding: 16px 20px;
border-left: 3px solid;
}
.alert-dialog__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.alert-dialog__title {
font-family: var(--font-lato);
font-size: 13px;
font-weight: 700;
}
.alert-dialog__body {
font-family: var(--font-lato);
font-size: 13px;
color: var(--color-graphite);
line-height: 1.5;
}
/* Success */
.alert-dialog--success {
background: var(--color-good-sage-5);
border-left-color: var(--color-good-sage);
}
.alert-dialog--success .alert-dialog__title,
.alert-dialog--success .material-icons-outlined {
color: var(--color-good-sage);
}
/* Error */
.alert-dialog--error {
background: var(--color-good-wine-bg);
border-left-color: var(--color-good-wine);
}
.alert-dialog--error .alert-dialog__title,
.alert-dialog--error .material-icons-outlined {
color: var(--color-good-wine);
}
/* Warning */
.alert-dialog--warning {
background: var(--color-good-ochre-bg);
border-left-color: var(--color-good-ochre);
}
.alert-dialog--warning .alert-dialog__title,
.alert-dialog--warning .material-icons-outlined {
color: var(--color-good-ochre);
}
/* Info */
.alert-dialog--info {
background: var(--color-good-night-bg);
border-left-color: var(--color-good-night);
}
.alert-dialog--info .alert-dialog__title,
.alert-dialog--info .material-icons-outlined {
color: var(--color-good-night);
} Component Details
Input fields, search bars, and dropdowns with GfG styling.
Text Input
Follows MUI's outlined input pattern. The label sits inside the field at rest, then floats into the top-left border notch on focus or when filled.
<!-- HTML -->
<div class="mui-text-field">
<input type="text" id="myField" class="mui-text-field__input" placeholder=" ">
<label for="myField" class="mui-text-field__label">Campaign Name</label>
</div> /* Outlined text field with floating label */
.mui-text-field {
position: relative;
}
.mui-text-field__input {
width: 100%;
padding: 14px 14px;
border: 1px solid var(--color-smoke);
border-radius: var(--radius-btn);
font-family: var(--font-lato);
font-size: 14px;
color: var(--color-rodeo-black);
background: var(--color-white);
transition: border-color 0.15s;
}
.mui-text-field__label {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
font-family: var(--font-lato);
font-size: 14px;
color: var(--color-graphite);
background: var(--color-white);
padding: 0 4px;
pointer-events: none;
transition: all 0.15s ease;
}
/* Float the label on focus or when filled */
.mui-text-field__input:focus + .mui-text-field__label,
.mui-text-field__input:not(:placeholder-shown) + .mui-text-field__label {
top: 0;
transform: translateY(-50%);
font-size: 11px;
color: var(--color-dark-dust);
}
.mui-text-field__input:focus {
outline: none;
border-color: var(--color-dark-dust);
}
/* MUI Theme Override */
MuiOutlinedInput: {
styleOverrides: {
root: {
borderRadius: '6px',
fontFamily: '"Lato", sans-serif',
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: '#AF9577',
},
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
borderColor: '#AF9577',
borderWidth: '1px',
},
},
},
},
MuiInputLabel: {
styleOverrides: {
root: {
fontFamily: '"Lato", sans-serif',
color: '#6D6D6D',
'&.Mui-focused': {
color: '#AF9577',
},
},
},
} Search Input
Select Dropdown
Mirrors MUI's <Select> with outlined variant — floating label, helper text, and error state. The label floats into the top border notch on focus or when a value is selected.
<!-- Outlined select with floating label + helper text -->
<div class="mui-select">
<div class="mui-select__field">
<select id="mySelect" class="mui-select__native">
<option value=""></option>
<option value="active">Active</option>
<option value="pending">Pending</option>
</select>
<label for="mySelect" class="mui-select__label">Status</label>
</div>
<div class="mui-select__helper">Filter by campaign status</div>
</div>
<!-- Error variant -->
<div class="mui-select mui-select--error">
<div class="mui-select__field">
<select id="cause" class="mui-select__native">
<option value=""></option>
<option value="sage">Good Sage</option>
</select>
<label for="cause" class="mui-select__label">Cause Area</label>
</div>
<div class="mui-select__helper mui-select__helper--error">Required</div>
</div> /* Outlined Select — mirrors MUI <Select variant="outlined"> */
.mui-select__field { position: relative; }
.mui-select__native {
width: 100%;
padding: 14px 44px 14px 14px;
border: 1px solid var(--color-smoke);
border-radius: var(--radius-btn);
font-family: var(--font-lato);
font-size: 14px;
color: var(--color-rodeo-black);
background-color: var(--color-white);
cursor: pointer;
appearance: none;
background-image: url("..chevron-svg..");
background-repeat: no-repeat;
background-position: right 14px center;
transition: border-color 250ms cubic-bezier(0.4,0,0.2,1);
}
.mui-select__label {
position: absolute;
left: 12px; top: 50%;
transform: translateY(-50%);
font-size: 14px;
color: var(--color-graphite);
background: var(--color-white);
padding: 0 4px;
pointer-events: none;
transition: top 250ms cubic-bezier(0.4,0,0.2,1),
font-size 250ms cubic-bezier(0.4,0,0.2,1),
color 250ms cubic-bezier(0.4,0,0.2,1);
}
/* Float into notch on focus or filled */
.mui-select__native:focus + .mui-select__label,
.mui-select--filled .mui-select__label {
top: 0; font-size: 11px;
color: var(--color-dark-dust);
}
.mui-select__native:focus {
border-color: var(--color-dark-dust);
}
/* Helper text */
.mui-select__helper {
font-size: 12px;
color: var(--color-graphite);
margin-top: 4px;
padding-left: 14px;
}
/* Error state */
.mui-select--error .mui-select__native { border-color: var(--color-good-wine); }
.mui-select--error .mui-select__label { color: var(--color-good-wine); }
.mui-select__helper--error { color: var(--color-good-wine); } // MUI Theme Override
MuiSelect: {
defaultProps: { variant: 'outlined' },
styleOverrides: {
root: {
fontFamily: '"Lato", sans-serif',
fontSize: '14px',
},
icon: { color: '#6D6D6D' },
},
},
MuiFormHelperText: {
styleOverrides: {
root: {
fontFamily: '"Lato", sans-serif',
fontSize: '12px',
marginLeft: '14px',
marginTop: '4px',
},
},
}, Component Details
Progress bars and metric display patterns.
Progress Bar
Campaign Goal: 25 Gifts
Donor Participation: 85%
.progress-bar {
height: 4px;
background-color: var(--color-smoke);
border-radius: var(--radius-sm);
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: var(--color-dark-dust);
border-radius: var(--radius-sm);
transition: width 0.3s ease;
} Metric Drawer Pattern
A collapsible metrics section with a grid of metric cards. Ideal for dashboard overviews.
Component Details
Grid system and spacing guidelines for consistent layouts.
Grid System
Standard grid gap is 20px (defined as --grid-gap). Use CSS Grid for flexible layouts.
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--grid-gap);
} Spacing Scale
MUI uses an 8px base unit. Standard spacing value is var(--grid-gap) = 20px.
| Size | Pixels | Use Case |
|---|---|---|
| xs | 4px | Small gaps within components |
| sm | 8px | Small spacing, icon gaps |
| md | 16px | Card padding, moderate spacing |
| lg | 20px | Standard grid gap, section spacing |
| xl | 30px | Major section gaps, page padding |
| 2xl | 40px | Large section separation |
Responsive Breakpoints
| Breakpoint | Width | Device |
|---|---|---|
| xs | 0px - 600px | Mobile phones |
| sm | 600px - 960px | Small tablets |
| md | 960px - 1264px | Tablets & small desktops |
| lg | 1264px - 1904px | Desktop computers |
| xl | 1904px+ | Large displays |
Component Details
Material Icons Outlined from Google Fonts. Use 24px as the standard size.
Common Icons
Icon Usage
<span class="material-icons-outlined">campaign</span>
<!-- Sizes -->
<span class="material-icons-outlined" style="font-size: 18px;">home</span>
<span class="material-icons-outlined" style="font-size: 24px;">home</span>
<span class="material-icons-outlined" style="font-size: 32px;">home</span>
<!-- Colors -->
<span class="material-icons-outlined" style="color: var(--color-dark-dust);">home</span> Component Details
Complete Material-UI v6 theme configuration. Copy this file to your project.
gfgTheme.ts
Save this as src/theme/gfgTheme.ts in your React project.
import { createTheme } from '@mui/material/styles';
const gfgTheme = createTheme({
palette: {
primary: {
main: '#AF9577', // Dark Dust
light: '#C8B298', // Rodeo Dust
lighter: '#F4EFEA', // Light Dust
lightest: '#FBF9F7', // Clear Dust
},
secondary: {
main: '#6D6D6D', // Graphite
},
text: {
primary: '#333333', // Rodeo Black
secondary: '#6D6D6D', // Graphite
},
background: {
default: '#FAFAFA',
paper: '#FFFFFF',
},
divider: '#E0E0E0', // Smoke
success: {
main: '#637058', // Good Sage
light: '#E0E3DD', // Good Sage 4
},
error: {
main: '#A34740', // Good Wine — NOT MUI default #d32f2f
light: '#EED4D3', // Good Wine 4
},
warning: {
main: '#8A5D1F', // Good Ochre
light: '#F3E3CD', // Good Ochre 4
},
info: {
main: '#2B4A60', // Good Night
light: '#C7D9E6', // Good Night 4
},
grey: {
100: '#F5F5F5', // Wax
},
},
typography: {
fontFamily: '"Lato", sans-serif',
h1: {
fontFamily: '"Crimson Text", Georgia, serif',
fontSize: '30px',
fontWeight: 400,
lineHeight: 1.2,
},
h2: {
fontFamily: '"Crimson Text", Georgia, serif',
fontSize: '24px',
fontWeight: 400,
lineHeight: 1.2,
},
h3: {
fontFamily: '"Crimson Text", Georgia, serif',
fontSize: '20px',
fontWeight: 400,
lineHeight: 1.2,
},
h4: {
fontFamily: '"Crimson Text", Georgia, serif',
fontSize: '18px',
fontWeight: 400,
lineHeight: 1.2,
},
h5: {
fontFamily: '"Lato", sans-serif',
fontSize: '16px',
fontWeight: 600,
},
subtitle1: {
fontFamily: '"Lato", sans-serif',
fontSize: '18px',
fontWeight: 600,
},
body1: {
fontFamily: '"Lato", sans-serif',
fontSize: '14px',
fontWeight: 400,
lineHeight: 1.5,
color: '#6D6D6D',
},
body2: {
fontFamily: '"Lato", sans-serif',
fontSize: '13px',
fontWeight: 400,
lineHeight: 1.5,
},
caption: {
fontFamily: '"Lato", sans-serif',
fontSize: '12px',
fontWeight: 400,
},
overline: {
fontFamily: '"Lato", sans-serif',
fontSize: '11px',
fontWeight: 700,
color: '#AF9577', // Dark Dust
textTransform: 'uppercase',
letterSpacing: '2px',
},
// Custom variant — stat card numbers
statValue: {
fontFamily: '"Crimson Text", Georgia, serif',
fontSize: '42px',
fontWeight: 400,
lineHeight: 1,
color: '#333333', // Rodeo Black
},
},
shape: {
borderRadius: 10,
},
spacing: 8,
mixins: {
toolbar: {
minHeight: 76,
},
},
components: {
MuiCard: {
styleOverrides: {
root: {
borderRadius: '10px',
border: '1px solid #E0E0E0',
boxShadow: 'none',
},
},
},
MuiPaper: {
styleOverrides: {
rounded: { borderRadius: 10 },
},
},
MuiCardContent: {
styleOverrides: {
root: {
padding: '20px',
'&:last-child': {
paddingBottom: '20px',
},
},
},
},
MuiButton: {
defaultProps: {
disableElevation: true,
// Ripple is enabled by default in MUI — keep it on
},
styleOverrides: {
root: {
borderRadius: '6px',
fontFamily: '"Lato", sans-serif',
fontSize: '11px',
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '2px',
},
contained: {
backgroundColor: '#AF9577',
color: '#FFFFFF',
boxShadow: 'none',
'&:hover': {
backgroundColor: '#9B7D63',
boxShadow: 'none',
},
},
outlined: {
borderColor: '#AF9577',
color: '#AF9577',
'&:hover': {
backgroundColor: 'rgba(175, 149, 119, 0.05)',
},
},
},
},
// Brand-colored ripple across all components
MuiTouchRipple: {
styleOverrides: {
rippleVisible: {
animationDuration: '550ms',
},
child: {
// Dark Dust at 30% — on-brand ink
backgroundColor: 'rgba(175, 149, 119, 0.3)',
},
childLeaving: {
animationDuration: '550ms',
},
},
},
MuiChip: {
styleOverrides: {
root: {
borderRadius: '20px',
fontWeight: 600,
fontSize: '12px',
fontFamily: '"Lato", sans-serif',
},
},
},
MuiAppBar: {
styleOverrides: {
root: {
backgroundColor: '#FFFFFF',
color: '#333333',
boxShadow: 'none',
borderBottom: '1px solid #E0E0E0',
},
},
},
MuiToolbar: {
styleOverrides: {
root: {
minHeight: '76px',
},
},
},
MuiDrawer: {
styleOverrides: {
paper: {
backgroundColor: '#FFFFFF',
borderRight: '1px solid #E0E0E0',
width: '240px',
},
},
},
MuiTextField: {
styleOverrides: {
root: {
'& .MuiOutlinedInput-root': {
borderRadius: '6px',
'& fieldset': {
borderColor: '#E0E0E0',
},
'&:hover fieldset': {
borderColor: '#AF9577',
},
'&.Mui-focused fieldset': {
borderColor: '#AF9577',
},
},
},
},
},
},
});
export default gfgTheme; Component Details
Helper functions and utility objects for common patterns.
Status Pill Styles Utility
Use this utility to get styles for status pills. Returns color and backgroundColor.
export const statusPillStyles: Record<string, { color: string; backgroundColor: string }> = {
active: { color: '#637058', backgroundColor: '#E0E3DD' },
fulfilled: { color: '#637058', backgroundColor: '#E0E3DD' },
pending: { color: '#8A5D1F', backgroundColor: '#F3E3CD' },
processing: { color: '#2B4A60', backgroundColor: '#C7D9E6' },
cancelled: { color: '#A34740', backgroundColor: '#EED4D3' },
draft: { color: '#6D6D6D', backgroundColor: '#F4EFEA' },
closed: { color: '#6D6D6D', backgroundColor: '#F4EFEA' },
archived: { color: '#FFFFFF', backgroundColor: '#333333' },
};
// Usage
const status = 'active';
const styles = statusPillStyles[status];
// Returns: { color: '#637058', backgroundColor: '#E0E3DD' } Color Utilities
// Get CSS variable value
export const getCSSVariable = (varName: string): string => {
return getComputedStyle(document.documentElement)
.getPropertyValue(varName)
.trim();
};
// Format currency
export const formatCurrency = (value: number): string => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(value);
};
// Format percentage
export const formatPercent = (value: number): string => {
return `${Math.round(value)}%`;
};
// Get semantic color based on status
export const getStatusColor = (status: string): string => {
const colorMap: Record<string, string> = {
active: '#637058',
fulfilled: '#637058',
pending: '#8A5D1F',
processing: '#2B4A60',
cancelled: '#A34740',
draft: '#6D6D6D',
closed: '#6D6D6D',
archived: '#333333',
};
return colorMap[status] || '#6D6D6D';
}; Component Helpers
// Get button variant based on context
export const getButtonVariant = (context: 'primary' | 'secondary' | 'tertiary') => {
const variants = {
primary: { variant: 'contained', color: 'primary' },
secondary: { variant: 'outlined', color: 'primary' },
tertiary: { variant: 'text', color: 'primary' },
};
return variants[context];
};
// Truncate long text with ellipsis
export const truncateText = (text: string, length: number): string => {
return text.length > length ? text.substring(0, length) + '...' : text;
};
// Combine class names conditionally
export const classNames = (...classes: (string | boolean | undefined)[]): string => {
return classes.filter(Boolean).join(' ');
}; Component Details
Track all design system updates and version history here.
Version History
- Complete color palette documentation
- Typography system with all variants
- CSS custom properties (design tokens)
- Border radius scale
- Card, button, and status pill components
- Navigation patterns
- Form controls
- Progress bars and metrics
- Layout patterns and grid system
- Complete MUI v6 theme file
- Utility functions and helpers
How to Document Changes
GfG Design System Reference • Last updated March 16, 2026 • v1.0.0
This is the single source of truth for design decisions across the Gifts for Good Dashboard.