Skip to content

chore(ui): design system refactor — close gap with Craft Agents #406

Description

@Astro-Han

Problem

Audited Maka's frontend against Craft Agents OSS (same stack: Electron + React + Tailwind v4 + shadcn-like). Both projects share --radius: 0rem and 15px base font. The gap is not tech selection; it is governance execution.

Maka already defines many of the right primitives, tokens, and renderer contracts, but the system is still too easy to drift:

  • overlapping primitive/component layers still exist in places;
  • color tokens have had undefined references and too many ad-hoc derivation styles;
  • decorative enter/exit motion still leaks through Base UI primitives;
  • radius values were spread across hardcoded CSS, arbitrary Tailwind values, and competing token aliases;
  • --accent has been overloaded across CTA, control, focus, link, live/status, toast, and navigation roles;
  • hard border: declarations are used where shadow-ring recipes should own visual outlines;
  • Storybook has product regression fixtures, but the CSS/token foundation itself is not yet exposed as a complete governance surface.

This issue tracks the design-system refactor needed to close that governance gap. Each PR below should be independently reviewable and should preserve existing product behavior unless its section explicitly calls out a visual correction.

PR plan

PR1. Dead primitive cleanup

Clean up the remaining shadcn leftovers and zero-usage primitives so future governance applies to one real component layer, not dead branches.

Keep: Maka-specific primitives

chat.tsx, alert.tsx, item.tsx, menu.tsx, empty.tsx, accordion.tsx, choice-card.tsx, settings-segmented.tsx, settings-select.tsx, settings-switch.tsx, input-group.tsx, badge.tsx, tabs.tsx, spinner.tsx, kbd.tsx, toolbar.tsx

Delete or migrate: overlapping shadcn leftovers

FileOverlapsAction
button.tsxui.tsx Buttonmigrate the remaining consumer to ui.tsx Button, then delete
input.tsxui.tsx Inputdelete if still unused
separator.tsxui.tsx Separatordelete if still unused
sheet.tsxui.tsx Sheet*delete if still unused
skeleton.tsxui.tsx Skeletondelete if still unused
textarea.tsxui.tsx Textareadelete if still unused
tooltip.tsxui.tsx TooltipTrigger/Popupdelete if still unused

Delete: zero-usage primitives

frame.tsx, pagination.tsx, table.tsx, command.tsx, drawer.tsx, preview-card.tsx, sidebar.tsx, group.tsx, plus their barrel exports if still present.

Also remove: dead .maka-button[data-variant="primary"] CSS if it still has no production usage.

Acceptance: production imports resolve through ui.tsx or kept Maka-specific primitives; dead primitive exports are gone; relevant dead-export/primitive contracts pass.

PR2. Color token closure

Close the color token system so every referenced token exists and each derivation style has a clear purpose.

Scope:

  • Map every undefined foreground/accent token reference to an existing tier or explicit alpha use.
  • Keep foreground tiers intentionally small and documented; do not let every one-off opacity become a named tier.
  • Ensure Tailwind @theme inline exposes the supported renderer tokens.
  • Keep Storybook Design System/Tokens useful as a quick visual audit of the supported color roles.

Acceptance: no undefined var(--foreground-*) or var(--accent-*) references remain; contracts catch future undefined token references; Storybook shows the supported semantic color roles and representative foreground tiers.

PR3. Motion governance

Remove decorative mount/unmount motion from primitives and keep only functional motion.

Goal: popovers, dialogs, sheets, menus, and tooltips should appear/disappear without Base UI enter/exit animation. Keep spinner, status pulse, streaming caret, shimmer, and other functional progress/status motion.

Scope:

  • Strip data-starting-style, data-ending-style, @starting-style, and decorative near-duplicate keyframes from non-whitelisted primitives/CSS.
  • Keep motion tokens (--duration-*, --ease-*) for feedback/status motion.
  • Extend the Storybook Animation Catalog so retained motion is visible and named.

Acceptance: contract bans unapproved enter/exit animation patterns; retained functional motion is visible in Storybook; no product flow relies on decorative mount/unmount animation.

PR4. Radius governance

Converge radius to one component-type scale and remove hardcoded pixel drift.

Target scale:

Component typeTokenValue
button / input / compact chip--radius-control6px
card / popover / code block--radius-surface8px
modal / composer-scale surface--radius-modal12px
badge / pill / dot--radius-pill999px
flat panel / full-bleed surfacenone / 00

Scope:

  • Remove competing radius token aliases where possible.
  • Replace hardcoded CSS border-radius: Npx and TSX arbitrary rounded-[Npx] values with the canonical tokens.
  • Classify intermediate historical values by component type, not nearest numeric value.

Acceptance: radius contract rejects new bare pixel radius values except intentional exemptions; Storybook Design System/Tokens/Radius reflects the canonical scale; current focused PR: #418.

PR5. Primary/accent semantic split

Split --accent into semantic roles so one green no longer controls unrelated UI meanings.

Rejected direction: flipping --color-primary to var(--foreground) just copies Craft's black/white button look and only fixes one contrast case. It leaves checkbox/switch/radio/progress and other selected states tied to the same low-contrast green.

Target roles:

  • --action / --action-foreground: solid CTA fills such as primary button, submit, send, apply, done.
  • --control / --control-foreground: selected/on/progress states such as checkbox, radio, switch, progress, tabs underline.
  • Thin emphasis aliases: --link, --focus-ring, --status-running, --nav-active, --toast-accent.

Scope:

  • Call sites stop writing raw var(--accent) when the intent is action/control/link/focus/status/nav/toast.
  • Darken action and control fills enough for white text to clear the contrast target in light and dark modes.
  • Keep initial aliases visually close unless a contrast fix is explicitly required.

Acceptance: computed contrast contract protects action/control foreground pairs; semantic-token contract catches raw accent usage in governed contexts; Storybook shows the semantic roles separately even when several currently alias the same value.

PR6. Borders as shadows

Move visual-only hard borders to shadow-ring recipes while preserving layout borders.

Problem: hard border: occupies layout space. Replacing every border blindly can shift geometry by 2px and break tight layouts.

Strategy:

  • Migrate visual-only outlines first: cards, popovers, tool cards, toast-like surfaces, code/table wrappers where layout does not depend on border width.
  • Keep layout borders where the border is structural: sidebar divider, titlebar divider, modal header/footer dividers, list separators, table rules where spacing depends on the line.
  • Use the existing shadow recipes: --shadow-minimal, --shadow-minimal-flat, --shadow-medium, --shadow-modal.

Acceptance: governed surfaces use shadow-ring recipes; geometry-sensitive layout borders are explicitly retained; contract protects the distinction and catches accidental broad border reintroduction.

PR7. Storybook foundation governance acceptance

Make the design-system foundation visible, reviewable, and lockable in Storybook. This is the acceptance pass for #406, not a new low-level gap.

Scope:

  • Add or extend Design System/* stories for foundation governance, not more Product pages:
    • semantic color roles;
    • full theme/palette matrix;
    • typography / Markdown / code;
    • spacing / density / layout;
    • elevation / borders / shadow rings;
    • motion tokens and retained functional motion;
    • layering / z-index / overlay stack;
    • interaction states.
  • Show primitive token consumers only where they prove foundation rules: buttons/actions, form controls, surfaces/overlays, navigation/feedback/data primitives.
  • Use the real renderer Storybook substrate: renderer CSS, light/dark globals, and every palette id from THEME_PALETTES.
  • Add a focused Storybook governance contract that locks required story exports, palette toolbar completeness, and token-reference usage. Prefer structure/token assertions over fragile pixel assertions.
  • Keep existing Product stories as regression fixtures; do not expand them as part of this governance pass.

Non-goals: no token value changes, no primitive behavior changes, no Storybook addon/docs-site work, no token JSON/style-dictionary export, and no Product surface expansion.

Acceptance: Storybook becomes the visible review surface for PR2-PR6 foundation rules; npm run -w @maka/desktop build-storybook passes; Storybook contracts lock the foundation story structure and palette coverage.

Dependency order

PR1 + PR2 can land early as cleanup/token closure
↓
PR3 motion governance and PR4 radius governance can proceed after dead primitive cleanup
↓
PR5 semantic action/control split depends on color token closure
PR6 borders-as-shadows depends on radius/shadow-ring clarity
↓
PR7 Storybook foundation governance is the final acceptance pass, but pieces may be built earlier when their tokens are ready

Global acceptance

#406 can close when:

  • dead/overlapping primitives are removed or migrated;
  • color tokens are closed and undefined-token references are contract-protected;
  • decorative enter/exit motion is removed from governed primitives;
  • radius values converge to the canonical scale;
  • action/control/emphasis color semantics are split and contrast-protected;
  • visual-only borders use shadow-ring recipes while layout borders remain intentional;
  • Storybook exposes the governed CSS/token foundation clearly enough for review without driving the desktop app into product states;
  • the relevant desktop/UI contract tests, typecheck, and focused Storybook build pass.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions