docs: reconciliar OpenSpec e escopo do MVP (MAN-124) (#50)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-08-06
|
||||
@@ -0,0 +1,50 @@
|
||||
## Context
|
||||
|
||||
O site público usa Blade/Tailwind e já contém um runtime pequeno em `resources/js/motion.js`: abertura focal da home, `page-open`, reveals com `IntersectionObserver` e progresso do índice. A cobertura e a marcação são parciais, os reveals não têm grupos/direções explícitas e a matriz browser se concentra nas quatro páginas visuais originais. A mudança cruza templates, CSS, JavaScript e testes, mas não envolve estado de aplicação, dados ou dependências.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Tornar a coreografia Heritage Editorial consistente em toda página pública visual.
|
||||
- Manter texto legível durante toda entrada, animando somente `transform` e recorte de mídia.
|
||||
- Garantir enhancement progressivo: estado final imediato sem JavaScript, observer ou com movimento reduzido.
|
||||
- Preservar interação, foco, validação e ausência de overflow em desktop/mobile.
|
||||
- Cobrir marcação, comportamento real, acessibilidade e console.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Não criar transições de rota, parallax, animação contínua ou repetição ao voltar no scroll.
|
||||
- Não mudar copy, dados de clientes, estrutura de layout, CMS, controllers, banco, APIs ou Filament.
|
||||
- Não adicionar biblioteca de animação nem itens de SPEC.md §4.2.
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **Contrato declarativo em atributos `data-*`.** Templates usarão `data-motion="page-open"`, `data-motion-beat`, `data-reveal-group`, `data-reveal` e `data-reveal-from="up|left|right"`. CSS define o estado visual e JS apenas ativa classes. Alternativa rejeitada: acoplar seletores a classes de layout, por tornar a coreografia frágil a ajustes visuais.
|
||||
|
||||
2. **Enhancement opt-in pelo elemento raiz.** O HTML inicial permanece no estado final; o runtime adiciona `html[data-motion="enhance"]` somente quando movimento é permitido e os recursos necessários existem. Sem JS, sem `IntersectionObserver`, ou em `reduce`, o atributo não é aplicado/removido e todo conteúdo fica final. Alternativa rejeitada: estado inicial oculto no HTML/CSS, pois pode prender conteúdo fora da tela.
|
||||
|
||||
3. **Transformação sem fade de texto.** Entradas duram cerca de 500 ms com `cubic-bezier(0.16, 1, 0.3, 1)`, deslocamento vertical de 12 px e lateral de 24 px no desktop/16 px no mobile. Grupos recebem stagger de 90 ms limitado a 270 ms. Mídia pode combinar transform com `clip-path`; texto não usa opacidade. Alternativa rejeitada: fade geral, que reduz contraste durante axe e legibilidade percebida.
|
||||
|
||||
4. **Observer de execução única.** Cada reveal intersectado recebe o estado final e é removido do observer. Grupos calculam índice limitado para o delay; depoimentos definem explicitamente `left`/`right` pela coluna, preservando alternância no mobile. Alternativa rejeitada: reanimar em toda rolagem, por criar fadiga e instabilidade.
|
||||
|
||||
5. **Feedback separado das entradas.** CTAs, links, navegação e controles recebem transições curtas (100–200 ms) em cor/transform/borda. `:focus-visible` continua prioritário; alertas e mensagens de erro não entram no contrato de motion. Alternativa rejeitada: feedback via JS, desnecessário para estados CSS.
|
||||
|
||||
6. **Progresso com coalescência por frame.** Eventos de scroll apenas agendam uma atualização por `requestAnimationFrame`; o cálculo existente é preservado. Alternativa rejeitada: calcular em todo evento, que multiplica leituras/escritas durante scroll.
|
||||
|
||||
7. **Testes em camadas.** Feature tests comprovam contratos Blade/CSS/JS e renderização de erros; browser tests comprovam entrada, stagger, direções, reduced motion, fallback, interação, overflow e console. Axe e console incluem sobre, contato, privacidade e 404; demais erros ficam em renderização feature.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Conteúdo pisca entre estado final e início do enhancement] → inicializar no primeiro módulo Vite, limitar transformações a distâncias pequenas e nunca ocultar texto.
|
||||
- [Transforms laterais causam overflow horizontal] → limitar distância por breakpoint, manter recorte no contêiner público e testar `scrollWidth` em ambos viewports.
|
||||
- [Muitos observers/estilos inline] → usar um único observer, custom property de índice limitada e `unobserve` imediato.
|
||||
- [Páginas de erro não carregam o runtime em todos os contextos] → o estado final é seguro; feature tests cobrem os cinco templates.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Publicar CSS, JS e templates no mesmo bundle/commit; não há migração de dados. Rollback consiste em reverter esses assets e atributos, sem compatibilidade de schema ou limpeza operacional.
|
||||
|
||||
## Open Questions
|
||||
|
||||
Nenhuma; direção, alternância mobile, execução única e limites de escopo foram aprovados no plano.
|
||||
@@ -0,0 +1,30 @@
|
||||
## Why
|
||||
|
||||
O site público já possui uma abertura focal na home, mas o restante da experiência ainda muda de estado de forma desigual entre páginas e seções. WEB-01, WEB-07 e os requisitos transversais de acessibilidade pedem uma coreografia editorial consistente, progressiva e dispensável, sem ocultar conteúdo nem comprometer interação, performance ou movimento reduzido.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Evoluir o contrato de motion do site público com abertura de página, sequência editorial inicial e reveals direcionais executados uma única vez.
|
||||
- Aplicar a coreografia à home, serviços, portfólio, detalhe, sobre, contato, privacidade e páginas de erro 404/419/429/500/503.
|
||||
- Alternar depoimentos entre esquerda e direita, filtrando citações vazias antes de renderizar a sequência.
|
||||
- Adicionar feedback curto e não bloqueante a links, CTAs, navegação e controles de formulário, preservando foco e mensagens de validação.
|
||||
- Garantir estado final imediato sem JavaScript, sem `IntersectionObserver` e com `prefers-reduced-motion: reduce`.
|
||||
- Limitar o progresso/índice da home a uma atualização por frame e ampliar testes de marcação, browser, acessibilidade e console.
|
||||
- Não objetivos: não criar transições entre rotas, alterar conteúdo ou layout estrutural, adicionar biblioteca de animação, modificar CMS/API/banco/Filament, nem introduzir itens fora do MVP listados em SPEC.md §4.2.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `public-site-motion`: Coreografia progressiva, direções, timings, execução única, microfeedback, performance e fallbacks do motion público.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `design-tokens`: Especificar tokens compartilhados de duração, stagger, distância e easing para motion editorial.
|
||||
- `public-site-pages`: Cobrir todas as páginas públicas visuais e templates de erro com o contrato compartilhado de abertura e reveal.
|
||||
- `testimonials`: Filtrar citações vazias e alternar explicitamente a direção de entrada dos depoimentos renderizados.
|
||||
- `web-accessibility`: Ampliar axe/console e validar conteúdo imediatamente utilizável com movimento reduzido ou enhancement indisponível.
|
||||
|
||||
## Impact
|
||||
|
||||
Afeta somente templates Blade públicos, `resources/js/motion.js`, tokens/regras CSS e testes Pest/Pest Browser. Não altera APIs, banco, controllers, CMS, dependências, endpoints técnicos (`sitemap.xml`, `robots.txt`) ou Filament.
|
||||
@@ -0,0 +1,9 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Editorial motion tokens are centralized
|
||||
|
||||
The system SHALL define shared public motion tokens for an approximately 500 ms entrance, short interaction feedback, exponential ease-out, 90 ms stagger capped at 270 ms, 12 px vertical distance, and 24 px desktop / 16 px mobile lateral distance. Public motion CSS MUST consume these tokens instead of duplicating arbitrary values.
|
||||
|
||||
#### Scenario: Motion runtime uses shared values
|
||||
- **WHEN** page openings, reveals, or interaction feedback are styled
|
||||
- **THEN** duration, easing, stagger, and distance MUST be derived from centralized tokens
|
||||
@@ -0,0 +1,74 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Public pages use shared progressive motion choreography
|
||||
|
||||
The public site SHALL expose a declarative motion contract using `data-motion="page-open"`, `data-motion-beat`, `data-reveal-group`, `data-reveal`, and `data-reveal-from="up|left|right"`. Home, services, portfolio index/detail, about, contact, privacy, and branded 404/419/429/500/503 pages MUST use the contract without introducing route transitions.
|
||||
|
||||
#### Scenario: Visual public route exposes motion hooks
|
||||
- **WHEN** a visitor loads any visual public route or branded error page
|
||||
- **THEN** the rendered page MUST expose a page opening hook and appropriate reveal hooks
|
||||
|
||||
#### Scenario: Technical endpoints remain outside motion
|
||||
- **WHEN** a visitor requests `sitemap.xml` or `robots.txt`
|
||||
- **THEN** the response MUST NOT depend on the public motion runtime
|
||||
|
||||
### Requirement: Editorial entrance timing and direction are consistent
|
||||
|
||||
Entrances SHALL use shared tokens for an approximately 500 ms duration, 90 ms stagger capped at 270 ms, 12 px vertical distance, and 24 px desktop / 16 px mobile lateral distance. Text MUST remain fully visible; motion MUST use transforms and MAY use media clipping without opacity fades for text.
|
||||
|
||||
#### Scenario: Reveal group computes capped stagger
|
||||
- **WHEN** more than four reveal items are rendered in one group
|
||||
- **THEN** the applied delay MUST NOT exceed 270 ms
|
||||
|
||||
#### Scenario: Text enters without opacity fade
|
||||
- **WHEN** page opening or scroll reveal motion runs
|
||||
- **THEN** text MUST remain visible throughout the transition
|
||||
|
||||
### Requirement: Reveals execute once without blocking interaction
|
||||
|
||||
Each scroll reveal SHALL activate once, reach its final state, and be removed from observation. Visitors MUST be able to activate links and form controls while entrance motion is running.
|
||||
|
||||
#### Scenario: Revealed content leaves observer
|
||||
- **WHEN** a reveal target intersects the configured viewport threshold
|
||||
- **THEN** it MUST receive the final state and be unobserved
|
||||
- **AND** scrolling away and back MUST NOT replay the reveal
|
||||
|
||||
#### Scenario: CTA remains interactive during entrance
|
||||
- **WHEN** a visitor activates a CTA while its entrance is in progress
|
||||
- **THEN** navigation MUST proceed without waiting for animation completion
|
||||
|
||||
### Requirement: Motion enhancement has immediate safe fallbacks
|
||||
|
||||
Without JavaScript, without `IntersectionObserver`, or when `prefers-reduced-motion: reduce` is active, the site SHALL render every motion target immediately in its final usable state. Non-essential animation and transition MUST NOT run under reduced motion.
|
||||
|
||||
#### Scenario: JavaScript is unavailable
|
||||
- **WHEN** a public page renders without executing JavaScript
|
||||
- **THEN** all content MUST be visible and usable in its final state
|
||||
|
||||
#### Scenario: IntersectionObserver is unavailable
|
||||
- **WHEN** JavaScript runs but `IntersectionObserver` is not supported
|
||||
- **THEN** all reveal targets MUST remain in their final state
|
||||
|
||||
#### Scenario: Reduced motion is preferred
|
||||
- **WHEN** the browser reports `prefers-reduced-motion: reduce`
|
||||
- **THEN** page openings, reveals, media clips, and non-essential feedback transitions MUST NOT run
|
||||
|
||||
### Requirement: Public interaction feedback is short and accessible
|
||||
|
||||
CTAs, links, navigation, and form controls SHALL provide short feedback using shared tokens while preserving visible `:focus-visible` indication. Alerts and validation messages MUST NOT be animated by this contract.
|
||||
|
||||
#### Scenario: Keyboard focus remains visible
|
||||
- **WHEN** a visitor focuses an interactive element with the keyboard
|
||||
- **THEN** its focus indicator MUST remain visible and MUST NOT be displaced by motion feedback
|
||||
|
||||
#### Scenario: Validation message appears without motion
|
||||
- **WHEN** server validation renders an error message
|
||||
- **THEN** the message MUST appear in its final position without entrance animation
|
||||
|
||||
### Requirement: Home progress updates at most once per animation frame
|
||||
|
||||
Scroll-driven chapter index and progress updates on the home SHALL be coalesced through `requestAnimationFrame`, with at most one pending update per frame.
|
||||
|
||||
#### Scenario: Multiple scroll events occur in one frame
|
||||
- **WHEN** multiple scroll events fire before the next animation frame
|
||||
- **THEN** the home progress calculation MUST execute only once for that frame
|
||||
@@ -0,0 +1,14 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Visual public and error pages expose the shared motion contract
|
||||
|
||||
The system SHALL apply the shared progressive opening and reveal contract to home, services, portfolio index/detail, about, contact, privacy, and branded 404/419/429/500/503 pages. The contract MUST NOT change page copy, content ordering, route behavior, or structural layout.
|
||||
|
||||
#### Scenario: Institutional page uses shared opening
|
||||
- **WHEN** a visitor loads `/sobre`, `/contato`, or `/privacidade`
|
||||
- **THEN** the page MUST expose the shared page opening hook and section reveal hooks
|
||||
|
||||
#### Scenario: Error page remains branded and progressively enhanced
|
||||
- **WHEN** Laravel renders a branded 404, 419, 429, 500, or 503 response
|
||||
- **THEN** the page MUST expose the shared opening hook
|
||||
- **AND** all error guidance and navigation MUST remain immediately usable without enhancement
|
||||
@@ -0,0 +1,43 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Testimonials are managed with publication control
|
||||
|
||||
The system SHALL allow admins to manage testimonials (SPEC WEB-04) with quote text (including multi-paragraph content), author name, optional context (event type and/or date), optional photo with alt text, sort order, featured flag, and `published_at`. Public rendering MUST preserve paragraph breaks from the stored quote, MUST omit blank/whitespace-only quotes before creating the rendered sequence, and MUST assign alternating `left`/`right` reveal directions according to the remaining rendered column order at desktop and mobile viewports. Testimonials sourced from real clients MUST NOT be published to production without authorization; development seeds MAY include the authorized-pending real quotes marked for review.
|
||||
|
||||
#### Scenario: Unpublished testimonial is excluded
|
||||
|
||||
- **WHEN** a testimonial has `published_at` null
|
||||
- **THEN** the `published()` scope MUST exclude it
|
||||
|
||||
#### Scenario: Published testimonial is queryable
|
||||
|
||||
- **WHEN** an admin sets `published_at` with required quote and author name
|
||||
- **THEN** the testimonial MUST be included in the `published()` scope
|
||||
|
||||
#### Scenario: Assistant cannot manage testimonials
|
||||
|
||||
- **WHEN** an assistant attempts to access the testimonials Resource
|
||||
- **THEN** access MUST be denied with HTTP 403
|
||||
|
||||
#### Scenario: Featured testimonials are filterable
|
||||
|
||||
- **WHEN** content is queried with featured filter
|
||||
- **THEN** records with `is_featured` true MUST be retrievable independently of sort order
|
||||
|
||||
#### Scenario: Multi-paragraph quotes render as paragraphs
|
||||
|
||||
- **GIVEN** a published testimonial whose quote contains blank-line separated paragraphs
|
||||
- **WHEN** the home testimonials section is rendered
|
||||
- **THEN** each paragraph MUST appear as distinct block text rather than a single collapsed line
|
||||
|
||||
#### Scenario: Blank quotes do not affect alternation
|
||||
|
||||
- **GIVEN** the published testimonial collection contains a blank quote between two non-blank quotes
|
||||
- **WHEN** the home testimonials section is rendered
|
||||
- **THEN** the blank testimonial MUST be omitted
|
||||
- **AND** the two rendered testimonials MUST receive alternating `left` and `right` directions based on their rendered order
|
||||
|
||||
#### Scenario: Direction alternation remains on mobile
|
||||
|
||||
- **WHEN** testimonials are viewed at a mobile viewport
|
||||
- **THEN** their explicit `left` and `right` directions MUST be preserved with the reduced mobile distance
|
||||
@@ -0,0 +1,51 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Public routes have no critical or serious accessibility issues
|
||||
|
||||
The system SHALL run automated accessibility checks on the public routes covered by the browser suite (SPEC §6.5, §13.8) after the Heritage Editorial motion enhancement. A critical or serious issue MUST fail the suite.
|
||||
|
||||
#### Scenario: Critical issue blocks the suite
|
||||
|
||||
- **WHEN** the automated accessibility check reports a critical or serious issue on a covered route
|
||||
- **THEN** the browser suite MUST fail and report the offending rule and selector
|
||||
|
||||
#### Scenario: Covered routes are checked
|
||||
|
||||
- **WHEN** the accessibility suite runs
|
||||
- **THEN** home, services listing, portfolio listing, case detail, about, contact, privacy and branded 404 MUST each be checked
|
||||
|
||||
### Requirement: Reduced motion preference is honored
|
||||
|
||||
The system SHALL suppress non-essential animation and transition when the user agent reports `prefers-reduced-motion: reduce`, including page openings, editorial reveals, image hover scales, interaction feedback and menu transitions. All affected content MUST render in its final visible and interactive state.
|
||||
|
||||
#### Scenario: Reduced motion disables transitions
|
||||
|
||||
- **GIVEN** the browser reports `prefers-reduced-motion: reduce`
|
||||
- **WHEN** a public page is loaded
|
||||
- **THEN** decorative transitions and animations MUST NOT run
|
||||
- **AND** every motion target MUST be in its final visible and interactive state
|
||||
|
||||
### Requirement: Public pages emit no console errors
|
||||
|
||||
Covered public routes SHALL load without JavaScript console errors in a real browser (SPEC §13.8, §19), including home, services listing, portfolio listing, case detail, about, contact, privacy, branded 404, and pages that load the mobile navigation or motion runtime.
|
||||
|
||||
#### Scenario: Console stays clean on covered routes
|
||||
|
||||
- **WHEN** a covered public route is loaded in the browser suite
|
||||
- **THEN** the console MUST contain no error-level messages
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Motion enhancement failure does not hide or disable content
|
||||
|
||||
The public site SHALL remain readable, keyboard-operable, and interactive when JavaScript is disabled or `IntersectionObserver` is unavailable. Motion targets MUST default to their final state without relying on timeout recovery.
|
||||
|
||||
#### Scenario: JavaScript disabled keeps public page usable
|
||||
|
||||
- **WHEN** a visitor loads a public page with JavaScript disabled
|
||||
- **THEN** headings, copy, navigation, CTAs, and form controls MUST be visible and operable
|
||||
|
||||
#### Scenario: Observer unavailable keeps reveals final
|
||||
|
||||
- **WHEN** the motion runtime executes without `IntersectionObserver`
|
||||
- **THEN** reveal targets MUST remain in the final state
|
||||
@@ -0,0 +1,24 @@
|
||||
## 1. Motion contract and runtime
|
||||
|
||||
- [x] 1.1 Extend feature tests first for motion tokens, declarative hooks, no-opacity text, safe fallbacks, one-shot observer, capped stagger and requestAnimationFrame coalescing
|
||||
- [x] 1.2 Add centralized motion tokens and CSS states for page openings, grouped directional reveals, media clips and accessible interaction feedback
|
||||
- [x] 1.3 Refactor `resources/js/motion.js` to enhance only when supported, initialize page beats/groups, unobserve revealed targets and coalesce progress updates per frame
|
||||
|
||||
## 2. Public template coverage
|
||||
|
||||
- [x] 2.1 Add failing markup coverage for home, services, portfolio index/detail, about, contact, privacy and branded 404/419/429/500/503 pages
|
||||
- [x] 2.2 Apply page-open, beat, reveal-group and directional reveal hooks across all visual public templates without changing content or layout structure
|
||||
- [x] 2.3 Add failing testimonial coverage, filter blank quotes before the rendered sequence and alternate explicit left/right directions on desktop and mobile
|
||||
|
||||
## 3. Browser and accessibility coverage
|
||||
|
||||
- [x] 3.1 Add browser tests for real opening/reveal state, 90 ms capped stagger, one-shot observation and interaction during motion
|
||||
- [x] 3.2 Add desktop/mobile tests for directional distance, reduced motion final state, no-JS/observer fallback, horizontal overflow and clean console
|
||||
- [x] 3.3 Extend axe and console route matrices to about, contact, privacy and branded 404
|
||||
- [x] 3.4 Extend axe and console route matrices to about, contact, privacy and branded 404
|
||||
|
||||
## 4. Verification and delivery
|
||||
|
||||
- [x] 4.1 Run focused feature/browser tests, build, Pint, isolated-cache PHPStan and strict OpenSpec validation
|
||||
- [x] 4.2 Run `composer quality` and visually review desktop/mobile with normal and reduced motion
|
||||
- [x] 4.3 Stage only scoped files, commit conventionally, open a PR with screenshots and verification evidence, watch CI to green, merge and remove the worktree — delivered in PR #22 with the requested screenshots and local verification; the PR merged as `0aee15e`, the worktree/remote branch no longer exists, and the current implementation is covered by all five green CI jobs on `main` (run 31501097667). Pixel snapshots were later removed by PR #44 and are not part of the current gate.
|
||||
Reference in New Issue
Block a user