Blog
Projetar um sistema de design fora do Figma
A ideia central
Este projeto nasceu de uma pergunta que parece técnica, mas na verdade é uma pergunta de produto: o que acontece se o sistema de design não vive primeiro em uma ferramenta visual, mas no mesmo lugar onde o produto é construído, testado e consumido?
Quando digo “projetar um sistema de design fora do Figma”, não estou falando de rejeitar o Figma. O Figma continua sendo uma ferramenta valiosa para explorar, alinhar e comunicar. A diferença está na fonte da verdade. Neste projeto, a fonte da verdade deixou de ser um arquivo visual e passou a ser um sistema vivo em código: tokens, componentes, documentação, regras, testes e automações.
Um token é uma decisão de design convertida em uma variável reutilizável. Por exemplo: uma cor de fundo, um raio de borda, uma sombra, um espaçamento entre elementos ou uma duração de animação. Em vez de decidir esses valores repetidamente em cada tela, o sistema os nomeia, centraliza e reutiliza. Isso importa porque transforma o design em infraestrutura: algo que pode crescer, mudar e se manter com menos improviso.
Essa foi a intenção do Design System Sho: passar de uma galeria de componentes para um monorepo capaz de sustentar um sistema de design real.
O ponto de partida: uma galeria que servia como inventário
O repositório começou como uma extração inicial de um sistema de componentes. Nessa primeira etapa já havia uma base importante: tokens, componentes, layouts e uma app de documentação em Next.js. A galeria era o lugar onde era possível ver o que existia.
Esse primeiro showcase tinha um valor muito concreto: permitia inventariar. Antes de pensar em arquitetura avançada, era preciso responder perguntas básicas:
- Quais componentes temos?
- Como eles aparecem visualmente?
- Quais variantes existem?
- Quais partes são primitivas e quais são composições?
- Quais decisões visuais se repetem?
- O que está documentado e o que só existe porque alguém lembra?
A galeria foi a primeira ponte entre intenção e realidade. Ainda não era um sistema maduro, mas já era um espelho. Mostrava o estado do projeto como ele era, com suas forças e suas lacunas.
Esse aprendizado foi importante: uma galeria de componentes não é apenas uma página bonita. É uma ferramenta de auditoria. Serve para descobrir inconsistências, entender o inventário e começar a tomar decisões com evidência.
A mudança de pergunta: de “como se vê?” para “como escala?”
No início, a pergunta principal era visual: fazer os componentes ficarem bons, ter uma UI definida, construir uma identidade clara. Mas, à medida que o projeto cresceu, a pergunta mudou.
A nova pergunta foi: como fazemos isso escalar?
Escalar não significa adicionar mais componentes sem ordem. Escalar significa que o sistema consiga receber novas peças sem perder coerência. Significa que uma decisão de cor não seja copiada manualmente em vinte arquivos. Significa que o modo escuro não dependa de exceções componente por componente. Significa que os nomes sejam claros, que as variantes sejam previsíveis e que qualquer pessoa consiga consumir o sistema a partir de outro projeto.
Essa mudança de pergunta foi o ponto de virada. O projeto deixou de ser apenas uma coleção visível e começou a se transformar em uma plataforma.
Decisão 1: token-first como princípio de design
A primeira grande decisão foi adotar uma lógica token-first.
Token-first significa que os componentes não devem inventar estilos por conta própria. Um botão não deveria decidir um azul específico. Um card não deveria decidir uma sombra isolada. Um input não deveria ter uma borda que só vive dentro daquele arquivo. Tudo deve vir de tokens nomeados semanticamente.
Na prática, isso levou a uma regra clara: usar variáveis --sho-* como base visual do sistema. Cores, espaçamentos, raios, tamanhos, sombras, motion, layout e pesos tipográficos começaram a se organizar como linguagem comum.
Isso importa por três razões.
Primeiro, porque permite consistência. Se a cor de ação muda, não é preciso procurar componente por componente. Atualiza-se o token.
Segundo, porque permite temas. O modo escuro não é resolvido com classes especiais por componente, mas redefinindo os mesmos tokens dentro de .dark. O componente usa o mesmo nome semântico; o tema decide o valor.
Terceiro, porque permite governança. Se o sistema tem regras claras, Cursor pode ajudar a implementá-las. A IA deixa de improvisar estilos e começa a trabalhar dentro de um marco definido.
Aqui aparece uma das ideias mais fortes do projeto: não basta ter bom critério. É preciso transformar esse critério em regras do repositório.
Decisão 2: construir identidade própria, não copiar nomenclaturas externas
Outra decisão importante foi limpar referências externas e definir uma nomenclatura própria para Sho.
Nomear componentes parece um detalhe menor, mas não é. Os nomes criam o mapa mental do sistema. Se um componente se chama de uma forma na documentação, de outra no código e de outra na conversa do time, o sistema começa a perder clareza.
Por isso foram definidos nomes canônicos. Alguns nomes herdados foram substituídos por nomes mais claros ou mais alinhados com Sho: Snackbar passou a Toast, Textfield a Input, BoxSelector a Choicebox, Message a Alert, FeedbackScreen a Result, entre outros.
A mudança não foi apenas estética. Foi uma decisão estratégica: o sistema precisava falar com sua própria voz.
Para que essa transição não fosse caótica, foi documentado um guia de migração, aliases de compatibilidade foram mantidos por um tempo e um codemod foi preparado. Um codemod é um script que ajuda a atualizar código automaticamente quando há mudanças de API ou de nomes. Em outras palavras, não apenas se decidiu mudar nomes; o caminho foi preparado para que outros projetos pudessem migrar.
Decisão 3: passar do monólito para primitivos modulares
Durante uma fase do projeto, component-system.tsx concentrou grande parte da API pública. Isso teve valor naquele momento: permitiu reunir, ordenar e expor componentes a partir de um lugar claro.
Mas um monólito também tem limites. Quando decisões demais vivem em um único arquivo, o sistema fica difícil de auditar, testar e estender.
A evolução seguinte foi mover as implementações reais para módulos por primitivo. Um primitivo é uma peça base: Button, Input, Modal, Table, Badge, Toast, Tabs, etc. Cada primitivo começou a viver em sua própria pasta, com suas variantes e sua API.
Aqui aparece CVA, que significa Class Variance Authority. É uma biblioteca que permite declarar variantes de componentes de forma organizada. Por exemplo, um botão pode ter variantes de hierarquia, tamanho, estado destrutivo ou loading. Em vez de espalhar condições de estilo por todo o componente, CVA ajuda a definir essa matriz de variantes de maneira explícita.
A decisão prática foi esta: component-system.tsx deixou de ser o lugar onde tudo vive e passou a funcionar como um barrel, ou seja, um arquivo que reexporta componentes a partir de sua fonte real.
Essa mudança tornou o sistema mais manutenível. Cada componente podia crescer sem contaminar os demais. E cada decisão de variante podia ser revisada em seu próprio contexto.
Decisão 4: o layout também precisava ser uma fundação
Muitas vezes, sistemas de design se concentram em cor, tipografia e componentes, mas deixam o layout para decisões locais. Este projeto tomou outro caminho.
Foi definida uma fundação de layout baseada em uma mini unidade de 8px, colunas fluidas por breakpoint e primitivas como Grid, GridColumn, Stack, Cluster e PageShell.
Isso importa porque o layout é onde muitas interfaces quebram. Um componente pode estar bem projetado de forma isolada, mas, se cada tela usa gaps, colunas, margens e contêineres diferentes, o produto inteiro passa uma sensação de inconsistência.
A decisão de layout 2x foi transformar a geometria do produto em parte do sistema. Não apenas projetar botões, mas também projetar o espaço onde esses botões vivem.
Decisão 5: a documentação passou de galeria a produto
A app de docs evoluiu muito. No início era uma galeria. Depois começou a se comportar como um produto interno.
Surgiram uma home de marketing, navegação por foundations e components, um playbook de cenários reais, busca com command palette e previews com toggle de modo escuro. Os showcases também foram separados em arquivos lazy-loaded para que o sistema não dependesse de um único arquivo gigante.
Essa mudança é importante porque a documentação de um design system não apenas explica o sistema. Ela também o testa.
Um componente isolado pode parecer bom. Mas o playbook faz outra pergunta: como ele se comporta quando é combinado com outros componentes em fluxos reais? Formulários, checkout, scheduling, feedback, tabelas, filtros, estados vazios. Esse tipo de documentação aproxima o sistema da realidade do produto.
Nesse sentido, a documentação se tornou uma segunda camada de validação. Não apenas mostrava componentes. Mostrava decisões funcionando juntas.
Decisão 6: governança automatizada
A palavra governança pode soar pesada, mas a ideia é simples: como evitamos que o sistema se degrade com o tempo?
Um design system se degrada quando as regras vivem apenas na memória de uma pessoa. Degrada-se quando alguém adiciona uma cor direta porque estava com pressa. Degrada-se quando um componente novo não respeita os tokens. Degrada-se quando o modo escuro é resolvido com remendos por arquivo. Degrada-se quando não há uma forma rápida de saber se uma decisão quebrou outra.
Para evitar isso, o projeto incorporou várias camadas de controle:
- Regras de Cursor para trabalhar com tokens Sho.
- Uma skill interna para construir componentes seguindo o checklist do sistema.
- Scripts de verificação para tokens, estilos e layout.
- ESLint personalizado para bloquear estilos não permitidos.
- Dependency Cruiser para cuidar dos limites entre camadas.
- Typecheck, lint, tests, smoke tests e CI.
A ideia-chave é que o sistema não dependa apenas de revisão manual. A revisão humana continua sendo importante, mas agora é acompanhada por ferramentas que detectam desvios.
Este é um dos aprendizados centrais do projeto: se você vai usar IA para construir um sistema de design, precisa dar a ela regras verificáveis. Cursor foi potente não porque substituiu o critério, mas porque conseguiu trabalhar dentro de um marco que eu fui definindo.
Decisão 7: migrar para monorepo
A passagem para monorepo foi uma das decisões mais importantes.
Um monorepo é um repositório que contém vários pacotes ou aplicações relacionados. Neste caso, o projeto passou a se organizar com apps/ e packages/:
@sho/reactpara os componentes, primitivas e layouts.@sho/stylespara tokens e CSS publicável.@sho/docspara a documentação e showcase.@sho/playgroundpara testar consumo isolado.- Pacotes de tooling como
@sho/codemod,@sho/eslint-confige@sho/tsconfig.
Essa estrutura importa porque separa responsabilidades. A documentação já não é a mesma coisa que a biblioteca. Os estilos já não estão misturados com a app. Os componentes podem ser construídos, versionados e consumidos como pacote.
O monorepo foi o passo que transformou o projeto em algo mais próximo de uma infraestrutura reutilizável. Já não era apenas “um repo onde se veem componentes”. Era uma base para que outros projetos pudessem consumir Sho.
A tensão da migração: conservar a UI sem perder a nova estrutura
Uma das conversas mais importantes aconteceu depois da migração para monorepo. A nova estrutura era mais limpa, mas alguns componentes já não apareciam como antes. Havia estilos alterados, partes quebradas e falta de cor.
Esse momento foi decisivo porque revelou uma tensão real em qualquer migração: uma boa arquitetura não serve se perde a experiência visual que já havia sido conquistada.
O objetivo não era voltar atrás. O objetivo era recuperar a UI definida antes da migração, mas mantendo a estrutura nova.
Esse aprendizado é muito importante de compartilhar: a arquitetura deve proteger o design, não substituí-lo. Uma migração bem-sucedida não é aquela que apenas deixa pastas mais limpas. É aquela que conserva o comportamento e a aparência que já funcionavam, enquanto melhora a capacidade de manutenção.
Esse tipo de decisão é exatamente onde o modo plan do Cursor foi útil. Antes de editar, era preciso entender o que tinha acontecido: imports, tokens, CSS modularizado, detecção do Tailwind, dist de pacotes, rotas antigas, servidor stale, componentes que apontavam para paths anteriores. O trabalho não era “corrigir cores”; era diagnosticar o sistema.
Theme Studio: fechar o loop visual dentro do próprio sistema
Outro marco importante foi Theme Studio.
Theme Studio leva uma ideia poderosa ao projeto: se os tokens são a fonte da verdade, também deveria existir uma forma visual de editá-los, testá-los e exportá-los dentro do mesmo ambiente onde o sistema vive.
Isso aproxima o projeto de uma alternativa real às Figma Variables para certos fluxos. Não porque replique todo o Figma, mas porque permite iterar tema visualmente no browser e conectar essa iteração com os tokens que a app realmente consome.
A promessa é clara: ver, ajustar, testar e exportar sem romper a cadeia entre design e desenvolvimento.
O papel do Cursor: não magia, mas amplificação do critério
Cursor foi uma ferramenta central nesse processo, mas não como substituto da experiência.
A IA acelera quando há direção. Sem direção, pode produzir código que funciona na aparência, mas quebra o sistema. Com regras claras, pode se tornar uma extensão do critério do time.
Neste projeto, o modo plan teve um papel estratégico. Serviu para pensar antes de tocar no código, separar sintomas de causas, discutir tradeoffs e transformar uma intenção ampla em passos concretos.
A experiência prévia também foi essencial. Saber o que pedir, o que revisar, o que bloquear, o que aceitar e o que transformar em regra faz parte do trabalho. Cursor ajudou a executar, explorar, auditar e migrar. Mas as decisões importantes continuaram sendo decisões de produto e de sistema.
A frase que resume isso é: não usei Cursor para decidir por mim; usei Cursor para multiplicar minha capacidade de transformar decisões em estrutura.
O que realmente foi construído
Visto de fora, o resultado poderia ser resumido como um monorepo com React, tokens e docs. Mas isso fica curto.
O que foi construído foi uma forma de trabalhar:
- Um sistema token-first onde o design vive como variáveis, não como valores soltos.
- Um conjunto de primitivas modulares com variantes explícitas.
- Uma nomenclatura própria para Sho.
- Uma documentação que funciona como inventário, teste e produto interno.
- Uma fundação de layout para organizar o espaço, não apenas os componentes.
- Um Theme Studio para iterar visualmente com tokens reais.
- Um monorepo preparado para consumo a partir de outros projetos.
- Uma camada de governança para que humanos e agentes trabalhem com as mesmas regras.
Também houve escala concreta: cerca de 26 commits em uma etapa intensa, mais de 500 arquivos tocados, aproximadamente 42 primitivas modulares, uma matriz de 45 componentes, variantes P0-P2, renomeações canônicas, melhorias de acessibilidade WCAG 2.2 AA, CI, testes e empacotamento @sho/*.
Esses números importam não pelo volume, mas porque mostram que o trabalho deixou de ser uma maquete. Passou a ser uma plataforma.
Aprendizados para a audiência
O primeiro aprendizado é que um sistema de design não começa quando tudo está perfeito. Começa quando você decide tornar visível o que existe. A galeria foi esse primeiro ato de honestidade.
O segundo aprendizado é que tokens são mais do que variáveis. São decisões. Quando um time nomeia bem seus tokens, está nomeando sua forma de pensar.
O terceiro aprendizado é que a arquitetura não deve ir contra a UI. Se uma migração quebra o que já funcionava, é preciso corrigir a migração, não sacrificar a experiência.
O quarto aprendizado é que documentar não é escrever depois. Documentar pode ser uma forma de projetar. O showcase, o playbook e as matrizes de variantes ajudaram a decidir, não apenas a explicar.
O quinto aprendizado é que a IA precisa de governo. Cursor foi útil porque o projeto tinha regras, skills, verificações e uma visão clara. A IA não elimina a necessidade de critério; torna essa necessidade mais urgente.
O sexto aprendizado é que Figma não precisa ser o único centro do sistema. Para muitos times, o código pode ser a fonte da verdade quando o objetivo é que o sistema seja consumível, verificável e versionável.
A história em uma frase
Design System Sho passou de uma galeria de componentes a um sistema de design em código: token-first, modular, documentado, verificável e preparado para outros projetos.
A história não é apenas “fizemos um monorepo”. A história é que o design começou a viver onde a execução também vive. E, graças ao Cursor, ao modo plan e à experiência acumulada, esse trabalho pôde avançar com velocidade sem perder intenção.
Fechamento possível para a apresentação
Se eu tivesse que encerrar essa história diante de uma audiência, diria isto:
Eu não estava tentando tirar o Figma da conversa. Eu estava tentando demonstrar que um sistema de design pode ter outra fonte da verdade.
Figma ajuda a imaginar. Mas este projeto precisava de algo mais: precisava de regras, pacotes, tokens, testes, documentação, migrações e consumo real.
A galeria foi o primeiro espelho. Os tokens foram a linguagem. Os componentes foram a matéria-prima. O monorepo foi a infraestrutura. Cursor foi o copiloto. E o modo plan foi o espaço onde as decisões deixaram de ser intuições soltas e se transformaram em estratégia.
Isso é o que construí com Design System Sho: não apenas uma coleção de componentes, mas uma maneira de transformar critério de design em sistema.
Fontes internas revisadas
README.mdCHANGELOG.mdCONTRIBUTING.mddocs/architecture.mddocs/MIGRATION.mddocs/component-checklist.mddocs/component-variants-roadmap.mddocs/component-variants-audit-report.mddocs/layout-foundation.md.cursor/rules/token-first-components.mdc.cursor/skills/build-sho-component/SKILL.md- Histórico de commits do repositório
- Transcrições anteriores de trabalho, incluindo resumo das últimas duas semanas e plano de recuperação visual depois da migração para monorepo