CodeGym /Cursos /ChatGPT Apps /Padrões inline: cartões, listas, carrosséis e CTAs

Padrões inline: cartões, listas, carrosséis e CTAs

ChatGPT Apps
Nível 8 , Lição 1
Disponível

1. O que é o modo inline e por que ele é o “padrão”

As diretrizes oficiais da OpenAI destacam: a apresentação inline é o modo principal para ChatGPT Apps. O widget inline é renderizado diretamente no feed do chat, junto da resposta do modelo, e inclui um pequeno bloco de UI (cartão, lista, carrossel) mais uma mensagem de follow‑up do GPT abaixo.

A ideia é simples: em vez de levar o usuário para uma interface grande separada, oferecemos um “estalo” visual compacto diretamente no contexto da conversa: o modelo explica o que aconteceu e quais são as próximas opções, e o widget apresenta, de forma limpa, a estrutura e as ações disponíveis.

Widget inline:

  • é leve em conteúdo e não exige muitos passos;
  • não empurra o usuário para uma navegação complexa com abas e barras de rolagem internas;
  • resolve uma ou duas tarefas pequenas: mostrar uma lista de opções, permitir escolher, confirmar uma ação, exibir status.

O modo fullscreen (falaremos dele na próxima aula) é necessário para assistentes mais extensos e conteúdo complexo. Agora, o ponto é: por padrão, pense em inline, e use fullscreen conscientemente, quando o inline claramente não der conta.

Nossa tarefa nesta aula é dominar com segurança três padrões principais de inline e, sobre eles, trabalhar com CTA:

  • cartões
  • listas
  • carrosséis

e também anexar corretamente a eles CTA (Call to Action).

2. Quando inline é melhor que fullscreen

Simplificando, o modo inline é um “assistente rápido”, enquanto fullscreen é um “aplicativo à parte dentro do ChatGPT”.

Inline é especialmente bom quando:

  • é preciso mostrar algumas opções e permitir escolher uma ou duas;
  • o resultado cabe em uma estrutura compacta: cartão de presente, resumo de pedido, mini tabela;
  • o usuário executa uma ação curta: “escolher”, “mostrar detalhes”, “alterar filtro”;
  • o diálogo permanece central: o GPT explica, brinca, comenta, e o widget apenas oferece um formulário ou visual convenientes.

Em termos do GiftGenius, inline é:

  • mostrar 3–5 melhores presentes para a pessoa escolhida;
  • oferecer um filtro rápido: “mostre apenas presentes digitais”;
  • confirmar a escolha: “aqui está o resumo do pedido, tudo certo?”.

O fullscreen será útil mais adiante para um assistente de checkout em três etapas. Por ora, ficamos na zona leve: um resultado de chamada de ferramenta → um widget inline.

Para ficar mais claro — uma tabela pequena:

Padrão Quando se encaixa perfeitamente Exemplo no GiftGenius
Cartão 1–3 entidades com parâmetros‑chave e CTA 3 melhores presentes
Lista 5–10 itens de texto; a legibilidade é importante lista de ideias sem imagens
Carrossel 3–8 opções semelhantes com visual; exige rolagem lista longa de presentes

Com isso em mente, vamos ao concreto: como esses padrões se materializam em UI e código. Em seguida, veremos cada padrão no mesmo formato: primeiro — o que é do ponto de vista de UX, depois — um componente React simples para o GiftGenius e, por fim, como tudo isso se encaixa no widget inline.

3. Cartões: a base do UI inline

O que é um cartão no contexto do Apps SDK

Segundo as diretrizes da OpenAI, um cartão inline é um widget leve, de uso individual, que exibe uma pequena quantidade de dados estruturados e 1–2 ações na parte inferior. Ele pode ter um título, imagem, algumas linhas de metadados e um botão CTA primário (mais um secundário opcional).

No GiftGenius, cada cartão representa um presente. Nele cabe:

  • nome do presente;
  • preço;
  • para quem se destina (por exemplo, “colega”, “amigo próximo”);
  • uma breve explicação de por que é uma boa opção;
  • um botão “Escolher este presente” ou “Ver detalhes”.

O cartão deve ser autossuficiente: ao bater o olho, o usuário já entende que entidade está à sua frente e qual é a ação principal.

Tipo de dados e um componente simples GiftCard

Primeiro, vamos definir o tipo de dados para o presente. Suponha que já tenhamos um ToolOutput com um array desses objetos; aqui nos interessa apenas a parte de UI.

// Estrutura geral do presente para UI
export type GiftSuggestion = {
  id: string;
  title: string;
  priceLabel: string;         // por exemplo, "≈ 40 $"
  recipientLabel: string;     // "para colega"
  reason?: string;            // explicação do modelo
  imageUrl?: string;
};

Agora vamos criar um componente React simples de cartão:

type GiftCardProps = {
  gift: GiftSuggestion;
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftCard({ gift, onSelect }: GiftCardProps) {
  return (
    <div className="flex flex-col gap-2 rounded-lg border p-3">
      <div className="text-sm font-medium">{gift.title}</div>
      <div className="text-xs text-muted-foreground">
        Para: {gift.recipientLabel} · {gift.priceLabel}
      </div>
      {gift.reason && (
        <div className="text-xs text-muted-foreground">{gift.reason}</div>
      )}
      <button
        className="mt-2 self-start rounded bg-primary px-3 py-1 text-xs text-primary-foreground"
        onClick={() => onSelect(gift)}
      >
        Escolher este presente
      </button>
    </div>
  );
}

Alguns detalhes de imediato:

  • não sobrecarregamos o cartão com texto; no máximo 2–3 linhas de metadados e uma explicação curta;
  • uma CTA principal — “Escolher este presente”; não tente enfiar 5 opções diferentes aqui;
  • o componente é fácil de reutilizar tanto em uma lista inline quanto dentro de um carrossel.

Como os cartões se encaixam no widget geral

Suponha que tenhamos um array gifts, obtido após chamar a ferramenta giftgenius.suggestGifts via nosso MCP. O widget no modo inline pode simplesmente renderizá-los em uma grade de 1–3 colunas.

type GiftGridProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftGrid({ gifts, onSelect }: GiftGridProps) {
  return (
    <div className="grid gap-3 sm:grid-cols-2">
      {gifts.map((gift) => (
        <GiftCard key={gift.id} gift={gift} onSelect={onSelect} />
      ))}
    </div>
  );
}

Aqui nós:

  • usamos uma grade de 1–2 colunas para não transformar o widget em uma “parede de tijolos”;
  • podemos facilmente limitar o número de cartões, por exemplo, exibindo apenas os 3–6 primeiros.

O próprio handler onSelect pode chamar a ferramenta de checkout ou apenas salvar a escolha no estado do Widget e deixar o modelo continuar o diálogo. Um exemplo simples de integração com a ferramenta:

async function handleSelect(gift: GiftSuggestion) {
  await window.openai.actions.call("giftgenius.startCheckout", {
    giftId: gift.id,
  });
}

Aqui, window.openai.actions.call é a ponte para chamar uma ferramenta registrada do MCP diretamente do widget.

Normalmente, após essa chamada, o modelo exibirá um status ou abrirá o próximo widget (por exemplo, um resumo do pedido). O principal é — não tentar realizar todo o checkout dentro da lógica do cartão; o cartão deve iniciar o próximo passo de forma clara.

4. Listas: quando o visual não é o principal

Se o cartão é um pequeno “poster”, a lista é um inventário textual limpo. A documentação e as recomendações de UX mostram que listas são boas quando o mais importante é o conteúdo do texto, e não um destaque visual chamativo.

A lista é adequada quando:

  • é preciso mostrar 5–10 opções, mas elas não requerem imagem;
  • o usuário quer simplesmente “passar os olhos” pelos nomes e descrições curtas;
  • as ações são iguais para todos os itens e o UI não deve distrair.

Exemplos no GiftGenius:

  • lista de ideias “rápidas” de presentes sem detalhes;
  • lista de categorias favoritas: “para colegas”, “para pais”, “para crianças”;
  • lista de seleções salvas (“Presentes para o departamento de RH”, “Mimos de Ano‑Novo até $20”).

Componente simples de lista

Vamos criar uma lista compacta com um botão CTA “Ver detalhes” à direita.

type GiftListProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftList({ gifts, onSelect }: GiftListProps) {
  return (
    <ul className="flex flex-col gap-2">
      {gifts.map((gift) => (
        <li
          key={gift.id}
          className="flex items-center justify-between rounded-md border px-3 py-2 text-sm"
        >
          <span className="truncate">{gift.title}</span>
          <button
            className="text-xs text-primary"
            onClick={() => onSelect(gift)}
          >
            Ver detalhes
          </button>
        </li>
      ))}
    </ul>
  );
}

Aqui nós:

  • damos ao título truncate, para que nomes longos não quebrem o layout;
  • novamente usamos uma única CTA por item;
  • deixamos toda a informação “rica” (descrição, imagem, avaliações) para o próximo passo — por exemplo, ao clicar, abrir um cartão separado ou uma visualização fullscreen.

A lista combina especialmente bem com as sugestões de follow‑up do GPT. O widget mostra a lista de “candidatos” e, abaixo dele, o GPT escreve algo como:

“Posso restringir a presentes de até $30 ou mostrar apenas os digitais. O que escolhemos?” e oferece duas ou três opções de follow‑up.

Em uma seção separada, veremos exatamente como é melhor combinar widgets inline e mensagens de follow‑up em diferentes cenários.

5. Carrosséis: quando há muitas opções, mas tudo é parecido

O carrossel é um conjunto de cartões dispostos horizontalmente e percorridos deslizando (swipe) ou com botões de navegação. As diretrizes recomendam usar carrosséis quando você mostra uma pequena lista de itens semelhantes (geralmente 3–8), cada um contendo imagem, título e alguns metadados.

A ideia principal: o usuário consegue escanear rapidamente o conjunto de opções, sem adormecer sob uma lista vertical interminável.

No GiftGenius, o carrossel é útil se:

  • temos 10–15 presentes adequados, mas o widget inline deve mostrar apenas a “melhor oito”;
  • cada presente é visualmente agradável (imagem, apresentação);
  • é importante que o usuário passe as opções sem descer muito no chat.

Regras de UX para carrosséis

Com base nas diretrizes e na pesquisa:

  • a quantidade de cartões no carrossel — de 3 a 8; se houver mais, é melhor oferecer um comando “Mostrar mais”;
  • cada cartão:
    • deve ter uma imagem ou outro elemento visual;
    • não deve conter mais que duas linhas de metadados;
    • tem uma CTA clara, como “Escolher” ou “Ver detalhes”;
  • nada de navegação aninhada complexa (abas, subfluxos) dentro do cartão;
  • evite barras de rolagem internas (verticais): deixe a altura do cartão se adaptar até um limite razoável, mas sem rolagem própria.

Carrossel simples pelo princípio “um cartão por vez”

Para evitar mexer com rolagem horizontal complexa, dá para implementar a opção mais simples: exibir um cartão por vez e oferecer botões “anterior/próximo”.

import { useState } from "react";

type GiftCarouselProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftCarousel({ gifts, onSelect }: GiftCarouselProps) {
  const [index, setIndex] = useState(0);
  const gift = gifts[index];

  return (
    <div className="flex flex-col gap-2">
      <GiftCard gift={gift} onSelect={onSelect} />
      <div className="flex items-center justify-between text-xs">
        <button
          disabled={index === 0}
          onClick={() => setIndex((i) => i - 1)}
        >
          ← Anterior
        </button>
        <span>
          {index + 1} / {gifts.length}
        </span>
        <button
          disabled={index === gifts.length - 1}
          onClick={() => setIndex((i) => i + 1)}
        >
          Próximo →
        </button>
      </div>
    </div>
  );
}

Isso já dá sensação de “carrossel”, e ao mesmo tempo:

  • o código permanece compacto;
  • não é preciso lidar com a largura do contêiner e rolagens horizontais dentro do widget;
  • é fácil limitar gifts a 8 itens antes de passá-los ao componente.

Se você quiser um carrossel mais “real”, pode usar overflow-x-auto e largura fixa nos cartões, mas é um caso em que é mais simples adotar um componente pronto de uma biblioteca de UI (shadcn/ui, soluções compatíveis com Radix, etc.) do que reinventar do zero.

6. Botões de CTA: poucos, claros e objetivos

CTA (Call to Action) é o coração de qualquer padrão inline. São os botões que transformam seu widget de uma figurinha em uma ferramenta de trabalho.

Princípios básicos

As diretrizes da OpenAI são relativamente rígidas:

  • em um cartão — no máximo dois botões principais (um primário e, no máximo, um secundário);
  • no carrossel — de preferência uma CTA por item;
  • o texto da CTA deve ser um verbo concreto: “Mostrar detalhes”, “Adicionar à lista”, “Ir para o pagamento”, e não algo genérico como “Ok” ou “Ação”.

Quanto menos botões, mais simples para o modelo e para o usuário. Não esqueça que acima e abaixo do widget há a parte textual da resposta, além de sugestões de follow‑up do GPT.

Vinculando CTA à lógica do aplicativo

No nosso GiftGenius, a maioria das CTA vai:

  • alterar filtros/critérios de seleção (nova chamada de ferramenta giftgenius.refineSearch),
  • iniciar o checkout (giftgenius.startCheckout),
  • abrir um site externo (via openExternal, que você já conhece das aulas anteriores).

Exemplo de handler simples para a CTA “Alterar filtros”:

async function handleRefineFilters(gift: GiftSuggestion) {
  await window.openai.actions.call("giftgenius.refineSearch", {
    baseGiftId: gift.id,
  });
}

Do ponto de vista de UX, é muito importante explicitar nas instruções do sistema quando e quais botões de CTA o modelo deve sugerir. Por exemplo:

  • se o usuário pedir “mostre mais opções”, é melhor exibir um novo carrossel com o botão “Escolher”;
  • se chegou a hora da compra, a CTA “Ir para o pagamento” deve levar a uma chamada de ferramenta que inicia o checkout ACP (vamos chegar nisso no módulo sobre comércio e pagamentos).

Outra prática útil é não duplicar as funções do ChatGPT nas CTA. Não é necessário criar um botão “Perguntar ao ChatGPT”; o usuário já tem o campo de entrada e a voz. As diretrizes recomendam evitar entradas “duplicadas” dentro do cartão.

7. Inline + follow‑up: jogo em dupla

O widget inline nunca vive no vácuo. A estrutura da resposta geralmente é:

  1. o modelo decide usar seu App e chama uma ferramenta;
  2. seu MCP retorna os dados;
  3. o ChatGPT renderiza um widget inline com esses dados;
  4. abaixo dele, o modelo adiciona um pequeno texto de follow‑up e opções prontas de continuação.

Para o GiftGenius, isso pode ficar assim:

  • widget inline: três cartões de presentes com CTA “Escolher”;
  • texto abaixo:
    “Aqui vão três ideias para um colega: uma luminária de mesa, um curso de oratória e um gift card de café. Posso: — mostrar apenas opções até $30; — sugerir mais algumas ideias no mesmo estilo; — ajudar a ir direto à compra de um deles.”

No follow‑up, o modelo pode fazer referência às CTA do seu widget (“clique em ‘Escolher’ abaixo da opção que você gostou”) ou sugerir comandos em texto, que novamente levarão a uma chamada de ferramenta e à atualização do UI inline.

É importante lembrar: o widget não precisa fazer tudo. Às vezes, é melhor deixar parte do cenário para o diálogo em texto, usando o widget como um “bloco visual” dentro da conversa.

8. Como isso se encaixa no fluxo geral do GiftGenius

Para ficar mais claro, vamos resumir tudo em um diagrama de sequência simples:

sequenceDiagram
  participant U as Usuário
  participant C as ChatGPT
  participant A as Widget do GiftGenius
  participant B as MCP/Backend

  U->>C: "Encontre 3 presentes de até $50 para um colega"
  C->>B: call_tool(giftgenius.suggestGifts)
  B-->>C: 3 melhores opções
  C->>A: renderiza widget inline (cartões/carrossel)
  A-->>U: cartões com CTA "Escolher"
  U->>A: clique na CTA
  A->>B: call_tool(giftgenius.startCheckout)
  B-->>A: status / link de pagamento
  A-->>U: resumo da escolha / status
  C-->>U: follow-up: "Posso sugerir mais ideias ou ajudar com um cartão de felicitações"

Do ponto de vista da arquitetura:

  • o MCP permanece o “cérebro” (seleção, lógica de negócios, ACP),
  • o widget é a “face” (cartões/listas/carrosséis),
  • o ChatGPT é o “condutor do diálogo”, que explica o que aconteceu e sugere os próximos passos.

Para que esse fluxo seja conveniente:

  • não sobrecarregue o widget com ações;
  • mantenha os dados nos cartões compactos;
  • pense em quais opções de follow‑up serão úteis após cada exibição inline.

9. Um pouco sobre o lado visual dos padrões inline

Falaremos em detalhes sobre design visual em uma das próximas aulas do módulo, mas vale mencionar desde já alguns pontos críticos para os padrões inline.

Primeiro, certifique-se de que seus cartões e listas não pareçam um site estranho dentro do ChatGPT. Cores e espaçamentos devem ser discretos, sem gradientes “ácidos” e fontes Comic Sans. O widget inline é parte do UI geral do ChatGPT, não um banner de 2007.

Segundo, evite barras de rolagem internas. Se seu cartão for tão longo a ponto de aparecer uma rolagem própria dentro dele, algo saiu errado: ou você está tentando colocar conteúdo demais, ou o padrão foi escolhido errado (talvez seja caso de fullscreen).

Terceiro, mantenha a densidade sob controle:

  • deve haver um espaço visível entre os cartões;
  • a CTA deve ser facilmente clicável (padding adequado);
  • o texto deve ser legível mesmo no celular, sem fontes microscópicas.

Tudo isso pode soar como “exigências de design”, mas a prática mostra: se o widget inline parece “nativo”, o modelo tende a usá-lo mais, e os usuários se confundem menos.

10. Prática: como evoluir o GiftGenius com base na aula

Se quiser fixar o conteúdo, aqui vai um checklist simples:

Primeiro, pegue o resultado atual da ferramenta giftgenius.suggestGifts (array de presentes) e:

  1. Implemente três variantes de UI em um único componente:
    • GiftGrid com cartões;
    • GiftList com lista textual;
    • GiftCarousel com navegação “anterior/próximo”.
  2. Adicione a cada um uma ou duas CTAs:
    • para cartões — “Escolher”;
    • para lista — “Ver detalhes”;
    • para carrossel — também “Escolher”, mais um botão separado abaixo do widget “Mostrar mais opções”.
  3. Dependendo do estado (por exemplo, quantos presentes a ferramenta retornou), escolha qual padrão usar:
    • se houver poucas opções (≤ 3) — grade de cartões;
    • se houver muitas ideias textuais — lista;
    • se houver muitos presentes visuais — carrossel.

Assim, você não só treina o UI, como também começa a pensar na escolha dinâmica do padrão conforme o contexto, o que agrada tanto aos usuários quanto aos revisores na Store.

Em suma, os padrões inline são uma camada de UI rápida e leve, que vive diretamente no feed do chat e não tenta substituir um aplicativo à parte. Cartões, listas e carrosséis cobrem 80% dos casos típicos: mostrar opções, permitir escolher e continuar o diálogo com elegância.

Na próxima aula deste módulo, veremos o que fazer quando o inline já “não dá conta”: vamos analisar assistentes fullscreen, o modo PiP e cenários em que seu App realmente precisa de uma tela grande separada dentro do ChatGPT.

11. Erros típicos ao trabalhar com padrões inline

Erro nº 1: transformar o widget inline em um mini‑site.
Às vezes, desenvolvedores tentam enfiar em um único cartão abas, acordeões, formulários, tabela e um monte de outros elementos. O resultado é um UI pesado que quebra o ritmo do chat e fica desconfortável no celular. As diretrizes dizem claramente: nada de navegações profundas e views complexas dentro de cartões inline; cenários complexos vão para o fullscreen.

Erro nº 2: CTAs demais.
“Vamos colocar no cartão ‘Ver detalhes’, ‘Comprar’, ‘Favoritar’, ‘Compartilhar’, ‘Denunciar’ e ‘Gerar cartão de felicitações’.” No fim, o usuário se perde, o modelo também, e a chance de apertarem o botão certo despenca. Lembre a regra: uma CTA principal e, no máximo, uma secundária. Os outros cenários é melhor levar para a mensagem de follow‑up do GPT ou para passos subsequentes.

Erro nº 3: misturar lista, cartões e carrossel em uma única resposta sem motivo.
Se o mesmo conteúdo aparece ora como lista, ora como cartões e ora como carrossel “só porque dá para fazer”, o usuário perde a sensação de consistência. É melhor escolher um padrão para um tipo específico de resultado (por exemplo, ideias sem imagens — lista; presentes com imagens — carrossel) e manter-se nele.

Erro nº 4: cartões sobrecarregados de texto.
Um cartão com três parágrafos de descrição, três preços e dois blocos de “por que isso é ótimo” vira um muro de texto. O usuário deixa de “escanear” e apenas passa direto. Tente deixar no cartão apenas o essencial: título, um parâmetro-chave, um motivo curto e CTA. O resto pode ser explicado na resposta textual do GPT ao lado.

Erro nº 5: confiar só no UI e ignorar o diálogo de follow‑up.
Às vezes aparece a abordagem “fazemos tudo por botões, o usuário não precisa conversar”. Isso contradiz a própria ideia do ChatGPT. O widget inline deve complementar o diálogo, não substituí-lo. Não esqueça de pensar em quais opções de follow‑up o modelo pode sugerir abaixo do widget: alterar filtros, pedir mais opções, ir para o próximo passo.

Erro nº 6: ignorar limites de quantidade de itens.
Um carrossel com 25 cartões ou uma lista com 50 itens dentro de um único widget inline é receita para o usuário rolar e ignorar tudo. A documentação recomenda 3–8 itens no carrossel e 5–10 itens na lista. Se houver mais dados, é útil adicionar uma CTA como “Mostrar mais” ou “Mostrar tudo em texto”.

Erro nº 7: usar inline onde já é preciso fullscreen.
Há a tentação de “fazer tudo inline”, mesmo quando já existem 4 passos, formulários com dezenas de campos e tabelas grandes. O resultado é um monstro, ou então você começa a inventar rolagens internas e “pseudopassos” dentro dos cartões. Assim que perceber que os passos e campos estão ficando muitos — é sinal de considerar migrar para um assistente fullscreen, mantendo o inline para prévias rápidas e resumos de ações.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION