Construí um design system open source em dois dias com Claude Code. E não foi vibe coding.
O método que transformou intenção de produto em um DS brutalista funcional: skills, memória persistente, subagentes e o julgamento do designer no comando.
O que existe hoje no github.com/thiagoxikota/touch-grass
Dois dias atrás esse repositório não existia. Hoje ele tem:
Um pacote de tokens construído em cima do Style Dictionary 4 que emite CSS variables, um @theme block de Tailwind v4, JSON no formato W3C Design Tokens pro Figma e constantes Swift pro iOS consumir via SPM. Um pacote de componentes React 19 com dez primitives (Button, Input, Badge, Card, Tag, Divider, Stat, Timer, Checkbox, Switch) e oito patterns (LeaderboardRow, FocusTimerDisplay, BeRealStamp, PatternInterruptModal, SessionSummaryCard, Sparkline, Toast, Field), todos com forwardRef, todos com token-only colors, todos cobertos por Vitest + React Testing Library + Playwright Component Testing, 17 suites verdes. Um docs site em Vite + React 19 com oito páginas de foundations (color, typography, spacing, borders, grid, motion, states, brand), dez de primitives e oito de patterns, todas rodando os componentes reais ao vivo. Um Package.swift na raiz pra expor os tokens como pacote SPM. Licença MIT, CHANGELOG, CONTRIBUTING escrito com as regras brutalistas na cara do colaborador, CI rodando no GitHub Actions. Primeiro release tagueado v0.1.0, segundo v0.1.1 com suporte a subpath, terceiro v0.1.2 com o docs refresh completo.
Esse DS serve de base pra Timeouts, o app iOS de academia social pra tempo fora do celular que eu estou construindo. Mas a história interessante não é o que tem no repo. É como ele apareceu lá.
Por que um DS, e por que brutalista
Eu poderia ter usado shadcn/ui e ter um app de pé na mesma tarde. Não seria errado. Seria genérico. E Timeouts não é um produto genérico: é uma proposta contra o modelo dominante de app de "foco" (bloquear, envergonhar, fazer arvorezinha morrer). Um DS brutalista com zero cantos arredondados, zero texto cinza, zero motion, Geist Mono em todo lugar que conta, era a forma de transformar a tese do produto em força visual. Restrição como decisão.
O brutalismo aqui não é estética. É forcing function. Quando o DS proíbe border-radius, proíbe text-gray-500, proíbe transition-all, o designer para de simular comodidade que não está lá. O produto fica mais honesto porque o sistema não deixa ele mentir.
Essa é a primeira coisa que AI-driven workflows não sabem fazer sozinhos: escolher o que o produto deveria ser. O Claude não decide que você vai fazer um DS brutalista. Você decide. O Claude ajuda a executar.
O método: não é autocomplete, é sistema operacional
Existe uma diferença entre tratar IA como autocomplete inteligente e tratar IA como sistema operacional pro trabalho de design. Autocomplete te deixa digitar mais rápido. Sistema operacional muda o que você pode fazer no tempo que tem.
O Claude Code, que é o terminal que eu uso, tem três conceitos que juntos viram esse sistema operacional:
-
Skills. Workflows nomeados que encodam como fazer algo específico. Eu tenho skills como
touch-grass-component(que sabe o lockstep de 6 arquivos pra adicionar um primitive),touch-grass-figma-bridge(que sabe as limitações do Plugin API do Figma que já mordi antes),touch-grass-brand-asset(que sabe o pipeline de SVG → token compliance). Cada skill é conhecimento procedural destilado. Não é prompt esperto. É método. -
Memória persistente. Um diretório onde eu gravo o contexto do usuário (quem é, como prefere trabalhar), do projeto (decisões, por que), e do feedback (o que não deve se repetir). Cada conversa nova começa com esse contexto já carregado. Eu não preciso re-explicar, a cada sessão, que o Timeouts não é focus timer, é sim Strava pra tempo fora do celular. Uma vez que eu disse, ficou.
-
Subagentes. Sub-processos do Claude que fazem tarefas paralelas sem poluir o contexto principal. Quando eu preciso de uma busca larga em codebase, uma revisão de código, uma exploração de docs: eu delego. O contexto principal fica limpo pra decisão, não pra busca.
Junte os três e você tem uma forma de trabalhar onde você mantém o julgamento, o design intent, o critério de qualidade, e delega a parte mecânica. Product designer continua fazendo product design. A IA vira o assistant técnico que nunca se cansa.
O loop, em quatro passos
Eu não escrevi nenhuma linha de código do touch-grass à mão. Cada feature passou por um loop de quatro etapas. Esse loop é o que separa trabalho assistido por IA de vibe coding:
1. Brainstorming. Antes de qualquer código, uma conversa. O que estou tentando fazer, quais são as restrições, quais abordagens existem, qual eu escolho e por que. Essa etapa não gera código, gera um documento de spec curto (três a dez parágrafos). O spec fica versionado em docs/superpowers/specs/. Se o spec está errado, o código vai estar errado. Melhor descobrir antes de escrever.
2. Planning. Pega o spec e transforma em uma lista de tarefas com dependências explícitas. Essa etapa revela o que eu não tinha pensado: "pra adicionar o Button eu preciso antes do cn() helper, do base.css, do token pipeline funcionando". O plano fica em docs/superpowers/plans/. Claude o usa pra se auto-dirigir depois.
3. Test-driven execution. Cada componente começa pelo teste. O Vitest test do Button foi escrito antes do JSX do Button. Isso parece dogma, mas tem razão prática: quando IA escreve código, a única prova de que fez certo é o teste passar. Sem teste, todo código assistido por IA é uma promessa sem garantia.
4. Code review. Depois da execução, um agent específico (superpowers:requesting-code-review) lê o diff, compara com o spec, aponta tudo que não bate. Não sou eu, não é o mesmo contexto, é uma terceira voz cética. Se passa nessa revisão, commit. Se não, volta.
Os quatro passos são ritual. Todo componente, toda feature, todo fix grande. O loop não é burocracia. É o que garante que cada commit adiciona algo verificável ao repo, não uma aproximação.
Um exemplo concreto: o Button
O Button foi a primeira primitive. A conversa com Claude começou assim:
Eu: "adicionar um Button brutalista, três variants (primary, ghost, danger), cinco states (default, hover, focus, disabled, loading), 48px tap target mínimo, uppercase mono, loading state usa block characters não spinner."
Claude: disparou o skill touch-grass-component, que injetou no contexto o lockstep de seis arquivos que eu tinha escrito antes. Depois abriu brainstorming: "confirma que ghost não tem hover background, só border color flip?". Eu confirmei. "Danger deve ter hover? Parece contraditório com a semântica." Bom ponto. "Não, fica estático." OK.
Saiu o spec. Saiu o plano. O plano virou uma lista de TODOs. O primeiro TODO foi o teste:
describe('Button', () => {
it('renders with primary variant by default', () => {
render(<Button>START</Button>);
expect(screen.getByRole('button')).toHaveClass('bg-earned', 'text-bg');
});
// ...mais 7 cases
});
Teste antes do componente, vermelho. Depois o componente, verde. Depois a demo page, a barrel export, a rota no docs site. Depois o code-reviewer agent rodou e apontou que eu tinha esquecido de definir o type="button" default (clicks dentro de form submetiam). Fix, teste novo, verde de novo. Commit.
Tempo total: 20 minutos. Isso inclui a conversa, o teste, o componente, a demo, o review, o commit. Um Button que faz sentido, coberto por 8 testes, documentado numa página ao vivo, que segue as mesmas regras brutalistas que todos os outros vão seguir. Não é vibe coding. É engenharia de produto com IA fazendo o trabalho manual.
O que a memória salva
No segundo dia, enquanto eu estava construindo a landing do timeouts.app, Claude começou a escrever copy tipo "focus timer that rewards stepping away" e "earn your screen time". Eu parei: "Timeouts não é focus timer. É academia social pra tempo fora do celular. Comprovantes por foto, leaderboard com seus amigos, IA coach olhando seus padrões. Por favor não esquece isso nunca mais."
Claude salvou uma memória marcada como project no diretório de memória:
Timeouts core concept. Strava/gym-rats for time OFF the phone. Social proof, photo proofs, group competition, AI coach. NOT a focus timer. NEVER frame as "earn screen time" or "block apps".
Essa memória entra no contexto toda vez que eu abro uma nova conversa nesse projeto. Uma lição aprendida. Ao contrário de um PDF de brand book que ninguém lê, essa memória é consultada por quem vai escrever cada linha seguinte. O erro de posicionamento não se repete porque o sistema impede a repetição.
Esse é um ganho que eu não tinha antes de usar memória persistente: o contexto do produto fica com você como equipe, não na sua cabeça como indivíduo. Cada sessão nova começa informada, não em branco.
Subagents em paralelo: como economizar o contexto principal
Quando eu estava escrevendo o README matador pro launch público, eu precisava de três coisas ao mesmo tempo: screenshots do docs site rodando local, revisão de linguagem do README, e um inventário do que já existia no repo pra eu não prometer o que não tem. Em vez de rodar tudo sequencial poluindo o contexto, eu despachei três subagents em paralelo:
- Um agent com Playwright pra rodar
pnpm dev, tirar screenshots de três páginas e salvar. - Um agent de code review pra ler o rascunho do README e apontar trechos genéricos.
- Um agent de exploração pra mapear o que tem em
packages/ds/src/epackages/docs-site/src/pages/.
Os três rodaram juntos. Cada um voltou com um relatório conciso. Eu consolidei. Isso não é um detalhe. É o que torna o contexto principal focado em decisão e não em busca. Se eu tivesse feito os três sequenciais, o ruído de saída teria empurrado decisões importantes pra fora da janela de contexto.
Design é 80% decidir e 20% executar. Subagents te devolvem o 20%.
O que o product designer continua fazendo
Nada disso funciona sem julgamento de produto. Olha cada passo e me diz quem decide:
- Fazer um DS em vez de usar shadcn → produto. Eu.
- Fazer brutalista em vez de soft → produto. Eu.
- Button tem cinco states ou três → produto. Eu.
- Ghost variant tem hover fundo ou só border → produto. Eu.
- Timeouts é academia social e não focus timer → produto. Eu.
- Landing do timeouts.app não pode usar "earn screen time" → produto. Eu.
- Tokens são
fg/bg/earnede nãoink/primary→ produto. Eu.
O Claude escreveu praticamente todo o código. Não escreveu nenhuma das decisões. Eu mantive o julgamento em cada ponto de decisão e deleguei a execução. Esse é o truque. Product designers que vão sobreviver à IA não são os que digitam mais rápido. São os que têm gosto forte e direção clara o suficiente pra guiar máquinas de execução sem perder identidade no meio do caminho.
O que eu não faço
Pra ser justo com o método, preciso dizer o que ele evita:
Não faço spec-less sprints. Nunca peço código sem antes ter uma conversa de brainstorming, por mais simples que seja a tarefa. Simples é exatamente onde premissas erradas fazem estrago.
Não deixo IA rodar sem teste. Código sem teste é promessa. Promessa não merece commit.
Não aceito sugestão por autoridade. Quando o code reviewer aponta algo que parece errado, eu investigo e respondo com evidência, não concordo só porque veio de outro agent. IA tem confiança estatística, não tem razão.
Não acumulo tech debt por pressa. Cada decisão de arquitetura que eu tomo agora vira restrição do futuro. Se eu deixo um atalho sujo no Button, cada primitive depois herda a sujeira. Claude respeita a arquitetura que você estabelece. Não cria arquitetura sozinho.
Não uso IA pra desenhar em vez de mim. Eu ainda faço wireframe. Eu ainda faço audit de acessibilidade. Eu ainda penso em hierarquia, densidade, ritmo. IA não substitui essas decisões. Ela as documenta em código depois que eu as fiz.
Use cases reais que eu rodo como product designer
Deixando o touch-grass de lado, essas são as coisas que eu uso IA pra fazer no dia a dia do meu trabalho de produto:
Síntese de entrevistas de usuário. Jogo as transcrições, peço categorias por frequência e intensidade, Claude mapeia pain points recorrentes e cita as falas textuais. Depois eu valido à mão e descarto o que não bate com o que eu ouvi ao vivo. Economia de tempo: horas. Risco: baixo se você lê as citações.
Leitura profunda de PDFs densos. Paper acadêmico, regulação do governo, spec de API de terceiros. Eu rodo pelo Gemini Pro primeiro (melhor em contexto longo), peço um sumário executivo e uma lista de decisões que preciso tomar. Depois leio só as partes relevantes. A alternativa é ler tudo ou não ler nada. Ambas ruins.
Audit de copy. Cola todo o texto de uma tela, peço pra apontar: ambiguidade, jargão, passos cognitivos ocultos, termos que não batem com o glossário do produto. IA acha padrões que eu passo batido porque eu escrevi o texto e meu olho cega.
Geração de variantes pra teste A/B. A decisão de QUAL hipótese testar é minha. A geração das três redações da mesma hipótese com framings diferentes é mecânica. Delego.
Conexão Figma ↔ código. Skill próprio que encoda o Plugin API do Figma. Quando eu mudo um token, rodo uma skill que atualiza variáveis no Figma, rebinda componentes, valida com screenshots. O que antes custava uma tarde de trabalho sujo vira um comando.
Revisão de acessibilidade. Audit WCAG automático via skill a11y-audit que roda o Playwright, checa contrast ratios reais, navega por teclado, testa com screen reader simulado. IA não substitui o teste com usuário real com deficiência, mas cobre os 80% de erros básicos antes de chegar lá.
Prompting pra outras IAs. A parte meta. Quando preciso gerar mockup em Lovable, Figma Make, Paper.design. Uso um skill dedicado que transforma briefing de produto em um prompt longo e estruturado pro gerador. O output de outra IA é só tão bom quanto o prompt de entrada.
A lição que vale levar
O mito da IA substituindo designer é o de uma máquina que desenha sozinha. Não é o que acontece em alta performance. O que acontece em alta performance é um designer com gosto forte, direção clara e um sistema de trabalho onde IA faz a execução mecânica enquanto ele mantém o julgamento. O caro ficou caro. O mecânico virou commodity.
Um DS brutalista pronto em dois dias não é milagre. É método. Skills que encodam processo, memória que preserva contexto, subagents que protegem foco, testes que validam execução, revisão que garante qualidade. Nada disso substitui você decidir que Timeouts é academia social e não focus timer. Nada disso substitui você escolher brutalismo como tese visual. Nada disso substitui você saber que um Button precisa de 48px de tap target porque pessoas com tremor não acertam 40px.
Tudo isso substitui a parte do seu trabalho que você não devia estar fazendo de qualquer forma.
O código está em github.com/thiagoxikota/touch-grass. O landing do produto está em timeouts.app. Os docs vivos estão em timeouts.app/touch-grass. Se algum dos dois parece cru pra você agora, é porque é: o segundo dia é o começo, não o fim. O método continua rodando.