UI Quality Score (UQS)
design specs/design/ui-quality-score.kmd
Índice composto 0–100 da qualidade de UI de uma surface (tela/form/ícone/ layout/landing): 5 pilares (Efetividade/Eficiência/Satisfação/Visual/A11y) com pesos por profile, sinais AUTO determinísticos, gate duro de contraste e bandas Sauro–Lewis. Implementado no core puro `engines/sdk/koder_ui_score` + harness `tools/ui-score` (CLI `ui-score`, runner `run-headless.py`); rodável por `/k-ui-score <url>`. Este spec é a fonte normativa da lógica. Funda: stack-RFC-030.
Quando esta spec se aplica
Todos os triggers
- Medir, pontuar ou auditar a qualidade de UI de uma surface (tela/form/landing/ícone): nota, contraste, tap-target, acessibilidade, estética
- Validar antes/depois um polish de UI (a nota deve subir)
- Definir alvo de UQS / banda para uma surface ou profile
Corpo da especificação
Regra: Cálculo do UI Quality Score (UQS) de uma surface Koder
Usar este spec sempre que for necessário medir, pontuar ou auditar a qualidade de UI de uma
surface Koder (tela, formulário, ícone, layout, landing). O modelo é o implementado no core puro
engines/sdk/koder_ui_score + harness tools/ui-score — este arquivo é a fonte normativa da
lógica. É o irmão de medição do /k-arch (arquitetura) e do brand-score.kmd (nomes).
Visão geral
O UQS é um número 0–100 (maior = melhor) que compõe 5 pilares, cada um também 0–100 no mesmo sentido. Os três primeiros são os eixos de usabilidade da ISO 9241-11; os dois últimos são as dimensões AUTO-mensuráveis a partir de um render estático:
| Pilar | Mede | Fonte |
|---|---|---|
| Efetividade | conclusão da tarefa, ausência de erro | task-flow (JUDGE / não-estático) |
| Eficiência | esforço/tempo (Fitts/Hick/KLM) | task-flow (JUDGE / não-estático) |
| Satisfação | percepção, estética percebida | survey (SURVEY / não-estático) |
| Visual | estética computável (forma + disciplina + cor + clutter) | AUTO (geometria + pixels) |
| A11y | acessibilidade WCAG AUTO | AUTO (DOM + pixels) |
R1 — Composição. O UQS é a média ponderada dos pilares fornecidos sob os pesos do profile, renormalizada sobre os pilares presentes (scoring parcial): pilares não medidos numa surface estática — Efetividade/Eficiência/Satisfação — são simplesmente omitidos, não pontuam 0.
R2 — Cap de gate duro. Qualquer falha de gate duro (ver R6) capa o UQS em ≤ 59
(GateCap = 59.0), mesmo que a média ponderada seja maior. O gate nunca é "diluído" na média.
Pesos por profile (seed v1)
R3 — Pesos. Cada profile reponta os pilares; cada linha soma 1.0 (RFC-030 §5 / síntese §4):
| profile | Efet. | Efic. | Satisf. | Visual | A11y |
|---|---|---|---|---|---|
screen | 0.25 | 0.22 | 0.18 | 0.17 | 0.18 |
form | 0.22 | 0.20 | 0.18 | 0.10 | 0.30 |
icon | 0.05 | 0.05 | 0.05 | 0.75 | 0.10 |
layout | 0.20 | 0.18 | 0.10 | 0.37 | 0.15 |
landing | 0.15 | 0.12 | 0.20 | 0.38 | 0.15 |
São seed v1 (síntese 07); calibração PCA contra surfaces reais é meta#387 slice 1 (baseline
reprodutível já coletado em tools/ui-score/calibration/).
Pilar Visual — blend dos sinais (síntese §3.3)
R4 — Visual. O pilar Visual é o blend dos sinais computados, com os pesos renormalizados sobre os sinais medidos:
Visual = 100 · ( 0.45·NgoOM + 0.25·discipline + 0.15·(1−clutter) + 0.15·colorfulnessFit ) / Σ(pesos presentes)
- NgoOM — roll-up Ngo, Teo & Byrne 2003 (12 das 13 medidas-componente; rhythm pendente).
As medidas Ngo (balance/symmetry/proportion) operam sobre o canvas da composição = o
bounding box do conteúdo, NÃO o viewport: surface rolável tem conteúdo além da dobra (o
cockpit: linhas até y≈6900 num viewport de 757px), e medir peso contra o frame do viewport dá
resultado sem sentido (CoM offset 763%, ngo deprimido). O harness usa
max(viewport, bbox). - discipline — média de aderência grid-8pt / type-scale / vertical-rhythm.
- clutter — edge-density (Sobel; Mack–Oliva). Entra como
1−clutter. - colorfulnessFit — Hasler–Süsstrunk M3 mapeado a um ótimo moderado (drab E caótico pontuam baixo).
Contraste NUNCA entra aqui — é gate de A11y, jamais média.
Pilar A11y — checagens AUTO + gate (compêndio ch.06 §7)
R5 — A11y = cobertura. O pilar A11y é a fração de elementos aplicáveis que passam somando todas as checagens AUTO (coverage %): contrast (#1), target-size (#6, exceção 2.5.8 inline-text), accessible-name (#2), form-label (#5), focus-visible (#9), page-language (#14), landmark-main + heading-order (#15, condicionais). Checagem sem elementos aplicáveis é excluída.
R6 — Gate duro de contraste (G-AA). Qualquer texto abaixo de WCAG 2.x AA — 4.5:1 normal, 3.0:1 large (≥24px ou ≥18.66px bold) — dispara o gate → UQS capado em ≤59 (R2). O gate é a única checagem dura no v1; as demais são dedução suave de cobertura. Tap-target mínimo = 24×24px (2.5.8), exceto links de texto inline.
Bandas de rating (Sauro–Lewis; ch.03 + ch.07 §6)
R7 — Bandas. 68 = âncora neutra SUS.
| UQS | Banda |
|---|---|
| ≥ 90 | A+/A |
| ≥ 80 | A-/B+ |
| ≥ 68 | B/C+ |
| ≥ 51 | C/D |
| < 51 | F |
Como medir
R8 — Rodar o harness. Medir via /k-ui-score <url> (ou direto: run-headless.py →
ui-score). O render headless é trabalho pesado → off-laptop (heavy-work-isolation.kmd).
Read-only: medir não muta o repo.
R9 — Pontuar AMBOS os temas, pegar o pior. A nota é tema-específica (o contraste muda por
claro/escuro). Surfaces com tema claro/escuro DEVEM ser pontuadas nos dois temas
(run-headless.py --theme light|dark); a nota da surface = a do pior tema (ambos são
user-facing). Evidência: na calibração 2026-06-29 o pior tema variou por surface (flow-login pior no
claro, www.koder.dev pior no escuro).
R10 — Surface atrás de auth / canvas. Surface atrás de auth (Koder ID OAuth) precisa de token de
sessão injetado (owner-decision). App canvas/Flutter/Kroma (GPU) tem DOM vazio → só o pilar
Visual (pixels: clutter + colorfulness) é medível (Mode:"pixel-only"); A11y/comportamentais
ficam não-medidos (precisam da introspecção da plataforma — kroma#088).
Tier JUDGE/SURVEY — os 3 pilares comportamentais (RFC-030 §9 slice 3)
Os pilares Efetividade, Eficiência e Satisfação (ISO 9241-11) não saem de um snapshot estático. Dois caminhos:
R11 — Eficiência é AUTO (preditiva). Computável sem juiz, via KLM/Fitts (compêndio ch.05):
EfficiencyScore = T_KLM(caminho_ótimo) / T_KLM(caminho_real) ∈ [0,1] — a UI é eficiente na medida
em que mantém o usuário perto do caminho de esforço mínimo do objetivo
(engines/sdk/koder_ui_score::EfficiencyPillar). O harness deriva os fluxos do descriptor para
o perfil form (tools/ui-score deriveEfficiency): tarefa canônica = completar o formulário —
caminho real = point-and-click Fitts por cada controle na ordem de leitura (distância/largura
reais); caminho ótimo = o mínimo por teclado (Home + 1 Tab por controle). Como o ótimo é
constante para uma contagem fixa de controles, a razão isola a qualidade do layout (tamanho/
proximidade dos alvos). É AUTO (não marca Mode:"full", que é do tier JUDGE) e entra no composto
sempre que derivável; um fluxo gravado explícito (--judge com efficiency) tem precedência. Outros
perfis não têm tarefa canônica → Eficiência fica ausente e o composto re-normaliza (R1/R13).
R12 — Efetividade e Satisfação são JUDGE (juízo-IA). Pontuadas por um AI-judge
(/k-meta-judge), 0–100, contra a rubrica abaixo — não por fórmula. O juiz resolve surface→spec
via registries/spec-triggers.kmd e pontua com evidência citada:
| Pilar | O que o juiz avalia (grounded) | Fonte |
|---|---|---|
| Efetividade | a UI permite concluir a tarefa sem erro? prevenção de erro, mensagens úteis, recuperação/undo, estados vazios/carregando claros | Nielsen H5/H9 (ch.02), ISO 9241-110 §6 (use error robustness), specs/errors/* |
| Satisfação | a UI é agradável e confiável de usar? estética percebida (aesthetic-usability), consistência, voz/tom, ausência de atrito | Tractinsky aesthetic-usability (ch.04 §5), Nielsen H4/H8, specs/themes/verge.kmd |
R13 — Composição dos 5 pilares. Os scores JUDGE/SURVEY entram no composto via os pesos por
profile (R3); o UQS re-normaliza sobre os pilares presentes (R1). O harness ingere os scores
comportamentais (ui-score --judge judge.json → {effectiveness, efficiency, satisfaction} 0–100)
e compõe Mode:"full"; sem eles, Mode:"dom" (só os 2 AUTO, re-normalizado). Reportar quais
pilares foram medidos — uma nota de 2 pilares e uma de 5 não são a mesma coisa.
R14 — Protocolo de juízo (produtor/consumidor, model-agnóstico). O harness não embute cliente de IA (acoplaria a um endpoint + credenciais + não-determinismo). Em vez disso emite um pedido de juízo autocontido e ingere a resposta:
- Produtor:
ui-score --emit-judge-request <desc.json> [shot.png]→judge-request.json(schemauqs-judge-request/v1) com a rubrica R12 verbatim + resumo da surface (profile, frame, contagens, outline de headings, nomes interativos) + o response_schema exato. O screenshot acompanha para o eixo estético da Satisfação. - Juiz: qualquer AI-judge preenche —
/k-meta-judge, Kortex, um cron, ou humano — pontuando 0–100 com evidência citada, resolvendo surface→spec viaspec-triggers.kmd. - Consumidor:
ui-score --judge judge.jsoningere{effectiveness, satisfaction[, efficiency]}(campos de evidência extras são ignorados) →Mode:"full".
Mantém o harness determinístico/testável e o tier de IA plugável (headless-first, reuse-first).
Aplicação
- Achou falha no gate (R6): consertar na camada certa — texto em token accent/muted que
falha AA → trocar pelo role token
--on-*do Verge (specs/themes/verge.kmd), não a cor crua (classe TRIAGE-027). Re-pontuar: o gate deve limpar. - Antes/depois de polish: pontuar nos dois estados; a nota deve subir (RFC-030 §9 slice 6).
- Informative-first → CI-gate: o UQS é informativo agora; vira gate de CI por surface após soak
(RFC-030 §11). Conserto de artefato =
/k-go; conserto da regra =/k-meta-tune.
Referências
meta/docs/stack/rfcs/stack-RFC-030-ui-quality-score.kmdmeta/docs/hci/compendium-hci/07-sintese-uqs.kmdengines/sdk/koder_ui_scoretools/ui-scoremeta/context/commands/k-ui-score.mdmeta/docs/stack/specs/naming/brand-score.kmd