Introduce Tailwind v4 and shadcn/ui into the frontend/ workspace via a coexistence-first strategy. This issue tracks the foundation PR; subsequent component migrations have their own follow-up issue.
What this includes
- Tailwind v4 install + PostCSS config (
@tailwindcss/postcss)
@theme block in frontend/app/globals.css mapping brand tokens (accent, bg, surface, text variants, fonts) to CSS variables → utility classes like bg-bg, text-accent, font-display
- shadcn/ui setup:
components.json (new-york style, RSC, aliases match tsconfig), lib/utils.ts with cn() helper, frontend/components/ui/ directory
- First shadcn primitive:
Button
- Migration of
ApiKeysSection and ConnectedAppsSection outer card + header to Tailwind utility classes — first real-world test of the migration pattern
What this does NOT include
- Modifying the legacy
globals.css (3,790 lines of design system used by /app/*, /oauth/*, landing) — coexistence preserved
- Migration of any other screens or components
- shadcn primitives other than Button — added on demand when a real consumer needs them
Why this approach
The legacy CSS in globals.css is the app's entire current design system (verified during planning — .dash-*, .btn-*, .profile-card, .modal-*, .field-* are used across all authenticated routes, not just the landing). Wholesale isolation or refactor risks breaking the whole product. Instead, Tailwind coexists with legacy and migration happens gradually one component at a time.
The ! modifier convention (discovered during this PR)
The legacy * { padding: 0; margin: 0 } reset is unlayered, while all Tailwind utilities live in @layer utilities. Per CSS Cascade L5, unlayered author styles beat layered styles regardless of specificity, so utility classes like p-7 lose to the universal reset.
We tried wrapping legacy in @layer legacy to fix this, but Tailwind v4's Preflight (in @layer base) then started winning over legacy element styles (h1, body, ul), breaking layouts across the entire app. We reverted.
The convention now: properties that fight the universal reset (padding, margin) use Tailwind's ! modifier (e.g., p-7!) which generates !important and beats the unlayered legacy reset. The convention is documented in detail in docs/superpowers/specs/2026-05-17-tailwind-adoption-design.md under "Migration convention — the ! modifier".
Spec & plan
- Spec:
docs/superpowers/specs/2026-05-17-tailwind-adoption-design.md
- Plan:
docs/superpowers/plans/2026-05-17-tailwind-adoption-foundation.md
Follow-up
See the separate issue for the broader app-wide migration roadmap.
Introduce Tailwind v4 and shadcn/ui into the
frontend/workspace via a coexistence-first strategy. This issue tracks the foundation PR; subsequent component migrations have their own follow-up issue.What this includes
@tailwindcss/postcss)@themeblock infrontend/app/globals.cssmapping brand tokens (accent, bg, surface, text variants, fonts) to CSS variables → utility classes likebg-bg,text-accent,font-displaycomponents.json(new-york style, RSC, aliases matchtsconfig),lib/utils.tswithcn()helper,frontend/components/ui/directoryButtonApiKeysSectionandConnectedAppsSectionouter card + header to Tailwind utility classes — first real-world test of the migration patternWhat this does NOT include
globals.css(3,790 lines of design system used by/app/*,/oauth/*, landing) — coexistence preservedWhy this approach
The legacy CSS in
globals.cssis the app's entire current design system (verified during planning —.dash-*,.btn-*,.profile-card,.modal-*,.field-*are used across all authenticated routes, not just the landing). Wholesale isolation or refactor risks breaking the whole product. Instead, Tailwind coexists with legacy and migration happens gradually one component at a time.The
!modifier convention (discovered during this PR)The legacy
* { padding: 0; margin: 0 }reset is unlayered, while all Tailwind utilities live in@layer utilities. Per CSS Cascade L5, unlayered author styles beat layered styles regardless of specificity, so utility classes likep-7lose to the universal reset.We tried wrapping legacy in
@layer legacyto fix this, but Tailwind v4's Preflight (in@layer base) then started winning over legacy element styles (h1,body,ul), breaking layouts across the entire app. We reverted.The convention now: properties that fight the universal reset (
padding,margin) use Tailwind's!modifier (e.g.,p-7!) which generates!importantand beats the unlayered legacy reset. The convention is documented in detail indocs/superpowers/specs/2026-05-17-tailwind-adoption-design.mdunder "Migration convention — the!modifier".Spec & plan
docs/superpowers/specs/2026-05-17-tailwind-adoption-design.mddocs/superpowers/plans/2026-05-17-tailwind-adoption-foundation.mdFollow-up
See the separate issue for the broader app-wide migration roadmap.