figma-lens: construa UI que corresponda ao Figma
figma-lens é um plugin do Figma mais uma ponte local. O usuário seleciona frames no Figma desktop e clica em Exportar para Claude. A ponte escreve uma pasta por frame:
design/
INDEX.md ← lista de todos os frames exportados (comece aqui)
<frame>/
spec.md ← LEIA PRIMEIRO: árvore de camadas compacta com valores exatos
spec.json ← dados completos (+ CSS por camada do Figma) quando spec.md não é suficiente
tokens.json ← variáveis (todos os modos), estilos, contagens de uso de valores brutos
[email protected] ← renderização completa (2x por padrão)
sections/NN-nome.png ← recortes dos blocos de nível superior em resolução total
assets/*.svg ← ícones/arte vetorial, prontos para uso
images/* ← bitmaps originais usados como preenchimentos de imagem
compare/ ← escrito por `figma-lens check` (report.md, side-by-side.png, diff.png)
figma-lens.map.json ← (raiz do projeto) componente/token do Figma → código real neste projeto
O comando figma-lens é a CLI do repositório figma-lens (npm link lá o torna global; caso contrário, execute node <figma-lens>/bin/figma-lens.js …). Execute figma-lens help para todos os comandos.
Se ainda não houver exportação
- Inicie a ponte para o projeto do usuário e mantenha-a em execução (em segundo plano):
figma-lens bridge --out <projeto>/design. - Peça ao usuário para selecionar o(s) frame(s) no Figma desktop → Plugins → Desenvolvimento → Figma Lens → Exportar para Claude.
- Leia
design/INDEX.mdpara confirmar que chegou.
Nunca adivinhe um design a partir de uma descrição quando uma exportação for possível. Peça a exportação.
Fluxo de Trabalho
- Leia
spec.mdcompletamente (é pequeno). Observe o tamanho do frame, a árvore de layout, as variáveis e estilos, e os ativos. - Veja as imagens. Leia
sections/*.pnguma a uma para obter detalhes. Use[email protected]apenas para a visão geral, porque imagens grandes são redimensionadas quando você as visualiza. Os valores sempre vêm do spec, nunca medindo pixels. - Use o mapa de componentes (veja "Mapa de componentes" abaixo). Cada
⇒ USE …em spec.md é obrigatório: use esse widget/componente e seu mapeamento de props, e use tokens mapeados ((color/primary ⇒ AppColors.primary)) em vez de valores brutos. Para itens marcados como(não no mapa), pesquise no código-fonte o tema e widgets existentes (FlutterThemeData,AppColors,TextTheme, constantes de espaçamento,lib/widgets; web variáveis CSS, configuração Tailwind,src/components). Use valores brutos apenas quando nenhum token existir, e informe ao usuário quais você adicionou. - Reutilize componentes. Uma camada marcada como
component Button {Tipo=Primário}deve usar o widget ou componente Button existente do projeto com props correspondentes. Construa um novo apenas se nenhum existir, torne-o reutilizável e adicione-o ao mapa. - Construa de cima para baixo, seguindo a árvore. Layout automático mapeia para Row/Column/flex (tabelas abaixo). Apenas camadas
@x,y(sem layout automático, ouabsolute) precisam de Stack/Positioned ou posicionamento absoluto. - Use os arquivos exportados. Coloque
assets/*.svgeimages/*na pasta de ativos do projeto e faça referência a eles. Nunca redesenhe ícones e nunca substitua por um conjunto de ícones diferente. - Copie o texto exatamente de
"…"em spec.md, incluindo maiúsculas/minúsculas, pontuação e strings em árabe/RTL. Texto com várias linhasrunprecisa de rich text (TextSpan / spans aninhados). - Verifique com o loop de comparação e correção (abaixo). Não declare uma tela como concluída apenas lendo o código; prove com um diff de screenshot.
Loop de comparação e correção
Depois de construir uma tela, compare o resultado real com o Figma e corrija as diferenças:
- Execute o build mostrando aquela tela:
- Web: inicie o servidor de desenvolvimento e abra a rota.
- Flutter web:
flutter run -d web-server --web-port 8080, depois use essa URL. - Flutter Android: execute o aplicativo em um emulador/dispositivo e navegue até a tela.
- Verifique:
- Web / Flutter web:
figma-lens check --design design/<frame> --url http://localhost:<porta>/<rota>(o viewport é definido para o tamanho do frame e escala automaticamente). - Android:
figma-lens check --design design/<frame> --adb --crop-top <px barra de status> [--crop-bottom <px barra de navegação>](barra de status ≈ 24–48 dp × densidade do dispositivo; corte-a para que apenas o conteúdo do aplicativo seja comparado).
- Web / Flutter web:
- Leia
design/<frame>/compare/report.md(% de incompatibilidade, as maiores áreas de diferença e os ids das camadas do Figma em cada uma) e vejacompare/side-by-side.png(Figma | build | diff, vermelho = diferente). - Corrija de cima para baixo: primeiro diferenças de altura ou espaçamento (elas deslocam tudo abaixo), depois tamanhos, cores, tipografia, raios, sombras. Procure cada id de camada listado em spec.md para os valores exatos.
- Execute a verificação novamente. O relatório mostra a pontuação anterior, para que você veja o progresso.
- Pare quando a incompatibilidade for ≤ 3% (o anti-aliasing de texto nunca chega a 0) ou quando duas execuções consecutivas pararem de melhorar. Em seguida, relate a pontuação final e tudo o que não pôde ser correspondido, e o porquê (fonte ausente, renderização da plataforma, dados dinâmicos).
Regras: corrija o código real, nunca manipule o diff (sem screenshots ou imagens do design dentro do aplicativo). Use os mesmos dados/texto do design ao verificar (dados simulados, se necessário), caso contrário, diferenças de texto dominam a pontuação.
Mapa de componentes
figma-lens.map.json (raiz do projeto) mapeia componentes e tokens do Figma para o código deste projeto:
{
"components": {
"Button": { "flutter": "lib/widgets/app_button.dart#AppButton", "web": "src/components/Button.tsx#Button", "props": { "Label": "label", "Type": "variant" } },
"Icon/*": "SvgPicture.asset('assets/icons/<nome>.svg')"
},
"tokens": { "color/primary": { "flutter": "AppColors.primary", "web": "var(--color-primary)" } }
}
- Nenhum mapa ainda → execute
figma-lens map --design design. Ele adiciona todos os componentes e tokens das exportações com valores vazios (""). - Preencha você mesmo: pesquise no código-fonte cada componente (por nome, por aparência, por props) e cada valor de token; escreva
caminho#Símbolo(ou uma expressão). Deixe""apenas quando nada existir ainda."Prefix/*"corresponde a uma família inteira (ícones). - Após editar o mapa, execute
figma-lens render design/<frame>para que spec.md mostre as setas⇒ USE. A ponte também aplica o mapa em cada nova exportação. - Quando você criar um novo widget reutilizável para um componente não mapeado, adicione-o ao mapa.
Lendo spec.md
- As unidades são px de design, que equivalem a px CSS e px lógicos do Flutter. Nunca multiplique pela escala do screenshot.
row|column gap G pad t,r,b,l main:X cross:Yé layout automático.pad 16significa que todos os quatro lados são 16.gap autosignifica space-between.w:FILL h:HUGfornece dimensionamento em cada eixo: FIXED (tamanho exato mostrado), HUG (envolver conteúdo) ou FILL (ocupar espaço disponível).@x,yé a posição dentro do pai.absolutesignifica um filho posicionado absolutamente dentro de um layout automático.pin H/Vfornece restrições (MIN = esquerda/topo, MAX = direita/inferior, STRETCH = ambos, CENTER, SCALE).bg A + Blista pinturas de baixo para cima.(nome)é uma variável do Figma e[nome do estilo]é um estilo do Figma.- As cores são
#RRGGBBou#RRGGBBAA(alfa por último). - O texto usa o formato
"caracteres" · Família Estilo (peso) tamanho/alturaLinha cor. alturaLinha é px, % ou auto.lsé espaçamento entre letras em px, ou em % do tamanho da fonte. radius 12define todos os cantos.radius tl,tr,br,bldefine cada canto.smoothingé suavização de cantos estilo iOS; aproxime ou ignore no Flutter, a menos que um pacote squircle esteja em uso.drop-shadow x,y blur B spread S cor,inner-shadow,layer-blur R,background-blur R.→ assets/x.svgsignifica que a camada é um ícone. Use o SVG no tamanho listado.vars{itemSpacing=space/md}significa que a propriedade da camada está vinculada a essa variável. Use o token.
Mapeamento Flutter
| Figma | Flutter |
|---|---|
| column / row | Column / Row (use spacing: G no Flutter ≥ 3.27, caso contrário SizedBox gaps) |
| wrap, rowGap R | Wrap(spacing: G, runSpacing: R) |
| pad t,r,b,l | Padding(padding: EdgeInsets.fromLTRB(l, t, r, b)). Cuidado com a ordem. |
| main MIN / CENTER / MAX / SPACE_BETWEEN | MainAxisAlignment.start / center / end / spaceBetween |
| cross MIN / CENTER / MAX / BASELINE | CrossAxisAlignment.start / center / end / baseline (baseline precisa de textBaseline) |
| HUG no eixo principal | mainAxisSize: MainAxisSize.min |
| FILL no eixo principal do pai | Expanded (ou Flexible); grow N define flex: N |
| FILL no eixo transversal do pai | CrossAxisAlignment.stretch no pai, ou SizedBox(width: double.infinity) |
| FIXED w×h | SizedBox(width: w, height: h) |
@x,y / absolute | Stack + Positioned(left: x, top: y); use pin para right/bottom |
| bg sólido | BoxDecoration(color: Color(0xAARRGGBB)). Mova alfa para a frente: #11223380 → 0x80112233. |
| gradient-linear de/para | LinearGradient(begin: Alignment(fx*2-1, fy*2-1), end: Alignment(tx*2-1, ty*2-1), colors, stops) usando from/to em spec.json |
| radius | BorderRadius.circular(r) ou BorderRadius.only(...) |
| border W cor dentro | Border.all(width: W, color: …) (dentro é o padrão do Flutter; centro/fora precisam de strokeAlign) |
| drop-shadow x,y blur B spread S | BoxShadow(offset: Offset(x, y), blurRadius: B, spreadRadius: S, color: …) |
| background-blur R | ClipRRect + BackdropFilter(filter: ImageFilter.blur(sigmaX: R/2, sigmaY: R/2)) |
| opacidade | Opacity (ou alpha de cor para pinturas) |
| text tamanho/alturaLinha | TextStyle(fontSize: size, height: lineHeight / size). Para %: height: pct/100. Defina leadingDistribution: TextLeadingDistribution.even para corresponder ao centralizamento vertical do Figma. |
| weight 600 | FontWeight.w600 |
| ls px / ls % | letterSpacing: px / letterSpacing: size * pct / 100 |
| ellipsis maxLines N | maxLines: N, overflow: TextOverflow.ellipsis |
| align CENTER | textAlign: TextAlign.center. Para telas RTL, use TextAlign.start e EdgeInsetsDirectional. |
| assets/x.svg | SvgPicture.asset('assets/…/x.svg', width:, height:) (flutter_svg) |
| image fill FILL / FIT | Image.asset(fit: BoxFit.cover / BoxFit.contain) |
Mapeamento Web (CSS / Tailwind / React)
| Figma | CSS |
|---|---|
| column / row | display:flex; flex-direction: column / row; gap: Gpx |
| wrap, rowGap R | flex-wrap: wrap; column-gap: G; row-gap: R |
| pad t,r,b,l | padding: t r b l (mesma ordem do CSS) |
| main MIN / CENTER / MAX / SPACE_BETWEEN | justify-content: flex-start / center / flex-end / space-between |
| cross MIN / CENTER / MAX / BASELINE | align-items: flex-start / center / flex-end / baseline |
| HUG | width/height: fit-content (ou apenas auto) |
| FILL (eixo principal) | flex: 1 1 0; min-width: 0 |
| FILL (eixo transversal) | align-self: stretch |
| FIXED | width: Wpx; height: Hpx; flex-shrink: 0 |
@x,y / absolute | pai position: relative; filho position:absolute; left:x; top:y (use pin para right/bottom) |
cores #RRGGBBAA | CSS válido como está |
gradient-linear Ndeg | linear-gradient(Ndeg, c1 p1%, c2 p2%) |
| drop-shadow | box-shadow: x y B S cor (inner-shadow adiciona inset) |
| layer-blur / background-blur R | filter: blur(R/2 px) / backdrop-filter: blur(R/2 px) |
| text | font: weight size/lineHeight family; letter-spacing (ls % → pct/100 em) |
| ellipsis maxLines N | display:-webkit-box; -webkit-line-clamp:N; -webkit-box-orient:vertical; overflow:hidden |
| assets/x.svg | <img src> ou SVG inline; mantenha o tamanho exportado |
spec.json também contém o CSS gerado pelo próprio Figma (css) para a maioria das camadas. Use-o para verificar cruzadamente os valores CSS; tokens e estrutura de layout ainda vêm da árvore acima.
Regras
- Valores do spec superam o que você vê na imagem. Quando houver conflito, confie no spec e mencione o conflito.
- Não arredonde ou "melhore" valores (14 permanece 14, não 16) a menos que o usuário peça normalização para tokens.
- Camadas ocultas não são exportadas. Não invente estados (hover, erro, vazio) que não estejam na exportação; peça esses frames.
- Sinalize fontes ausentes. Se a família de fontes não estiver incluída no projeto, informe e diga ao usuário o que adicionar (fontes pubspec / @font-face).
- Para vários breakpoints ou estados, peça ao usuário para exportar cada frame. Cada um se torna sua própria pasta.