ADR-003: Incremental TypeScript + Tailwind CSS Migration
ADR-003: Incremental TypeScript + Tailwind CSS Migration
Section titled “ADR-003: Incremental TypeScript + Tailwind CSS Migration”Status: accepted
Date: 2026-06-14
Deciders: @ErnestNuma
Context
Section titled “Context”ClinicFlow started as an all-JavaScript project with inline CSS styles throughout the frontend. By June 2026, the codebase had grown to:
- Backend: ~30 route/service files, ~40 test files, ~458 tests
- Frontend: ~18 page components, ~20+ shared components, all with inline
style={{}}objects
Two pain points had become acute:
- TypeScript: No static type checking. Refactoring was risky — changing a function signature meant manually finding every call site. The backend’s JWT payload, Supabase query results, and API request/response shapes were all implicitly typed, causing runtime errors that
tscwould have caught. - Tailwind CSS: Inline styles are verbose, hard to maintain for responsive design, and cause unnecessary re-renders (new object references on every render). Dark mode support required manual style overrides per component. The landing page and LoginPage already used Tailwind and looked better than the inline-styled dashboard pages.
Decision
Section titled “Decision”Adopt an incremental, opt-in migration to TypeScript + Tailwind CSS. No big-bang rewrite.
Strategy
Section titled “Strategy”| Phase | Scope | Status |
|---|---|---|
| CI | Install TypeScript, configure tsconfig.json with allowJs: true, add tsc --noEmit to CI pipeline |
✅ Shipped (PR #78) |
| A | Backend: type-check clean on key modules (auth, middleware, db) | ✅ Shipped (PR #79, all green) |
| B | Backend: remaining routes + services | 🔜 Next |
| C | Frontend: type-check clean on API client, context, hooks | ⏳ |
| D | Frontend: page/component migration | ⏳ |
| Tailwind pages | Migrate inline-styled pages to Tailwind (utility classes, no CSS modules) | ⏳ |
allowJs: trueduring transition —.jsand.tsfiles coexist- Node native type-stripping (Node ≥22.18) runs
.tsfiles directly — no build step for backend - Vite handles frontend
.tstransformation automatically - Tailwind config has the brand green (
#059669) as thebrandtoken - New components in
frontend/src/should use Tailwind by default unless touching a file that’s entirely inline-styled (match the surrounding code) - No CSS modules — Tailwind utility classes or inline styles only (reduces API surface; one less thing to migrate later)
What this unblocks
Section titled “What this unblocks”- Safer refactoring (type-check catches missed call sites)
- Better dark mode support (Tailwind’s
dark:variant vs. manual overrides) - Eliminates inline-style re-render perf tax
- Single Tailwind config for all brand tokens
Consequences
Section titled “Consequences”Positive:
- Type errors caught in CI before they reach production
- Tailwind’s utility classes make responsive design and dark mode trivial
- Incremental approach means no blocking migration — ship anytime
- Node’s native type-stripping means zero build overhead for backend
.tsfiles
Negative:
- Two syntaxes coexist for months — JS and TS, inline and Tailwind. Developers must context-switch per file.
allowJsweakens strictness — a fully.tscodebase would catch more errors- Tailwind’s compile step adds ~2s to frontend build time
- Inline styles being migrated means git history has more churn than a greenfield approach
Risks:
- Migration stalls and the project lives with mixed syntax forever (mitigation: phased, no-regression approach; each phase ships independently)
- New hires find the hybrid state confusing (mitigation: this ADR serves as the canonical explanation)
Compliance
Section titled “Compliance”tsc --noEmitmust pass in bothbackend/andfrontend/before any PR merges- ESLint rules prevent
anytype where possible - New frontend components default to Tailwind; inline styles require justification in PR
- Related ADR: ADR-002: Full-RLS Tenant Isolation (parallel migration pattern)
- CLAUDE.md — project instructions document the current TS/Tailwind rules