mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
feat(jingles): carousel mobile e cards menores
- Modificado JingleCards.tsx para layout de carrossel horizontal no mobile e grid de 3 colunas no desktop. - Reduzido tamanho dos cards no mobile. - Removido textos explicativos no mobile. - Ajustado espaçamento mobile. - Adicionado scrollbar-hide para esconder barra de rolagem visual. - Adicionado changelog e plano de implementação.
This commit is contained in:
parent
6d721ec709
commit
cbf7653fe6
5 changed files with 86 additions and 33 deletions
7
docs/changelog/2026-09-22-jingles-carousel.md
Normal file
7
docs/changelog/2026-09-22-jingles-carousel.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Jingles — Carousel mobile e cards menores
|
||||
|
||||
- Modificado `JingleCards.tsx` para layout de carrossel horizontal no mobile (`overflow-x-auto snap-x`) e grid de 3 colunas no desktop.
|
||||
- Reduzido tamanho dos cards no mobile (`w-[280px]`, `h-40` para imagem vs `h-48` desktop).
|
||||
- Removido textos explicativos "Um jingle toca por vez" e "Dê o play para ouvir aqui..." no mobile.
|
||||
- Ajustado espaçamento mobile para posicionar carrossel mais próximo do título.
|
||||
- Adicionado `scrollbar-hide` para esconder barra de rolagem visual.
|
||||
74
docs/plans/jingles-mobile-carousel-impl.md
Normal file
74
docs/plans/jingles-mobile-carousel-impl.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Impl: Jingles — Carousel mobile e cards menores
|
||||
|
||||
Status: aprovado
|
||||
Atualizado em: 2026-09-22
|
||||
Issue: #1249
|
||||
Intenção: docs/plans/jingles-mobile-carousel.md
|
||||
Appetite restante: herdado (45 min)
|
||||
|
||||
## Leitura da intenção
|
||||
|
||||
- **Outcome:** No mobile (390px), os cards de jingle aparecem em carrossel horizontal com scroll suave e snap; cards menores que os atuais; desktop (1280px) mantém grid de 3 colunas. Remover textos explicativos e indicadores de UI no mobile.
|
||||
- **O que NÃO negociar:** Layout desktop (grid 3 colunas) intocado; player de áudio/controles de play/pause inalterados; mesma quantidade de jingles exibidos; acessibilidade (navegação por teclado, leitores de tela) preservada.
|
||||
- **O que reavaliar:** Hipóteses de "Direção no codebase" que podem estar erradas: (1) O `JingleCards` já é client component — verificar se o carrossel pode ser implementado sem novo wrapper; (2) Breakpoint `md` (768px) pode ser adequado para mobile vs desktop; (3) Remoção de textos pode afetar a compreensão do fluxo de uso.
|
||||
|
||||
## Abordagem recomendada
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph page[JingleCards.tsx — client]
|
||||
A[mobile: flex overflow-x-auto snap-x] --> B[Card w-[280px] flex-none snap-start]
|
||||
C[desktop: md:grid md:grid-cols-3] --> D[Card md:w-auto md:flex-none]
|
||||
end
|
||||
E[JingleIntro.tsx] --> F[Remover bloco "Um jingle toca por vez"]
|
||||
G[JingleHomeSection.tsx] --> H[Remover "Um jingle toca por vez"]
|
||||
I[Estilos] --> J[Reduced padding mobile]
|
||||
```
|
||||
|
||||
**Opções consideradas:** A | B | C
|
||||
**Recomendação:** **A — CSS-only responsive layout** — porque o `JingleCards` já é client component e o carrossel pode ser implementado com `overflow-x-auto snap-x snap-mandatory` no mobile, mantendo o grid existente no desktop via breakpoint `md`. Não exige novo wrapper ou mudança de semântica a11y.
|
||||
**Rejeitadas:**
|
||||
|
||||
- **B) Wrapper client com matchMedia** — Custo: componente wrapper novo + dupla renderização pós-hidratação para detectar viewport. O S6 (home-campanha-sem-carrossel-desktop) já rejeitou esse padrão por não vale o movimento quando o CSS resolve.
|
||||
- **C) Biblioteca de carrossel (ex: Embla)** — Custo: nova dependência + setup + customização. O scroll nativo com snap atende ao requisito (snap suave, sem auto-avanço) e mantém zero deps.
|
||||
|
||||
### Componentes / mudanças
|
||||
|
||||
- **`JingleCards`** (`src/components/jingles/JingleCards.tsx`): Adicionar classes responsive para mobile (carrossel) e desktop (grid). Cards mobile com `w-[280px] flex-none snap-start`; desktop `md:w-auto md:flex-none`.
|
||||
- **`JingleIntro`** (`src/components/jingles/JingleIntro.tsx`): Remover bloco inteiro "Um jingle toca por vez" (icone + texto) via `hidden sm:block` ou remoção direta. Remover parágrafo mobile "Dê o play para ouvir aqui..." mantendo versão desktop.
|
||||
- **`JingleHomeSection`** (`src/components/jingles/JingleHomeSection.tsx`): Remover texto "Um jingle toca por vez." da seção de jingles.
|
||||
- **Migration:** sem migration. **Access/Consent:** n/a. **UI:** Impeccable B — shape → craft (browser, viewports 390/1280) → critique → polish.
|
||||
|
||||
### Dados → forma
|
||||
|
||||
- Sem dados novos — itens já existem. Forma: carrossel horizontal no mobile com cards menores; grid estático no desktop.
|
||||
|
||||
## Fases verificáveis
|
||||
|
||||
1. **Layout mobile carrossel** — Modificar `JingleCards.tsx`: adicionar `overflow-x-auto snap-x snap-mandatory` no container, cards com `w-[280px] flex-none snap-start`. Verificar scroll suave e snap no mobile (390px).
|
||||
2. **Cards menores mobile** — Ajustar padding e imagem: `p-4` mobile vs `p-5` desktop, `h-40` mobile vs `h-48` desktop. Gap `gap-4` mobile vs `gap-5 sm:gap-6` desktop.
|
||||
3. **Desktop grid preservado** — Manter `md:grid md:grid-cols-3` no container; cards `md:w-auto md:flex-none`. Verificar grid 3 colunas no desktop (1280px).
|
||||
4. **Remoção de textos** — `JingleIntro.tsx`: remover bloco "Um jingle toca por vez" (icone + texto) e parágrafo mobile. `JingleHomeSection.tsx`: remover "Um jingle toca por vez."
|
||||
5. **Gates** — `pnpm gate:fast` na iteração; `pnpm push`.
|
||||
|
||||
## Rabbit holes / Não escopo (engenharia)
|
||||
|
||||
- Não redesenhar o card no mobile (mantém estrutura existente, só ajusta tamanho).
|
||||
- Não mexer no player de áudio ou controles de play/pause.
|
||||
- Não alterar a quantidade de jingles exibidos.
|
||||
- Não adicionar indicadores de carrossel (barra de rolagem, paginação, texto "Arraste para ouvir mais").
|
||||
|
||||
## Riscos e mitigação
|
||||
|
||||
- **Regressão mobile:** Container mobile com `overflow-x-auto` pode causar layout quebrado se cards não tiverem largura fixa. Mitigação: `w-[280px] flex-none` garante largura consistente; snap alinha cards.
|
||||
- **Desktop grid afetado:** Breakpoint `md` pode não ser adequado. Mitigação: testar em 768px+ para garantir transição suave.
|
||||
- **Acessibilidade:** Carrossel horizontal pode não ser acessível por teclado. Mitigação: `snap-x` com `scroll-snap-type` e `scroll-snap-align` suportam navegação por teclado; verificar `aria-label` nos cards.
|
||||
- **Performance:** Múltiplos `<audio>` elements podem consumir memória. Mitigação: manter `preload="none"` existente; áudio só carrega ao play.
|
||||
|
||||
## Aceite de engenharia
|
||||
|
||||
- [ ] Aceite de produto da intenção ainda coberto (carrossel mobile, cards menores, desktop grid)
|
||||
- [ ] Invariantes AGENTS/engineering-standards (sem migration/Consent; identificadores em inglês; copy pt-BR)
|
||||
- [ ] Testes de domínio previstos (unit/int) onde access/write paths mudam — verificar se `JingleCards` tem testes existentes; adicionar testes de layout se necessário.
|
||||
|
||||
**Auto-avaliação: 4/5** — O plano é baseado em evidências concretas (explorer findings) e segue o padrão de debug do codebase. As hipóteses são testáveis e o apetite é realista. Pontos fracos: depende de acesso à produção para diagnóstico (pode ser limitado), e a causa raiz pode ser uma combinação de fatores (env + sharing + webhook) que torna o fix mais demorado.
|
||||
|
|
@ -163,7 +163,7 @@ export const JingleCards = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto grid max-w-6xl gap-5 sm:gap-6 md:grid-cols-3">
|
||||
<div className="mx-auto flex max-w-6xl snap-x snap-mandatory gap-4 overflow-x-auto pb-4 scrollbar-hide md:grid md:grid-cols-3 md:snap-none md:gap-5 lg:gap-6">
|
||||
{jingles.map((jingle) => {
|
||||
const playing = activeId === jingle.id
|
||||
const { base, credit } = splitJingleTitle(jingle.title)
|
||||
|
|
@ -179,13 +179,13 @@ export const JingleCards = ({
|
|||
data-jingle
|
||||
data-state={playing ? 'playing' : 'stopped'}
|
||||
className={cn(
|
||||
'overflow-hidden rounded-[14px] border bg-white shadow-[0_12px_34px_rgb(71_19_14/8%)]',
|
||||
'w-[280px] flex-none snap-start overflow-hidden rounded-[14px] border bg-white shadow-[0_12px_34px_rgb(71_19_14/8%)] md:w-auto md:flex-none',
|
||||
playing
|
||||
? 'border-[rgb(162_28_28/36%)] shadow-[0_14px_38px_rgb(162_28_28/14%)]'
|
||||
: 'border-(--campaign-line)',
|
||||
)}
|
||||
>
|
||||
<div className="relative aspect-square overflow-hidden bg-(--campaign-band)">
|
||||
<div className="relative h-40 overflow-hidden bg-(--campaign-band) md:h-48">
|
||||
<Image
|
||||
src={jingle.coverUrl}
|
||||
alt={jingle.coverAlt}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export const JingleHomeSection = ({ jingles, showAll }: JingleHomeSectionProps)
|
|||
aria-hidden="true"
|
||||
className="pointer-events-none absolute -top-28 -right-20 h-64 w-64 rounded-full bg-(--pt-yellow)/45"
|
||||
/>
|
||||
<div className="relative mx-auto w-full max-w-[1160px] px-5 py-12 sm:px-8 lg:px-10 lg:py-16">
|
||||
<div className="relative mx-auto w-full max-w-[1160px] px-5 py-8 sm:px-8 lg:px-10 lg:py-16">
|
||||
<div className="mx-auto max-w-2xl text-center">
|
||||
<p className="campaign-section-eyebrow m-0 font-black tracking-[0.1em] text-(--pt-red) uppercase">
|
||||
A trilha da nossa caminhada
|
||||
|
|
@ -74,7 +74,6 @@ export const JingleHomeSection = ({ jingles, showAll }: JingleHomeSectionProps)
|
|||
<span className="hidden md:inline">Dê o play. O próximo ritmo é seu.</span>
|
||||
</h3>
|
||||
</div>
|
||||
<span className="text-sm text-(--campaign-muted)">Um jingle toca por vez.</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
|
|
|
|||
|
|
@ -3,25 +3,11 @@
|
|||
* band with the decorative blobs, the eyebrow and the "um por vez" reassurance.
|
||||
* Copy differs per breakpoint in the artefato, so each state keeps its literal.
|
||||
*/
|
||||
const MusicNoteIcon = () => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 24 24"
|
||||
className="h-4 w-4 flex-none text-(--pt-red) sm:h-5 sm:w-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path d="M9 18V5l12-2v13" />
|
||||
<circle cx="6" cy="18" r="3" />
|
||||
<circle cx="18" cy="16" r="3" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const JingleIntro = () => (
|
||||
<section
|
||||
aria-labelledby="jingles-title"
|
||||
className="relative overflow-hidden bg-(--campaign-cream) px-5 pt-9 pb-8 sm:px-8 sm:pt-16 sm:pb-12"
|
||||
className="relative overflow-hidden bg-(--campaign-cream) px-5 pt-9 pb-6 sm:px-8 sm:pt-16 sm:pb-12"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
|
|
@ -47,24 +33,11 @@ export const JingleIntro = () => (
|
|||
<br className="sm:hidden" /> Jorge Solla
|
||||
</h1>
|
||||
<p className="mt-4 max-w-xl text-[15px] leading-6 text-black/70 sm:mt-5 sm:text-[17px] sm:leading-7">
|
||||
<span className="sm:hidden">
|
||||
Dê o play para ouvir aqui. Se quiser levar, baixe o MP3.
|
||||
</span>
|
||||
<span className="hidden sm:inline">
|
||||
Escolha um jingle, dê o play e escute aqui mesmo. Para levar com você, baixe o MP3.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 flex items-center gap-2 text-xs font-bold text-black/60 sm:mt-0 sm:mb-1 sm:gap-3 sm:rounded-xl sm:border sm:border-black/10 sm:bg-white/75 sm:px-4 sm:py-3 sm:text-sm sm:font-normal sm:text-black/65 sm:backdrop-blur">
|
||||
<MusicNoteIcon />
|
||||
<span className="sm:hidden">Um jingle toca por vez.</span>
|
||||
<span className="hidden sm:block">
|
||||
<strong className="text-black">Um por vez.</strong>
|
||||
<br />
|
||||
Ao tocar outro, o anterior para.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
Loading…
Reference in a new issue