Kroma Modality Model
kroma specs/kroma/modality-model.kmd
O modelo conceitual de UIObject do Kroma — north star da UI da Koder Stack. Uma UI **não** é um conjunto de "elementos por sentido"; é uma **árvore de intenção** que se **projeta** em canais de modalidade conforme o hardware do device. Duas correções fundam o modelo: (1) organiza-se por **papel × modalidade** (não pelos "5 sentidos"), e o papel inclui **entrada** — que o modelo de sentidos omite; (2) uma modalidade de UI é um canal **transdutor** (sinal codificado → percept fiel e reversível), não **atuação no mundo** (assar um bolo não é renderizar um sabor). Visual/áudio/háptico são v1; olfato/gosto são extension point de pesquisa, não alvo. Emergente (S1 do epic kroma#292); vira normativo quando S2/S3 landarem.
When this spec applies
All triggers
- Criar/classificar um objeto de UI (widget) do Kroma ou definir a que modalidade ele projeta
- Projetar suporte a um canal de saída/entrada novo (áudio, háptico, voz, gaze) numa UI Koder
Specification body
Spec — Kroma Modality Model
A reformulação central. "Quais grupos de elementos existem numa UI?" tem uma resposta popular — "um por sentido humano" — que é boa como intuição e ruim como engenharia. Uma UI é uma árvore de intenção (o que o usuário quer fazer e o que o sistema quer comunicar); a modalidade — visão, som, tato — é como essa árvore é projetada no device. Separar as duas é o que torna "UI para vários sentidos" alcançável em vez de aspiracional — e é, não por acaso, a mesma doutrina do
headless-first(lógica separada de render) e do KDX (significado é a fonte, superfície é projeção).
1. Dois eixos ortogonais (não "5 grupos")
Um UIObject é classificado por dois eixos independentes:
Eixo A — Papel (o organizador primário)
| papel | o que faz | exemplos |
|---|---|---|
| container | agrupa/posiciona outros objetos | Form, Panel, Card, Column, Row, ToolBar |
| output | apresenta um percept ao humano | Text, Image, Chart, VideoPlayer, (áudio: Tone, Speech) |
| input | captura ação do humano | Button, Edit, Slider, Checkbox, Select |
| navigation | move entre destinos | Menu, TabStrip, back/ESC, link/intent |
Correção ao modelo de sentidos: os exemplos de classe do brainstorm (
Button,Edit,Slider) são papéis de entrada/interação — organizados por função, não por sentido. O modelo de sentidos só enxerga saída (percepção humana) e não tem lugar para entrada — que é a metade mais difícil de um framework de UI. Papel é o eixo que organiza; sentido/modalidade é atributo do eixo B.
Eixo B — Modalidade (atributo do canal)
Modalidade é por qual canal o papel se realiza. É ortogonal ao papel: um output
pode ser visual ou auditivo; um input pode ser ponteiro ou voz.
- Saída (transdutor):
visual·auditivo·háptico— v1.olfativo·gustativo— extension point (hardware de pesquisa existe; não é alvo de v1). - Entrada:
ponteiro·toque·teclado·voz·gaze/câmera·caneta.
Háptico não é um canal, são vários: vibrotátil, força (force-feedback) e térmico são atuadores distintos. Um objeto háptico declara o sub-canal; não existe "o" háptico genérico.
2. Modalidade de UI é transdução, não atuação (o limite honesto do modelo)
Uma modalidade de UI é um canal transdutor: o software escreve um valor num canal endereçável e o hardware reproduz o percept de forma fiel e reversível (o pixel acende e apaga; o alto-falante emite e silencia; o motor vibra e para).
Não é modalidade de UI a atuação no mundo físico. O exemplo do brainstorm — um
robô que assa um bolo com sabor de limão — é atuação/robótica, não um display que
renderiza o percept "limão": não há canal onde taste = lemon seja escrito,
reproduzido fielmente e desfeito. Displays olfativos/gustativos de pesquisa são
transdutores (por isso olfato/gosto entram como extension point, não ficção); "fazer
um bolo" não é. Regra: a superfície de UIObject cobre transdução; atuação no mundo
é outro domínio (robótica/IoT), integrado por API, não por widget.
3. Projeção e headless — por que "todos os sentidos" é alcançável
A árvore de UIObject é modalidade-agnóstica. Quem escolhe o canal é o backend de projeção (paint), plugável. O Kroma já é assim: retained-mode com árvore reconciliada + backend único (wgpu/Vello) trocável por plataforma. Consequências:
- Adicionar uma modalidade = adicionar um backend de projeção + semântica de widget, sem tocar a árvore. É por isso que a ambição "UI para vários sentidos" é viável: a arquitetura já separou árvore de pintura.
- Headless é uma projeção como qualquer outra — a projeção nula/introspecção
(
src/introspect.rs+semantics.rs). Isso realiza oheadless-firstpor construção: toda UI tem caminho headless porque headless é só mais um backend. - Acessibilidade É reprojeção de modalidade. Um leitor de tela reprojeta a árvore
visual em áudio;
forced-colorsreprojeta a cor. Os perfis a11y (piso WCAG 2.2 AA da Stack) são o mesmo mecanismo deste modelo — não um anexo. Todaoutputvisual SHALL carregar a semântica que permite sua reprojeção (rótulo, papel, estado).
4. Layout é fluxo/constraint, não pixel absoluto
Posição/tamanho SHALL ser expressos por fluxo e constraint referenciando
tokens (espaçamento, densidade), nunca por left/top/width/height absolutos em px.
Motivo: pixel absoluto contradiz a própria tese multi-device/multi-modalidade —
left: 100 não significa nada num relógio, numa TV a 3 m, ou numa output auditiva.
O layout absoluto do esboço do brainstorm é o padrão Delphi/VB-forms e está
explicitamente vetado na UI go-forward. (A compose DSL já é uma árvore
{component, props, children}, não coordenadas — este modelo confirma essa escolha.)
5. Relação com a compose DSL e com eventos
- A árvore é a compose DSL (
src/compose/):Node = {component, props, children}, props com refs de token{$token}. Ocomponentcarrega o papel; a modalidade é onde/como ele projeta. Widget não-visual = novocomponent+ novo backend de paint; a forma do documento não muda. - Eventos despacham intents nomeadas, não destinos concretos (um
onclicknão aponta para um arquivo/URL). O mapeamento intent→destino é do router (S2 do epic). Isso mantém o objeto testável, reusável e reprojetável entre modalidades (uma intent "abrir cadastro" vale para clique, toque ou comando de voz).
6. Escopo v1, extension points e não-objetivos
- v1 (construir):
visual+auditivo+háptico(vibrotátil primeiro). - Extension point (não construir agora):
olfativo,gustativo,hápticotérmico/força — a superfície os prevê como modalidade de saída, sem prometer API que nenhum device de consumidor entrega. Sobre-abstrair para "todos os sentidos" hoje é pagar custo de abstração por capacidade inexistente. - Não-objetivo: atuação no mundo (robótica/IoT) — §2. Integra-se por API, não por UIObject.
- Governança: este modelo não introduz formato/linguagem novo
(
language-and-format-creation): é taxonomia sobre a compose DSL + backends do Kroma que já existem. Viramandatory(com gatilho emspec-triggers.kmd) quando S2/S3 do epic kroma#292 landarem e houver widget não-visual real consumindo o modelo.
References
meta/docs/stack/rfcs/stack-RFC-019-koder-ui-render-strategy.kmdmeta/docs/stack/policies/headless-first.kmdmeta/docs/stack/specs/kdx/format.kmdmeta/docs/stack/specs/design-system/component-semantic-groups.kmdengines/sdk/kroma/backlog/pending/292-declarative-web-deploy-epic.kmdengines/sdk/kroma/src/compose/engines/sdk/kroma/src/introspect.rs