Time to joy.
The first render should feel good. Sensible defaults, no configuration required, something useful and beautiful on screen in under a minute.
Democracy League OS design system
A living inventory of the Democracy League OS design language. Everything here runs on the same package and tokens as the staff shell itself.
Design philosophy
We make components that help people enjoy accessible, meaningful, and thoughtful experiences.
The first render should feel good. Sensible defaults, no configuration required, something useful and beautiful on screen in under a minute.
Spacing, language, behavior, and API design should reveal that someone paid attention, especially in the states few people celebrate.
Loading, empty, error, disabled, focused, interrupted. These are not edge cases. A component is unfinished until its difficult states feel intentional.
The API is pedagogy. Components, props, and variants should reveal the model they belong to. Each new option creates a decision someone must understand, so prefer strong defaults and a few durable concepts over speculative flexibility.
We maintain what people have trusted us to build upon. Deprecations include reasons, migration paths, and time to adapt. When replacement is necessary, we carry people across.
Utility and beauty belong together. Each component should feel coherent, alive, and worth returning to.
Foundations
Every value below comes from the generated token bundle (@dlos/design-tokens/demleagueos.json), which the components read as CSS variables. The system ships light only; a dark set is a variable block in the tokens, not a component change. Families in use: Aileron, 'Arial Black', 'Helvetica Neue', Arial, sans-serif, Asap, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif; 4 self-hosted font faces declared.
Foundations / Type scale
Problem solving over political fighting.
Problem solving over political fighting.
Problem solving over political fighting.
Problem solving over political fighting.
Problem solving over political fighting.
Problem solving over political fighting.
Problem solving over political fighting.
Problem solving over political fighting.
Problem solving over political fighting
Problem solving over political fighting.
Problem solving over political fighting
Foundations / Color roles
Contrast is the WCAG 2.2 ratio of the token against the page color and against ink. Text needs AA (4.5:1), large text and controls AA large (3:1). A token below AA is never used for text on that ground.
Palette colors
| Token | Value | On page | On ink |
|---|---|---|---|
ink--color-ink | #002942 | 15.04:1 AAA | 1.00:1 below AA |
page--color-page | #FFFFFF | 1.00:1 below AA | 15.04:1 AAA |
primary--color-primary | #338498 | 4.29:1 AA large | 3.51:1 AA large |
primary-deep--color-primary-deep | #2A6D7F | 5.85:1 AA | 2.57:1 below AA |
primary-hover--color-primary-hover | #004D61 | 9.40:1 AAA | 1.60:1 below AA |
accent--color-accent | #F54E4E | 3.45:1 AA large | 4.36:1 AA large |
accent-deep--color-accent-deep | #CC2E2E | 5.25:1 AA | 2.86:1 below AA |
accent-hover--color-accent-hover | #B30B0B | 7.07:1 AAA | 2.13:1 below AA |
highlight--color-highlight | #5AD0D6 | 1.84:1 below AA | 8.19:1 AAA |
Surfaces surface
| Token | Value | On page | On ink |
|---|---|---|---|
card--surface-card | #FFFFFF | 1.00:1 below AA | 15.04:1 AAA |
subtle--surface-subtle | #F5F6F7 | 1.08:1 below AA | 13.90:1 AAA |
tint--surface-tint | #E1FBFD | 1.08:1 below AA | 13.91:1 AAA |
tint-blue--surface-tint-blue | #D6F1FE | 1.17:1 below AA | 12.80:1 AAA |
tint-cream--surface-tint-cream | #FCEFD4 | 1.14:1 below AA | 13.21:1 AAA |
tint-lilac--surface-tint-lilac | #EBDAFC | 1.32:1 below AA | 11.44:1 AAA |
inverse--surface-inverse | #002942 | 15.04:1 AAA | 1.00:1 below AA |
panel-seafoam--surface-panel-seafoam | rgba(45, 202, 140, 0.27) | 2.11:1 below AA | 7.12:1 AAA |
panel-goldenrod--surface-panel-goldenrod | rgba(255, 194, 71, 0.27) | 1.61:1 below AA | 9.36:1 AAA |
panel-orchid--surface-panel-orchid | rgba(186, 116, 255, 0.27) | 2.99:1 below AA | 5.04:1 AA |
panel-skyblue--surface-panel-skyblue | rgba(50, 205, 255, 0.27) | 1.86:1 below AA | 8.10:1 AAA |
States state
| Token | Value | On page | On ink |
|---|---|---|---|
good--state-good | #14704A | 6.09:1 AA | 2.47:1 below AA |
warn--state-warn | #8A6210 | 5.48:1 AA | 2.75:1 below AA |
bad--state-bad | #B30B0B | 7.07:1 AAA | 2.13:1 below AA |
good-bg--state-good-bg | #D5F0E0 | 1.21:1 below AA | 12.44:1 AAA |
warn-bg--state-warn-bg | #FBF3E4 | 1.10:1 below AA | 13.64:1 AAA |
bad-bg--state-bad-bg | #FFDEDE | 1.25:1 below AA | 12.00:1 AAA |
info--state-info | #002942 | 15.04:1 AAA | 1.00:1 below AA |
info-bg--state-info-bg | #D6F1FE | 1.17:1 below AA | 12.80:1 AAA |
info-mark--state-info-mark | #0089DB | 3.75:1 AA large | 4.01:1 AA large |
Borders border
| Token | Value | On page | On ink |
|---|---|---|---|
default--border-default | #D3D4D5 | 1.48:1 below AA | 10.14:1 AAA |
subtle--border-subtle | #E4E4E4 | 1.27:1 below AA | 11.83:1 AAA |
ink-faint--border-ink-faint | rgba(0, 41, 66, 0.08) | 15.04:1 AAA | 1.00:1 below AA |
ink-soft--border-ink-soft | rgba(0, 41, 66, 0.12) | 15.04:1 AAA | 1.00:1 below AA |
page-faint--border-page-faint | rgba(255, 255, 255, 0.1) | 1.00:1 below AA | 15.04:1 AAA |
page-strong--border-page-strong | rgba(255, 255, 255, 0.9) | 1.00:1 below AA | 15.04:1 AAA |
Foundations / Spacing and layout
04px8px12px16px24px32px40px48px64px80px96px128pxContainers
prose768pxform896pxpage1128pxwide1440pxfull1440pxfluid92vwBreakpoints
xs479pxsm767pxmd991pxlg1128pxxl1440px2xl1440pxphone479pxphone-landscape767pxtablet991pxcolumn1128pxwide1440pxFoundations / Radii
04px6.4px8px8px16px9999px12px10px16pxFoundations / Shadows
nonenone0 2px 20px -6px rgba(41, 41, 41, 0.27)0 8px 20px -11px rgba(0, 0, 0, 0.2)0 2px 20px -6px rgba(41, 41, 41, 0.27)Foundations / Motion
| Timing | Duration | Use |
|---|---|---|
instant | 0ms | Changes that should be invisible: the focus ring, a hover colour. |
brisk | 150ms | A direct response to input: a menu opening, a switch moving. |
measured | 250ms | A standard transition: a dialog opening, a sheet sliding in. |
deliberate | 400ms | The one pause after a decision that cannot be undone, before the next state. |
Named patterns in the tokens
1.8s, ease; keyframes at 0%, 40%, 75%, 100%. Pauses under reduced motion.
Component / Button
Component / Input + Field
Component / PasswordField
Component / Card patterns
A clear content boundary groups related information without decorative elevation.
An interactive card lifts on hover and takes focus; a persistent text link names the destination.
A visible control makes the available next step explicit.
The main content stays distinct from its heading and its action.
Problem solving over political fighting.
Inputs + Selection
Feedback + Status
Notice: info, success, warning; with an action; dismissible
This invite was already used. Sign in instead.
Signed out everywhere.
That email and password do not match.
This invite was already used. Sign in instead.
Invite sent to jane.doe@example.org. It expires in seven days.
Alert
Use this space to say what changed and what to do next.
It expires in seven days.
This changes the assembly for every volunteer in it.
Export anything you need before removing the assembly.
A notice the person can close.
Status, progress, loading
Empty state: actionable, not actionable, filtered to nothing, page size
People appear here when you invite them or when they sign up through a form.
Events appear here once a staff member schedules the first one.
Clear the search or a filter to see everyone.
Problems and ideas appear here as the assembly raises them.
Overlays + Actions
ConfirmDialog: default, danger, busy
Tooltip
Avatar
Content + Data
DataTable with FilterBar: sortable, selectable, filtered, paginated
| Role | Status | Actions | ||
|---|---|---|---|---|
| Jane Doe | Volunteer | Invited | ||
| Sam Lee | Staff | Active | ||
| Maria Alvarez | Volunteer | Active | ||
| Devon Okafor | Volunteer | Expired |
DataTable: loading, empty, the standard density
| Role |
|---|
| Role | ||
|---|---|---|
No people yetPeople appear here when you invite them. | ||
DataTable with eight columns: scrolls inside its own container, the key column stays
| Role | Assembly | Phone | Status | |||||
|---|---|---|---|---|---|---|---|---|
| Jane Doe | Volunteer | 12 Springfield | jane.doe@example.org | 217 555 0142 | Sep 14 | Invited | 3 | |
| Sam Lee | Staff | sam.lee@unifyamerica.org | 312 555 0199 | Sep 2 | Active | 11 | ||
| Maria Alvarez | Volunteer | 14 Peoria | maria.alvarez@example.org | 309 555 0107 | Sep 9 | Active | 6 |
| Assembly | Volunteers |
|---|---|
| 12 Springfield | 8 |
| 14 Peoria | 5 |
The plain table, search, list and metrics
Of the invites sent this month.
| Name | Role | Assembly |
|---|---|---|
| Jane Doe | volunteer | 12 |
| Sam Lee | staff |
Application Structures
PageHeader: league line and action; with breadcrumbs and meta; read only
Everyone in the league, staff and volunteers.
Volunteer in Assembly 12 (Springfield).
Page and Stack: the content column, its gutters and the rhythm every screen inherits
Every assembly you run, the people in the league, and your own account.
Home / Staff, with the league column
Everyone in the league, staff and volunteers.
| Assembly | Volunteers | Next event |
|---|---|---|
| 12 Springfield | 8 | Sep 20 |
| 14 Peoria | 5 | Sep 27 |
Settings / General
Your account and how the league reaches you.
Dashboard / Overview (gallery only; the shell uses PageHeader)
The current work, held in one place.
Component / Composer
Roles
Staff shell / Phone
The same routes in a 390px viewport: the top bar holds the brand and one account control, tables scroll inside their container with the key column in place, the page keeps 16px gutters and 24px between sections, and every control is at least 44px tall.
Staff shell / Module screens