From 5c027b256dd3df9512b29e75ffef1e8d92705668 Mon Sep 17 00:00:00 2001 From: alexsoyes Date: Tue, 30 Jun 2026 17:35:03 +0200 Subject: [PATCH] Refine print stylesheet for document output --- .codex/hooks.json | 18 + .../2026_06_30_safari_pdf_print/phase-1.md | 87 +++ .../2026_06_30_safari_pdf_print/plan.md | 38 ++ .../2026_06_30_safari_pdf_print/review.md | 52 ++ .../2026_06_30_safari_pdf_print/spec.md | 35 ++ app/src/styles/sections/print.css | 522 ++++++++++++++++-- app/tests/e2e/print.spec.ts | 54 ++ 7 files changed, 764 insertions(+), 42 deletions(-) create mode 100644 .codex/hooks.json create mode 100644 aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/phase-1.md create mode 100644 aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/plan.md create mode 100644 aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/review.md create mode 100644 aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/spec.md create mode 100644 app/tests/e2e/print.spec.ts diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 0000000..eb67758 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,18 @@ +{ + "description": "Impeccable design detector: runs after Edit/Write/apply_patch on UI files and surfaces findings as system reminders.", + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|apply_patch", + "hooks": [ + { + "type": "command", + "command": "node \"/Users/alexsoyes/.agents/skills/impeccable/scripts/hook.mjs\"", + "timeout": 5, + "statusMessage": "Checking UI changes" + } + ] + } + ] + } +} diff --git a/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/phase-1.md b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/phase-1.md new file mode 100644 index 0000000..b8e36dc --- /dev/null +++ b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/phase-1.md @@ -0,0 +1,87 @@ +--- +status: done +--- + +# Instruction: Stabilize Print Rendering + +Part of [`plan.md`](./plan.md). + +## Architecture projection + +```txt +. +├── app/ +│ ├── src/styles/sections/print.css 🔁 +│ └── tests/e2e/print.spec.ts ✅ +└── aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/ + ├── spec.md ✅ + ├── plan.md ✅ + └── phase-1.md ✅ +``` + +## User Journey + +```mermaid +--- +title: Safari PDF Print +--- +flowchart TD + Reader["Reader opens manifesto"] + BrowserPrint["Browser switches to print media"] + PrintStyles["Print stylesheet flattens layout"] + PdfOutput["PDF contains complete readable content"] + + Reader --> BrowserPrint + BrowserPrint --> PrintStyles + PrintStyles --> PdfOutput +``` + +## Wireframe + +```txt +┌─────────────────────────────────────┐ +│ (1) Cover │ +├─────────────────────────────────────┤ +│ (2) Definition │ +├─────────────────────────────────────┤ +│ (3) Values │ +├─────────────────────────────────────┤ +│ (4) Principles │ +├─────────────────────────────────────┤ +│ (5) Signatures + footer │ +└─────────────────────────────────────┘ +``` + +1. Cover: printed as a document title page. +2. Definition: dictionary and comparison content visible in normal flow. +3. Values: every value row and supporting visual content visible. +4. Principles: every commitment visible without hover, reveal, or scroll state. +5. Signatures + footer: signatory wall and document footer included. + +## Tasks to do + +### `1)` Print stylesheet + +> Make print media use a paged document flow instead of the interactive screen layout. + +1. Reset clipped overflow, sticky/fixed positioning, transforms, animations, transitions, and reveal opacity under `@media print`. +2. Flatten the document layout to one column and remove sidebar/interactivity from the printable output. +3. Add current section classes to print-specific spacing, borders, and page-break behavior. +4. Keep visual content visible while preventing large blocks from being clipped or hidden. + +### `2)` Print test + +> Prove representative content survives print-media rendering. + +1. Add a Playwright test that emulates `print` media. +2. Assert major page sections are visible. +3. Assert representative definition, value, principle, signature, and footer text exists in the rendered print document. + +## Test acceptance criteria + +| Task | Acceptance criteria | +| ---- | ------------------- | +| 1 | Print CSS includes `Definition`, `Values`, `Principles`, `Signature`, and footer in normal paged flow with screen-only UI hidden. | +| 1 | Reveal and animated content is force-visible in print media. | +| 2 | `npx playwright test tests/e2e/print.spec.ts` passes. | +| 2 | `npm run build` passes. | diff --git a/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/plan.md b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/plan.md new file mode 100644 index 0000000..40bba15 --- /dev/null +++ b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/plan.md @@ -0,0 +1,38 @@ +--- +objective: "Make the manifesto homepage reliably printable as a complete Safari PDF." +status: reviewed +--- + +# Plan: Safari PDF Print + +## Overview + +| Field | Value | +| ----- | ----- | +| **Goal** | Flatten screen-only layout and animation behavior under print media so Safari includes the complete manifesto in generated PDFs. | +| **Source** | User request: "rend la page printable en pdf avec toutes les informations à l'intérieur, sur Safari ça ne fonctionne pas" | + +## Phases + +| # | Phase | File | +| - | ----- | ---- | +| 1 | Stabilize print rendering | [`phase-1.md`](./phase-1.md) | + +## Resources + +| Source | Verified | +| ------ | -------- | +| `app/src/styles/sections/print.css` | Existing print rules are narrow and miss current sections such as `Definition`. | +| `app/src/styles/sections/base.css` | Screen styles use clipped overflow and reveal transforms that should be neutralized for print. | +| `app/src/styles/sections/layout.css` | The sticky spec index and viewport-height sidebar should not drive paged layout. | +| `app/src/styles/sections/{cover,values,principles,signature}.css` | Screen sections use large paddings, min-heights, transforms, and animated visual states. | +| `app/tests/e2e/*.spec.ts` | Playwright already drives browser checks; a focused print-media test fits the suite. | + +## Decisions + +| Decision | Why | +| -------- | --- | +| Keep the fix in print CSS plus a print-media e2e test. | The failure is browser paged-layout behavior, not content generation. | +| Hide the sticky navigation and interactive controls for print. | The PDF must prioritize the document body and not depend on sidebar or client interaction. | +| Force print-visible states for reveal/animated content. | Safari may snapshot before scroll-driven or JS-driven animation states become visible. | +| Avoid Safari-specific hacks where standard print rules work. | A deterministic print stylesheet is easier to maintain across browsers. | diff --git a/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/review.md b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/review.md new file mode 100644 index 0000000..a346feb --- /dev/null +++ b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/review.md @@ -0,0 +1,52 @@ +# Review: Safari PDF Print + +The print-media implementation meets the task acceptance criteria and the focused validation passes; one minor code-health cleanup remains around residual print-rule duplication. + +- **Verdict**: approve +- **Diff scope**: `HEAD...working tree` (tracked `app/src/styles/sections/print.css` plus untracked task/test files) +- **Axes run**: code, functional, relevancy +- **Date**: 2026_06_30 +- **Findings**: 0 critical, 0 warning, 1 minor + +Verdict: `approve` = ship it; `changes-requested` = fix the warnings or fixable criticals first; `blocked` = a critical that must not merge. The overall verdict is the strictest across the axes run. + +## Code + +Clean-code findings on the changed lines (or "Not run"). + +| Sev | Category | Location | Issue | Suggested fix | +| --- | -------- | -------- | ----- | ------------- | +| minor | code-health | `app/src/styles/sections/print.css:98`; existing overlap in `app/src/styles/sections/cover.css:227` and `app/src/styles/sections/signature.css:630` | The new work centralizes print layout in `print.css`, but older section-local `@media print` blocks still define overlapping cover/signature print behavior. Current cascade is coherent because `print.css` imports later, but the print source of truth is split. | In a cleanup pass, move or remove the residual section-local print blocks so `print.css` is the canonical print stylesheet. | + +## Functional + +Each acceptance criterion traced to the diff (or "Not run"). + +| Criterion | Met | Evidence / gap | +| --------- | --- | -------------- | +| Printable document includes cover, definition, values, principles, signatures, and footer. | Yes | `print.css` includes explicit print rules for `.cover`, `.definition`, `.values`, `.principles`, `.signature`, and `footer.doc-footer` (`app/src/styles/sections/print.css:79`). `print.spec.ts` asserts those selectors are visible (`app/tests/e2e/print.spec.ts:8`). WebKit print-media probe found all six visible and counted 4 value rows, 12 principles, and 39 signature cards. | +| Screen rendering remains unchanged. | Yes | The stylesheet change is entirely scoped under `@media print` (`app/src/styles/sections/print.css:2`), and the new test file only exercises print media. No normal-media CSS or component markup is changed. | +| Interactive-only controls are not required in PDF output. | Yes | `print.css` hides `.spec-index`, `.skip-link`, `#tweaks`, `.cover-cta`, `.sign-cta`, `.share-popup`, `.confetti-emoji`, and `.sig-linkedin` (`app/src/styles/sections/print.css:51`). The Playwright test asserts `.cover-cta`, `.sign-cta`, and `.spec-index` compute to `display: none` in print (`app/tests/e2e/print.spec.ts:41`). | +| Manifesto remains English-only. | Yes | The changed CSS adds no content, and the new test assertions use existing English manifesto strings only. | +| Print-media browser check verifies major sections are visible. | Yes | `npx playwright test tests/e2e/print.spec.ts` passed in both configured projects; the WebKit one-off print-media DOM/style probe also passed via `http://localhost:4399/`. | +| Print-media browser check verifies representative definition, value, principle, signature, and footer text. | Yes | `print.spec.ts` asserts representative text for definition, values, principles, signatures, and footer (`app/tests/e2e/print.spec.ts:12`). WebKit probe confirmed the same text using DOM `textContent`. | +| Existing build and tests still pass. | Yes | `npm run build` passed, `npm test` passed with 3 files / 52 tests, and `npx playwright test tests/e2e/print.spec.ts` passed with 2 tests. | +| Phase AC: Definition, Values, Principles, Signature, and footer are in normal paged flow with screen-only UI hidden. | Yes | Layout wrappers are flattened to `display: block`, auto dimensions, no containment, and visible overflow (`app/src/styles/sections/print.css:37`); screen-only UI is hidden (`app/src/styles/sections/print.css:51`). | +| Phase AC: Reveal and animated content is force-visible in print media. | Yes | Global print rules remove animation/transform/overflow constraints (`app/src/styles/sections/print.css:7`), reveal/value/demo/signature descendants are forced to opacity 1 and no transform (`app/src/styles/sections/print.css:62`), and the test asserts representative computed opacity/transform states (`app/tests/e2e/print.spec.ts:24`). | + +- **Missing behaviors**: none against the stated acceptance criteria. +- **Unplanned behaviors**: none. The only visible behavior changes are scoped to print media. +- **Edge-case gaps**: no automated saved-PDF artifact comparison; validation uses print-media DOM/style checks as the task plan requires. The WebKit proxy check passed. + +## Relevancy + +Does the change belong (or "Not run"). Every finding ties to evidence, never an opinion. + +| Sev | Lens | Location / rule | Misfit | Suggested fix | +| --- | ---- | --------------- | ------ | ------------- | +| minor | rot | Project baseline: no information duplication; `app/src/styles/sections/print.css:2`, `app/src/styles/sections/cover.css:227`, `app/src/styles/sections/signature.css:630` | Print behavior now lives mostly in `print.css`, but small legacy print blocks remain in section styles. This is not functionally contradictory today, but it leaves maintainers with multiple places to check for print behavior. | Consolidate the residual section-local print rules into `print.css` in a later cleanup. | + +## Follow-up + +- **Top fixes** (ranked): consolidate residual section-local print rules into `app/src/styles/sections/print.css` when convenient. +- **Notes**: Validation run in this review: `npm run build` passed; `npm test` passed (3 files, 52 tests); `npx playwright test tests/e2e/print.spec.ts` passed (2 tests); WebKit print-media DOM/style probe passed on `http://localhost:4399/` with all major sections visible and representative content present. Checklist result: DRY partial because of the minor residual print-rule duplication; naming/docs/code coherent; no over-engineering observed for the Safari print scope; no debug leftovers found by `rg` for console/debugger/TODO/test-only markers in the changed task files. diff --git a/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/spec.md b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/spec.md new file mode 100644 index 0000000..b91ba3f --- /dev/null +++ b/aidd_docs/tasks/2026_06/2026_06_30_safari_pdf_print/spec.md @@ -0,0 +1,35 @@ +# Safari PDF Print + +## Target + +Allow the manifesto homepage to be printed or saved as a PDF in Safari with the complete manifesto content visible and readable. + +## Hard constraints + +- The printable document includes the cover, definition, values, principles, signatures, and footer content. +- Screen rendering must remain unchanged for normal browsing. +- Interactive-only controls and affordances must not be required for the PDF to contain the information. +- The manifesto remains English-only. + +## Non-goals + +- Add a dedicated PDF export service. +- Add browser-specific user instructions or visible print help text to the page. +- Redesign the screen layout. +- Change manifesto editorial content. + +## Done-when + +- A print-media browser check verifies that the major homepage sections are visible under `print` media. +- A print-media browser check verifies that representative value, principle, definition, signature, and footer text are present in the rendered document. +- Existing build and test commands still pass. + +## Stakeholders + +- Decider: project owner. +- Owner: manifesto app maintainers. +- Consumer: readers who print or save the manifesto as PDF from Safari. + +## Context + +Safari/WebKit print rendering is sensitive to screen-only layout constraints such as sticky positioning, viewport heights, clipped overflow, transforms, and reveal animations. The current app already has print CSS, but it does not fully flatten the current page structure for paged output. diff --git a/app/src/styles/sections/print.css b/app/src/styles/sections/print.css index 500aed6..da33f7e 100644 --- a/app/src/styles/sections/print.css +++ b/app/src/styles/sections/print.css @@ -1,76 +1,514 @@ /* ============ PRINT ============ */ @media print { @page { - size: A3 portrait; - margin: 18mm; + margin: 14mm; } + + *, + *::before, + *::after { + animation: none !important; + box-shadow: none !important; + filter: none !important; + max-height: none !important; + overflow: visible !important; + text-shadow: none !important; + transform: none !important; + transition: none !important; + } + html, body { - background: white; - color: black; + width: auto !important; + min-width: 0 !important; + background: var(--paper) !important; + color: var(--ink) !important; + color-scheme: light; + overflow: visible !important; + scroll-behavior: auto !important; } + body::before, body::after { - display: none; + content: none !important; + display: none !important; + } + + #main, + .wrap, + .doc-layout, + .doc { + display: block !important; + width: auto !important; + max-width: none !important; + min-width: 0 !important; + margin: 0 !important; + padding: 0 !important; + contain: none !important; + overflow: visible !important; + } + + .spec-index, + .skip-link, + #tweaks, + .cover-cta, + .sign-cta, + .share-popup, + .confetti-emoji, + .sig-linkedin { + display: none !important; } + + .reveal, + .reveal *, + .reveal-word, + .cover-title .word, + .value-art *, + .p-demo *, + .signature * { + clip-path: none !important; + opacity: 1 !important; + transform: none !important; + } + + a { + color: var(--ink) !important; + text-decoration: none; + } + + .cover, + .definition, + .values, + .principles, + .signature, + footer.doc-footer { + position: static !important; + display: block !important; + height: auto !important; + min-height: 0 !important; + max-height: none !important; + margin: 0 !important; + background: var(--paper) !important; + color: var(--ink) !important; + overflow: visible !important; + page-break-inside: auto; + break-inside: auto; + } + .cover { - height: auto; - min-height: 0; - padding: 0 0 20mm; + padding: 0 0 12mm !important; + border: 0 !important; page-break-after: always; + break-after: page; + } + + .cover::before, + .cover::after, + .cover-watermark, + .cover-rule { + content: none !important; + display: none !important; + } + + .cover-stage { + max-width: none !important; } - .cover-scroll { - color: oklch(0.55 0 0); + + .cover-std { + margin-bottom: 8mm !important; + color: var(--ink-3) !important; + font-size: 10pt !important; } + .cover-title { - color: oklch(0.18 0 0); + max-width: 11ch !important; + color: var(--ink) !important; + font-size: 48pt !important; + line-height: 0.95 !important; } - .cover-title .word { - opacity: 1; - transform: none; - animation: none; + + .cover-title .for { + color: var(--ink-3) !important; + opacity: 1 !important; } - .cover-title .ai::after { - transform: scaleX(1); - animation: none; - background: oklch(0.62 0 0); + + .cover-title .ai-strike { + background: var(--rule) !important; } - .preamble, - .chapter-open, + + .cover-foot { + max-width: 54ch !important; + margin-top: 9mm !important; + } + + .cover-lede { + color: var(--ink) !important; + font-size: 14pt !important; + } + + .definition, .values, - .principles { + .principles, + .signature { + padding: 0 0 10mm !important; + border-top: 1px solid var(--rule); + } + + .chapter-open, + .lex, + .lex-vs, + .plate, + .plate-row, + .p-grid, + .principle, + .signature-open, + .sig-wall, + .sig-card { + position: static !important; + overflow: visible !important; + background: var(--paper) !important; + } + + .chapter-open { + min-height: 0 !important; + padding: 9mm 0 5mm !important; + break-inside: avoid-page; + page-break-inside: avoid; + } + + .chapter-open::before, + .signature-open::before, + .signature-open::after, + .plate-row::before, + .p-grid, + .principle, + .sig-card, + footer.doc-footer { + border-color: var(--rule) !important; + } + + .chapter-open::before, + .signature-open::before, + .signature-open::after, + .plate-row::before { + background: var(--rule) !important; + } + + .chapter-body h2, + .lex-word, + .lex-vs-intro h3 { + color: var(--ink) !important; + } + + .chapter-lede p, + .lex-meta, + .lex-syn, + .vsch-note, + .pr-text .note, + .p-sub, + .sig-statement, + .sig-date, + .footer-text, + .footer-year { + color: var(--ink-3) !important; + } + + .lex, + .vsch, + .p-demo, + .sig-card { + padding: 5mm !important; + border: 1px solid var(--rule) !important; + border-radius: 0 !important; + break-inside: avoid-page; page-break-inside: avoid; - break-inside: avoid; - border-color: oklch(0.78 0 0); } - .reveal { - text-align: center; - opacity: 1; - transform: none; + + .lex::before, + .plate-row::after, + .principle::before, + .sig-card::before { + content: none !important; + display: none !important; + } + + .lex-def { + max-width: none !important; + color: var(--ink) !important; + font-size: 17pt !important; + } + + .lex-tag, + .lex-abbr, + .lex-def em, + .chapter-body h2 .amp, + .pr-text .note .emph, + .p-lead .hl, + .signature-kicker, + .signature-seal strong { + color: var(--ink) !important; + } + + .lex-abbr, + .signature-seal, + .signature-seal::before, + .signature-seal::after, + .sig-entry { + border-color: var(--rule) !important; + } + + .vsch-grid, + .signature-open-body, + .sig-grid { + display: block !important; + } + + .vsch, + .plate-row, + .principle, + .sig-card { + margin: 0 0 5mm !important; + } + + .vsch-svg, + .scene-svg { + max-height: 58mm !important; + } + + .plate { + display: block !important; + padding: 0 !important; + } + + .plate-row { + display: block !important; + padding: 8mm 0 !important; + border-top: 1px solid var(--rule); + page-break-inside: auto; + break-inside: auto; + } + + .plate-row .pr-icon, + .p-num .p-icon { + display: none !important; } - .plate-row.seen .right svg.strike path, - .plate-row .right svg.strike path { - stroke-dashoffset: 0; + + .pr-folio, + .p-num { + display: inline-block !important; + min-width: 0 !important; + min-height: 0 !important; + width: auto !important; + margin: 0 0 3mm !important; + padding: 0 !important; + color: var(--ink-3) !important; + font-size: 18pt !important; + opacity: 1 !important; } + + .pr-text .left { + color: var(--ink) !important; + font-size: 28pt !important; + } + + .pr-text .right, + .pr-text .over { + color: var(--ink-3) !important; + } + + .pr-text .right svg.strike path, + .pr-text .note .emph svg.und path, + .p-lead .hl { + stroke-dashoffset: 0 !important; + background-size: 100% 2px !important; + } + + .plate-row .pr-art { + display: block !important; + margin-top: 5mm !important; + } + + .pr-art { + background: var(--paper-2) !important; + border: 1px solid var(--rule) !important; + border-radius: 0 !important; + } + + .pr-art::after { + content: none !important; + display: none !important; + } + + .value-art { + display: block !important; + min-height: 0 !important; + padding: 5mm !important; + color: var(--ink) !important; + font-size: 9pt !important; + line-height: 1.45 !important; + } + + .value-art *, + .value-art pre, + .value-art pre.va-dim, + .value-art .va-col.va-bad pre, + .value-art .va-sub, + .value-art .va-label, + .value-art .va-dim2 { + color: var(--ink) !important; + } + + .va-col { + display: block !important; + margin-bottom: 4mm !important; + } + + .va-sep { + display: none !important; + } + + .va-l, + .va-c, + .va-sub, + .va-cursor, + .ascii-line, + .handoff-token { + clip-path: none !important; + opacity: 1 !important; + transform: none !important; + } + + .principles { + padding-bottom: 10mm !important; + } + .p-grid { - border-color: oklch(0.78 0 0); + display: block !important; + border-top: 1px solid var(--rule) !important; } + .principle { - border-color: oklch(0.78 0 0) !important; - cursor: default; + display: block !important; + padding: 7mm 0 !important; + border-bottom: 1px solid var(--rule) !important; + break-inside: avoid-page; + page-break-inside: avoid; } - .principle:hover { - background: transparent; + + .p-body { + padding-top: 0 !important; } - #tweaks, - .cover-scroll { + + .p-lead { + color: var(--ink) !important; + font-size: 14pt !important; + } + + .principle .p-demo { + display: block !important; + max-width: none !important; + min-height: 0 !important; + margin-top: 4mm !important; + background: var(--paper-2) !important; + } + + .p-demo p { + color: var(--ink-3) !important; + } + + .scene-ascii-doc, + .plan-person, + .agent-core, + .plan-talk, + .plan-artifact, + .code-tile, + .map-row i { + background: var(--paper) !important; + border-color: var(--rule) !important; + } + + .signature { + padding-bottom: 8mm !important; + } + + .signature-open { + min-height: 0 !important; + padding: 9mm 0 5mm !important; + } + + .signature-open-mark { display: none !important; } - a { - color: oklch(0.18 0 0); - text-decoration: none; + + .signature-open-copy, + .signature-open .chapter-lede { + max-width: none !important; + } + + .signature-seal { + width: 38mm !important; + margin-top: 5mm !important; + background: var(--paper) !important; + } + + .sig-wall { + padding-top: 0 !important; + } + + .sig-wall-head { + display: block !important; + margin-bottom: 5mm !important; + } + + .sig-wall-rule { + display: none !important; } + + .sig-grid { + list-style: none !important; + margin: 0 !important; + padding: 0 !important; + } + + .sig-card { + background: var(--paper) !important; + } + + .sig-avatar { + display: none !important; + } + + .sig-name { + color: var(--ink) !important; + white-space: normal !important; + } + + .sig-affil, + .sig-wall-count, + .signature-seal-label, + .signature-seal-caption { + color: var(--ink-3) !important; + } + footer.doc-footer { - border-color: oklch(0.78 0 0); + display: block !important; + padding: 7mm 0 0 !important; + border-top: 1px solid var(--rule) !important; + } + + .footer-nav { + display: block !important; + } + + .footer-link { + display: block !important; + min-width: 0 !important; + min-height: 0 !important; + margin-bottom: 3mm !important; + color: var(--ink-3) !important; + } + + .footer-link svg { + display: none !important; } } diff --git a/app/tests/e2e/print.spec.ts b/app/tests/e2e/print.spec.ts new file mode 100644 index 0000000..26f7cd5 --- /dev/null +++ b/app/tests/e2e/print.spec.ts @@ -0,0 +1,54 @@ +import { expect, test } from '@playwright/test'; + +test.describe('print rendering', () => { + test('homepage keeps manifesto content visible in print media', async ({ page }) => { + await page.emulateMedia({ media: 'print' }); + await page.goto('/', { waitUntil: 'domcontentloaded' }); + + for (const selector of ['.cover', '#definition', '#values', '#principles', '#sign', 'footer.doc-footer']) { + await expect(page.locator(selector), `${selector} should render for print`).toBeVisible(); + } + + await expect(page.locator('h1.cover-title')).toContainText(/AI-Driven\s+Development\s+Manifesto/); + await expect(page.locator('#definition')).toContainText('A way of building software'); + await expect(page.locator('#values')).toContainText('Bet on the method, not the model'); + await expect(page.locator('#principles')).toContainText('Do not delegate what you cannot evaluate'); + await expect(page.locator('#sign')).toContainText('Public signature registry'); + await expect(page.locator('#sign')).toContainText(/signator(y|ies)/); + await expect(page.locator('footer.doc-footer')).toContainText('github.com/ai-driven-dev/manifest'); + + await expect(page.locator('.plate-row')).toHaveCount(4); + await expect(page.locator('.principle')).toHaveCount(12); + await expect(page.locator('.sig-card').first()).toBeVisible(); + + const printState = await page.evaluate(() => { + const css = (selector: string) => getComputedStyle(document.querySelector(selector)!); + + return { + bodyOverflowX: css('body').overflowX, + coverCtaDisplay: css('.cover-cta').display, + docLayoutDisplay: css('.doc-layout').display, + firstRevealOpacity: css('.reveal').opacity, + firstRevealTransform: css('.reveal').transform, + htmlOverflowX: css('html').overflowX, + signCtaDisplay: css('.sign-cta').display, + specIndexDisplay: css('.spec-index').display, + valueLineOpacity: css('.value-art .va-l').opacity, + valueLineTransform: css('.value-art .va-l').transform, + }; + }); + + expect(printState).toEqual({ + bodyOverflowX: 'visible', + coverCtaDisplay: 'none', + docLayoutDisplay: 'block', + firstRevealOpacity: '1', + firstRevealTransform: 'none', + htmlOverflowX: 'visible', + signCtaDisplay: 'none', + specIndexDisplay: 'none', + valueLineOpacity: '1', + valueLineTransform: 'none', + }); + }); +});