Approach
A concept exploring how an open enterprise system can be adapted into a distinctive editorial brand, without throwing away the engineering rigour that makes it dependable.
Mandara keeps Carbon's structure and changes its voice: dark editorial surfaces, a serif display face and a warm orange accent, then closes the accessibility gaps that a re-theme tends to open.
Inherited from IBM Carbon
- Token architecture: primitives feed semantic tokens (
--interactive,--focus,--support-*), and components only reference the semantic layer. - Foundations: 8px base grid, IBM Plex typefaces, a Carbon-aligned type scale.
- Motion model: productive vs expressive easing with Carbon-style durations.
- Status palette: Carbon's dark-theme support colours for success, warning, error and info.
Added by Mandara
- Editorial identity: near-black surfaces with warm cream light surfaces, and an orange brand accent in place of Carbon blue.
- Serif display voice: IBM Plex Serif for headlines, Sans for UI, Mono for data and labels.
- Brand effects: brand gradient, mesh gradient and glow shadows.
- Event patterns: countdown, programme schedule rows and programme cards.
- Audited contrast: every text and fill pair measured against WCAG 2.2 AA, with fixes applied (see Accessibility).
- Usage guidelines for buttons, forms, alerts and cards, and downloadable tokens (JSON and CSS).
Colors
Semantic tokens map to a fixed palette of primitives. Always reference semantic tokens in components — never raw hex values.
Typography
Three IBM Plex families create a clear hierarchy — Serif for display, Sans for UI, Mono for data and labels.
Spacing
8px base grid. All spacing tokens are multiples of 4px. Use --space-N tokens exclusively — never raw pixel values.
Motion
Carbon-aligned durations with expressive easing curves. Productive motion is subtle; expressive is intentional.
Grid
12-column grid with 16px gutters. Use .ds-grid-2 through .ds-grid-4 helpers or custom CSS grid with the column token set.
Accessibility
Every text and fill pair was measured against WCAG 2.2 contrast (4.5:1 for text, 3:1 for large text and UI). A re-theme to dark with a saturated orange accent broke several pairs inherited from the default styling; the fixes below are now in the tokens.
| Pair | Before | After | Result |
|---|---|---|---|
| Primary button label | White on #E84700 · 3.94:1 | Near-black on #E84700 · 5.03:1 | Fixed |
| Primary button hover | White on #FF5722 · 3.16:1 | Near-black on #FF5722 · 6.26:1 | Fixed |
| Danger button label | White on #FA4D56 · 3.35:1 | Near-black on #FA4D56 · 5.90:1 | Fixed |
| Gradient button label (worst stop) | White on #FFD166 · 1.44:1 | Near-black on #FF5722 (darkest stop) · 6.26:1 | Fixed |
| Input placeholder · dark | #555555 on #1A1A1A · 2.33:1 | #9A9A9A on #1A1A1A · 6.19:1 | Fixed |
| Input placeholder · light | #BBBBBB on #EDE6D8 · 1.55:1 | #5F5F5F on #EDE6D8 · 5.14:1 | Fixed |
| Pair | Colours | Ratio | Result |
|---|---|---|---|
| Body text · dark | #FDFAF5 on #0A0A0A | 19.01:1 | Pass AA |
| Secondary text · dark | #D9CFC0 on #1A1A1A | 11.30:1 | Pass AA |
| Brand text · dark | #FF5722 on #0A0A0A | 6.26:1 | Pass AA |
| Brand text on card · dark | #FF5722 on #1A1A1A | 5.50:1 | Pass AA |
| Brand text · light | #C43B00 on #FDFAF5 | 5.08:1 | Pass AA |
| Success / error / info · dark | #42BE65 · #FA4D56 · #4589FF on #0A0A0A | 8.28 · 5.90 · 5.92:1 | Pass AA |
| Pair | Colours | Ratio | Rule |
|---|---|---|---|
| Orange-500 as text on a card | #E84700 on #1A1A1A | 4.42:1 | Rule Use orange-400 for orange text on dark surfaces; keep orange-500 for fills. |
| Orange-500 as text on cream | #E84700 on #FDFAF5 | 3.78:1 | Rule On light surfaces use orange-600 (5.08:1) for orange text. |
A new token, --text-on-interactive, holds the dark label colour used on every orange or red fill. Disabled text is exempt from WCAG but is kept above 3:1 so it stays legible.
Buttons
Seven variants, four sizes, icon-only, and disabled state. All respect focus ring and active press feedback.
Badges & Tags
Forms
Cards
21
Alerts
Feedback
Progress indicators, loading states, and skeleton placeholders.
Overlays
Usage Guidelines
Rules of thumb for the four components used most. Each pairs what to do with what to avoid, so the system stays consistent across teams.
- One primary action per view; everything else is secondary or ghost.
- Start labels with a verb that says the outcome ("Save changes", "Send invite").
- Keep the focus ring visible, and confirm before a Danger action runs.
- Put two primary buttons side by side; the user can't tell what matters most.
- Use the gradient button for routine actions. Reserve it for one hero call to action per page.
- Use Danger for anything that isn't destructive or irreversible.
- Give every field a persistent label above the input.
- Write errors that say what went wrong and how to fix it, next to the field.
- Group related fields and keep a single column on small screens.
- Use the placeholder as the label; it disappears as soon as the user types.
- Disable the submit button without explaining why.
- Signal an error with colour alone. Add an icon and text.
- Match the severity: info, success, warning or error.
- Keep to one sentence and, where it helps, one clear action.
- Pair colour with an icon and text so meaning survives for colour-blind users.
- Stack more than two alerts on a screen.
- Auto-dismiss errors or warnings; the user may not have read them.
- Use error styling for something the user can ignore.
- Give each card one primary action.
- Keep title, metadata and action in the same positions across a set.
- Use surface steps (01 → 02 → 03) to show depth rather than borders alone.
- Nest cards inside cards.
- Make the whole card clickable and also add links inside it.
- Run body copy beyond about three lines; link out to the detail view.
Event UI
Countdown, schedule rows, and programme cards — the core event page pattern from the Mandara theme.
Token Reference
Copy this :root block into your project. All tokens are available globally via CSS custom properties.
/* ── Mandara — copy into your project ── */ :root { /* Color */ --background: #0A0A0A; --surface-01: #111111; --surface-02: #1A1A1A; --text-primary: #FDFAF5; --text-secondary: #D9CFC0; --text-brand: #FF5722; --interactive: #E84700; --text-on-interactive: #0A0A0A; --border-subtle: rgba(255,255,255,0.08); --border-brand: #E84700; --gradient-brand: linear-gradient(135deg,#FF5722,#FF9A3C,#FFD166); /* Typography */ --font-serif: 'IBM Plex Serif', Georgia, serif; --font-sans: 'IBM Plex Sans', system-ui, sans-serif; --font-mono: 'IBM Plex Mono', monospace; /* Spacing */ --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; --space-4: 1rem; --space-6: 1.5rem; --space-7: 2rem; --space-8: 2.5rem; --space-10: 4rem; /* Motion */ --duration-fast: 100ms; --duration-base: 200ms; --ease-standard: cubic-bezier(0.2,0,0.38,0.9); --ease-expressive: cubic-bezier(0.4,0.14,0.3,1); /* Radius */ --radius-md: 4px; --radius-lg: 8px; --radius-xl: 12px; --radius-2xl: 20px; --radius-full: 9999px; }