@digital-democracy-project/design-system
v0.4.0
Published
DDP design tokens, SCSS foundation, and React component library shared across Digital Democracy Project web products.
Readme
@digital-democracy-project/design-system
DDP design tokens, SCSS foundation, and React component library shared across Digital Democracy Project web products.
Installation
npm install @digital-democracy-project/design-systemUsage
import { Button, Badge, Icon } from '@digital-democracy-project/design-system';
import '@digital-democracy-project/design-system/styles.css';
<Button variant="primary">Save</Button>
<Badge tone="secondary">New</Badge>
<Icon name="check" size="sm" />One stylesheet import is all that's needed — styles.css ships both the token definitions (every --ddp-* custom property) and the compiled component CSS together, tokens first. In a Next.js App Router project, import it once in the root layout (global CSS can't be imported from nested pages). A token-only stylesheet is also available separately at @digital-democracy-project/design-system/styles/tokens.css, for non-component use.
Raw Sass (tokens, mixins) is also available for consumers authoring their own components:
@use '@digital-democracy-project/design-system/styles' as *;
.thing {
padding: space(3);
color: color('primary');
}Static assets — brand marks, raw icon SVGs, and the font files — are published too, each under its own subpath:
import wordmark from '@digital-democracy-project/design-system/brand/ddp-wordmark.svg'
import searchIcon from '@digital-democracy-project/design-system/icons/search.svg'<link
rel="preload"
as="font"
type="font/woff2"
crossorigin
href="@digital-democracy-project/design-system/fonts/PublicSans-Roman-VF.woff2"
/>The subpaths are deliberately shorter than the on-disk layout (./brand/*, not ./assets/brand/*) so assets/ can be reorganized without a breaking change. Every asset is stored once, at the largest size the web needs — there are no parallel "master" copies in this repo, so treat these as the canonical files. React consumers should prefer the <Icon> component over importing icon SVGs directly; it inlines the same files at build time. Fonts need no import at all in the normal case — styles.css already declares every @font-face and resolves the files relatively.
See each component's own README under components/<Name>/ for its full prop reference.
Development roadmap
Temporary section — remove once the initial component library and consuming-app migration are far enough along that this reads as a finished package, not a work in progress.
Phase 1 — Core component library
- [x] Build Icon
- [x] Build Button
- [x] Build Badge/Pill
- [x] Checkpoint: review Button + Badge together, lock in conventions
- [x] Build IconButton (new, separate from Button — opinionated icon-only control)
- [x] Build Text Input
- [x] Build Checkbox
- [x] Build ToggleButton (renamed from "Toggle button/group"; folds in an optional checkbox indicator, validated against
CheckboxItem/ToggleChipinddp-scorecard-reactand the legislative-scorecard hi-fi mockup) - [x] Build MenuItem, Menu, DropdownMenu, Select (single-select only — see component READMEs for the multi-select deferral reasoning)
- [x] Build SearchBar (composes TextInput + DropdownMenu + Menu; standalone mode for Select's own internal search field, combobox mode for suggestions)
- [ ] Theming/visual refinements to
SearchBarandSelect— flagged during Storybook review, deliberately deferred to keep pace; not blocking - [x] Build Card (generic container — reprioritized ahead of ToggleSwitch, needed for upcoming page mockups)
- [x] Build ClickableTile (single-element link/button surface, whole box including padding is the hit target — the answer to "can the whole Card be a clickable link")
- [x] Build ToggleSwitch (the track-and-knob control, deliberately built last — reuses
Checkbox's real-input-driven structural pattern, notToggleButton's button-based one) — last v1 component done - [x] Introduce
SizeOptions(shared'sm' | 'md' | 'lg'type incomponents/shared/types.ts), retrofitted into Button/Badge/Checkbox/Select/SearchBar/TextInput/ToggleButton/ToggleSwitch - [x] Eyebrow audit: added the missing
section-labelmixin/.ddp .section-labelclass (quieter field-label roleeyebrowdidn't cover) and theSectionLabelcomponent; documented the.ddp-scoping requirement instyles/README.md - [ ]
SectionLabel: allow the rendered element to be flexible (e.g. anas/polymorphic prop) — real usage will commonly want it as an actualh*tag, not always a<span> - [ ] Scope decision:
ddp-mapapp-derived candidates (Accordion, Stat, Spinner, Slide-over, Drawer, Expandable text)
Phase 2 — Package/publish readiness
- [x] Claim npm scope
- [x]
"type": "module"in package.json - [x] First
npm publish(0.1.0) - [x] Root README (this file)
- [x] CSS distribution: merged into one stylesheet (tokens + components, tokens first)
- [x] Delete Tier 4 legacy token aliases in
colors_and_type.css - [ ] License field decision
- [ ] Set up Changesets
Phase 3 — Keep the Claude Design project in sync (parallel to Phase 1)
- [x] Run
/design-syncfor current state (Icon, Button, Badge, tokens, fonts) — uploaded and verified - [ ] Re-sync after each new component lands
- [ ] Retire superseded static previews as real components land
- [ ] Decide fate of
ui_kits/*and token-doc preview pages
Phase 4 — Migrate consuming apps
- [x]
ddp-next: replace Tailwind with design-system tokens, mixins and components (removed 2026-10-08; the remaining ad hoc page code is slated for a rebuild) - [ ]
ddp-scorecard-react: adapt at its own pace - [ ]
ddp-mapapp→ddp-nextpage migration
Phase 5 — Deferred hygiene
- [ ] Tests (Vitest/RTL/axe)
- [ ] Visual regression
- [ ] CI
- [ ] Lint/format tooling
- [x] Convert component CSS from CSS Modules to manual BEM naming (
ddp-prefix) - [ ]
stylelintrule enforcing theddp-prefix (pairs with lint/format tooling above)
