frontend/STYLING.md
Rama master · réplica de lectura del repositorio original.
# Sublarr Frontend Styling Policy
**Adopted:** 2026-04-18
**Status:** Strategic direction — all new code follows this; legacy migrates organically.
## TL;DR
Pure Tailwind utility classes for all static styling. Inline `style={{ ... }}`
is reserved for **runtime-computed values only** (dynamic widths, transforms,
data-dependent colors).
## Token system
Design tokens live in [`src/index.css`](./src/index.css). The `@theme inline`
block maps Tailwind utility classes to the existing CSS variables, so
`className="bg-surface text-muted rounded-md p-3"` produces the exact same
style as the legacy `style={{ background: 'var(--bg-surface)', color:
'var(--text-muted)', borderRadius: 'var(--radius-md)', padding:
'var(--space-3)' }}` form.
Light/dark variants are handled by the existing `:root` / `.dark` CSS-var
overrides — **no `dark:` class suffixes are needed** on Tailwind utilities.
## Migration map
| Legacy inline-style | New Tailwind |
|---|---|
| `background: 'var(--bg-surface)'` | `className="bg-surface"` |
| `color: 'var(--text-muted)'` | `className="text-muted"` |
| `border: '1px solid var(--border)'` | `className="border border-border"` |
| `borderRadius: 'var(--radius-md)'` | `className="rounded-md"` |
| `padding: 'var(--space-3)'` | `className="p-3"` |
| `padding: '12px'` (hardcoded) | `className="p-3"` |
| `gap: '8px'` | `className="gap-2"` |
| `fontSize: '14px'` | `className="text-sm"` |
| `fontFamily: 'var(--font-mono)'` | `className="font-mono"` |
## Available color tokens
Defined in the `@theme inline` block at the top of `src/index.css`. Each
generates `bg-*`, `text-*`, `border-*`, `ring-*`, `outline-*` utilities.
**Backgrounds:** `page`, `surface`, `surface-hover`, `elevated`, `deep`
**Borders:** `border`, `border-hover`
**Text:** `foreground`, `secondary`, `muted`
**Brand:** `accent`, `accent-hover`, `accent-dim`, `accent-subtle`, `accent-bg`
**Status:** `success`, `success-bg`, `error`, `error-bg`, `warning`, `warning-bg`, `upgrade`, `upgrade-bg`
## Allowed inline-style patterns
Do **NOT** migrate these — they are legitimate runtime-computed values:
- Dynamic dimensions: `style={{ width: \`${pct}%\` }}`
- Dynamic transforms: `style={{ transform: \`rotate(${deg}deg)\` }}`
- Custom CSS properties: `style={{ '--custom-prop': value }}`
- Third-party component overrides where Tailwind can't reach (e.g. CodeMirror theme props)
- Runtime-computed colors: `style={{ color: someState }}`
- Position + z-index combinations that need precise pixel values
## Migration workflow
1. **Never a "Big Bang" sweep.** Legacy inline-styles migrate organically —
every time you edit a TSX file for any reason, convert its inline-styles
as part of the change.
2. **Use the tool:**
```bash
python tools/migrate_inline_styles.py frontend/src/pages/YourFile.tsx
```
The tool scans the file and emits a per-attribute report:
- `[OK] FULLY CONVERTIBLE` → apply the suggested `className`, delete `style={{...}}`
- `[PARTIAL] needs manual review` → extract the convertible keys into `className`, leave the rest in `style={{...}}`
- `[DYN] DYNAMIC-ONLY` → leave as-is
3. **Visual smoke test before committing.** Run `npm run dev` and click
through the pages you edited. Tailwind's atomic classes should produce
pixel-identical output to the legacy inline styles (modulo known
arbitrary-value approximations documented in the tool).
4. **ESLint warns on new inline styles.** See `eslint.config.js`
(`no-restricted-syntax` rule). Warnings don't block CI but surface as a
reminder during code review.
## Future direction
When the legacy inline-style count drops below ~100, the ESLint rule is
promoted from `warn` to `error` in a dedicated cleanup cycle. Until then,
existing inline-styles are tolerated; new ones are discouraged.
## References
- [`src/index.css`](./src/index.css) — design tokens + `@theme inline` block
- [`tools/migrate_inline_styles.py`](../tools/migrate_inline_styles.py) — conversion tool
- [`eslint.config.js`](./eslint.config.js) — ESLint rule (`no-restricted-syntax`)
- Tailwind v4 docs: [Theme configuration](https://tailwindcss.com/docs/theme), [@theme directive](https://tailwindcss.com/docs/functions-and-directives#theme-directive)