Skip to content

DesignSystem pt BR

Edgar Mesquita edited this page Aug 14, 2026 · 4 revisions

Photon Design System

🌐 Esta página em: English · Português

O design system por trás dos componentes write-once: uma especificação completa de tokens/componentes (preservada no repositório em docs/design/Photon-Design-System.dc.html, a fonte da verdade) implementada como tokens C# tipados em eQuantic.UI.Primitives e consumida por todos os alvos de realização.

Camadas de tokens (§01–§09)

  • Cores: toda cor é um ColorToken par claro/escuro; componentes nunca guardam cores cruas. Variantes interativas resolvem cinco sub-tokens (Base, OnBase, Pressed, que é um token real e não uma sobreposição, Subtle, OnSubtle). Desabilitado não é uma cor: é um grupo com 38% de opacidade.
  • Escala tipográfica: dirigida por papéis (DisplayBodyM, Caption, Label) com clamps de Dynamic Type por papel, mais a ponta densa onde todo chrome de desktop vive: TitleSmall (15/700, o degrau entre Label 13 e Title 20), LabelSmall (11, para trilhos de status, leituras de inspetor, legendas de espécime) e Overline (10/800, com tracking, a sobrancelha em maiúsculas acima de um grupo). Uma escala que para em 12dp é uma escala de toque; uma sidebar, uma toolbar e uma barra de status vivem todas abaixo dela, e sem esses degraus cada uma sai um quinto grande demais. Um estilo também carrega sua face: TypeStyle.Mono seleciona a monoespaçada (a do próprio sistema, SF Mono num Mac), e TypeStyle.Italic o corte inclinado (desde a 0.2.0-preview.28 é um EIXO, então negrito itálico e código itálico existem os dois), que o medidor, o rasterizador e o cache de raster honram porque já chaveiam pelo estilo.
  • Caixas de linha seguem o tamanho: TypeStyle.WithSize(size) escala a altura de linha na mesma proporção (o line-height sem unidade do CSS), e TypeStyle.OfSize(size, weight) deriva uma no 1.25× tipográfico. Remendar só o tamanho deixa um glifo maior na caixa antiga, e é assim que uma descendente termina fora da linha dela.
  • Espaçamento (Space.S1S16, base 4dp, pertencente ao gap, margem não existe), raio (Radius.XsFull, clampado pelo engine), tamanhos de ícone (whitelist §07 16/20/24/32; tamanhos arbitrários lançam), toque (contrato de alvo ≥48dp), elevação (uma sombra analítica por nó), movimento (durações + curvas + mola).

Densidade: o mesmo botão, 32dp sob um polegar e 26dp sob um mouse

Density (Comfortable | Compact) é uma propriedade do alvo, nunca do local da chamada: um app nunca pede um botão menor no Mac; o Mac é que pede um app mais denso. É a densidade do Material e o control size da Apple, e é o que mantém UMA árvore honesta num telefone e num desktop.

Sizing.Height, PaddingX, LabelSize e HitTarget resolvem por ela (Small 32→26, Medium 40→32, Large 48→40, XLarge 56→48), e a escada de seleção também (SwitchWidth/SwitchHeight/ SwitchThumb/SwitchTravel, SelectionBox, RadioDot). Sob um ponteiro o alvo de toque para de inflar: o mínimo do §08 é um contrato de dedo, e numa toolbar de botões de 26dp aquelas margens invisíveis se sobreporiam umas às outras.

Ela chega aos componentes por ComponentContext.Density, a mesma porta pela qual o tema chega, então nenhuma assinatura de Build mudou. Quem decide é o alvo: o shell do macOS diz Compact; o boot do web lê (pointer: fine).

A escada vive na camada de tokens, sempre. Um componente nunca guarda uma cópia privada da própria tabela de tamanhos, porque uma cópia não consegue seguir a densidade que o alvo pede. Sizing.Avatar(size) e os degraus de seleção existem para que quem chama leia o mesmo número que o componente desenha.

Fidelidade à spec é testada, não desejada

  • Valores de token são pinados; o contraste WCAG é recalculado nos testes para cada par afirmado.
  • Métricas de componente vêm das tabelas da spec (ex.: a tabela de tamanhos do Button: alturas 32/40/48/56) e são afirmadas em todos os eixos: pins do realizer web em C#, imagens golden nativas (claro + escuro), execução de fixture transpilada no vitest.
  • As regras do resolvedor de estilo (variantes derivadas Outline/Ghost/Link, pressed como troca de token, anel duplo de foco) vivem em C# neutro de alvo (ButtonStyles).

A regra "gerado, nunca escrito à mão"

Artefatos client-side que carregam valores do design system são gerados da fonte única em C# e pinados byte a byte na CI (um desvio falha o build; fluxos por variável de ambiente regeneram):

Artefato Gerador Regenerar com
Stylesheet normativo (custom properties, .eq-type-*, .eq-elevation-*, vars de motion) PhotonCssGenerator (função pura, testada por valor)
design-system.generated.ts (tokens, objeto de tema, tabela de tamanhos do Button) DesignSystemTsGenerator EQ_UPDATE_DESIGN_TS=1
icons.generated.ts (dados de path dos glifos) IconTsGenerator EQ_UPDATE_ICONS_TS=1
Módulos de componente transpilados embarcados saída viva do eqc EQ_UPDATE_TRANSPILED=1

A mesma regra sustenta a hidratação: o SSR (realizer C#) e o lowering do cliente (TS) são mantidos byte-idênticos por literais cross-pinadas nas duas suítes de teste.

Pipeline de ícones (spec A10)

Um enum Icons curado (23 glifos e crescendo: Menu, o gatilho de navegação do shell compacto, chegou na 0.2.0-preview.26) com os dados de path vivendo uma vez no IconRegistry em C# (máscaras alpha de path única 24×24): o web emite <svg fill="currentColor"> inline (o tom monta no token de cor exatamente como o texto faz), o lado TS consome o módulo gerado, e o futuro atlas de glifos nativo rasteriza do mesmo registro. Outline ↔ preenchido são glifos distintos; ícones ignoram Dynamic Type.

Estilo sem CSS: o engine atômico

A autoria é 100% C# tipado (BoxStyle, StyleDiff, tokens), e não existe um plano CSS. Três leis governam a realização web:

  1. Uma semântica: o mesmo vocabulário de estilo dirige valores dp no Photon e CSS no web.
  2. Sem teto de expressividade: Wrap, Grid, AspectRatio, AlignSelf, Opacity de grupo, Transform2D estático, diffs de estado Hover/Focus, adaptatividade por classe de tamanho de janela (AdaptiveNode), Sticky, ZIndex, scroll nos dois eixos.
  3. Sem reescrita por construção: toda declaração regular desce para UMA classe atômica deduplicada (eq- + hash FNV-1a de prop:valor); cores de tema referenciam var(--eq-color-*); o SSR junta as regras da página num único <style id="eq-atomic"> que o cliente adota por identidade (hidratação = igualdade de classe, garantida de ponta a ponta pelo teste e2e de identidade). ~118 regras / 4,8 KB cobriram o showroom inteiro.

Pseudo-estados descem para regras atômicas de pseudo-variante (:hover/:focus-visible, zero JS); classes de tamanho para classes fixas com media gate. O StyleAtomizer em C# e o style-atomizer.ts em TS são gêmeos byte-idênticos (fixture cross-pinada).

Tema = fornecer um IAppTheme

Uma interface (cores como pares ColorToken claro/escuro, papéis de tipo, escala de forma, specs de elevação, opacidade de desabilitado), selecionada via AddUI(...).UseTheme(...) e transportada do SSR para o cliente.

Trocar claro/escuro em tempo de execução é um SERVIÇO: IThemeController (Mode + Apply) é tomado por construtor como qualquer capacidade, e o componente nunca aprende qual alvo respondeu. O runner nativo repinta a janela contra a outra paleta; o browser vira uma declaração color-scheme (a paleta web inteira é autorada como pares light-dark(), então a troca não custa re-render). ITextClipboard é oferecido do mesmo jeito para o botão de copiar de uma página. Duas implementações acompanham: PhotonTheme (o padrão) e MaterialTheme (Material 3 de verdade: baseline MaterialTheme.Instance ou cor dinâmica FromSeed(cor) via um port CAM16/HCT validado contra os valores do Google). A forma é dirigida pelo tema; a mesma árvore de componentes se rebrandiza com uma linha.

Clone this wiki locally