App Topbar — Placement & Layout Rules

draft mandatory

Posição canônica do avatar (KoderUserBadge) e demais elementos primários da topbar em todos os apps Koder, per surface (web, desktop tradicional, desktop browser/IDE, mobile, TV, CLI/TUI). Spec extensível pra cobrir search bar, primary nav, contextual actions, breadcrumbs em §N futuro — v0.1 normativa apenas pra avatar.

Spec — App Topbar Placement

§1 — Applicability

All Koder *pps*that surface a user identity (KoderUserBadge ou KoderSignInButton) in chrome:

  • ✅ Web apps (Hub, Drive, Talk, Cal, Flow Web, …)
  • ✅ Desktop apps tradicionais (Flutter LinuxmacOSWindows — Hub Desktop, Drive Desktop, Talk Desktop, …)
  • ✅ Mobile (Flutter Android/iOS — Hub Mobile, Drive Mobile, Talk Mobile, …)
  • ✅ TV (Tizen/WebOS — Kortex TV, …)
  • ⚠️ Desktop browserIDE (Kruze, devgrid, devdok, devkterm) — *xception* ver §3 R2
  • ❌ CLI / TUI — N/A (sem topbar visual; identidade na status line bottom-right via [user@scope] opcional)
  • ❌ Landing pages — N/A (estáticas; auth lives elsewhere)

§2 — Decision matrix per surface

Surface Default placement Rationale
*eb*(qualquer produto) Topbar *ireita* último item (após contextual actions) Padrão universal (Gmail, GitHub, Slack, Linear, Notion). Reading LTR — branding esquerda, content middle, account direita. Espaço pra dropdown menu (account, settings, sign-out) abrir alinhado pra esquerda
*esktop tradicional*(Hub, Drive, Talk, Cal, Flow Web) Topbar *ireita* último item Idem web; mesma metáfora visual cross-surface dá memória muscular
*obile*(default) Topbar *ireita* Material 3 default; Apple HIG matches
*obile*(drawer-based, ex: ChatGPT/Telegram) *rawer header*(top do drawer aberto via ham menu) Quando primary nav é drawer, account vai no header do drawer. Topbar direita fica pra contextual actions (busca, +, menu)
*V*(Kortex, futuro) *idebar esquerda primária* topo (above primary nav items) TV é vertical-first; topbar fina não é foco da atenção; account precisa estar visível sem navegação adicional
*rowser / IDE*(Kruze, future grid/dok) *idebar vertical esquerda* topo Convenção firme do nicho (Arc, Vivaldi, Brave, VSCode account, JetBrains account) — topbar de browserIDE é dos painéisabas, não do app shell

§3 — Required rules

R1 — Avatar sempre presente em chrome no estado logado

Apps cobertos por §1 *EVEM*mostrar KoderUserBadge (logado) ou KoderSignInButton (deslogado) no chrome principal a partir do primeiro frame após cold-start. Não é aceitável esconder o badge atrás de menu (Mais → Conta) — discoverability falha.

R2 — Posição segue §2 sem variação per-produto

Cada produto *EVE*seguir o default do seu surface conforme §2. Variação *er-produto*é exception que requer:

  • Tag explícito no koder.toml do produto: [ui.topbar] avatar_placement_override = "sidebar-left" # reason: <inline justification>
  • Inclusão em registro meta/docs/stack/registries/topbar-overrides.md (a criar quando 1ª exception aparecer)

Exception ratificadas:

  • *ruze*— sidebar vertical esquerda. Reason: browser app shell mirrors ArcVivaldiBrave convention; topbar belongs to the active tab, not the app.

R3 — Touch target ≥ 40×40 px

Conforme specs/themes/verge.kmd token touch-target-min, o avatar consome área tappable ≥ 40×40 px mesmo se o ícone visual é menor (24×24 ou 32×32). Padding/Gestures absorvem o resto.

R4 — Dropdown / popup abre alinhado

Ao tap no avatar:

  • *opbar direita* dropdown menu abre *linhado à direita*(border-radius corner alinhado com avatar; menu cresce pra esquerda)
  • *idebar esquerda* popup abre *ateralmente à direita do avatar*(não obstrui sidebar; respeita safe area top)
  • *V* focus highlight visualmente óbvio (Verge focus token); A button abre full-screen account modal

R5 — Estado consistente com KoderUserBadge canonical

Os 3 estados visuais e behaviour seguem KoderUserBadge do koder_kit:

Estado Visual Behavior on tap
Signed out Icons.account_circle_outlined (silhouette outlined) — futura KoderIcons.avatar_default Open KoderSignInButton flow (per specs/auth/oauth-flow.kmd)
Signed in, com avatar URL NetworkImage(avatarUrl) em CircleAvatar Open user menu (Account, Settings, Sign out, Switch account)
Signed in, sem avatar URL Initials (1-2 chars) em CircleAvatar com background color hashed do user ID Idem signed-in com avatar

§4 — Tests (T1-T5)

ID Verificação
T1 Audit walk em todo app/lib/ de produto coberto por §1: existe exatamente 1 ocorrência de KoderUserBadge no widget tree do main screen chrome (topbar ou sidebar conforme §2)
T2 Render snapshot do main screen em estado deslogado: avatar visível na posição declarada por §2 ± 8 px de margin
T3 Render snapshot do main screen em estado logadocomavatar: NetworkImage carregado, posição idem T2
T4 Tap region: posição do avatar tem hit-test area ≥ 40×40 px (T2/T3 + golden de bounding box)
T5 Dropdown alignment: tap no avatar → menu aparece com edge alinhado conforme R4

koder-spec-audit topbar-placement (CLI a shipar em devkodertools) roda T1 estaticamente lendo widget trees + checking koder.toml overrides. T2T5 ficam pra widget testgolden test per produto.

§5 — Extensibility (não-normativo v0.1)

Spec aberta pra cobrir, em §6+ futuras, outros elementos da topbar com mesma estrutura (decision matrix per surface + required rules + tests):

  • *6 — Branding/logo placement*(esquerda, sempre)
  • *7 — Primary navigation*(tab bar, hamburger drawer, sidebar — per surface)
  • *8 — Search bar / Cmd+K trigger*(centro ou direitaaoladodoavatar; keybinding spec)
  • *9 — Contextual actions*(icons antes do avatar, max 3, overflow → kebab menu)
  • *10 — Breadcrumbs*(abaixo da topbar OU dentro dela, depending on surface)

Cada §N futura segue mesmo padrão R1R5 + T1T5.

§6 — Migration & rollout

Para produtos já existentes em conformidade com R1 (têm KoderUserBadge):

  1. Audit estático identifica produtos com avatar fora do default per §2
  2. Cada não-conformidade ganha ticket no backlog do produto: <product>#XYZ topbar avatar placement
  3. Migração não-bloqueante pra release (cosmetic) — agendar em release window dedicada por produto

Pra produtos novos: §1+§2+§3 são gatilho obrigatório no momento de implementar topbar (entry em CLAUDE.md).

§7 — Open questions (para owner ratification — status draft)

  1. *obile drawer threshold*— qual produto/feature decide drawerbased vs topbarbased mobile layout? Ou é decisão per-produto?
  2. *V avatar size*— mesma touch target 40×40 ou TV-tuned (10ft viewing distance) min 64×64?
  3. *ulti-tenant / workspace switcher*— quando o avatar precisa expor switcher (ex: user com múltiplos workspaces no Hub), abre menu híbrido OU avatar tem ícone secundário (chevron) indicando "click to switch"?
  4. *eyboard accessibility*— qual tecla aciona avatarmenu? Padrão `CtrlCmd+Shift+A`? Diferente per surface?
  5. *otification badge*— quando há notificações user-scope (ex: invite recebido), avatar mostra dotcount overlay? Spec separada (`specsnotifications/`) ou inline aqui?
  6. *profileastab pattern** — Hub (productsdevhubapp`) hoje (20260522) implementa avatar como *ab "Profile" dedicado no NavigationRailNavigationBar* sem KoderUserBadge no chrome. UX pattern comum em apps com 4-5 destinos de nav primária (YouTube, Maps, Twitter/X, Instagram). Decisão é sobre *lacement*(estrutura de layout), independente da linguagem visual — Verge (canonical KDS) ou Material (atual implementação do Hub via Flutter widgets) renderizam o mesmo placement com tokens diferentes. Decisão owner:
    • *a)*Variant ratificada — adicionar 3ª linha em §2 ("apps com nav primária por tabs OU NavigationRail/NavigationBar com 4+ destinos podem usar profile-as-tab"), Hub passa automatically conforme
    • *b)*Override perproduto registrável — Hub declara `[ui.topbar].avatarplacementoverride = "profileastab" em koder.toml + entry em registries/topbaroverrides.md`
    • *c)*Refit pra default — Hub adiciona KoderUserBadge em AppBar.actions (mantendo tab Profile como atalho duplicado, OU removendo tab e usando só badge)

Tradeoff: (a) é mais aberto, valida o pattern, mas dilui spec; (b) preserva spec restrita e força ratificação per-produto; (c) é mais consistente mas força mudança em production de Hub. Default sugerido: *b)*— alinha com Kruze (browser/IDE exception) como precedent.

§8 — Histórico

  • *0260522*— draft v0.1 criada. Motivada por revisão visual do Kruze sidebar vs Hub login modal (KRUZE170 hotfix); gap identificado: nenhuma spec governava crossproduct avatar placement antes. Status draft aguardando owner answers às 5 questões em §7 + 1 cycle de implementação prática (audit + 1 produto migrado) antes de promover pra ratified.
  • *0260522 (later)*— Audit walk identificou 6ª open question: Hub usa profile-as-tab UX pattern (perfil como tab de nav primária — pattern comum em apps com 45 destinos) não previsto em §2. Registry `topbaroverrides.md` adicionou Hub como "Pending owner ratification — 3rd pattern observed". Owner decide se ratifica como variant em §2, override per-produto, OU refit. Nota: decisão é sobre *lacement*(estrutura), não sobre linguagem visual — Verge canonical aplica os tokens; Material em Hub é detalhe transitório de implementação.

§9 — Spec/policy alignment

  • specs/koder-app/behaviors.kmd §1 — diz "app DEVE ter user badge"; este spec diz *nde*colocar
  • specs/auth/oauth-flow.kmd — fluxo que dispara no tap do avatar deslogado
  • engines/sdk/koder_kit::KoderUserBadge — widget canonical referenciado em R5
  • policies/reuse-first.kmd — proíbe re-implementar widget de avatar fora do SDK
  • specs/themes/verge.kmdtouch-target-min referenciado em R3
  • stack-RFC-003 — Koder Icons (draft) — quando ratificada, Icons.account_circle_outlined em R5 vira KoderIcons.avatar_default

Source: ../home/koder/dev/koder/meta/docs/stack/specs/app-layout/topbar-placement.kmd