From bafc7953554a57015387e878a83b8e79031c6f9b Mon Sep 17 00:00:00 2001 From: Alberto Andre Date: Tue, 16 Jun 2026 21:51:53 -0300 Subject: [PATCH 01/25] feat(ui): adopt Seven design system in frontend (phases 1-3) Foundation: reconcile index.css so @etus/tokens wins (drop colliding radius/shadow/breakpoint/text tokens, the 88 color palettes, and the decorative utilities banned by DESIGN.md); load Inter + JetBrains Mono. Shell: sidebar.tsx becomes AppSidebar built on Seven's Sidebar system (single mode, collapsible icon, SidebarAvatar); _authenticated.tsx mounts SidebarProvider + SidebarInset. Screens: settings/account/team/integrations move to TextInput, Divider, Switch and Checkbox; main.tsx uses Seven's Toaster; error-boundary uses Button; loading-skeleton uses SkeletonLoader. The local input, separator, skeleton and sonner components now have zero consumers. Validated in the browser. Plan: docs/plans/2026-06-16-001-migrate-frontend-seven-ds-plan.md --- ...6-16-001-migrate-frontend-seven-ds-plan.md | 314 +++++++++++++++ index.html | 2 +- src/client/components/error-boundary.tsx | 7 +- src/client/components/sidebar.tsx | 381 +++++++----------- src/client/components/ui/loading-skeleton.tsx | 3 +- src/client/index.css | 291 ++----------- src/client/main.tsx | 12 +- src/client/routes/_authenticated.tsx | 92 +++-- src/client/routes/_authenticated/account.tsx | 14 +- .../routes/_authenticated/integrations.tsx | 30 +- src/client/routes/_authenticated/settings.tsx | 37 +- src/client/routes/_authenticated/team.tsx | 6 +- 12 files changed, 590 insertions(+), 599 deletions(-) create mode 100644 docs/plans/2026-06-16-001-migrate-frontend-seven-ds-plan.md diff --git a/docs/plans/2026-06-16-001-migrate-frontend-seven-ds-plan.md b/docs/plans/2026-06-16-001-migrate-frontend-seven-ds-plan.md new file mode 100644 index 0000000..b877030 --- /dev/null +++ b/docs/plans/2026-06-16-001-migrate-frontend-seven-ds-plan.md @@ -0,0 +1,314 @@ +--- +title: Implementation Plan — Adotar o Seven (DS) em todo o frontend +type: migration +date: 2026-06-16 +status: ready-for-execution — D1–D5 decididos; fases reescritas p/ rewrite guiado por blocks (runtime @etus/seven-react). Pendência: confirmar Geist→JetBrains (D3) +package_target: "@etus/seven-react@^0.1.0-beta.3 (→ @etus/ui@0.4.0-beta.2 + @etus/tokens@0.4.0-beta.2)" +scope: src/client/** + src/client/index.css +--- + +# Implementation Plan — Adotar o Seven em todo o frontend + +Plano tático para fazer o **Seven** (`@etus/seven-react`, umbrella sobre `@etus/ui` + `@etus/tokens`) +ser a fonte única de todo elemento de UI do `boilerplate-hono`, de modo que o boilerplate sirva +de base de Design System para os produtos derivados. + +## Estado atual (diagnóstico 2026-06-16) + +A migração **já começou e está parcial** (≈60% das telas). O que já está correto: + +- Pacotes instalados e ligados; `index.css` faz `@import "@etus/seven-react/styles.css"` + `@source` (linha 5/11). +- Cores semânticas **já neutralizadas** do `@theme` local — o Seven vence em light e dark. +- Dark mode compatível: `use-theme.tsx` aplica `.dark` no ``, igual à estratégia do `@etus/tokens` (`.dark`, sem `prefers-color-scheme`). +- Telas já no Seven: `Button`, `Card`, `Avatar`, `Dialog`, `Form`, `Tabs`, `Badge`, `Label` em ~13 rotas + `sidebar`/`error-fallback`/`loading-skeleton`. + +**Achado central (o gap mais grave, invisível):** o bloco `@theme` local (`src/client/index.css:17+`) +roda **depois** do `@import` do Seven e, no Tailwind v4, *last wins* — então tokens locais de +`radius`/`shadow`/`breakpoint`/tipografia **sobrescrevem os do Seven com valores diferentes**, e os +componentes do Seven renderizam fora do tom. Esta é a "Etapa 2" que os próprios comentários do arquivo +prometem mas não executaram. + +## Objetivo + +Todo elemento de UI vem do `@etus/seven-react`; os tokens do `@etus/tokens` são a fonte da verdade +(o `@theme` local só guarda o que o Seven comprovadamente não fornece); nenhum HTML cru de controle e +nenhum componente caseiro de UI sobrevive; e há guard-rails que impedem regressão nos produtos derivados. + +## Referências canônicas do Seven (repo `~/Dropbox/aa-projects/Github/seven`) + +A composição idiomática NÃO será inventada — será ancorada no repo do Seven: +- **`DESIGN.md`** — espec de marca (regras de ouro abaixo). Fonte de verdade #3 (após Figma e `tokens.css`). +- **Showcase `apps/showcase`** (código) — composição canônica. O **shell** vive em `src/routes/__root.tsx` (`SidebarProvider mode="dual"` → Rail 64px + Panel 216px + `SidebarInset` + content `rounded-tl-[24px] border`). +- **`seven.etus.io`** — o showcase **publicado e renderizado**: referência de **validação visual real**. Toda comparação de UI com o Seven compara screenshots do app contra o showcase publicado (não só código/`.d.ts`/tokens). +- **209 blocks** em `apps/showcase/src/data/blocks/` (29 categorias) — **copiáveis como ponto de partida** (não distribuídos via CLI). Relevantes: `page-shells` (3), `logins`, `account-and-user-management` (15), `form-layouts` (5), `tables`/`data-table`, `empty-states`, `dialogs`, `dashboard`. +- **CLI registry** — `npx @etus/seven add ` copia 185 componentes como **source local com token-slicing** (resolve o megabundle de 1.4MB — ver D5). +- **Skill `seven-design-system`** + pacote `@etus/seven-skill` (plugin Claude Code + `npx seven detect`, 35 regras) — guard-rails automáticos; vale instalar. + +### Regras de ouro (DESIGN.md) — guard-rails inegociáveis +- **Tipografia:** Inter (400/500/600/700, evitar 800/900); **JetBrains Mono** p/ código; **nunca Geist/Space Grotesk**. Sentence case pt-BR; sem ponto final em botões/labels. +- **Cor:** só **mint** (`--primary #19e699`) + **lime** (`--secondary`) são marca; **fuchsia só em charts**; neutros = cinza puro; status semânticos. Nunca inventar cor, nunca indigo/"AI purple", nunca `#000`/`#fff` puros. +- **Forma:** dois stacks de radius **não intercambiáveis** — componente (4/8/12 = inputs/botões) vs superfície (16/20/24/32 = cards/dialogs); **pills só** em badges/tags, nunca botões. +- **Elevação/Motion:** bordas 1px p/ hierarquia, sombras só p/ overlays; micro-press tátil (`translate-y` + inset shadow) em todo clicável; 200ms `--ease-default`, sem bounce/spring. +- **Tailwind v4:** type hints obrigatórios — `bg-[color:var(--token)]`, `text-[length:var(--token)]`, dimensões sem prefixo; **nunca** `bg-[var(...)]` nem `hsl(var(...))` (Lightning CSS dropa em silêncio). +- **Ícones:** Lucide, stroke 2px, `currentColor`; sem emoji no chrome. + +## Estratégia de branch + +- Branch nova a partir de `master` (ex.: `feat/frontend-seven-ds`). +- Migração incremental por fase — `pnpm build` + `pnpm typecheck:client` verdes ao fim de cada fase. +- Cada fase de UI é verificável por screenshot/E2E `@visual` antes de seguir. + +## Definition of Ready (DoR) + +- [x] Diagnóstico do estado atual concluído (este doc) +- [x] Catálogo do Seven mapeado e **verificado em runtime** (não só `.d.ts`): `TextInput`, `Divider`, `SkeletonLoader`, `Switch`, `Checkbox`, `Toaster` existem em `@etus/ui@0.4.0-beta.2` (1215 exports) +- [x] Spikes técnicos da Fase 0 (D2 tokens, D4 toast) resolvidos +- [x] Decisões do usuário: D1 = reescrever efeitos/remover paletas; D3 = Inter variable + Geist Mono + remover serif +- [ ] Branch nova criada a partir de `master` + +--- + +## Fase 0 — Decisões + de-risking (GATE) + +Spikes técnicos resolvidos nesta sessão (D2, D4 ✅). Restam **2 decisões do usuário** (D1, D3). + +### D1 — Paletas de cor locais (DECISÃO DO USUÁRIO) — dado corrigido +As paletas `--color-{green,blue,gray,red,yellow,pink,main1,main2}-*` (88 tokens, `index.css:43–145`) +**não colidem por nome** com o Seven (que usa `--brand-*` + semânticas). Uso real medido: +- **0** ocorrências como classe utilitária (`bg-green-500` etc.) em `src/client`. +- **Mas NÃO são órfãs:** são consumidas via `var(--color-…)` por ~10 utilitários visuais do próprio `index.css` + (`.gradient-{primary,accent,warm}`, `.glow-*`, spinner/rainbow, `.skeleton` shimmer — linhas ~514–721). +- **Decidir:** (a) **remapear** os valores das paletas para as primitivas do Seven (mantém os efeitos, alinha a marca); + (b) reescrever os efeitos usando `--brand-*`/semânticas e remover as paletas; (c) manter como estão (dois sistemas). +- **✅ DECISÃO (2026-06-16): opção (b)** — reescrever os ~10 efeitos (`.gradient-*`, `.glow-*`, spinner/rainbow, `.skeleton`) com tokens `--brand-*`/semânticos do Seven e **remover as 88 paletas locais**. Vocabulário único de cor (boilerplate-base limpo). ⚠️ Nem todo gradiente terá equivalente de marca exato — escolher o token Seven mais próximo por efeito e conferir visual. +- **Verde:** paletas removidas; nenhum `var(--color-{green,blue,…})` sobra; efeitos conferidos. + +### D2 — Set de tokens a remover/manter ✅ RESOLVIDO +Valores reais do Seven (lidos de `@etus/tokens/dist/tokens.css`): +- **radius:** none 0 · xs **2** · sm **4** · md **8** · lg **12** · xl **16** · 2xl **20** · 3xl 24 · 4xl 32 · pill 999 · full 9999 (px). + Local diverge em toda a escala (xs 1 · sm 2 · md 4 · lg 8 · xl 12 · 2xl 14) → **remover locais**. +- **shadow:** Seven fornece xs–xl (com variante dark) → **remover** locais xs–xl; **manter** `--shadow-2xl` e `--shadow-inner` (Seven não tem). +- **breakpoint:** Seven xs 320 · sm **360** · md 768 · lg 1024 · xl 1280 · 2xl **1440** · 3xl 1920 · 4xl 2560. + Local diverge em **sm (640)** e **2xl (1536)**; md/lg/xl batem → **remover locais** (⚠️ ver R1). +- **`--text-*`:** Seven **define** (2xs…9xl) → **remover** local `--text-xs…5xl`. +- **`--font-{thin…extrabold}`:** o Tailwind v4 lê `--font-weight-*` (Seven fornece 100–900); os `--font-*` locais são redundantes → **remover** (checar refs `var(--font-bold)`). +- **`--leading-*`:** Seven usa namespace próprio `--font-leading-*` (não toca `--leading-*`) → **manter** local. + +### D3 — Fontes ⚠️ CORRIGIDO pelo DESIGN.md do Seven +O `DESIGN.md` canônico contradiz parte do que foi decidido antes (decisão original feita sem acesso a ele): +- **Sans = Inter, pesos 400/500/600/700 apenas** ("avoid 800/900"). App já está assim ✓ → **NÃO** carregar variable 100–900 (revertido). +- **Mono = JetBrains Mono** (oficial). **Geist é PROIBIDO** ("never Geist"). → trocar `--font-mono` de Geist Mono p/ **JetBrains Mono** (Google Fonts). ⚠️ revoga a escolha "Geist Mono" — **confirmar com o usuário**. +- **Sem Space Grotesk** (DESIGN.md: legacy Figma que não shipou; os tokens `--typography-display-*` são legado). Display usa Inter. +- **Sem serif na marca** (o Instrument Serif do showcase é o tema próprio dele, não o DS). `--font-serif` órfão → **remover**. +- **Verde:** Inter 400–700 + JetBrains Mono carregadas; sem Geist/Space Grotesk/serif. + +### D4 — Toast ✅ RESOLVIDO +`@etus/ui` exporta `Toaster` (tipado sobre `sonner`) e depende de `sonner@^2.0.7` (mesma versão do app), mas **não reexporta a função `toast`**. +- **Padrão:** usar `` de `@etus/seven-react` (com `theme={resolvedTheme}` p/ dark) e manter `import { toast } from 'sonner'` nas 4 chamadas. Deletar `components/ui/sonner.tsx`. + +### D5 — Arquitetura de consumo (NOVA — DECISÃO DO USUÁRIO) +Como o boilerplate consome os componentes do Seven? +- **(a) Runtime `@etus/seven-react`** (atual) — 1 import, atualiza via `pnpm up`, mas **megabundle ~1.4MB** + CSS ~664KB (R4); rota pública carrega tudo (mitigável com lazy/code-split). +- **(b) CLI source `npx @etus/seven add`** (modelo shadcn) — copia 185 componentes como **source local** com **token-slicing** (só os tokens usados) → bundle enxuto, controle total; **mas** vira código "seu" (sem update automático; re-rodar `add` p/ acompanhar o DS — relevante porque está em beta). +- **Trade-off:** (a) acompanha o DS em evolução com menos esforço; (b) resolve o bundle e é mais idiomático p/ um boilerplate-base, ao custo de manutenção manual. +- **✅ DECISÃO (2026-06-16): (a) Runtime `@etus/seven-react`** — manter alinhamento contínuo ao DS (beta evolui); domar o bundle via lazy-load nas rotas públicas (U4.2). + +**Checkpoint Fase 0:** D1/D2/D4/D5 fechados; **D3 corrigido pelo DESIGN.md** (aguarda só o OK sobre Geist→JetBrains Mono). Fases 1–5 reescritas abaixo para "rewrite guiado por blocks". + +--- + +## Fase 1 — Fundação de tokens (P0) + +**Objetivo:** o Seven passa a ditar radius/shadow/breakpoint/tipografia; o `@theme` local guarda só os extras. +**Depende de:** D1, D2. + +### U1.1 — Remover tokens colidentes do `@theme` +Conforme D2 (resolvido): + +| Família (local) | Ação | Razão | +|---|---|---| +| `--radius-xs…2xl`, `--radius-full` | **Remover** | colide com Seven; escala local ≈ metade (xs 1 vs 2, md 4 vs 8…) | +| `--shadow-xs…xl` | **Remover** | colide com Seven (`--shadow-xs…xl`) | +| `--shadow-2xl`, `--shadow-inner` | **Manter** | Seven não fornece | +| `--breakpoint-sm…2xl` | **Remover** | colide; sm 640→**360**, 2xl 1536→**1440** — ⚠️ validar responsividade (R1) | +| `--text-xs…5xl` | **Remover** | Seven define `--text-*` (2xs…9xl) | +| `--font-{thin…extrabold}` | **Remover** | redundante; Tailwind usa `--font-weight-*` (Seven fornece 100–900) | +| `--leading-*` | **Manter** | Seven usa namespace `--font-leading-*`, não toca `--leading-*` | +| `--blur-*`, `--container-*`, `--grid-gap-*`, `--content-*` | **Manter** | utilitários locais, não conflitam com Seven | +| `--spacing-{page,section,group,element,tight,xs}`, `--layout-*`, `--card-padding*` | **Manter** | semântica de layout própria | +| `--animate-duration-*`, `--animate-ease-*`, `--animate-spring` | **Manter (P2: alinhar)** | Seven usa `--duration-*`/`--ease-*` (nomes diferentes) — alinhar é opcional | +| `--color-error*`, `--color-*-solid` | **Manter** | Seven usa `destructive`; não tem variantes `*-solid` | + +- **Verde:** `pnpm build` ok; `grep -nE "\-\-(radius|breakpoint|shadow-(xs|sm|md|lg|xl))" src/client/index.css` só retorna o que a tabela manda manter; screenshot de uma tela de referência (ex.: dashboard) batendo com o tom do Seven. + +### U1.2 — Reescrever efeitos e remover paletas (D1 = opção b) +- Para cada utilitário em `index.css` que usa `var(--color-{green,blue,gray,red,yellow,pink,main1,main2}-*)` + (`.gradient-{primary,accent,warm}`, `.glow-*`, spinner/rainbow, `.skeleton` shimmer — linhas ~514–721), + trocar pelos tokens `--brand-*`/semânticos do Seven mais próximos; depois **deletar os 88 tokens de paleta** do `@theme`. +- **Verde:** `grep -nE "var\(--color-(green|blue|gray|red|yellow|pink|main1|main2)-" src/client` vazio; efeitos conferidos visualmente. + +### U1.3 — Fontes (D3 corrigido pelo DESIGN.md) +- `index.html`: manter **Inter 400/500/600/700** (já presente; NÃO adicionar 800/900); trocar o `` para incluir **JetBrains Mono**. +- `index.css`: `--font-mono` → JetBrains Mono (era Geist, proibido pelo DESIGN.md); **remover** `--font-serif` (órfão); não introduzir Space Grotesk. +- **Verde:** `font-mono` (6 usos) renderiza JetBrains Mono; `grep -iE "geist|space grotesk" src/client` vazio; sem `--font-serif`. + +**Checkpoint Fase 1:** ✅ executado 2026-06-16 (branch `feat/frontend-seven-ds`) — `pnpm typecheck:client` + `pnpm build` (988ms) verdes; limpeza confirmada (0 paletas, 0 tokens colidentes, JetBrains Mono no lugar de Geist). Revisão visual no browser: **pendente**. + +--- + +## Fase 2 — Shell canônico (rewrite guiado pelo showcase) + +**Objetivo:** trocar o shell caseiro (`sidebar.tsx`, 267 linhas) pelo sistema de navegação do Seven, replicando o shell canônico do showcase. +**Depende de:** Fase 1. +**Referência:** `seven/apps/showcase/src/routes/__root.tsx` (+ blocks `data/blocks/page-shells/*`; screenshots `showcase-canonical-shell-*`). + +### U2.1 — Navegação com o sistema Sidebar do Seven +- Substituir `components/sidebar.tsx` por `SidebarProvider mode="dual"` → `SidebarRailContent` (rail 64px: logo + ícones de seção + theme toggle + ações) + `SidebarPanelContent` (216px: `SidebarHeader` + `SidebarContent` com `SidebarMenu`/`SidebarMenuItem`/`SidebarMenuButton`). Manter os ícones lucide e o user/account menu. +- Ligar às rotas autenticadas (TanStack `Link`), preservando `useAuth`/`use-theme`. +- **Verde:** rail+panel como o canonical; navegação e theme toggle funcionam; nenhum markup de sidebar caseiro resta. + +### U2.2 — Layout autenticado com `SidebarInset` +- `routes/_authenticated.tsx`: `SidebarInset` → header `h-12` (Breadcrumb derivado do pathname + ações) + content `rounded-tl-[24px] border bg-background px-8 py-8` (assinatura do shell). `SidebarTrigger` p/ colapsar; skeletons → `SkeletonLoader`/`SidebarMenuSkeleton`. +- **Verde:** layout idêntico ao canonical; breadcrumb correto; loading sem CLS; E2E de navegação verde. + +**Checkpoint Fase 2:** ✅ executado 2026-06-16 — `sidebar.tsx` reescrito como `AppSidebar` (single mode: `SidebarProvider` + `Sidebar collapsible="icon"` + `SidebarHeader/Content/Footer` + `SidebarInset` com header `h-12` e content `rounded-tl-[24px]`); `_authenticated.tsx` monta o shell. `pnpm typecheck:client` + `pnpm build` verdes. ✅ **validação visual feita** (dev server + `/auth/test-login` + `/dashboard`, viewport 1440×900): shell **expandido e colapsado** conferidos contra o canônico, console **sem erros**. Bug pego só na renderização real (não no build): `SidebarAvatar` não compacta sozinho no modo ícone → ligado ao `useSidebar` (`compact={state==='collapsed'}`). 3 refinamentos aplicados: `SidebarAvatar`+dropdown no rodapé, `aria-current="page"`, `size="lg"` no header. + +--- + +## Fase 3 — Telas guiadas por blocks (rewrite da apresentação, lógica preservada) + +**Objetivo:** reescrever a árvore visual de cada tela partindo do block correspondente do showcase, **preservando a lógica** (hooks/rotas/forms RHF+zod/queries/handlers do `@etus/auth`). Nesta fase somem os caseiros e o HTML cru de controle. +**Depende de:** Fase 1 (+ Fase 2 p/ as telas autenticadas). + +> **Regra de execução:** abrir o block no showcase (`seven/apps/showcase/src/data/blocks//` e `routes/layouts/marketing/*`), copiar a composição e religar aos dados/forms existentes. Validar **runtime** dos componentes (`Object.keys(await import('@etus/ui'))`), nunca o `.d.ts` (substring → falso positivo). Props do Seven ≠ shadcn: `TextInput` (`size`/`leadingIcon`), `Divider`, `SkeletonLoader`, `Switch`, `Checkbox` — ajustar call-sites. Seguir as regras de ouro (sentence case pt-BR, type hints `[color:var(...)]`, micro-press, radius stacks, `Badge` `type`+`color` — R5). +> +> Primitivos substituídos ao longo das telas: `input.tsx`→`TextInput`, `separator.tsx`→`Divider`, `skeleton.tsx`→`SkeletonLoader`, `sonner.tsx`→`Toaster` (D4), toggles `