1. Contexto: seu App — um convidado na casa do ChatGPT
Antes de desenhar botões e escolher fontes, é importante encarar a realidade: o usuário não abre “seu site”, ele está no ChatGPT. O ChatGPT já tem seus próprios:
- esquema de cores,
- fontes e tamanhos,
- espaçamentos e composição dos elementos.
Seu widget é exibido dentro desse ambiente, na maioria das vezes em um iframe. Daí uma conclusão importante: visualmente o App deve parecer uma extensão natural da interface do ChatGPT, não um banner trazido de 2008.
As diretrizes oficiais da OpenAI são justamente sobre isso: não quebrar as cores e fontes do sistema, adicionar apenas acentos de marca moderados e seguir a tipografia e a grade básicas da plataforma.
Em termos práticos, isso significa três coisas.
Primeiro, o fundo, a cor base do texto e a tipografia padrão — tudo isso deve ser herdado do ChatGPT ou das variáveis do sistema, e não “sou artista e é assim que eu vejo”.
Segundo, se você quiser “seu estilo”, ele deve se concentrar nos acentos: botões principais, badges, estados em destaque. Mas nada de fundo arco-íris ou fonte customizada tipo Comic Sans — mesmo que seu coração peça isso.
Terceiro, os modos inline e fullscreen do mesmo App devem parecer parte do mesmo mundo: mesmas cores de CTA, mesmos raios e espaçamentos das cards, mesma tipografia. O usuário não deve sentir que ao mudar de inline para fullscreen caiu em outro produto.
Vamos por camadas: cores e temas, tipografia, espaçamentos e grade, e depois — como Tailwind e shadcn/ui ajudam a juntar tudo isso.
Insight
A sandbox do ChatGPT não apenas limita a funcionalidade do seu widget, como também adiciona seus próprios estilos.
Primeiro — este é o cabeçalho HTML
Original do site:
<html lang="ru">
Na sandbox:
<html lang="en-US" data-theme="light" class="light" style="--safe-area-inset-top: 0px; --safe-area-inset-bottom: 0px; --safe-area-inset-left: 0px; --safe-area-inset-right: 0px;">
Segundo — estes são estilos CSS nativos, para que seu widget se pareça mais com o ChatGPT:
<style>
html,body,#root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;padding:0}
html,body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Helvetica Neue,Arial,sans-serif!important}
button,input,textarea,select{font-family:inherit}
html{background-color:#fff}
html.dark{background-color:#212121}
html.mobileSkybridge.dark{background-color:#000}
@supports (font: -apple-system-body){html.mobileSkybridge{font:-apple-system-body}}
</style>
É melhor lembrar disso — haverá menos surpresas.
2. Temas e cores: vivendo nos universos claro e escuro
Tema claro e tema escuro
A interface do ChatGPT já suporta temas claro e escuro. Seu widget é exibido em um deles, e o usuário pode alternar entre eles a qualquer momento. Portanto, fundos branco ou preto “hardcoded” — são uma armadilha em potencial.
Imagine um widget que pinta fundo branco e texto preto. No tema claro ele passa. No escuro — é um holofote nos olhos. A situação inversa com fundo preto no tema claro não é melhor. É por isso que as recomendações oficiais sugerem não fixar cores, e sim basear-se no tema/variáveis do host.
No Apps SDK, o ambiente geralmente oferece uma API ou variáveis CSS para o tema atual. A documentação menciona opções como window.openai.theme e o uso das variáveis CSS padrão do ChatGPT. Além disso, ainda existem prefers-color-scheme e as utilitárias dark: no Tailwind.
A ideia é mais ou menos esta: seu widget deve se ajustar automaticamente ao tema do host em coisas como:
- fundo das cards (um pouco mais claro/escuro que o fundo base),
- cor do texto (contraste suficiente),
- bordas, sombras e estados de hover.
Exemplo de um wrapper simples de tema com Tailwind:
// components/AppShell.tsx
export function AppShell({ children }: { children: React.ReactNode }) {
return (
<div className="bg-background text-foreground">
{/* bg-background/text-foreground são substituídos pelo tema */}
{children}
</div>
);
}
Onde bg-background e text-foreground — não são classes padrão do Tailwind, mas aliases para variáveis CSS do seu design system (por exemplo, de shadcn/ui), que por sua vez estão vinculadas ao tema claro/escuro do ChatGPT.
Cores do sistema versus acentos de marca
A OpenAI é bastante direta: as cores do sistema do ChatGPT não podem ser alteradas. Texto base, painéis padrão do chat, fundo — tudo isso deve permanecer nas cores gerais da plataforma. Seu espaço — os acentos dentro do widget: botões de CTA (call to action — ação principal), badges, pequenos elementos.
Na prática do GiftGenius, isso significa que:
- o fundo da card de presente é próximo ao do sistema,
- o texto usa a cor padrão, como no chat,
- a cor da marca GiftGenius é usada no botão principal “Escolher presente” e, talvez, em um badge de desconto.
Pode-se imaginar uma tabela:
| Elemento | O que fazer | O que evitar |
|---|---|---|
| Fundo do widget | Herdar do ChatGPT | Aplicar um gradiente de marca chamativo |
| Texto principal | Herdar a cor do sistema | Deixar colorido/cinza a ponto de ficar ilegível |
| Botão principal de CTA | Usar a cor de acento da marca | Colocar “arco-íris” com 5 cores |
| Botões/links secundários | Parecidos com os links do sistema | Tão chamativos quanto o CTA |
| Sombras/bordas | Suaves, minimalistas | Bordas neon grossas |
Mini-exemplo com Tailwind para a cor principal:
// styles/globals.css (trecho)
:root {
--gift-accent: 222 84% 56%; /* hsl */
}
.dark {
--gift-accent: 222 84% 64%; /* um pouco mais claro para dark */
}
// components/GiftButton.tsx
export function GiftButton({ children }: { children: React.ReactNode }) {
return (
<button className="rounded-md bg-[hsl(var(--gift-accent))] px-4 py-2 text-sm font-medium text-white hover:opacity-90">
{children}
</button>
);
}
Você não toca no fundo de todo o widget, mas aplica discretamente sua cor ao botão principal de CTA.
Contraste e WCAG sem fanatismo
Mesmo que você não vá prestar prova de WCAG, há uma diretriz simples: o texto deve ser legível. Quanto menor a fonte, maior deve ser o contraste. Nos cursos de acessibilidade, recomenda-se manter o contraste do texto em relação ao fundo em pelo menos ~4,5:1 para o texto principal. Não vamos entrar a fundo nos padrões aqui: precisamos de um norte prático — contraste suficiente entre texto e fundo.
Na prática:
- não use texto cinza‑claro em fundo cinza‑claro por “elegância”;
- evite texto cinza‑escuro em fundo quase preto no tema escuro;
- verifique pelo menos “a olho”: se você precisa semicerrar os olhos, o usuário também vai sofrer.
Você pode combinar consigo mesmo: qualquer texto secundário (legendas, dicas) ainda é legível, apenas um pouco menos chamativo em cor e tamanho, mas não “totalmente fantasmagórico”.
3. Tipografia: fontes do sistema, hierarquia e um pouco de bom senso
Fontes do sistema em vez de uma “família própria”
As diretrizes oficiais recomendam usar as fontes do sistema da plataforma, como SF Pro, Roboto e equivalentes, e não inserir seu webfont. A razão não é só desempenho, mas também o fato de que seu App deve parecer um elemento nativo da interface.
No aplicativo Next.js, é mais fácil fazer com que tudo dentro do widget herde a pilha de fontes do sistema. No Tailwind isso geralmente já está configurado como font-sans. Se quiser ser mais explícito:
// app/layout.tsx (trecho)
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className="font-sans antialiased">
{children}
</body>
</html>
);
}
Não é necessário incluir três famílias via Google Fonts. Para o GiftGenius de estudo, uma fonte estritamente do sistema vai parecer mais arrumada do que um Lobster qualquer.
Hierarquia de tamanhos
Bastam alguns níveis de tipografia: título do bloco, subtítulo/parâmetro‑chave, texto principal e legenda.
Para a card inline do GiftGenius, por exemplo, é prático combinar estes níveis:
| Função | Classe Tailwind | Exemplo |
|---|---|---|
| Título da card | |
Nome do presente |
| Parâmetro‑chave | |
Preço ou categoria |
| Descrição | |
Descrição breve |
| Legenda/detalhes | |
Entrega, loja |
Mini‑componente de card:
// components/GiftCard.tsx
type GiftCardProps = {
title: string;
price: string;
description: string;
};
export function GiftCard({ title, price, description }: GiftCardProps) {
return (
<div className="rounded-lg border bg-card p-4">
<h3 className="text-base font-semibold">{title}</h3>
<p className="mt-1 text-sm font-medium text-emerald-600">{price}</p>
<p className="mt-2 text-sm text-muted-foreground">{description}</p>
</div>
);
}
Aqui:
- não há um H1 gigante;
- todas as informações são compactas;
- a hierarquia é percebida pelo tamanho e peso da fonte.
Alinhamento e comprimento das linhas
A interface de chat geralmente é estreita, especialmente no inline. Por isso, não é preciso esquentar a cabeça com tipografia complexa: alinhamento à esquerda e comprimento de linha de 40–60 caracteres são bem confortáveis.
Hábitos úteis:
- não centralizar textos longos na card — são mais difíceis de ler;
- não escrever TUDO EM CAIXA ALTA;
- não reduzir o texto base para menos de 14 px (no Tailwind é text-sm) sem uma razão muito forte.
Em caso de dúvida, lembre-se: quem vai ler é uma pessoa cansada no celular no metrô, não você num monitor perfeito de 27 polegadas.
4. Espaçamentos, densidade e grid
Se cores e fontes são as “tintas”, os espaçamentos são o ar. Sem eles, até as cards mais arrumadas viram uma bagunça.
A OpenAI ressalta em suas recomendações: os elementos não devem ficar “colados”, é melhor pegar espaçamentos e raios da sua design system ou framework de UI (Tailwind, shadcn/ui etc.), e o scroll horizontal deve ser minimizado.
Princípio de “deixar respirar”
O padrão mais simples: usar uma escala única de espaçamentos (por exemplo, passo de 4 px ou 8 px) e não inventar um tamanho “seu” a cada vez. No Tailwind isso já vem pronto: p-2, p-3, p-4, gap-3 etc.
Exemplo de uma pequena grade para a lista de presentes no inline:
// components/GiftListInline.tsx
export function GiftListInline({ children }: { children: React.ReactNode }) {
return (
<div className="flex flex-col gap-3">
{children}
</div>
);
}
Cada card é separado com gap-3, tem seus p-4 internos, e isso já basta para a lista não parecer uma “lençolada” de texto.
Colunas: inline versus fullscreen
Nos documentos de UX para o Apps SDK, recomenda-se manter 1–2 colunas de cards no widget inline, e no fullscreen podem ser 2–3, se houver largura suficiente.
O motivo é simples: no chat a largura é limitada, especialmente no mobile, e duas colunas já estão no limite da legibilidade. No fullscreen, você tem praticamente a tela toda e pode dispor o conteúdo com mais densidade.
Esquema aproximado:
flowchart LR
subgraph Inline
A[1 coluna
tela estreita]
B[2 colunas
no desktop]
end
subgraph Fullscreen
C[2 colunas
cenário principal]
D[3 colunas
para grades/catálogos]
end
Implementação no Tailwind para o GiftGenius:
// components/GiftGrid.tsx
export function GiftGrid({ fullscreen, children }: { fullscreen?: boolean; children: React.ReactNode }) {
const base = fullscreen ? "grid-cols-2 md:grid-cols-3" : "grid-cols-1 sm:grid-cols-2";
return (
<div className={`grid gap-4 ${base}`}>
{children}
</div>
);
}
No modo inline, você dá uma coluna no mobile e duas em telas mais largas. No fullscreen, você usa 2–3 colunas conforme a largura.
Evitando scroll horizontal
O chat é naturalmente vertical. O usuário está acostumado a rolar para baixo, não para os lados. Portanto:
- procure fazer com que tabelas e cards caibam na largura do contêiner;
- não defina larguras fixas como width: 600px; para um elemento que vive num contêiner flexível;
- use max-w-full, overflow-x-auto apenas como “último recurso”, não como padrão.
Para as cards do GiftGenius é conveniente definir w-full e deixar a grade decidir quantas cabem por linha.
5. Responsividade dentro do contêiner do ChatGPT
No frontend “normal” você tem controle total sobre o viewport. No ChatGPT esse controle é limitado: seu widget está no contêiner do chat, que tem seus tamanhos e regras. O Apps SDK oferece algumas pontes úteis: altura máxima, safe area para recortes de tela, tipo de dispositivo etc.
maxHeight e limitações verticais
No modo inline, o ChatGPT pode limitar a altura do widget, para ele não “engolir” a tela toda. Hooks como useMaxHeight() permitem saber quanto espaço se pode ocupar honestamente agora, e adicionar scrolls internos onde for necessário.
Pseudo-código:
// Pseudo-código, não é uma API real:
const maxHeight = useMaxHeight();
return (
<div style={{ maxHeight, overflowY: "auto" }}>
<GiftGrid>{/* ... */}</GiftGrid>
</div>
);
Assim você evita a situação em que o widget encosta no rodapé da tela e as mensagens do chat “somem” para a vida passada.
safeArea e dispositivos móveis
Em dispositivos móveis, podem existir recortes, barra de status e painéis do sistema. O Apps SDK permite obter safeArea e ajustar os espaçamentos para nada “ir parar” debaixo do notch do telefone.
No nível do CSS, é possível adicionar paddings extras:
// Pseudo-código
const { top, bottom } = useSafeArea(); // digamos que retorne { top: 8, bottom: 16 }
return (
<div style={{ paddingTop: top, paddingBottom: bottom }}>
{/* conteúdo */}
</div>
);
Para nós, o mais importante é o princípio: o widget deve respeitar o limitador de altura e a área segura, caso contrário o UX vira “role mais três vezes para ver o botão”.
6. Tailwind e shadcn/ui: não reinventar botões do zero
Escrever toda a UI na mão com CSS puro hoje em dia é quase esporte hardcore. No contexto de ChatGPT Apps, é muito mais simples pegar uma biblioteca consolidada e ajustá-la aos requisitos da plataforma. No curso, usamos Tailwind e shadcn/ui como stack base.
Tailwind como dicionário de espaçamentos e cores
O Tailwind fornece um conjunto conveniente de utilitárias:
- espaçamentos (p-4, gap-3),
- tamanhos (text-sm, text-base),
- cores (text-muted-foreground, bg-card), que em shadcn/ui e sistemas similares já estão vinculadas às variáveis CSS do tema.
Isso se encaixa perfeitamente nos requisitos do ChatGPT:
- você não inventa espaçamentos arbitrários,
- define tamanhos de texto de forma consistente,
- não quebra as cores do sistema, e sim usa tokens previamente acordados.
shadcn/ui como conjunto de componentes elegantes
shadcn/ui (e bibliotecas parecidas) fornece Card, Button, Input, Tabs etc., já ajustados ao tema do Tailwind. Isso acelera muito a montagem de uma interface limpa e minimalista, especialmente para as cards do GiftGenius.
Exemplo de GiftCard usando shadcn/ui:
// components/GiftCardShadcn.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
type GiftCardProps = {
title: string;
price: string;
description: string;
};
export function GiftCardShadcn(props: GiftCardProps) {
return (
<Card>
<CardHeader>
<CardTitle className="text-base">{props.title}</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<p className="text-sm font-medium text-emerald-600">{props.price}</p>
<p className="text-sm text-muted-foreground">{props.description}</p>
<Button className="mt-2">Escolher presente</Button>
</CardContent>
</Card>
);
}
O principal aqui não é o shadcn em si, e sim os princípios:
- o título não é gigante;
- a descrição é legível;
- o botão é estilizado pela design system comum, não “do seu jeito”.
Ajuste para o ChatGPT
Em um projeto real, você pode ajustar a paleta para o estilo minimalista do ChatGPT: fundo claro, sombras suaves, raios discretos. O plano do módulo incentiva basear-se numa design system existente, e não criar seu próprio universo.
Abordagem simples:
- pegar a base do shadcn/ui;
- manter a fonte do sistema;
- configurar uma ou duas cores de marca nos tokens primary / accent;
- garantir que inline e fullscreen usam os mesmos tokens.
Assim você obtém um núcleo visual coeso sem esforço extra.
7. Linguagem visual do GiftGenius: juntando tudo
Vamos sistematizar o que já podemos considerar “linguagem visual” para o nosso GiftGenius hipotético.
Primeiro, o esquema de cores. Fundo e texto são herdados do ChatGPT; a cor de acento — discreta, mas perceptível — é aplicada aos botões de CTA e, talvez, aos badges de desconto. No tema escuro, esse acento é um pouco mais claro, para manter o contraste.
Segundo, tipografia. Fonte do sistema, tamanhos text-sm para o texto principal e text-base para os títulos das cards. Itálico e caixa alta são usados raramente, apenas quando fizer sentido. Os títulos no wizard fullscreen ficam um passo acima, mas ainda sem text-4xl gritantes.
Terceiro, espaçamentos e grid. No modo inline, a lista de presentes tem uma ou duas colunas com gap-3/gap-4, cada card com p-4. No fullscreen — 2–3 colunas, passos do assistente com intervalos suficientes entre formulários e botões. Nada de scroll horizontal nos cenários principais.
Um pequeno diagrama para as telas do GiftGenius:
graph TD A[Inline: lista de presentes] --> B[GiftCard
cores/tipografia/CTA] A --> C[GiftGrid 1–2 colunas] D[Fullscreen: assistente de seleção] --> E[Etapa 1
formulário] D --> F[Etapa 2
filtros/faixas] D --> G[Etapa 3
confirmação] B --> H[GiftButton
acento de marca]
Quarto, compatibilidade com o contexto do host. Todos os elementos se comportam bem ao alternar entre tema claro/escuro, respeitam maxHeight e não ficam escondidos sob a safe‑area. As cores não brigam com o ChatGPT, e os botões de CTA têm aparência uniforme em todos os lugares, para que o usuário saiba onde clicar por memória muscular.
Esse conjunto de decisões já torna seu app apresentável não só para desenvolvedores, mas também para usuários reais ou product managers: haverá o que discutir além de “aqui temos MCP, ali Agents SDK”.
8. Acessibilidade (Accessibility Guidelines, WCAG AA)
Já mencionamos de passagem a WCAG quando falamos do contraste entre texto e fundo na seção 2.3. Lá nos interessava um ponto prático — não matar a legibilidade. Agora vamos olhar a acessibilidade um pouco mais de perto: como a interface é percebida por quem não a vê com os olhos, e pelo próprio ChatGPT no modo de voz.
WCAG AA — é o nível do padrão de acessibilidade do conjunto internacional de regras WCAG (Web Content Accessibility Guidelines), que descreve como tornar sites e interfaces acessíveis para pessoas com diferentes limitações de visão, motricidade, cognição etc.
A ideia principal do WCAG AA — transformar a interface de simplesmente “teoricamente acessível” em realmente utilizável. Esse nível inclui dezenas de requisitos que afetam diretamente a qualidade da interação. Entre eles — aquele limiar de contraste entre texto e fundo por volta de 4,5:1, de que já falamos, bem como requisitos para tamanhos de áreas clicáveis, estados de foco, erros em formulários etc.
Uma camada à parte — suporte a tecnologias assistivas, incluindo leitores de tela. O nível AA exige semântica correta: títulos devem ser títulos, listas — listas, botões — botões, e elementos interativos devem ter funções e textos alternativos adequados. Isso permite que usuários de VoiceOver, TalkBack ou NVDA entendam plenamente a estrutura e o sentido da interface.
Screen reader (leitor de tela)
Screen reader (leitor de tela) — é um programa que narra e/ou estrutura o conteúdo da tela, permitindo que pessoas com deficiência visual usem computador, smartphone ou web apps.
Mas um screen reader não é só “um programa que lê texto em voz alta”. É um sistema completo de interação com a interface, que transforma a apresentação visual do site ou app em uma navegação sonora e estruturada acessível à percepção.
ChatGPT, leitor de tela e WCAG AA
Se o seu widget está marcado segundo os princípios da WCAG AA (funções corretas, títulos, rótulos de botões), ele se torna compreensível não só para leitores de tela, mas também para o ChatGPT no modo de voz. O usuário fala com o ChatGPT por voz e o modelo, apoiando-se na mesma estrutura semântica, pode “virtualmente” fazer o mesmo que uma pessoa: encontrar elementos de interface, clicar em botões, seguir links etc.
Segundo os requisitos do ChatGPT Store, o suporte ao padrão WCAG AA é obrigatório para cada aplicativo. Cada widget e cada tool devem ter descrições de alta qualidade e detalhadas, e o markup — estar em conformidade com os padrões WCAG AA: semântica correta, rótulos legíveis, estados previsíveis.
Portanto, o requisito WCAG AA não é uma “feature para pessoas com necessidades especiais”, mas um princípio básico de design para que o ChatGPT Apps funcione plenamente com seu aplicativo, inclusive quando o usuário interage por voz.
Voltaremos a cenários de UX de voz, às diferenças entre diálogo por voz e por texto e aos requisitos do ChatGPT Store em outras aulas deste módulo e no módulo de publicação do App. Mas tudo isso se apoia no fundamento que você viu agora: modo de voz = multimodalidade + acessibilidade (WCAG AA + leitores de tela).
9. Erros comuns de design visual em ChatGPT App
Erro nº 1: Fundos branco/preto e cores de texto “hardcoded”.
O desenvolvedor pinta fundo branco e texto preto sem pensar no tema escuro. No tema claro até funciona, no escuro — vira um holofote e estraga o UX. O mais correto é usar as cores do sistema e o tema do host (variáveis CSS, prefers-color-scheme ou a API do Apps SDK), e guardar suas cores apenas para acentos.
Erro nº 2: Branding agressivo demais.
Aparece fundo com gradiente chamativo, fonte customizada, bordas coloridas. O widget passa a parecer um banner promocional, não parte da interface do ChatGPT. As diretrizes exigem o oposto: visual minimalista e “nativo”, com uso cuidadoso da cor da marca apenas em elementos-chave, como botões principais.
Erro nº 3: Ausência de hierarquia na tipografia.
Todos os textos com o mesmo tamanho e peso, ou o contrário — três níveis de título numa card pequena, ainda por cima em caixa alta. O usuário não entende o que é principal: nome, preço ou descrição. É melhor combinar previamente 3–4 níveis e segui-los em todo lugar: título, parâmetro‑chave, texto principal, legenda.
Erro nº 4: Elementos colados, sem espaçamentos.
As cards ficam grudadas, o texto encosta na borda, os botões colam no texto. No desktop isso até passa, no mobile vira ruído visual. Recomenda-se usar uma escala única de espaçamentos (por exemplo, classes do Tailwind p-4, gap-3) e não economizar no “ar”.
Erro nº 5: Tentar enfiar 4–5 colunas no modo inline.
O desenvolvedor ainda está mentalmente numa página de e-commerce e faz um grid de quatro cards estreitos no chat. Em tela larga é discutível, no mobile — ilegível, e ainda cria scroll horizontal. No widget inline, geralmente bastam uma ou duas colunas; deixe a terceira para o fullscreen.
Erro nº 6: Ignorar limitações de altura e safe‑area.
O widget desenha uma lista gigante sem scroll interno e sem considerar maxHeight, então os botões ficam “abaixo do fundo da tela”. Ou os elementos se escondem sob o recorte da tela no mobile. Use os dados de altura máxima e área segura para distribuir corretamente a altura e os espaçamentos internos.
Erro nº 7: Aparência inconsistente de botões e cards entre inline e fullscreen.
No modo inline o botão é verde e arredondado, no fullscreen — azul e quadrado. O usuário perde a sensação de um único produto. É preciso extrair os estilos base de botões e cards para um componente/tema comum e usá-los em todos os modos.
Erro nº 8: Fonte “autoral” e firulas decorativas.
Conectar um webfont pesado “para ficar bonito” quebra a consistência visual com o ChatGPT e às vezes prejudica o desempenho. As recomendações da plataforma pedem fontes do sistema e tipografia cuidadosa. Se você quer muito se expressar como designer — trabalhe melhor com ícones e microcopy, não com uma revolução tipográfica.
GO TO FULL VERSION