Landing Pages — Meta Portal (meta.koder.dev)
landing-pages specs/landing-pages/meta.kmd
Estrutura, IA, pipeline de render, deploy e governança do portal público de documentação da Koder Stack em `meta.koder.dev`. Renderiza o conteúdo de `meta/docs/{stack,ia,cryptography,blockchain}/` como HTML estático navegável, com nav hierárquico por tipo de documento (RFC, spec, policy, registry, module, runbook, compendium), search client-side, theme light/dark, i18n EN/PT. Distinto de landings de produto, área, sector, institucional, catálogo, packages e specs — é um **portal-indexador de documentos**, não uma landing-vendedor.
When this spec applies
Primary triggers
- Criar ou editar a landing/portal meta.koder.dev
All triggers
- Criar ou editar a landing/portal meta.koder.dev
- Decidir o que de meta/docs/ é público vs interno
- Definir IA, nav, search ou render pipeline do portal de docs
- Resolver dead-link a meta.koder.dev/* em landings existentes
Specification body
Landing Pages — Meta Portal (meta.koder.dev)
1. Visão Geral
meta.koder.dev é o portal público de documentação da Koder Stack.
Renderiza o conteúdo de meta/docs/ do monorepo como site estático
navegável.
Não é uma landing de produto, marca, área ou spec individual — é um indexador de documentos. Tem nav hierárquico por tipo (RFC, spec, policy, registry, module, runbook, compendium), uma página por arquivo fonte, search client-side e cross-links resolvidos.
Distinto de:
specs/landing-pages/products.kmd— landings de um produtospecs/landing-pages/specs.kmd— landing de uma spec/formato (KVG, KPKG)specs/landing-pages/areas.kmd— landing de Área L2 (foundation, ai, …)specs/landing-pages/institutional.kmd— landings sobre a Koder (www, company)specs/landing-pages/catalog.kmd— Hub catalog (hub.koder.dev)specs/landing-pages/packages.kmd— página individual de pacote no Hub
Histórico canônico (2026-05-24):
- ~10 landings de Área (
meta/sites/areas/*/),meta/sites/brand/emeta/sites/_ecosystem_gen.pyreferenciaramdocs.koder.devdesde 2026-Q2 como "Public documentation" (sempre dead link). - Sessão de 2026-05-24 turn 3: owner pediu site pro conteúdo de
meta/. Pragmaticamente decidido construirdocs.koder.devreusando o nome já referenciado. Implementação concluída em 5 turns (spec, renderer, vhost, audit, deploy paralelo) — portal shipped com 652 docs. - Mesma sessão turn 12: owner observou que o nome
docs.está semanticamente errado pro conteúdo (RFCs/specs/policies/registries = "meta", não "docs do usuário"). Decisão revertida: canonical émeta.koder.dev, mirror direto do L1 domainmeta/do monorepo. - Cutover atomic agendado (Phase B) quando
infra/net/jetliberar o lock atual (jet#160 Phase 1) — verjet#NNN follow-up ticketno umbrella META-DOCS-131.docs.koder.dev→ 301 →meta.koder.dev, landings editorialmente atualizadas, per-product docs ficam em<product>.koder.dev/docs/quando os produtos shipam.
2. Escopo de Conteúdo
2.1 Em escopo (público, vai pro portal)
| Fonte | Tipo | Sub-path no portal |
|---|---|---|
meta/docs/stack/rfcs/ | RFCs | /rfcs/ |
meta/docs/stack/specs/ | Specs normativas | /specs/ |
meta/docs/stack/policies/ | Policies comportamentais | /policies/ |
meta/docs/stack/registries/ | Registries (fontes de verdade) | /registries/ |
meta/docs/stack/modules/ | Module catalog | /modules/ |
meta/docs/stack/runbooks/ | Runbooks operacionais públicos | /runbooks/ |
meta/docs/stack/releases/ | Release notes consolidadas | /releases/ |
meta/docs/ai/compendium-ai/ | Compendium de IA | /compendiums/ia/ |
meta/docs/cryptography/compendium/ | Compendium de criptografia | /compendiums/cryptography/ |
meta/docs/blockchain/compendium/ | Compendium de blockchain | /compendiums/blockchain/ |
2.2 Fora de escopo (NUNCA publicar)
meta/context/— runbooks internos, hooks de IA, credenciais, notices, recaps. Privado por contrato.meta/brand/— assets de marca (vivem embrand.koder.dev)meta/sites/— landings (cada uma tem seu próprio domínio)- Qualquer arquivo com frontmatter
visibility: internalouaudit: private(ver §6.3) meta/docs/stack/backlog/— tickets de trabalho interno
2.3 Avaliação per-diretório (decidir caso a caso)
meta/docs/platform/— possivelmente público; auditar conteúdometa/docs/trust/— possivelmente público; auditar conteúdometa/docs/flow-releases/— público se substituirflow.koder.dev/releases; senão, manter privadometa/docs/company/— documentação de empresa/negócio pt-BR (marca/INPI, datacenter, cloud, projetos, processos, sessões). Mantida no monorepo (decisão de owner: tudo num repo); renomeada demeta/docs/docs/+.kd→.kmd(stack-docs#157).meta/docs/stack/diagrams/,presentation/,build/— públicos como assets/anexos, não páginas próprias
3. URL Canônica
Padrão: https://meta.koder.dev/<category>/<path>/
<category>∈ {rfcs,specs,policies,registries,modules,runbooks,releases,compendiums}<path>espelha 1:1 a estrutura de subdir na fonte, com.kmd/.mdremovidos e nomes preservados- Trailing slash sempre presente (URLs com slash final →
index.htmldo diretório virtual) - Casos especiais:
- Homepage:
https://meta.koder.dev/(não/index.html) - Search:
https://meta.koder.dev/search/?q=<query>(client-side) - Sitemap:
https://meta.koder.dev/sitemap.xml - 404:
https://meta.koder.dev/404.html
- Homepage:
Exemplos:
| Fonte | URL |
|---|---|
meta/docs/stack/rfcs/stack-RFC-001-kdb-as-unified-data-plane.kmd | /rfcs/stack-RFC-001-kdb-as-unified-data-plane/ |
meta/docs/stack/specs/auth/oauth-flow.kmd | /specs/auth/oauth-flow/ |
meta/docs/stack/policies/hyperscale-first.kmd | /policies/hyperscale-first/ |
meta/docs/stack/registries/stack-manifest.kmd | /registries/component-names/ |
meta/docs/ai/compendium-ai/01-models.md | /compendiums/ia/01-models/ |
Regra de imutabilidade: URLs uma vez publicadas não mudam. Renomes
em meta/docs/ precisam de redirect 301 em
/redirects.toml consumido pelo Jet (#131.5 audit).
4. Estrutura do Arquivo de Site
meta/sites/meta/
├── _assets/
│ ├── meta.css ← Estilos do portal (tokens, nav, sidebar, TOC, footer)
│ ├── meta-search.js ← Busca da rota /search/ (padrão KDS, vanilla)
│ ├── graph.css / graph.js / graph3d.js ← Graph explorer (home, §5.5)
│ ├── kvg.wasm / wasm_exec.js ← gerados (META-DOCS-150.8, gitignored)
│ ├── fonts/ ← Inter + JetBrains Mono woff2 self-hosted
│ ├── search-index.json ← Gerado pelo renderer (gitignored)
│ ├── icon.svg
│ └── og-image-default.png
├── _data/stack-graph.json ← Gerado por koder-stack-graph
├── index.html ← Gerado: homepage
├── <category>/ ← Gerado per category
│ ├── index.html
│ └── <doc-slug>/
│ └── index.html
├── search/index.html ← Gerado
├── sitemap.xml ← Gerado
├── robots.txt ← Gerado (env-aware, §14)
└── 404.html ← Gerado
Todos os HTMLs servidos são gerados pelo renderer (#131.2); os
templates vivem embedded no renderer
(products/dev/koder-tools/internal/docsrender/templates/*.html.tpl),
não em _layouts/ no site. O diretório _assets/ (exceto o que for
gerado) é commitado no monorepo; o resto (<category>/, search/,
sitemap.xml, robots.txt, 404.html, index.html,
search-index.json, kvg.wasm, wasm_exec.js) é build artifact —
gitignored, regenerado a cada build (per policies/hyperscale-first.kmd).
5. Information Architecture
5.1 Homepage (/) — Graph Explorer
A home é o grafo-explorador da Koder Stack (v0.3): um único painel full-bleed que renderiza, em KVG, um sunburst zoomable de todos os componentes e objetos do monorepo, e deixa o usuário navegar pela hierarquia (RFC-003: L1 Domínio → L2 Área → L3 Sector → folha). O nó-foco é o miolo; seus descendentes irradiam como anéis concêntricos (span angular ∝ nº de folhas). Contrato normativo completo na §5.5. Estrutura visível:
- Painel único do sunburst (ocupa a viewport) — anéis concêntricos KVG; sem chrome persistente lateral.
- Overlay de busca no topo (alinhada ao componente de busca do Koder Design — ver §5.4): filtra/realça nós e dá fly-to no setor.
- Botões voltar/avançar de navegação por nível (history de zoom).
- Card flutuante ao clicar num setor-folha (objeto concreto) — nome,
descrição e links de referência (Source/Landing/Module/Spec). Some o
"right sidebar"; o card é a view transiente do nó. Posicionamento:
abertura por clique de ponteiro ancora o card com a borda inferior
~12px acima do ponteiro, centrado nele (clamp na viewport; flip pra
baixo quando não há espaço acima); aberturas sem ponteiro (busca/fly-to,
teclado, breadcrumb) usam o dock superior-direito, e mobile (≤640px) o
bottom-sheet. Hover hint (dois níveis): em ponteiros finos
(
hover: hover+pointer: fine), pairar sobre um elemento mostra um tooltip leve — nome + tipo + 1 linha de descrição, sem links, delay ~220ms,pointer-events: none, segue o ponteiro, some em drag/clique/saída — complementando o card completo, que permanece exclusivo do clique. Desligado em touch/coarse.
Fallback sem-JS / SEO / a11y (obrigatório). Abaixo do grafo (e como conteúdo único quando JS está desligado ou o leitor é um crawler/leitor de tela), renderizar a home estática clássica — crawlable e navegável por teclado:
- Hero: eyebrow "The Koder Stack", H1 "Documentation" (palavra em accent gradient), lead de 2 frases, 2 CTAs ("Browse RFCs" / "Browse Specs").
- Category grid — 8 cards (uma por categoria de §2.1) com ícone +
título + contagem; card clicável →
/<category>/. - Recently updated — 10 últimas modificações (de
git logno build). - Featured — opcional, curado (RFC-001, headless-first.kmd, …).
- Footer dark (idêntico a
institutional.kmd§2.8).
O grafo é uma camada de progressive enhancement sobre esse conteúdo estático: o portal nunca depende de WASM/JS para ser indexável ou acessível (§13, §14).
5.2 Category listing (/<category>/)
Estrutura:
- Nav
- Breadcrumb:
Home / <Category> - Header: nome da categoria + descrição de 1 linha + total
- Filtros:
- Por sub-area (specs:
auth/,i18n/,themes/, …) - Por status (RFCs: draft / accepted / superseded; specs: pre-normative / normative / draft)
- Por data (modified last 7d / 30d / 90d / all)
- Por sub-area (specs:
- Lista de docs — tabela ou cards:
- Nome + sub-path + 1-line summary (do frontmatter
summary:) - Status badge
- Last-modified date
- Audit status se aplicável
- Nome + sub-path + 1-line summary (do frontmatter
- Footer
5.3 Document page (/<category>/<slug>/)
Estrutura (layout 3 colunas em desktop, stacked em mobile):
- Nav
- Left sidebar (sticky, scroll-independente):
- Navegação da categoria atual (árvore expandida no doc atual)
- Main content:
- Breadcrumb
- Frontmatter rendered como badge bar (status, version, mandatory, audit date)
- H1:
name:outitle:do frontmatter - Render do body markdown/kmd (ver §6)
- Footer da página: "Edit this page" (link pro Flow se público) +
prev/next (mesma categoria) + "Last updated
· commit "
- Right sidebar (TOC):
- Lista de headings H2/H3 do doc
- Scroll-spy: destaca seção atual
- Footer global
5.4 Search
A busca reusa o componente de busca do Koder Design (KDS —
tools/design-gen/assets/js/search.js + index gerado em Go), não uma
implementação própria (reuse-first / D10). Vanilla JS, self-hosted, sem
dependência externa; index client-side (título + frontmatter + headings,
não full-text — mantém <500KB).
Dois pontos de entrada, mesmo index:
- Overlay no grafo-explorador (home) — campo no topo do painel; tecla
/abre; filtra/realça nós do grafo e dá fly-to na bolha correspondente (além de listar docs). - Rota
/search/?q=…— resultados agrupados por categoria, highlight do termo, para deep-link e fallback sem-JS.
Com 2 consumidores (KDS + meta portal), copiar o padrão é aceitável pela regra-dos-3 (
stack-principles§1). Nomear extração praengines/sdkquando surgir o 3º consumidor.
5.5 Graph Explorer — modelo de dados & interação
Decisões aprovadas (2026-06-04): painel único · containment-only · card overlay na folha · history+deep-link. Modelo visual revisado para sunburst (2026-06-06): o force-graph original (sim-física + bolhas) resolveu o PoC mas ficou visualmente ruim (nuvem de pontos ilegível com 291 folhas); trocado por um sunburst zoomable — mesma chrome (busca/card/histórico/fallback), só a camada de layout+render+hit-test mudou. Comparados também treemap/icicle/circle-packing (protótipo em
meta/sites/meta/graph-prototypes/); sunburst é o default. Galeria de navegadores (#153, 2026-06-06): o home virou um seletor de 7 modelos de navegação hierárquica, todos sobre o mesmo dado + chrome (busca/card/histórico/deep-link/fallback), escolhidos num<select>no topbar e deep-linkáveis pelo#hash(#<modelo>[:<id>]):
- Canvas (KVG):
sunburst(default) ·treemap·icicle·circlepack(circle packing aninhado, enclose de Welzl) ·radialtree·graph(force-directed — rede completa: TODOS os nós (root+domínios+ áreas+sectors) + arestas de contenção sempre visíveis, seed de árvore radial
- sim que assenta e para (idle estático, barato p/ ~333 nós); arrasta qualquer nó (root incl.); clique = o nó vai pro CENTRO (fixado lá) + ampliado (raio ~2× + borda branca) em evidência, e realça linhagem (ancestrais) + subárvore (vívidos) enquanto o resto atenua e afasta — nada é escondido; clique no vazio devolve o root ao centro). Estilos de nó (sub-bar, react-force-graph parity 2D — KVG RFC-004):
circle·shape(polígono por domínio) ·text(nó = rótulo) ·html(card) + toggle de rótulos de aresta. A trilha 3D (text/shape/link-label/focus em 3D + large-graph GPU) é gated no Solid (RFC-002) + kvg#096.- DOM:
tree(árvore colapsável estilo outline/explorer) ·columns(Miller/Finder).Arquitetura: cada modelo canvas é um trio
{layout, draw, pick}; cada modelo DOM é um par{render, reveal}; o registryMODELSemgraph.jsdespacha.Encoding de contenção + cap justo (2026-06-10, owner): nas views network (2D
grapheforce3d), tamanho codifica contenção — bolas com filhos ("que explodem") são maiores que folhas, com crescimento log pela contagem de filhos diretos (containBoost, cap ~1.6×). O orçamentoF3CAP=80doforce3dé preenchido por BFS round-robin por pai (toda área ganha o 1º filho antes de qualquer área ganhar o 2º — nenhuma área pequena é silenciosamente excluída) e a poda é honesta: nós com filhos fora do orçamento exibem(+N)junto à contagem (princípio "no silent caps"). O full-graph 3D continua gated no GPU (kvg#096).Edge bundling shipou (#154, 2026-06-07) — 9º modelo
bundle. As arestas de dependência são mineradas do código real (importskoder.dev/<dom>/<área>/ <sector>nos.go), NÃO autoradas à mão (D1: nada de grafo que mente). O geradorkoder-stack-graphemiteedges: [{source,target,weight}]no root dostack-graph.json(só entre sectors que existem; sem self-loop). O modelobundlefaz layout radial + roteia cada aresta pelo caminho source→LCA→target e a β-agrupa (Holten, β=0.85) em spline Catmull-Rom. Escopo atual: imports Go (o grosso da Stack; ~52 arestas). Futuro: estender o extrator a path-deps Dart (pubspec) + deps Rust (Cargo) pra cobrir os componentes não-Go. Matriz de adjacência segue como opção futura. Voronoi treemap descartado (layout iterativo caro, ganho marginal vs. treemap/circlepack).
a) Fonte de dados — o filesystem é a espinha. O grafo é gerado por
products/dev/koder-tools/cmd/koder-stack-graph (build-time), que percorre
a árvore do monorepo nos níveis RFC-003 (L1 Domínio → L2 Área → L3 Sector
→ folha) e emite JSON aninhado em meta/sites/meta/_data/stack-graph.json.
- Containment vem do filesystem — única codificação completa e
confiável da hierarquia. Os 276
koder.tomlsão majoritariamente auto-stubs finos (prefix/compat); o registrystack-manifest.kmdé seed. - Enriquecimento é best-effort (registry → display name/slug;
module deep-dive
summary:→ descrição; existência delanding/,specs/<slug>/,modules/<slug>.md→ links). Um link só é emitido quando o alvo existe no disco — o grafo nunca afirma uma referência que não tem (D1/D6). Descrições crescem conforme registry/módulos amadurecem. - Arestas de dependência (
consumed_by) são camada futura, gated por completude de dado — desenhar de dado esparso produziria um grafo que mente. Quando oskoder.tomltiveremconsumed_bypopulado, entram como toggle opcional sobre o containment, sem retrabalhar a base.
b) Render — layout sunburst em JS, KVG rasteriza one-shot. O layout
(spans angulares ∝ nº de folhas, anéis por profundidade relativa ao foco)
é calculado em JS; o KVG desenha a cena via kvgRender(source,…) —
cada setor anular é um path tesselado em segmentos de reta (fan de
L, não flags de arco — previsível independente da convenção de eixo).
Render é one-shot por nível de foco (não há loop de física: o sunburst
é estático até o próximo drill). Como o JS é dono da geometria, o hit-test
é polar (raio→anel, ângulo→setor) no mesmo lugar. KVG renderiza y-up;
a única conversão kvg_y = H − y acontece na emissão do path (disciplina
herdada do force-graph, 150.4).
c) Interação.
- Estado inicial: foco = raiz — a Stack inteira num sunburst de 3 anéis (Domínio → Área → Sector), miolo rotulado "Koder Stack · N leaves".
- Drill-down: clicar num setor não-folha → ele vira o novo foco (zoom): o miolo passa a ser aquele nó e seus descendentes re-irradiam.
- Zoom-out: clicar no miolo (centre hole) volta ao foco-pai.
- Folha (objeto concreto): clique → card flutuante com nome, descrição e refs (Source/Landing/Module/Spec) do nó. Sem painel fixo.
- Hover: realça o caminho no
#status(nome · id) + cursor; sem re-rasterizar (custo de re-parse de ~300 setores é alto demais por hover). - Histórico: botões voltar/avançar percorrem focos já visitados (pilha
de estados; espelhada no
#hashda URL para deep-link e back/forward do browser). - Rótulos: HTML overlay, só em setores grandes o bastante (arco × banda acima do limiar); folhas finas se leem por hover/busca/drill (norma sunburst). Evita o amontoado ilegível de 291 rótulos simultâneos.
d) Disciplina de performance (D4). Renderizar apenas o nível ativo
(≤ ~100 nós em tela), nunca os 291 nós de uma vez — o KVG rasteriza no CPU
(software renderer), então o nº de nós por frame é o orçamento. O WASM do
KVG é lazy-loaded (IntersectionObserver) e versionado para cache-bust. Se o
runtime do KVG só expuser "parse string → raster", abrir um entrypoint fino
em engines/lang/kvg (render de cena pré-parseada com overrides de
transform por nó) para evitar reparse por frame — é o risco central que o
PoC valida.
6. Render Pipeline
Implementação: dev/koder-tools/cmd/koder-docs-render/ (Go) — ticket
META-DOCS-131.2. Esta seção é o contrato do renderer.
6.1 Input
- Walk de
meta/docs/{stack,ia,cryptography,blockchain}/(e diretórios promovidos por §2.3) - Aceita
.kmde.md. Frontmatter YAML obrigatório (sem frontmatter → log warning + skip ou render como "untyped doc") - Respeita
.docsignore(lista de globs) em cada diretório raiz
6.2 Parser
- Markdown: reutilizar parser canônico da Stack (mesmo de
kmd render); fallbackgoldmarkse kmd parser indisponível - Frontmatter:
yaml.v3 - Cross-links: resolver
[texto](relative-path.kmd)→ URL canônica do portal (ver §3);[[wiki-link]]→ busca porname:no frontmatter de todos os docs indexados, falha emite warning
6.3 Visibility filter
Skip arquivo se:
- Frontmatter contém
visibility: internal - Frontmatter contém
audit: private - Path bate com qualquer entrada de
meta/sites/docs/_excluded.toml(escape hatch curado)
6.4 Output
meta/sites/docs/<category>/<slug>/index.htmlper docmeta/sites/docs/<category>/index.htmlper categoriameta/sites/docs/index.htmlhomepagemeta/sites/docs/search/index.html+_assets/search-index.jsonmeta/sites/docs/sitemap.xml(Google + IndexNow)meta/sites/docs/404.htmlmeta/sites/docs/redirects.toml(Jet-consumível, se houver renames detectados)
6.5 Metadata extraída do frontmatter
| Frontmatter key | Onde aparece no HTML |
|---|---|
name / title | <title>, <h1>, og:title |
summary | <meta description>, og:description, listing card |
status | badge bar + filter em listing |
type | category routing |
mandatory: true | "Mandatory" badge no header |
triggers | section "When this applies" (collapsible) |
references | rendered como linked list no rodapé do doc |
audit | badge "Audit: |
facets | tags clicáveis |
6.6 Hyperscale gate
Per policies/hyperscale-first.kmd: renderer DEVE escalar a 10×
o volume atual sem mudança arquitetural (~5k docs). Decisões:
- HTML estático puro, sem SSR/CSR runtime
- Build incremental: hash do arquivo fonte + frontmatter; só re-renderiza o que mudou
- Search index gerado em pass único, paralelizado por worker
- Sem JS bundlers —
docs.jsé vanilla JS direto
7. Head — Meta Tags
Template aplicado a TODA página:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{page.title}} — Koder Meta</title>
<meta name="description" content="{{page.summary}}">
<meta name="theme-color" content="#0b1120">
<link rel="canonical" href="https://meta.koder.dev{{page.url}}">
<!-- Open Graph -->
<meta property="og:title" content="{{page.title}} — Koder Meta">
<meta property="og:description" content="{{page.summary}}">
<meta property="og:type" content="article">
<meta property="og:url" content="https://meta.koder.dev{{page.url}}">
<meta property="og:image" content="https://meta.koder.dev{{page.og_image | default('/_assets/og-image-default.png')}}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{page.title}} — Koder Meta">
<meta name="twitter:description" content="{{page.summary}}">
<meta name="twitter:image" content="https://meta.koder.dev{{page.og_image | default('/_assets/og-image-default.png')}}">
<link rel="icon" type="image/svg+xml" href="/_assets/icon.svg">
<script>
(function(){
const s = localStorage.getItem('theme');
const dark = s ? s === 'dark' : matchMedia('(prefers-color-scheme:dark)').matches;
if (dark) document.documentElement.setAttribute('data-theme','dark');
})();
</script>
<link rel="stylesheet" href="/_assets/meta.css">
</head>
<title>max 60 chars; trunca{{page.title}}se preciso (renderer warn)og:type = "article"em doc pages,"website"em homepage/category listingsog:imageper page é opcional; default =og-image-default.png(Koder Meta branded)
8. Nav
Idêntica ao padrão de outras landings (ecosystem.css):
<nav>
<div class="nav-inner">
<a class="nav-brand" href="/"><img src="/_assets/icon.svg" alt="">Koder Meta</a>
<button aria-label="Menu" class="nav-toggle" onclick="...">☰</button>
<div class="nav-links">
<a href="/rfcs/">RFCs</a>
<a href="/specs/">Specs</a>
<a href="/policies/">Policies</a>
<a href="/registries/">Registries</a>
<a href="/compendiums/">Compendiums</a>
<button id="lang-btn" onclick="toggleLang()">EN</button>
<button class="theme-toggle" onclick="toggleTheme()">…</button>
</div>
</div>
</nav>
Versão sticky com backdrop-filter: blur(16px). Ordem dos links é
canônica (RFCs antes de Specs antes de Policies, por densidade de
referência).
9. CSS — Tokens
O CSS do portal é meta/sites/meta/_assets/meta.css — standalone,
escrito sobre os mesmos tokens visuais Verge das demais landings.
(Reuso direto de meta/sites/ecosystem.css foi avaliado e adiado:
o chrome de portal-de-docs diverge do de landing-vendedor, e a direção
de longo prazo de tokens compartilhados é o KDS — ver §18.6.)
Cobre:
- Sidebar tree (left + right TOC)
- Code blocks com syntax highlighting (Prism vendored ou highlight.js)
- Tables responsivas (scroll-x em mobile)
- Badge bar (status, mandatory, audit)
- Search results (
/search/+ overlay do grafo) - Footer dark multi-coluna (padrão
institutional.kmd§8)
Theme: light/dark per specs/themes/light-dark.kmd. Toggle JS
idêntico ao das outras landings.
Tokens visuais: per specs/themes/verge.kmd §R9 — o portal consome o
Verge Web export (kds.koder.dev/tokens/verge-web.css, vendored em
_assets/tokens/), mapeando as vars locais em var(--kw-*). (Verge
default R4 é o preset de apps; propriedades web usam Verge Web.)
Fontes: per specs/fonts/typography.kmd:
- Sans: Inter (self-hosted woff2 via
/_assets/fonts/inter.woff2) - Mono: JetBrains Mono (self-hosted)
- Proibido Google Fonts / jsDelivr / unpkg
10. JavaScript — Comportamentos
_assets/docs.js (vanilla, ~10KB minified):
- Theme toggle (idêntico a outras landings)
- Language toggle EN ↔ PT-BR (i18n; conteúdo é EN por
policies/language.kmd, mas chrome traduzível) - Mobile nav hamburger
- TOC scroll-spy
- Sidebar tree expand/collapse + persistência em
localStorage - Search input (homepage + página search): query lunr index,
renderiza resultados ou redireciona pra
/search/?q=… - Copy-to-clipboard em code blocks (button hover)
- Anchor links em H2/H3 (hover icon → copy URL com fragment)
lunr.min.js carregado só na rota /search/ (lazy) — não
penaliza páginas de leitura.
11. Dead-Link Audit
A spec mandata que ao primeiro deploy em produção, o renderer
gere um relatório audit/dead-links-<date>.md listando todas as
refs a meta.koder.dev/* no monorepo (especialmente em
meta/sites/areas/*/index.html, meta/sites/brand/index.html,
meta/sites/_ecosystem_gen.py) e marque cada uma como:
- ✅
resolves— path canônico existe - 🔁
redirects-to: <new-path>— entrada emredirects.tomlnecessária - ❌
broken— bloqueia release até #131.5 decidir destino
CI gate: build do portal falha se houver broken sem resolução.
12. i18n
- Conteúdo (doc body) NÃO é traduzido — é EN por
policies/language.kmd - Chrome (nav, footer, breadcrumbs, status badges, "Recently updated") É traduzido EN ↔ PT-BR
- Toggle
EN/PTno nav (mesmo padrão das outras landings) - Default por geo / browser language; persiste em
localStorage - T1-T6 da spec
specs/i18n/contract.kmdaplicam
13. Acessibilidade
- WCAG 2.1 AA
- Skip-to-content link no topo
- Sidebar tree navegável por teclado (arrow keys)
- Theme toggle com
aria-label+aria-pressed - TOC com
aria-current="location"no item ativo - Code blocks com
role="region"earia-label="Code sample" - Tabela de conformance per page em audit periódico (#131 followup)
14. SEO
sitemap.xmlgerado no build, submetido ao Google Search Console- Per-page meta tags (§7)
robots.txtna raiz (/robots.txt— crawlers só leem a raiz) —Allow: /+Sitemap:em prd,Disallow: /em stg/dev (perpolicies/environments.kmd); gerado pelo renderer via--env- IndexNow ping no deploy de produção (lista de URLs alteradas)
- Schema.org
TechArticleJSON-LD em doc pages
15. Deploy
Per policies/web-server.kmd (Jet only) + policies/environments.kmd:
| Ambiente | Host | Branch source | noindex |
|---|---|---|---|
dev.meta.koder.dev | s.forge | dev | true |
stg.meta.koder.dev | s.forge | staging | true |
meta.koder.dev (prd) | s.forge | main | false |
DNS A record provisionado via ClouDNS (meta/context/infrastructure/dns.md).
TLS via Jet autocert (LE/ACME).
Pipeline de deploy:
- CI roda
koder-docs-render --env <env>no monorepo - Output em
meta/sites/docs/(sub-paths gitignored) rsyncpro/var/www/meta.koder.dev/no s.forge- Jet reload (zero-downtime)
- Smoke:
curl -I https://meta.koder.dev/→ 200 + TTFB <500ms - Lighthouse: a11y ≥ 95, perf ≥ 90, SEO = 100
16. Performance Targets
| Métrica | Target |
|---|---|
| TTFB | <200ms (CDN/edge se necessário, futuro) |
| LCP | <1.5s |
| CLS | <0.05 |
| Page weight (homepage) | <100KB (sem search index) |
| Page weight (doc page) | <150KB (sem code samples grandes) |
| Search index | <500KB |
| Build time (full) | <60s pra ~500 docs |
| Build time (incremental) | <5s |
17. Governança
- Owner: Koder Stack (Rodrigo)
- Spec maturity gate: v1.0 normative quando #131.2/.3/.4/.5/.6/.7 do umbrella META-DOCS-131 estiverem done + lighthouse scores atingidos + dead-link audit limpa em 2 builds consecutivos
- Versioning: a spec do portal segue semver per
specs/landing-pages/specs.kmd §6 - Audit:
koder-spec-audit landing-pages --slug docs(a ser implementado pelo audit harness emdev/koder-tools/)
18. Decisões Em Aberto
- Search backend long-term: lunr.js (v0) → kdb-search quando
rfcs/stack-RFC-001-kdb-as-unified-data-plane.kmdentregar layer de busca. Decisão revisitada no kickoff de #131.2. - Versionamento de docs: always-latest (HEAD) vs por tag (v1.0, v1.1). Default v0: always-latest; versões anteriores acessíveis via Flow se requestado. Revisitar quando portal tiver ≥1 ano de tráfego.
- OG image generation per page: usar
kicon generate --targets ogper doc, ou template estático compartilhado por categoria? Default v0: estático por categoria (8 imagens curadas). - Comments / feedback: deixar fora do v0. Avaliar Giscus (GitHub-backed) vs solução self-hosted em v2.
- Versioned dark mode: usar Verge default ou expor preset switcher como no KDS? Default v0: Verge default, sem switcher.
- Base CSS compartilhada — RESOLVIDO 2026-06-10 (c3, owner-aprovado
via /k-arch→/k-go): a paleta web virou o preset Verge Web
(verge.kmd §R9, export
kds.koder.dev/tokens/verge-web.css) e o portal consome o export vendored com zero mudança visual (META-DOCS-171 done). Migração das demais landings: follow-up.
19. Referência Cruzada
specs/landing-pages/institutional.kmd— padrão de footer dark + navspecs/landing-pages/specs.kmd— padrão de doc-as-pagespecs/kmd/— parser canônicopolicies/hyperscale-first.kmd— gate de build incrementalpolicies/reuse-first.kmd— reuso deecosystem.cssmeta/sites/_ecosystem_gen.py— gerador atual de landings de Área (referencia o dead linkmeta.koder.dev/ecosystem)- META-DOCS-131 — umbrella ticket do programa
- JET-161 — vhost ticket
20. Estado Atual (2026-05-24)
| Sub-stream | Ticket | Status |
|---|---|---|
| Landing spec | #131.1 | ✅ esta spec (v0.1.0 pre-normative) |
| Renderer pipeline | #131.2 | pending |
| Templates + nav | #131.3 | pending |
| Vhost + TLS | JET-161 | pending (blocked on #131.1+#131.2) |
| Dead-link audit + fix | #131.5 | pending |
| OG image + brand assets | #131.6 | pending |
| i18n chrome | #131.7 | pending |
References
specs/landing-pages/institutional.kmdspecs/landing-pages/specs.kmdspecs/landing-pages/areas.kmdspecs/kmd/specs/themes/light-dark.kmdspecs/themes/verge.kmdspecs/fonts/typography.kmdpolicies/language.kmdpolicies/web-server.kmdpolicies/security.kmdpolicies/environments.kmdpolicies/hyperscale-first.kmdpolicies/reuse-first.kmd