Landing Pages — Produtos Koder
Estrutura, seções, OG image e deploy de landing pages de produtos Koder. HTML monolítico, sem deps externas, floor de idiomas en-US+pt-BR (toggle EN/PT, default en-US). Inclui padrão canônico de URL para web apps (<produto>.koder.dev — nunca app.<produto>.koder.dev) e fluxo OAuth callback. Regra de privacidade: zero links pra código-fonte.
Visão Geral
Toda landing page de produto koder-* deve ser um arquivo HTML monolítico (site/index.html) dentro do repositório do produto, sem dependências externas (frameworks, CDNs, fontes web). Todo CSS e JS são inline.
Idioma (diretiva do owner — META-DOCS#184): toda landing de produto oferece o floor de idiomas en-US + pt-BR (× es onde a Área já o adota — ver policies/language.kmd "minimum language floor"), com toggle EN/PT canônico e default en-US. en-US é a fontefallback; o pt-BR está sempre presente como opção. Não auto-detectar locale a ponto de forçar pt-BR como default (o bug que motivou este registro: auto-detect tornou pt-BR default → uma sessão removeu o toggle inteiro como "não-conformidade"). Persistência + fallback chain seguem `specsi18n/contract.kmd`. Nunca remover um toggle de idioma existente como "não-conformidade" — a ausência de toggle é que é o drift.
Regra de privacidade: Nenhuma landing page deve conter links, botões ou referências ao repositório de código-fonte (Koder Flow, GitHub, ou qualquer hospedagem de código). Isso inclui botões "View Source", "View on Flow", links no footer para o repo, links para releases no Flow, etc. O código-fonte é privado e não deve ser exposto nas páginas públicas.
Estrutura do Arquivo
{categoria}/{produto}/site/
├── index.html ← Landing page (HTML + CSS + JS inline)
├── icon.svg ← Ícone do produto (cópia ou symlink)
└── og-image.png ← Imagem OG rasterizada (1200×630px) — gerada por kiconGeração canônica do
og-image.png: rodarkicon generate --module {categoria}/{produto} --targets og(targetogintroduzido pra unificar a composição entre landing pages, páginas de pacote no Hub e capa de docs). O composer lêname+descriptiondokoder.toml/kpkg.tomle o ícone master, e produz o PNG 1200×630 com layout uniforme (ícone à esquerda, nome+descrição+wordmark Koder à direita). Geração manual (og-image.svgeditado à mão) está obsoleta — cria inconsistência visual entre produtos e desatualiza quando o ícone muda. Repos legados que ainda têmog-image.svgpodem manter o arquivo até a próxima release; o CI vai sobrescreverog-image.pngcom o output do kicon.
Head — Meta Tags e Setup
Meta Tags Obrigatórias
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{Produto} — {Tagline curta}</title>
<meta name="description" content="{Descrição em 1-2 frases}">
<meta name="keywords" content="{palavras-chave separadas por vírgula}">Comprimento máximo do <title>: 60 caracteres incluindo o separador — (3 chars). Browsers truncam a aba por volta de 55–60 chars; Google Search trunca por volta de 55 chars. Exemplo dentro do limite: Koder KDB — The database for the age of AI (43 chars).
Open Graph + Twitter Card
<meta property="og:title" content="{Produto} — {Tagline}">
<meta property="og:description" content="{Descrição curta}">
<meta property="og:type" content="website">
<meta property="og:url" content="https://{subdominio}.koder.dev">
<meta property="og:image" content="https://{subdominio}.koder.dev/og-image.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="{Produto} — {Tagline}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{Produto} — {Tagline}">
<meta name="twitter:description" content="{Descrição curta}">
<meta name="twitter:image" content="https://{subdominio}.koder.dev/og-image.png">Favicon
<link rel="icon" type="image/svg+xml" href="icon.svg">Usar referência relativa icon.svg (o arquivo deve existir em site/). Alternativamente, usar data URI inline.
Script Anti-Flash de Tema (no <head>, antes do CSS)
<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>CSS — Design Tokens (Variáveis)
Tema Claro (:root)
:root {
--bg: #ffffff; --bg2: #f8fafc; --bg3: #f1f5f9;
--text: #0f172a; --text2: #475569; --text3: #94a3b8;
--accent: {COR_DOMINANTE}; --accent2: {COR_DOMINANTE_DARK}; --accent-light: {COR_DOMINANTE_BG};
--border: #e2e8f0;
--card: #ffffff; --card-shadow: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.04);
--code-bg: #1e293b; --code-text: #e2e8f0;
--gradient: linear-gradient(135deg, {COR_DOMINANTE} 0%, {COR_SECUNDARIA} 100%);
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--mono: 'SF Mono', 'Fira Code', 'Cascadia Code', 'JetBrains Mono', monospace;
color-scheme: light;
}Tema Escuro ([data-theme="dark"])
[data-theme="dark"] {
--bg: #0b1120; --bg2: #111827; --bg3: #1e293b;
--text: #f1f5f9; --text2: #94a3b8; --text3: #64748b;
--accent: {COR_DOMINANTE_LIGHT}; --accent2: {COR_DOMINANTE}; --accent-light: {COR_DOMINANTE_DARK_BG};
--border: #1e293b;
--card: #1e293b; --card-shadow: 0 1px 3px rgba(0,0,0,.3);
--code-bg: #0f172a; --code-text: #e2e8f0;
color-scheme: dark;
}Regra: A cor dominante (--accent) varia por produto. Todas as outras variáveis de fundo, texto, borda e card são padronizadas e não devem variar entre produtos.
CSS — Componentes
Navbar
position: fixed; top: 0; z-index: 100- Altura:
64px max-width: 1200pxcentralizadobackdrop-filter: blur(12px)+border-bottom: 1px solid var(--border)- Atributo
data-scrolledadicionado via JS quandoscrollY > 20(muda opacidade do fundo) - Regra CSS:
nav[data-scrolled] { background: rgba(255,255,255,.85); }(e equivalente dark) - Contém:
.nav-brand(SVG 32x32 + nome),.nav-links(âncoras para seções),.nav-actions(botão tema + botões CTA). O nome em.nav-brandusa a formadisplaydo componente (Koder <Bare>, ex.Koder Mediant) — nunca obarenem oslug. Fonte normativa:specs/naming/forms.kmd §7(R7.1). - Clique na marca (
.nav-brand): leva à home do próprio produto —href="/"(topo da própria landing). Nunca aponta para um site externo (www.koder.dev, área, catálogo). É a convenção universal "logo → home" (GitHubNotionLinearVercel, os mesmos que esta spec cita em §"SPA routing"); o clique mais proeminente serve seu propósito esperado e não ejeta o visitante do produto que ele está avaliando. A navegação "subir" para o ecossistema Koder (Koder › Área › Produto + grid das áreas) vive exclusivamente no rodapékoder-eco-nav— não no.nav-brand. Se um acesso proeminente ao ecossistema for desejado no topo, ele entra como link de nav nomeado (ex.:Koder ↗em.nav-links.nav-actions, espelhando as landings de Área emareas.kmd §Nav), nunca sequestrando o clique do logo. - Botão Login: Incluir somente em landing pages de produtos que possuem autenticaçãoconta de usuário (SaaS, plataformas web, apps com login). Não incluir em produtos que são apenas ferramentas de linha de comando, bibliotecas, SDKs, ou distros. Quando presente, o botão Login fica em
.nav-actions, comobtn-outline, antes do botão primário (Download / Get Started). URL do Login: A página de login centralizada da Koder Platform é provida pelo Koder ID em `https:id.koder.devuilogin. **Todos** os produtos devem apontar para essa URL, sem exceção. **Nunca** usar URLs relativas comouseroauth2...(as landing pages são estáticas e não passam pelo backend do produto). **Nunca** apontar parahttps:/ccount.koder.dev(essa é a página de gestão de conta, não a tela de login). **Nunca** apontar parahttps:/d.koder.devsem o pathuilogin(essa é a landing page do produto Koder ID, não a tela de login). **Sempre** passar os parâmetros OAuth do produto chamador:clientid(do registro OAuth do produto no Koder ID),redirecturi(URL-encoded,https:/produto>.koder.devauthcallback),responsetypecode,scopeopenid+profile+email. Ouiloginforwarda esses params prooauthv2authorize(handlerservicesfoundationidengineservicesgatewayinternalhandlerproxy.go::uiLoginHandler). **Sem esses params**, oui/loginfaz fallback pro cliente defaultkoder-platformcomredirecturihttps:/d.koder.dev/, e o usuário pós-login fica preso na landing do Koder ID em vez de voltar pro produto. Formato:<a href"https:/d.koder.devuilogin?clientid=<CLIENTID>&redirecturi=https%3A%2F%2Fproduto.koder.dev%2Fauth%2Fcallback&responsetypecode&scopeopenid+profile+email" class="btn btn-outline">Log In/a. Oclient_idé provisionado uma vez viakoder-id-cli client register --tenant koder --name 'Produto' --type public --redirect ...(gera ULID até a CLI ganhar--id; aid seed-clients/main.goé a source-of-truth canônica de slugs first-party). Em mobile (max-width: 768px), o botão Login fica oculto (display: nonena classe.nav-actions .btn-outline`). - Mobile:
.nav-toggle(hamburger) exibido emmax-width: 768px,.nav-linksvira coluna vertical
Botões
.btn { padding: 10px 20px; border-radius: 8px; font-size: 14px; font-weight: 600; }
.btn-primary { background: var(--accent); color: #fff; }
.btn-outline { border: 1px solid var(--border); color: var(--text); }
.btn-lg { padding: 14px 28px; font-size: 16px; border-radius: 10px; }Hover: translateY(-1px) e cor mais escura.
Animações
@keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }Usadas nos elementos do hero e ativadas via IntersectionObserver nos cards.
Seções Obrigatórias (em ordem)
1. Hero (duas colunas — obrigatório)
- Layout:
grid-template-columns: 1fr 1frcomgap: 60px. As duas colunas são obrigatórias para todos os produtos Koder, sem exceção. Não é permitido hero de coluna única no desktop. - Coluna esquerda (
.hero-text):- Badge: pill com ícone + texto curto (ex: "Built in Koder Koda")
<h1>com ícone do produto inline (<img class="hero-icon-inline">comwidth: 1.2em) + texto com gradient no nome do produto (background: var(--gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent). O nome no<h1>usa a formabareou um nome distintivo de marca — a convenção dominante do fleet é o templateMeet <Bare>(ex.Meet Jet,Meet Cron) ou marca lowercase distintiva (kicon,Kodix); a formadisplay(Koder <Bare>) também é válida. O gradiente cai sobre a palavra distintiva. Odisplayfica ancorado no navbar +<title>na mesma página. Fonte normativa:specs/naming/forms.kmd §7(R7.2).- Parágrafo descritivo (1-3 frases)
- Botões CTA: primário ("Get Started" / "Download") — não incluir botões de acesso ao repositório (View Source, View on Flow, etc.)
- Animações:
fadeInUpescalonadas (0s, 0.1s, 0.2s, 0.3s)
- Coluna direita (
.hero-right): conteúdo visual que ilustre o produto. Três variantes permitidas, escolher a que melhor representa o produto:- Variante A — Bloco de código (
<pre>): para produtos técnicos/dev (SDKs, bancos de dados, linguagens, APIs, ferramentas de programação, frameworks, motores de simulação). Exemplo:kdb,jobs,sim.- Syntax highlighting via spans com classes:
.kw(keyword, roxo),.str(string, verde),.num(número, amarelo),.cm(comentário, cinza),.fn(função, cyan),.op(operador, rosa) - Fundo escuro (
var(--code-bg)),border-radius: 12px,font-family: var(--mono)
- Syntax highlighting via spans com classes:
- Variante B — Mockup de UI (SVG inline ou HTML/CSS estilizado): para apps consumer e SaaS visuais (delivery, bus, ride, learn, hr, jobs, org, health, clinic). Renderize uma janela estilizada (browser, mobile ou desktop) contendo elementos reais e reconhecíveis do produto: cards de busca, listas de itens, mapas com rota, dashboard com gráficos, formulário de agendamento, etc.
- Janela com sombra (
box-shadowoufilter: drop-shadow), bordas arredondadas, barra de título estilizada (3 botões macOS, ou status bar mobile) - Conteúdo interno usa as cores do produto (
var(--accent))
- Janela com sombra (
- Variante C — Ilustração temática (SVG inline): figura conceitual ilustrando o domínio do produto, quando nem código nem UI são adequados (ex: produto puramente físico, hardware, infraestrutura).
- Em todos os casos: tudo inline, sem dependências externas (sem
<img>para PNG/JPG, sem fontes web). Visual moderno: sombras suaves, gradientes, bordas arredondadas (12-16px). Legível em mobile.
- Variante A — Bloco de código (
- Mobile (
max-width: 768px):grid-template-columns: 1fr, texto centralizado, coluna direita aparece abaixo da esquerda - Background sutil:
radial-gradient(circle, rgba({accent},.08) 0%, transparent 70%)posicionado atrás
2. Features / Funcionalidades Principais
- Classe
.alt-bg(fundovar(--bg2)) - Header centralizado:
<h2>+<p>descritivo - Grid:
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr))comgap: 24px - Cards com: ícone (emoji ou SVG em div 32x32 colorida), título
<h4>, descrição<p> - Border:
1px solid var(--border),border-radius: 12-16px - Hover:
border-color: var(--accent),translateY(-2px), sombra
3. Exemplos de Código / How It Works (opcional, mas recomendada)
- Layout alternado:
grid-template-columns: 1fr 1fr - Blocos: texto explicativo (esquerda) + código (direita), depois invertido (
.reverse) - Texto:
<h3>,<p>,<ul>com bullets coloridos (.accent) - Código:
<pre>com syntax highlighting
4. Comparativo com Concorrentes
- Classe
.alt-bg - Header centralizado: "How It Compares" + subtítulo
<table class="comparison-table">: cabeçalho com nomes (produto Koder + concorrentes)- Células:
✓verde (.check { color: #22c55e }) para features presentes,—cinza (.cross) para ausentes overflow-x: autopara scroll horizontal em mobile
5. FAQ com Accordion (se aplicável)
- Perguntas/respostas em acordeão expansível
- Implementação via
<details>/<summary>ou JS toggle - Tipografia: pergunta em
font-weight: 700, resposta emvar(--text2)
6. CTA Final
- Fundo:
var(--gradient)(gradiente da cor dominante),color: #fff <h2>com pergunta motivacional (ex: "Ready to get started?")<p>com subtítulo- Botões: fundo branco com texto na cor accent, + botão ghost
- Padding generoso:
100px 24px
7. Footer
border-top: 1px solid var(--border),padding: 40px 24px- Layout: flexbox,
justify-content: space-between - Esquerda:
© 2026 Koder. All rights reserved. - Direita: links (Docs, API, Architecture) — variam por produto. Nunca incluir links para o repositório ou Koder Flow.
- Links:
color: var(--text2), hovercolor: var(--accent),font-size: 13px
JavaScript — Comportamentos
Toggle de Tema (2 modos: claro ↔ escuro)
O botão alterna apenas entre claro e escuro. Não há opção "dispositivo" no toggle.
Comportamento padrão (primeiro acesso / cache limpo): Quando não há preferência salva em localStorage, usar a preferência do sistema operacional (prefers-color-scheme). Se o SO preferir escuro, abrir em escuro; caso contrário, abrir em claro.
Persistência: Quando o usuário clica no botão, a escolha é salva em localStorage e prevalece sobre a preferência do SO em acessos futuros.
function toggleTheme() {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
const next = isDark ? 'light' : 'dark';
localStorage.setItem('theme', next);
applyTheme();
}
function applyTheme() {
const saved = localStorage.getItem('theme');
const isDark = saved ? saved === 'dark' : matchMedia('(prefers-color-scheme:dark)').matches;
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
const sun = document.getElementById('icon-sun');
const moon = document.getElementById('icon-moon');
if (sun) sun.style.display = isDark ? 'none' : 'block';
if (moon) moon.style.display = isDark ? 'block' : 'none';
}
matchMedia('(prefers-color-scheme:dark)').addEventListener('change', () => {
if (!localStorage.getItem('theme')) applyTheme();
});
applyTheme();- Ícones SVG inline: sol (claro), lua (escuro)
- Persistência em
localStorage(chavetheme, valores:light,dark) - Sem valor salvo → segue preferência do SO
Navbar Scroll Effect
window.addEventListener('scroll', () => {
document.getElementById('navbar').toggleAttribute('data-scrolled', window.scrollY > 20);
});Mobile Nav Toggle
document.querySelector('.nav-toggle').onclick = () => {
document.querySelector('.nav-links').classList.toggle('open');
};Fade-In on Scroll (IntersectionObserver)
Opcional. Se usado, aplicar classe .fade-in nos cards e ativar com observer (threshold: 0.1).
OG Image (Imagem para Compartilhamento Social — WhatsApp, Telegram, Twitter, Slack, etc.)
Obrigatório para todas as landing pages de produto. Toda landing deve ser configurada para exibir uma thumb rica quando a URL for compartilhada via WhatsApp, Telegram, TwitterX, Slack, Discord, iMessage, LinkedIn ou qualquer outro apprede social que consuma Open Graph / Twitter Card. Sem isso, o link aparece como texto puro e perde impacto.
Conteúdo obrigatório da thumb
A imagem deve conter, nesta ordem de prioridade:
- Ícone do produto — o
icon.svgdo produto, destacado visualmente (lado esquerdo ou centralizado, com tamanho generoso ≈ 30% da altura). - Slogan do produto — a tagline curta oficial do produto em texto grande e legível (ex: "The database for the age of AI"). É o elemento de leitura principal.
- Frase complementar (se couber) — uma linha adicional de apoio (subtítulo, descrição de 1 frase, ou call-to-action) posicionada abaixo do slogan, somente se houver espaço visual sem poluir. Se a thumb já estiver cheia, omitir.
- (Opcional) URL do produto (
{subdominio}.koder.dev) discreta em um canto.
Evitar: blocos de texto longos, múltiplos parágrafos, tabelas, features listadas, ou qualquer coisa que não seja legível no preview reduzido do WhatsApp (tipicamente ≈ 400px de largura renderizada).
Especificações técnicas
- Dimensão: 1200×630px (ratio 1.91:1, exigência do Open Graph / WhatsApp)
- Formato: PNG (rasterizado a partir de SVG fonte)
- Arquivos:
site/og-image.svg(fonte editável) +site/og-image.png(rasterizado para servir) - Fundo: escuro (
#0b1120) ou gradient da cor accent do produto - Rasterização:
rsvg-convert -w 1200 -h 630 og-image.svg > og-image.png - Peso: manter
.pngabaixo de 300 KB (WhatsApp pode descartar thumbs muito grandes)
Meta Tags (já descritas na seção Head)
Usar URL absoluta HTTPS: https://{subdominio}.koder.dev/og-image.png. URLs relativas não funcionam no WhatsApp/Telegram — o scraper precisa da URL completa. Incluir sempre og:image:width=1200 e og:image:height=630 para acelerar o render da thumb.
Validação
Após deploy, testar o preview com:
- WhatsApp: enviar o link para si mesmo em uma conversa de teste
- OpenGraph.xyz ou metatags.io para debug visual
curl -A 'WhatsApp/2.0' -I https://{subdominio}.koder.devpara verificar que a resposta entrega as meta tags corretas
Responsividade
Regra: toda landing page de produto deve funcionar sem problemas em dispositivos móveis. Isso é obrigatório — não opcional.
Onde as breakpoint rules vivem
As regras de responsividade (@media (max-width: 768px) e @media (max-width: 480px)) podem ser declaradas em duas localizações, e o auditor (/k-audit-landings) aceita qualquer uma:
- Inline no próprio
<style>da landing — pattern original, usado por produtos com landing standalone. - External stylesheet linkado via
<link rel="stylesheet">— pattern usado quando a landing delega a estilo compartilhado (ex.:<link rel="stylesheet" href="/ecosystem.css">nas Áreas,<link rel="stylesheet" href="/sdk/koder-web-kit.css">em produtos que herdam o KDS). Nesse caso o arquivo externo já carrega os breakpoints + classes.nav-toggle,.nav-links, hamburger CSS, etc.
O auditor deve verificar: se o HTML linka
ecosystem.cssOUkoder-web-kit.cssOU qualquer stylesheet local que contenha as media queries canônicas, considerar OK. Caso contrário, exigir inline.
Breakpoints
max-width: 768px: Navbar vira hamburger (.nav-togglevisível,.nav-linkscolapsado), hero de 2 colunas vira 1 coluna, code sections viram 1 coluna, botão Login oculto (display: none)max-width: 480px: Fontes menores, todos os grids em 1 coluna, botões empilhados verticalmente, padding lateral reduzido (≥ 16px)
Regras obrigatórias
- Nenhum elemento deve causar overflow horizontal — a página nunca deve ter scrollbar horizontal em mobile
- Todos os grids
1fr 1frcolapsam para1fremmax-width: 768px - Texto do hero centralizado em mobile
- Tabela comparativa com
overflow-x: autopara scroll horizontal interno - Botões com área de toque mínima de 44×44px (padding adequado)
- Fontes nunca menores que
14pxem mobile - Imagens e SVGs com
max-width: 100%para não vazar o container - Nenhum
position: fixed(exceto navbar) que interfira com o conteúdo em viewports pequenas
iOS Safari — regras adicionais obrigatórias
iOS Safari tem comportamentos específicos que o Chrome DevTools não simula:
100vhbug: em iOS Safari o100vhinclui a barra de endereço, causando corte. Usarmin-height: 100svh(small viewport height) como fallback, oumin-height: -webkit-fill-available. Nunca depender de100vhpara seções full-screen.- Safe areas (notch / barra inferior): adicionar padding via variáveis de ambiente CSS para não sobrepor elementos de navegação do sistema:
body { padding-bottom: env(safe-area-inset-bottom); } nav { padding-top: env(safe-area-inset-top); } position: fixedem iOS: elementos fixos dentro de elementos comoverflow: scrollficam estáticos. Garantir que a navbar fixa esteja no nível de<body>, nunca dentro de um container com overflow.- Meta viewport: usar exatamente
content="width=device-width, initial-scale=1". Nunca adicionaruser-scalable=no(acessibilidade) nemmaximum-scale=1(impede zoom do usuário).
Hover em touchscreen — regras obrigatórias
Estados :hover puros em CSS "prendem" em dispositivos touch (o estilo persiste após o toque). Regra: todo :hover visual deve usar @media (hover: hover) para ser aplicado apenas quando o dispositivo tem cursor real:
.card:hover { transform: translateY(-2px); box-shadow: ...; }
@media (hover: hover) {
.card:hover { transform: translateY(-2px); box-shadow: ...; }
}Exceção: :hover usado apenas para mudança de color em links de texto pode ficar sem wrapper — o impacto visual "preso" é aceitável nesses casos.
Verificação obrigatória antes de deploy
Toda landing deve ser verificada nos seguintes pontos antes de ir ao ar:
- Chrome DevTools → modo responsivo → testar em:
- 375px (iPhone SE / padrão mínimo)
- 390px (iPhone 14)
- 768px (tablet / breakpoint de referência)
- Sem overflow horizontal: com DevTools aberto, verificar que
document.documentElement.scrollWidth === window.innerWidth - Navbar hamburger funcional: menu abre e fecha em 375px
- Hero legível: texto do h1 visível sem zoom, sem truncamento
- Grids colapsados: nenhuma grade de 2+ colunas visível abaixo de 480px
- Tabela responsiva: scroll horizontal interno funciona em mobile
- Botões clicáveis: área de toque visualmente adequada (mín. 44×44px)
- Fontes legíveis: nenhum texto com fonte < 14px
- iOS Safari: testar
100svh/env(safe-area-inset-*)se houver Safari disponível; ou validar via BrowserStack / Xcode Simulator - Hover não prende: verificar que cards e botões não ficam com estilo de hover travado após toque em mobile (Chrome DevTools → Touch emulation)
Padrão de URL para Produtos Koder
A URL de todo produto Koder é <produto>.koder.dev — sem prefixo app., sem path /app.
Nunca usar
app.<produto>.koder.dev— esse padrão de duplo-subdomain não existe na Koder Stack e não tem precedente nas principais suites de produtos do mercado (Google, Microsoft, Adobe).
Produtos com web app (padrão)
A maioria dos produtos Koder são web apps onde a URL raiz serve o próprio produto. A mesma URL (<produto>.koder.dev) serve conteúdos distintos dependendo do estado de autenticação — sem mudança de URL visível na barra de endereços:
Usuário acessa produto.koder.dev
↓
App (Flutter/JS) carrega o mesmo bundle
↓
Verifica token no storage local (client-side)
├── sem token → renderiza landing screen (hero, features, botão Login)
└── token válido → renderiza tela principal do produtoEsse comportamento é análogo ao de GitHub (github.com), Notion (notion.so), Linear (linear.app) e Vercel (vercel.com): a mesma URL, o servidor entrega sempre o mesmo bundle, e o cliente decide o que renderizar.
Exemplos:
| URL | Produto | Serve na raiz |
|---|---|---|
hub.koder.dev |
Koder Hub | Web app (loja aberta) |
flow.koder.dev |
Koder Flow | Web app (git forge) |
mail.koder.dev |
Kmail | Web app (webmail) |
kode.koder.dev |
Kode | Web app (AI assistant) |
OAuth callback URL
O redirect_uri registrado no Koder ID para web apps segue o padrão:
https://<produto>.koder.dev/auth/callbackExemplos:
https://kode.koder.dev/auth/callbackhttps://talk.koder.dev/auth/callbackhttps://hub.koder.dev/auth/callback
Nunca registrar https://app.<produto>.koder.dev/... como redirect URI.
Landing page em /about
Como a raiz serve o app, a landing page marketing fica em /about:
- O
og:urlda landing aponta parahttps://{produto}.koder.dev/about - O
og:imageaponta parahttps://{produto}.koder.dev/about/og-image.png - A landing e seus assets são deployados em
/var/www/{produto}.koder.dev/about/ - O servidor tem
spa = trueno koder-jet; arquivos estáticos emabout/são servidos diretamente sem conflito com o SPA
Fluxo de URLs completo
produto.koder.dev (não logado → landing screen no app)
↓ clica "Log In"
id.koder.dev/oauth/v2/authorize
?redirect_uri=https://produto.koder.dev/auth/callback
↓ autentica
produto.koder.dev/auth/callback?code=... (OAuth callback)
↓ app processa e salva token
produto.koder.dev (logado → tela principal do produto)Produtos sem web app (CLI, SDK, desktop-only)
Produtos que não têm web app (ferramentas de linha de comando, SDKs, bibliotecas, apps desktop-only) seguem o padrão das seções anteriores desta spec: landing page estática em site/index.html deployada na raiz de <produto>.koder.dev.
Deploy
- O site é deployado no servidor s.forge em
/var/www/{subdominio}.koder.dev/(s.k.lin foi descomissionado em 2026-04-16; verinfrastructure/servers.md) - Configurar entrada em
/etc/koder-jet/sites.toml - DNS: criar registro A em ClouDNS apontando para
177.136.231.237(s.forge). A API ClouDNS só aceita chamadas vindas desse mesmo IP — sempre rodar ocurlviassh -p 220 rodrigo@177.136.231.237(verinfrastructure/dns.md). - HTTPS automático via koder-jet (ACME/Let's Encrypt)
- Após deploy, sempre escrever a URL no chat
Seções Opcionais (conforme tipo de produto)
Dependendo do tipo de produto, algumas seções adicionais podem ser incluídas:
- Pricing: Cards de planos (FreeProEnterprise) — para produtos SaaS
- Architecture: Diagrama de camadas em bloco de código estilizado
- Integrations: Cards de outros produtos Koder que se integram
- Key Specs: Cards com números grandes (para produtos de infraestrutura)
- Timeline: Para projetos com cronograma (ex: datacenter)
- Location/Map: Para produtos com componente geográfico
A ordem dessas seções opcionais é flexível, mas devem vir entre Features e Comparativo.
Referência de Implementação
O arquivo platform/kdb/site/index.html (deployado em kdb.koder.dev) é a implementação de referência canônica deste padrão. Em caso de dúvida sobre qualquer aspecto visual ou estrutural, consultar esse arquivo.
Requisitos normativos (gramática RFC-013 — piloto do backfill #185)
Requirement: Mobile responsiveness
Every product landing SHALL declare a device-width viewport meta and SHALL render with at least one @media breakpoint; it SHALL NOT rely on overflow-x: hidden on html/body to mask horizontal overflow.
Scenario: Viewport meta present
- GIVEN a product landing
index.html - WHEN the static gate inspects its head
- THEN a
<meta name="viewport">withwidth=device-widthis found
Scenario: Breakpoints exist
- GIVEN the landing inline styles and local CSS
- WHEN the static gate scans them
- THEN at least one
@mediaquery exists
Scenario: Kodix-class regression blocked
- GIVEN a landing with zero breakpoints
- WHEN
koder-spec-audit landing-responsiveruns in CI - THEN the run exits non-zero naming LR2 and the release does not publish
Requirement: Self-contained single-file landing
A product landing SHALL be a single monolithic index.html with no external runtime dependencies (no CDN scripts/styles), per the privacy and resilience rules of this spec.
Scenario: No external runtime deps
- GIVEN a product landing
index.html - WHEN its
<script src>and<link href>URLs are inspected - THEN none resolve to a non-Koder external origin