Skip to content

feat(design): design system v2 — new components, refreshed tokens#210

Merged
antosubash merged 5 commits into
mainfrom
worktree-design-refresh
May 22, 2026
Merged

feat(design): design system v2 — new components, refreshed tokens#210
antosubash merged 5 commits into
mainfrom
worktree-design-refresh

Conversation

@antosubash
Copy link
Copy Markdown
Owner

Summary

  • Refresh design tokens: OKLCH palette, warm graphite dark mode, Fraunces+Geist typography, motion/z-index/opacity scales, removed all linear-gradient surface treatments
  • Six new shared React components: Stat, EmptyState, SearchInput, NumberInput, FilterBar, Kbd — exported from @simplemodule/ui
  • Migrated several pages to EmptyState; DataGridPage + Button + KpiCard refactored so every consumer inherits the new system
  • New docs/design-system/ reference site (43 sections, mobile-friendly, wired into Tailwind via @source)

Verification

  • Stage 0 — branch on worktree-design-refresh, up to date with origin/main
  • Stage 1 — app started on port 5001, health green in 7s
  • Stage 2a — browser smoke via MCP Playwright on /Identity/Account/Login: page renders, Fraunces <h1> "Welcome back", Geist body, OKLCH primary oklch(0.62 0.16 158), dark mode toggles to warm graphite oklch(0.205 0.006 60)
  • Stage 3 — server stopped, port freed
  • Stage 4 — local CI:
    • Lint: ✓
    • Typecheck: ✓ 13/13 workspaces
    • Build: ✓ all 14 module bundles + dotnet build -warnaserror (0 warnings, 0 errors)
    • Unit tests: ✓ ~1,000 dotnet tests across 19 projects
    • e2e smoke: ✓ 47/47 specs (covers Admin, AuditLogs, BackgroundJobs, Dashboard, Email, FeatureFlags, FileStorage, MenuManager, OpenIddict, RateLimiting, Settings, Tenants, Users account)

verification

Test plan

  • Reviewer opens any page that previously had a hand-rolled empty state and confirms EmptyState renders correctly (AuditLogs/Browse, Email/History, Email/Templates, Settings/MenuManager, RateLimiting tables)
  • Confirm dark mode toggle still works on the public login page and reads as warm graphite (no cold slate)
  • Confirm NumberInput (if you add one) can be cleared without snapping back to the prior numeric value
  • Confirm an interactive Stat is reachable via Tab and activates on Enter/Space (a11y)
  • CI is green

Notes

This PR was assembled in 5 commits:

  • feat(design): refresh design system + new components — tokens, components, layouts, design-system preview
  • refactor(pages): migrate remaining empty states to shared EmptyState — RateLimiting tables + MenuManager
  • fix(ui): address code review findings on design-system components — 12 review findings (NumberInput clear, Stat keyboard a11y, EmptyState heading hierarchy, SearchInput overlap, z-index migration, table hover scope, etc.)
  • fix(ui): EmptyState title type + biome ignore docs/design-system — Stage 4 typecheck + lint fixes
  • chore: add verification screenshot

Refresh tokens and ship six new shared components used across all pages.

Tokens (packages/SimpleModule.Theme.Default/theme.css)
  - OKLCH palette across both modes; warm graphite dark mode (was cold slate)
  - Fraunces (display) + Geist (body) + JetBrains Mono
  - Layered shadow scale, motion durations + easings, z-index, opacity
  - Removed all linear-gradient surface treatments; primary is now flat
  - Base table styles: proper cell padding, header bar, row hover, .num
    helper for tabular-num right-aligned columns

UI components (packages/SimpleModule.UI/components/)
  - Stat: dashboard tile metric (value/unit/label/trend/change)
  - EmptyState: icon + title + description + primary/secondary action
  - SearchInput: leading search icon + optional kbd shortcut suffix
  - NumberInput: input with - / + steppers, clamps min/max
  - FilterBar: data-grid toolbar (search / controls / actions slots)
  - Kbd: small inline key cap

Component upgrades that fan out
  - DataGridPage now renders EmptyState internally
  - Button primary variant: solid emerald, no gradient
  - AuditLogs KpiCard: thin wrapper around Stat

Pages migrated to EmptyState
  - AuditLogs/Browse, Email/History, Email/Templates

Layouts / inline gradients removed
  - app-layout, public-layout, public-layout-mobile, user-dropdown,
    page-header, section, registry templates

Design system reference
  - docs/design-system/ — 43-section live preview, mobile-friendly,
    wired into Tailwind via @source in template/SimpleModule.Host/Styles/app.css

Verified: all 14 modules build successfully (npm run build:dev).
Replaces three hand-rolled empty-state blocks with the new EmptyState
component shipped in the previous commit. Consistent icon size, copy
hierarchy, spacing, and theme-aware text colors.

  - modules/RateLimiting/components/ActivePoliciesTable
  - modules/RateLimiting/components/RulesTable
  - modules/Settings/Pages/MenuManager  (×2 — list + editor empty state)

Other audited surfaces:
  - Email/Dashboard.tsx "TopErrorsEmpty" is an inline tile message,
    intentionally lighter than EmptyState — left as is.
12 findings from a high-effort recall review; commits resolve all
bug-level issues and several visual/a11y regressions.

NumberInput (packages/SimpleModule.UI/components/number-input.tsx)
  - Track internal display string so users can clear the field; commit
    onChange only when the parsed value is valid. Restore min/0 on blur
    if left empty. Adds optional `emptyFallback` prop.
  - Decrement icon picks up matching strokeLinejoin for consistency.

Stat (packages/SimpleModule.UI/components/stat.tsx)
  - When onClick is provided the component now renders as a <button>,
    auto-promoting to a keyboard-accessible element with proper role.
  - Auto-enables `interactive` when onClick is set so callers can't
    accidentally ship a clickable tile without the hover affordance.
  - New `lift` prop gates the translate-y hover micro-motion, defaulting
    to off so KpiCard preserves its old shadow-only hover.

EmptyState (packages/SimpleModule.UI/components/empty-state.tsx)
  - Default heading level changed from h3 to h2 — EmptyState commonly
    sits directly under a PageShell h1, and h1→h2 keeps the document
    outline contiguous. New `headingLevel` prop overrides when needed.
  - Icon bubble shrunk from w-14 h-14 (56px) to w-11 h-11 (44px) so the
    22px SVGs we ship don't float in oversized circles.
  - Title now uses the `font-display` Tailwind utility (driven by the
    --font-display token) instead of an inline style — keeps with the
    Tailwind-only directive.

SearchInput (packages/SimpleModule.UI/components/search-input.tsx)
  - Suppress the native webkit search cancel button so it doesn't
    overlap the Kbd shortcut suffix.

Overlays — z-index migration
  - Dialog overlay + content: z-50 → z-[60] (matches --z-modal)
  - Sheet overlay + content: z-50 → z-[60]
  - Tooltip content: z-50 → z-[90] (matches --z-tooltip)
  - Resolves the contradiction where Dialog/Dropdown both rendered at
    z-50 despite the theme advertising a layered stack.

theme.css
  - Table row hover moved behind `[data-interactive]` opt-in so
    read-only tables (recovery codes, audit results, RateLimiting
    policies, dashboard error lists) don't pick up an interactive cue.
  - Dropped the explicit `border-collapse: collapse` — restores default
    behavior to avoid border-merge artifacts in two-tone cells.

Verified: all 14 modules build successfully (npm run build:dev).
Found by Stage 4 of /vf:

- EmptyStateProps must Omit 'title' from React.HTMLAttributes because
  the HTML title attribute is typed as string | undefined but the
  component repurposes title as React.ReactNode.
- docs/design-system/index.html is a static reference page with
  intentional inline styles from earlier iterations; add it to
  biome's includes ignore list so repo-wide lint runs clean.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying simplemodule-website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 22350c8
Status: ✅  Deploy successful!
Preview URL: https://663bccee.simplemodule-website.pages.dev
Branch Preview URL: https://worktree-design-refresh.simplemodule-website.pages.dev

View logs

@antosubash antosubash merged commit 6634771 into main May 22, 2026
6 checks passed
@antosubash antosubash deleted the worktree-design-refresh branch May 22, 2026 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant