Pular para o conteúdo
Challenge 18

Revisão profunda de UI · Medições próprias

Design

data_criacao 2026-09-10desafio C18

Relatório bruto de uma das sete lentes. A nota consolidada, com os achados agrupados por causa e já verificados, é Revisão Profunda de UI - 2026-09-10.

Achados medidos por mim (não por agente)

Base: medicoes-cor.txt (script determinístico, medir.py) e leitura direta do fonte. Tudo aqui é confirmado, não inferido.


FT-01 · O fio de 1px, que sustenta a doutrina de profundidade, está em 1,2:1

Onde: Bancada/tokens.jsonpapel.borda e papel.divisor (neutro.3 no claro, neutro.10 no escuro) Superfície: tokens · Gravidade: alta · Confiança: confirmado (medido)

O Sistema de Design declara: "Profundidade vem de camada e de fio." A revisão anterior registrou que os 3:1 de componente nunca foram medidos. Medidos agora, contra as seis superfícies, nos dois esquemas:

fundosuperficiesuperficieSutilcromofolhadado
borda/divisor · claro1,211,241,131,131,241,13
borda/divisor · escuro1,321,241,141,141,241,14

Doze de doze abaixo de 3:1. Nenhuma chega à metade do alvo.

Ressalva honesta: o separador nativo do macOS opera em faixa parecida (separatorColor fica por volta de 1,2:1 no claro), então isto não é um desvio da plataforma. O que torna o número um achado aqui é a doutrina: neste sistema o fio não decora, ele é a separação — ver FT-02.

Correção: não é engrossar o fio. É reconhecer que borda e divisor hoje são o mesmo papel com dois nomes, e separá-los: divisor (estrutural, entre painéis) precisa de um passo mais contrastado que borda (contorno de peça). Falta um papel, não um hex.


FT-02 · No claro, a elevação por camada praticamente não existe

Onde: Bancada/tokens.jsonpapel.fundo/superficie/folha Superfície: tokens · Gravidade: alta · Confiança: confirmado (medido)

Diferença de lightness perceptual (OKLCH) entre as superfícies que deveriam formar camadas:

parclaroescuro
fundo × superficieΔL 0,87 (1,03:1)ΔL 3,71 (1,06:1)
fundo × cromoΔL 2,14 (1,06:1)ΔL 7,15 (1,15:1)
superficie × cromoΔL 3,00ΔL 3,44

superficie e folha são o mesmo passo; superficieSutil, cromo e dado também — isso já é decisão registrada e não é achado. O achado é outro: a elevação no claro vale um quarto da elevação no escuro (ΔL 0,87 contra 3,71). #FFFFFF sobre #FCFCFD é imperceptível.

Isto é a causa do que o V-07 anotou como sintoma ("a folha do Diário quase não se separa"). Não é a folha: é que no claro a camada não faz trabalho nenhum, e o fio — que por FT-01 está em 1,2:1 — fica sozinho sustentando a doutrina inteira. Duas peças fracas empilhadas, cada uma contando com a outra.

Correção: no claro, fundo deveria descer um passo (neutro.2) ou superficie subir, para que a camada tenha ΔL comparável ao do escuro. Custa um papel remapeado, zero hex novo.


FT-03 · A janela mínima declarada é 220pt menor do que a tela mais larga precisa

Onde: Bancada/Sources/Bancada/JanelaPrincipal.swift:16, Telas/TelaDiario.swift:45,56,92, Telas/TelaAcervo.swift:58,66 Superfície: app · Gravidade: alta · Confiança: confirmado (aritmética do fonte)

A revisão anterior deixou isto como suspeita: "o comportamento de HSplitView perto do mínimo não foi testado — e V-02 sugere que é onde mais quebra." É aritmética:

telapainéis (minWidth)soma
Diáriolista 160 + folha 320 + fatos 260740
Acervograde 2×180 + detalhe 320680
garantido.frame(minWidth: 520) na coluna de detalhe520

O Diário pede 740 e a moldura garante 520: déficit de 220pt. Somando a barra lateral (min 180), o mínimo real do app é 920, e a janela abre em 1080 — restam 160pt de folga antes de a tela mais larga começar a ser espremida.

Pior: Sources/Bancada/main.swift:24 cria a NSWindow sem contentMinSize. A ausência do contentMinSize é fato do fonte; a consequência — se o NSHostingView deixa arrastar abaixo dos 520 do SwiftUI — está marcada como a verificar na tela, e é o primeiro teste que eu faço com o app aberto. Se deixar, o setFrameAutosaveName("BancadaPrincipal") restaura o tamanho encolhido na abertura seguinte e o estado quebrado vira permanente.

Correção: contentMinSize na NSWindow com o número real (≈920×400), e os minWidth internos revistos para caber nele. O número tem que sair da soma, não de estimativa.


FT-04 · O âmbar no claro é marrom, e as seis matizes não têm peso comparável

Onde: Bancada/tokens.jsonprimitivo.ambar.profundo e as seis matizes Superfície: tokens · Gravidade: média · Confiança: confirmado (medido)

Em OKLCH, os passos profundo (os que pesam sobre superfície clara):

matizLC
azul51,80,198265
vermelho54,00,17729
violeta50,00,138296
verde51,50,112156
âmbar52,90,10481
turquesa53,00,087204

A lightness está bem controlada (50,0–54,0, faixa de 4 pontos). A chroma varia 2,3×. O azul do acento é muito mais saturado que suas irmãs, e a turquesa — que é a cor de commit, o tipo de fato mais frequente do vault — é a mais fraca das seis.

O âmbar #8A6410 confirma a suspeita: matiz 81° é amarelo-alaranjado, mas a L de 52,9 com C de 0,104 é exatamente a receita de oliva/marrom. Amarelo escurecido não fica âmbar, fica terra. No escuro o mesmo matiz a L 76,0 lê âmbar de verdade.

Nos passos luz, a L varia 69,0 (azul) a 76,0 (âmbar) — 7 pontos. No escuro, em-andamento (azul) lê visivelmente mais pesado que concluida (verde) no mesmo papel.

Correção: normalizar as seis por L e C alvo em OKLCH em vez de por hex escolhido a olho. O âmbar do claro precisa perder escuridão e ganhar chroma para sair do marrom.


Não-achado, verificado e descartado

Deriva de matiz na rampa neutra. Medi: H° passeia entre 259° e 286° ao longo dos 14 passos. Parece muito, mas a chroma máxima da rampa é 0,0146 (passo 6) e nos extremos cai para 0,0013 — em chroma dessa ordem o matiz é matematicamente instável e opticamente invisível. A rampa é limpa. Registro para que ninguém volte a medir isto achando que achou algo.

Progressão da rampa. Os passos de ΔL vão de 0,87 nas pontas a 11,8 no meio. É irregular de propósito e está certo: as pontas são onde moram as superfícies e precisam de controle fino; o meio só precisa atravessar. Mesma estratégia das rampas do Radix. Não é achado.


FT-05 · Sete dos dezessete tamanhos fora de escala estão dentro do próprio Design System

Onde: Sources/DesignSystem/Componentes.swift:373,445,458,537,573,622 e TextoDeNota.swift:171 Superfície: app · Gravidade: alta · Confiança: confirmado (grep + leitura)

A revisão anterior contou "16 .font(.system(size:)) cru" e tratou como desvio das telas. Contei de novo: são 17, e a distribuição é o que importa.

camadaocorrênciastamanhos introduzidos
Sources/DesignSystem/728, 12, 11, 11, 10, 7, 10
Sources/Bancada/Telas/1020, 14, 10, 11, 9, 10, 7, 9, 9, 10

Isto não é tela driblando o sistema. É o sistema não obedecendo a si mesmo: Componentes.swift é a camada que define a escala 22/15/13/11/10 e escreve size: 28, size: 12, size: 7 dentro dos próprios componentes. Etiqueta, ChipRemovivel, MenuDeFiltro e SeletorSegmentado — peças que toda tela consome — carregam o desvio para dentro de quem as usa corretamente.

Nenhum dos 17 passa pelo overload View.font(_ estilo:), então nenhum recebe o tracking que a escala define. Um size: 11 cru e o estilo detalhe (11pt, tracking 0) são quase iguais; um size: 10 cru e rotulo (10pt, tracking 0,6, caixa alta) não são a mesma coisa de jeito nenhum.

Correção: a ordem certa é de dentro para fora — consertar os 7 do DS primeiro. Enquanto o vocabulário desobedecer, cobrar obediência das telas é cobrar do lado errado. E os dois size: 7 (o X do ChipRemovivel, o puxador do calendário) não têm passo correspondente na escala: ou a escala ganha um passo de glifo, ou os dois elementos estão pequenos demais para existir — ver a lente de acessibilidade, que mede o alvo de ponteiro deles.


FT-06 · No Acervo, selecionar e abrir são gestos de camadas diferentes, e a instrução some na hora que serve

Onde: Sources/Bancada/Telas/TelaAcervo.swift:51 (seleção), :131 (abertura), :198-202 (a instrução) Superfície: app · Gravidade: média · Confiança: confirmado

O clique simples que seleciona está no pai, na grade (:51, .onTapGesture { selecionada = midia.id }). O clique duplo que abre está dentro do componente (:131, .onTapGesture(count: 2)). Mesmo cartão, dois gestos, dois donos — e o cartão não sabe que é selecionável.

O consequente é que o CartaoDeMidia não pode sinalizar nada: não tem hover, não tem cursor de ponteiro, não tem estado pressionado. A única affordance é o anel de acento depois que já aconteceu.

E a instrução — "Clique num item da grade para ver o detalhe. Duplo clique abre no app do macOS" — vive no estado vazio do painel de detalhe (:198-202), que desaparece no instante em que você seleciona o primeiro item. Ou seja: o texto que ensina o duplo clique só é visível para quem ainda não descobriu o clique simples, e some para sempre assim que descobre.

Correção: os dois gestos pertencem ao cartão. Com ele dono do próprio estado, hover e pressionado passam a ser possíveis — e aí a affordance não precisa ser escrita em lugar nenhum.