Skip to content

Design system overhaul: unified tokens, component authority, standardized layout#1570

Merged
BunsDev merged 11 commits intostagingfrom
okcode/design-foundation-sweep
Apr 7, 2026
Merged

Design system overhaul: unified tokens, component authority, standardized layout#1570
BunsDev merged 11 commits intostagingfrom
okcode/design-foundation-sweep

Conversation

@BunsDev
Copy link
Copy Markdown
Member

@BunsDev BunsDev commented Apr 7, 2026

Summary

Comprehensive design system overhaul establishing a single token system and making React UI components the canonical styling authority. No product behavior changes — purely structural/visual consistency improvements across 48 files.

Phase 1 — Design Foundation

  • 25+ semantic CSS custom properties added to :root (status colors, form controls, interactive states, overlay, shadows, layout widths) with matching light-theme overrides
  • @theme block expanded from 24 → 80+ entries, mapping all tokens to Tailwind v4 utilities (spacing, typography, status, form, layout)
  • 11 ui/ primitives migrated to tokens: input, textarea, label, select, badge, button, card, dialog, sheet, dropdown-menu, toggle-group — eliminating all hardcoded rgba()/hex values
  • --accent-fg token introduced for guaranteed text contrast on accent backgrounds across both themes (fixes invisible button text in dark mode)
  • Layout tokens: --page-max (1536px), --page-narrow (900px), --page-min (320px) — all page/nav/footer max-widths standardized (was 6 different values: 900–1450px)
  • CSS classes deprecated (.btn, .tag, .card, .form-input, .form-label) with @deprecated comments — React components are canonical

Phase 2 — IA & Navigation

  • Shared nav config (src/lib/nav-items.ts) eliminates 3-place navigation duplication (Header desktop, Header mobile, Footer)
  • Route guard on /$owner/$slug catch-all validates owner handles, preventing confusing 404s on typos like /settigns
  • User hover tooltip on UserBadge — lazy-fetches stats (published skills, stars, downloads) via new getHoverStats Convex query
  • TooltipProvider added to AppProviders.tsx

Phase 3 — UI Sweep

  • 54 .btn<Button> across 13 files (management, dashboard, index, about, plugins, souls, search, u/$handle, SkillHeader, SkillMetadataSidebar, SkillDiffCard, Header, SkillsResults)
  • 24 .tag<Badge> across 12 files
  • ~89 inline style={{}} → Tailwind utilities across 17 files
  • .card divs → <Card> in detail/dashboard/search pages
  • text-whitetext-accent-fg in 5 locations (was invisible on white accent in dark mode)
  • Raw styled <a>/<button><Button asChild> pattern (e.g., plugins "Download zip")
  • min-width: var(--page-min) added to .app-shell

New files

  • src/lib/nav-items.ts — shared navigation config (NAV_ITEMS, FOOTER_NAV_SECTIONS, filterNavItems())
  • src/lib/icon-sizes.tsICON_SM / ICON_MD / ICON_LG constants

What's intentionally unchanged

  • Monochrome TUI aesthetic (colors, sharp radii, flat shadows)
  • All product behavior and user flows
  • Layout CSS classes (.browse-page, .navbar, .home-hero, etc.) — complex responsive layouts kept in CSS
  • Backend/Convex code (except new getHoverStats query)
  • Font choice (IBM Plex Mono)

Test plan

  • Open every route in dark theme — verify no visual regressions
  • Open every route in light theme — verify no visual regressions
  • Verify all buttons have readable text in both themes (especially primary variant)
  • Verify user hover tooltips show stats on UserBadge components
  • Test at 480px, 768px, 960px, 1280px widths — layouts should not break
  • Verify navigation links match between desktop header, mobile menu, and footer
  • Type /settigns in URL bar — should get clean 404, not broken page
  • Run npx tsc --noEmit — zero type errors
  • Run grep -r "rgba\|#[0-9a-f]\{3,6\}" src/components/ui/ — should return minimal/zero matches

…ized layout

Establish a single design token system and make React UI components the
canonical styling authority, replacing the dual CSS-class/inline-style
approach. No product behavior changes.

## Phase 1 — Design Foundation

- Add 25+ semantic CSS custom properties to :root (status colors, form
  controls, interactive states, overlay, shadows, layout widths) with
  matching light-theme overrides
- Expand @theme block from 24 → 80+ entries mapping all tokens to
  Tailwind v4 utilities (spacing, typography, status, form, layout)
- Migrate 11 ui/ primitives to use tokens instead of hardcoded rgba/hex:
  input, textarea, label, select, badge, button, card, dialog, sheet,
  dropdown-menu, toggle-group
- Introduce --accent-fg token for guaranteed text contrast on accent
  backgrounds across both themes
- Add --page-max (1536px), --page-narrow (900px), --page-min (320px)
  layout tokens; standardize all page/nav/footer max-widths
- Deprecate CSS classes (.btn, .tag, .card, .form-input, .form-label)
  with @deprecated comments — React components are now canonical

## Phase 2 — IA & Navigation

- Extract shared nav config (src/lib/nav-items.ts) consumed by Header
  and Footer, eliminating 3-place navigation duplication
- Add beforeLoad guard on /$owner/$slug catch-all route to validate
  owner handles and prevent confusing 404s on typos
- Add UserBadge hover tooltip with lazy-fetched stats (published skills,
  stars, downloads) via new getHoverStats Convex query
- Wrap app in TooltipProvider (src/components/AppProviders.tsx)

## Phase 3 — UI Sweep

- Migrate 54 .btn usages → <Button> across 13 route/component files
- Migrate 24 .tag usages → <Badge> across 12 files
- Eliminate ~89 inline style={{}} occurrences → Tailwind utilities
- Replace .card divs → <Card> component in detail/dashboard/search pages
- Fix text-white → text-accent-fg in 5 locations (SkillsToolbar,
  InstallSwitcher, toggle-group, .star-toggle, .btn-primary)
- Standardize all page max-widths to var(--page-max) token (was 6
  different values: 900–1450px)
- Add min-width: var(--page-min) to .app-shell
- Convert raw styled <a>/<button> elements to <Button asChild> pattern

## New files

- src/lib/nav-items.ts — shared navigation config
- src/lib/icon-sizes.ts — ICON_SM/MD/LG constants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clawhub Ready Ready Preview, Comment Apr 7, 2026 0:42am

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 7, 2026

Greptile Summary

Large but well-scoped design-system overhaul: unified CSS custom properties, Tailwind v4 @theme expansion, migration of ui/ primitives off hardcoded colors, shared nav config (nav-items.ts), and a route guard on /$owner/$slug. The only new backend code is the getHoverStats Convex query and UserStatsTooltipContent hover tooltip, both of which are functionally correct — P2 performance/convention observations only.

Confidence Score: 5/5

Safe to merge — no blocking correctness, security, or data-integrity issues found.

All findings are P2 style/performance suggestions. The design-system changes are mechanical token and component migrations with no product-behavior changes, and the new Convex query is functionally correct.

convex/users.ts and src/components/UserBadge.tsx for the reactive-subscription and unbounded-collect concerns.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/components/UserBadge.tsx
Line: 104-107

Comment:
**`useQuery` creates persistent reactive subscription for tooltip stats**

Per the project's Convex performance rules, `ConvexHttpClient.query()` (one-shot fetch) should be used for data that doesn't need real-time updates. After the first hover, `shouldFetch` flips to `true` and stays `true` for the component's lifetime, leaving a live subscription to the `skills` table. On a browse page with many `UserBadge` components this accumulates one reactive subscription per hovered user — all re-run whenever any of that user's skills change.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: convex/users.ts
Line: 474-477

Comment:
**Unbounded `.collect()` on the `skills` table**

The query fetches every skill for the user and filters in JS. For a prolific user this scans all their skill documents in a single transaction, approaching Convex's per-transaction read budget. Adding a reasonable cap (e.g. `.take(500)`) would guard against edge cases and keep the query predictably cheap.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Design system overhaul: unified tokens, ..." | Re-trigger Greptile

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e7e4e83bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Sign-in button: add size="sm" (min-h 34px) to align with the 38px
  theme toggle group in the navbar
- UserBadge tooltip: replace reactive useQuery with one-shot
  ConvexHttpClient.query() so hover stats don't leave persistent
  subscriptions on the skills table
- getHoverStats: replace unbounded .collect() with .take(500) to cap
  per-transaction reads for prolific users

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d880acccc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Replace all logo/favicon assets with the new lobster logo. All variants
use a solid white background with rounded corners (12% radius).

Updated assets: clawd-logo.png (512), clawd-mark.png (256),
logo192.png, logo512.png, favicon.ico (multi-res), logo.svg.
Added explicit favicon/icon/manifest link tags to __root.tsx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ceebcc5bca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The CSS minifier converts #808080 to the named color 'gray', which
Tailwind v4 rejects as an illegal theme token value. Use #818181
instead — visually identical but not a named CSS color.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84a48f28f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Replace <Container> wrapper with .section class so plugin detail uses
the same max-width token (--page-max) and padding as skill detail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each primary nav tab gets a monochrome lucide icon (Wrench, Plug, Ghost)
at 50% opacity for scanability. Icons brighten to 85% on hover and 100%
when active. Secondary tabs unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests that import src/convex/client.ts crash at module load time
because getRequiredRuntimeEnv("VITE_CONVEX_URL") throws when the
env var is missing. Set a non-resolvable dummy URL for CI since
unit tests don't make real Convex calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All 6 test files that mock convex/react now include a stub
ConvexReactClient class so src/convex/client.ts can import
without crashing at module load time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 74b834b4dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@BunsDev BunsDev merged commit f636b31 into staging Apr 7, 2026
5 checks passed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2c856e68a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

expect(convexQueryMock).not.toHaveBeenCalled();
});
});
});});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove stray closing token in header test file

The file now ends with });});, which is a syntax error, so Vitest cannot parse this test module and bun run test will fail before executing the suite. This blocks CI and hides regressions in unrelated tests until the extra }); is removed.

Useful? React with 👍 / 👎.


describe("skill route loader", () => {
it("allows numeric owner handles in beforeLoad", () => {
expect(() => runBeforeLoad({ owner: "123abc", slug: "weather" })).not.toThrow();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Await beforeLoad assertions in route guard tests

runBeforeLoad is async, but these checks use expect(() => runBeforeLoad(...)).not.toThrow(), which only verifies synchronous throws. If beforeLoad rejects (e.g., notFound()), the promise rejection is not asserted here, so these tests can pass even when the guard behavior regresses.

Useful? React with 👍 / 👎.


export const Route = createFileRoute("/$owner/$slug")({
beforeLoad: ({ params }) => {
const isHandle = /^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/.test(params.owner);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept persisted handle characters in owner guard

The new guard only allows owner handles matching [a-zA-Z0-9_-], but handle normalization elsewhere is permissive (trim/lowercase) and does not enforce this character set. Any existing owner handle containing other valid persisted characters (for example .) will now be rejected in beforeLoad with notFound() before canonical resolution, turning valid skill URLs into 404s.

Useful? React with 👍 / 👎.

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