Compare commits

...

4 commits

Author SHA1 Message Date
Francisco Solla
377a2c89b1 fix(ci): pina ubuntu-24.04 nos jobs com Playwright
Some checks failed
Deploy / preflight (push) Has been cancelled
Deploy / verify (push) Has been cancelled
Deploy / deploy staging (teqo-staging) (push) Has been cancelled
Deploy / requeue when main advanced (push) Has been cancelled
Deploy / deploy production (teqo-1313) (push) Has been cancelled
O label ubuntu-latest migrou para Ubuntu 26.04 em out/2026 e o Playwright 1.58.2 não tem build para 26.04 (run 42095564329: 'Playwright does not support chromium on ubuntu26.04-x64'). Pina o job checks do ci-pr e o verify do deploy em ubuntu-24.04 até o bump do Playwright.
2026-11-05 17:06:44 -03:00
Francisco Solla
ebbc662794 docs(postmortem): vincula o PR #1450 2026-11-05 17:06:44 -03:00
Francisco Solla
2f79e14a1b docs(postmortem): registra a regressão do destaque do board 2026-11-05 17:06:44 -03:00
Francisco Solla
a4c2ab226a fix(site): limita o título do board e remove text-balance dos cards (S47)
O card em destaque perdeu o line-clamp-3 na reescrita da S47: com o feed do Instagram na frente, a legenda inteira (889 caracteres no caso real) era renderizada a 31px e esticava o board. Restaura o clamp de 3 linhas no destaque, mantém o clamp do compacto, troca text-balance por break-words (balance quebra o line-clamp em alguns browsers) e adiciona min-w-0 às colunas do grid. O e2e passa a pinar o clamp do card em destaque, não só o do compacto.
2026-11-05 17:06:44 -03:00
7 changed files with 87 additions and 8 deletions

View file

@ -62,7 +62,10 @@ env:
jobs: jobs:
checks: checks:
if: github.event.pull_request.head.repo.full_name == github.repository if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest # Playwright 1.58.2 has no ubuntu26.04 build and `ubuntu-latest` migrated
# to 26.04 in Oct 2026 (run 42095564329). Pin 24.04 until the Playwright
# bump lands; `ubuntu-latest` here breaks every PR at browser install.
runs-on: ubuntu-24.04
timeout-minutes: 50 timeout-minutes: 50
services: services:
postgres-int: postgres-int:

View file

@ -101,7 +101,9 @@ jobs:
verify: verify:
needs: [preflight] needs: [preflight]
if: needs.preflight.outputs.should_deploy == 'true' if: needs.preflight.outputs.should_deploy == 'true'
runs-on: ubuntu-latest # Same pin as ci-pr: Playwright 1.58.2 has no ubuntu26.04 build and
# `ubuntu-latest` migrated to 26.04 in Oct 2026.
runs-on: ubuntu-24.04
timeout-minutes: 50 timeout-minutes: 50
services: services:
# OPS71 — GitHub hosted: jobs run on the runner VM, so services are # OPS71 — GitHub hosted: jobs run on the runner VM, so services are

View file

@ -0,0 +1 @@
**Board da home — destaque com legenda de Instagram volta a ser truncado (2026-11-05):** o card em destaque da seção “Acompanhe de perto” perdeu o `line-clamp-3` na reescrita da S47; com o feed do Instagram na frente, a legenda inteira (889 caracteres no caso real) era renderizada a 31px e esticava o board. O `h3` do destaque voltou a limitar 3 linhas, `text-balance` saiu dos dois títulos (a combinação com `-webkit-line-clamp` não é confiável em todos os browsers), `break-words` e `min-w-0` evitam overflow de palavras longas, e o e2e do feed do Instagram passou a pinar o clamp do destaque — não só o do compacto. Sem migration, sem mudança de conteúdo no CMS e sem alterar a ordem por recência do board; o texto completo da legenda continua no DOM e no compartilhamento. Verificação: `pnpm gate:fast` verde (461 arquivos, 5.069 testes) e e2e afetado no CI.

View file

@ -0,0 +1,69 @@
# Post-mortem: card em destaque do board renderizava a legenda inteira do Instagram
> Template do `/bug-fix`. Fatos apurados nesta sessão; o que não foi apurado fica marcado como tal.
## Registro
| Campo | Valor |
| ------------------- | ----------------------------------------------------------------------------------------- |
| Data do post-mortem | 2026-11-05 |
| Severidade | média (regressão visual na home; sem indisponibilidade, perda de dados ou erro funcional) |
| Ambiente | prod |
| Issue(s) | sem Issue |
| PR do fix | [#1450](https://github.com/fsolla/teqo/pull/1450) |
| Detectado por | humano (relato na sessão: “fontes super grandes e o texto não quebra” no board da home) |
## Timeline
| Momento | Data/hora | Evento |
| --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------ |
| Início provável | 2026-10-22 | S47 (`5635e652`) reescreve `CampaignContentCard` e remove o `line-clamp-3` do título do card em destaque. |
| Detecção | 2026-11-05 | Relato humano; o HTML de produção mostrava uma legenda de 889 caracteres como `h3` do destaque, a 31px, sem clamp. |
| Correção implementada | 2026-11-05 | `fix/board-featured-caption-clamp`: clamp de 3 linhas no destaque, remoção de `text-balance`, `break-words` e `min-w-0`. |
| Correção mergeada | não apurado | Pendente do CI/PR. |
| Deploy | não apurado | Pendente; produção exige approve humano no environment `production`. |
| Verificado em prod | não apurado | Pendente de confirmação humana pós-deploy. |
## O bug
Na home pública, o board “Acompanhe de perto” (seção de artigos, YouTube e Instagram) renderizava o card em destaque com a legenda completa de um post do Instagram. No caso real, eram **889 caracteres** em um `h3` de **31px** (25px no mobile), sem `line-clamp`, o que esticava a coluna e transformava o destaque em uma parede de texto. Os cards compactos tinham `line-clamp-3`, mas carregavam `text-balance`, que em alguns browsers quebra o funcionamento do `-webkit-line-clamp` — o texto deixava de truncar. Como o board é ordenado por recência e o Instagram era a fonte mais recente, as quatro posições eram legendas, não títulos de matéria.
## Causa-raiz
1. **Por que o texto ficou gigante?** Porque o `h3` do destaque não tinha `line-clamp` e usava o tamanho de título editorial (31px).
2. **Por que não tinha clamp?** A S47 (`5635e652`) separou o card em `featured` e `compact`; o `h3` antigo, que aplicava `line-clamp-3` a todas as variantes, foi substituído por um `h3` de destaque sem clamp (o design assumia um título curto de matéria, não uma legenda de feed).
3. **Por que a legenda inteira virou título?** `toInstagramCardData` usa `post.caption` como `title`; o board ordena posts + YouTube + Instagram por data e corta em 4. Com o feed do Instagram mais recente, a legenda ocupa o destaque.
4. **Por que os compactos também falhavam em alguns browsers?** `text-balance` foi adicionado aos dois `h3`; a combinação com `-webkit-line-clamp` não é confiável em todos os motores.
5. **Por que passou?** O e2e pinava o clamp apenas no card compacto (a legenda longa do stub), nunca no destaque; o card em destaque do stub tem legenda curta.
## Correção
- `src/components/CampaignContentCard.tsx`: `line-clamp-3` no `h3` do destaque; `text-balance` removido dos dois `h3`; `break-words` em ambos; `min-w-0` no wrapper do card.
- `src/components/CampaignContentSection.tsx`: `min-w-0` nas duas colunas do grid (destaque e laterais).
- `tests/e2e/frontend.e2e.spec.ts`: o teste do feed do Instagram agora exige `-webkit-line-clamp: 3` e `overflow: hidden` também no `h3` do card em destaque.
A causa é o card de destaque sem limite; o clamp restaura o comportamento anterior sem esconder a legenda do DOM (o texto completo continua acessível e compartilhável).
## Verificação
- Teste de regressão: `tests/e2e/frontend.e2e.spec.ts` → “renders the Instagram feed…” — a asserção do destaque falha sem o `line-clamp` e passa com o fix (verificação local de CSS; execução no CI).
- Suíte: `pnpm gate:fast` verde (461 arquivos, 5.069 testes).
- CI: pendente do PR.
- Prod: pendente de approve/deploy e confirmação humana.
## Prevenção
| Estratégia | Custo | Estado |
| --------------------------------------------------------------------------- | ------ | ------------------------------ |
| Pinar o clamp no card em destaque no e2e (além do compacto) | barata | implementada agora |
| Separar “título de exibição” de “legenda” no feed (excerpt curto na origem) | cara | documentada — não implementada |
**Estratégia implementada:** a asserção de `-webkit-line-clamp` no destaque fecha o buraco que deixou a regressão passar.
**Estratégia documentada (cara):** derivar um excerpt curto da legenda do Instagram para o card (primeira frase/limite de caracteres), mantendo a legenda completa apenas no `alt`/compartilhamento. Isso reduziria o nome acessível gigante do link e o texto do compartilhamento, mas mexe no contrato de conteúdo do board e merece decisão de produto.
## Lições
- Quando o design assume “título curto” e o dado real é uma legenda de feed, a proteção não pode depender do tamanho do conteúdo: o clamp é parte do componente, não da amostra do teste.
- `text-balance` + `line-clamp` é uma combinação frágil entre motores; usar `text-balance` só em títulos curtos e estáveis (headings de seção).
- O e2e deve pinar a variante que tem o maior tipo e a maior probabilidade de receber conteúdo longo — o destaque.

View file

@ -59,7 +59,7 @@ export const CampaignContentCard = ({ card, variant = 'compact' }: CampaignConte
) : null} ) : null}
</div> </div>
<p className="mt-4 text-xs font-bold text-(--pt-red) lg:mt-5">{card.meta}</p> <p className="mt-4 text-xs font-bold text-(--pt-red) lg:mt-5">{card.meta}</p>
<h3 className="mt-2 max-w-[690px] font-[family-name:var(--font-exo2)] text-[25px] leading-[1.12] font-extrabold tracking-[-0.02em] text-balance lg:text-[31px] lg:leading-[1.1] lg:tracking-[-0.025em]"> <h3 className="mt-2 line-clamp-3 max-w-[690px] font-[family-name:var(--font-exo2)] text-[25px] leading-[1.12] font-extrabold tracking-[-0.02em] break-words lg:text-[31px] lg:leading-[1.1] lg:tracking-[-0.025em]">
{card.title} {card.title}
</h3> </h3>
{card.subtitle ? ( {card.subtitle ? (
@ -74,7 +74,7 @@ export const CampaignContentCard = ({ card, variant = 'compact' }: CampaignConte
) : ( ) : (
<> <>
<p className="text-xs font-bold text-(--pt-red)">{card.meta}</p> <p className="text-xs font-bold text-(--pt-red)">{card.meta}</p>
<h3 className="mt-3 line-clamp-3 font-[family-name:var(--font-exo2)] text-xl leading-[1.18] font-extrabold tracking-[-0.02em] text-balance lg:text-[23px] lg:leading-[1.16]"> <h3 className="mt-3 line-clamp-3 font-[family-name:var(--font-exo2)] text-xl leading-[1.18] font-extrabold tracking-[-0.02em] break-words lg:text-[23px] lg:leading-[1.16]">
{card.title} {card.title}
</h3> </h3>
<p className="mt-4 text-sm font-bold text-(--pt-red)"> <p className="mt-4 text-sm font-bold text-(--pt-red)">
@ -84,7 +84,7 @@ export const CampaignContentCard = ({ card, variant = 'compact' }: CampaignConte
) )
return ( return (
<div className="group relative h-full"> <div className="group relative h-full min-w-0">
{card.external ? ( {card.external ? (
<a <a
href={card.href} href={card.href}

View file

@ -174,10 +174,10 @@ export const CampaignContentSection = async () => {
{featured ? ( {featured ? (
<div className="mt-8 grid border-y border-(--campaign-line) lg:mt-10 lg:grid-cols-12"> <div className="mt-8 grid border-y border-(--campaign-line) lg:mt-10 lg:grid-cols-12">
<article className="border-b border-(--campaign-line) py-7 lg:col-span-7 lg:border-r lg:border-b-0 lg:pr-8"> <article className="min-w-0 border-b border-(--campaign-line) py-7 lg:col-span-7 lg:border-r lg:border-b-0 lg:pr-8">
<CampaignContentCard card={featured} variant="featured" /> <CampaignContentCard card={featured} variant="featured" />
</article> </article>
<div className="divide-y divide-(--campaign-line) lg:col-span-5 lg:pl-8"> <div className="min-w-0 divide-y divide-(--campaign-line) lg:col-span-5 lg:pl-8">
{rest.map((card) => ( {rest.map((card) => (
<article key={card.id}> <article key={card.id}>
<CampaignContentCard card={card} variant="compact" /> <CampaignContentCard card={card} variant="compact" />

View file

@ -666,7 +666,11 @@ test.describe('Campaign home content section', () => {
// Long captions are clamped with an ellipsis so a single post cannot // Long captions are clamped with an ellipsis so a single post cannot
// stretch the bento row (production captions run into hundreds of // stretch the bento row (production captions run into hundreds of
// characters; the stub's reel caption is deliberately long). // characters; the stub's reel caption is deliberately long). The
// featured title must clamp too — it is the lead of the board and the
// one that renders at the largest type size.
await expect(featured.locator('h3')).toHaveCSS('-webkit-line-clamp', '3')
await expect(featured.locator('h3')).toHaveCSS('overflow', 'hidden')
const longCaptionTitle = section const longCaptionTitle = section
.getByRole('link', { name: /E2e Reel da caravana/ }) .getByRole('link', { name: /E2e Reel da caravana/ })
.locator('h3') .locator('h3')