fix: keep motion entrance at full opacity for contrast

Axe sampled hero/page-open text mid opacity fade (~0.58), reporting
false WCAG failures. Animate transform/clip-path only.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-05 20:28:40 -03:00
parent 35bc5a59d9
commit af8484c74e
2 changed files with 17 additions and 13 deletions

View File

@@ -179,9 +179,9 @@
} }
@media (prefers-reduced-motion: no-preference) { @media (prefers-reduced-motion: no-preference) {
/* Transform/clip only — never fade text opacity (axe + WCAG mid-transition). */
html[data-motion="enhance"] [data-motion="dossie-hero"] [data-motion-beat="seal"], html[data-motion="enhance"] [data-motion="dossie-hero"] [data-motion-beat="seal"],
html[data-motion="enhance"] [data-motion="dossie-hero"] [data-motion-beat="cta"] { html[data-motion="enhance"] [data-motion="dossie-hero"] [data-motion-beat="cta"] {
opacity: 0;
transform: translateY(0.5rem); transform: translateY(0.5rem);
} }
@@ -194,11 +194,9 @@
} }
html[data-motion="enhance"] [data-motion="dossie-hero"].is-active [data-motion-beat] { html[data-motion="enhance"] [data-motion="dossie-hero"].is-active [data-motion-beat] {
opacity: 1;
transform: none; transform: none;
clip-path: inset(0 0 0 0); clip-path: inset(0 0 0 0);
transition: transition:
opacity var(--amare-duration-slow) var(--amare-ease-arrival),
transform var(--amare-duration-slow) var(--amare-ease-arrival), transform var(--amare-duration-slow) var(--amare-ease-arrival),
clip-path var(--amare-duration-focal) var(--amare-ease-arrival); clip-path var(--amare-duration-focal) var(--amare-ease-arrival);
} }
@@ -220,29 +218,21 @@
} }
html[data-motion="enhance"] [data-reveal]:not(.is-revealed) { html[data-motion="enhance"] [data-reveal]:not(.is-revealed) {
opacity: 0;
transform: translateY(0.5rem); transform: translateY(0.5rem);
} }
html[data-motion="enhance"] [data-reveal].is-revealed { html[data-motion="enhance"] [data-reveal].is-revealed {
opacity: 1;
transform: none; transform: none;
transition: transition: transform var(--amare-duration-slow) var(--amare-ease-arrival);
opacity var(--amare-duration-slow) var(--amare-ease-arrival),
transform var(--amare-duration-slow) var(--amare-ease-arrival);
} }
html[data-motion="enhance"] [data-motion="page-open"]:not(.is-active) { html[data-motion="enhance"] [data-motion="page-open"]:not(.is-active) {
opacity: 0;
transform: translateY(0.4rem); transform: translateY(0.4rem);
} }
html[data-motion="enhance"] [data-motion="page-open"].is-active { html[data-motion="enhance"] [data-motion="page-open"].is-active {
opacity: 1;
transform: none; transform: none;
transition: transition: transform var(--amare-duration-normal) var(--amare-ease-arrival);
opacity var(--amare-duration-normal) var(--amare-ease-arrival),
transform var(--amare-duration-normal) var(--amare-ease-arrival);
} }
} }
} }

View File

@@ -92,6 +92,20 @@ class MotionMarkupTest extends TestCase
$this->assertStringContainsString('[data-chapter-index]', $appCss); $this->assertStringContainsString('[data-chapter-index]', $appCss);
$this->assertStringContainsString('prefers-reduced-motion: no-preference', $appCss); $this->assertStringContainsString('prefers-reduced-motion: no-preference', $appCss);
// Entrance motion must not fade text opacity — mid-fade fails WCAG contrast (axe).
$this->assertDoesNotMatchRegularExpression(
'/\[data-motion-beat="seal"\][^{]*\{[^}]*opacity:\s*0/s',
$appCss,
);
$this->assertDoesNotMatchRegularExpression(
'/\[data-motion="page-open"\]:not\(\.is-active\)[^{]*\{[^}]*opacity:\s*0/s',
$appCss,
);
$this->assertDoesNotMatchRegularExpression(
'/\[data-reveal\]:not\(\.is-revealed\)[^{]*\{[^}]*opacity:\s*0/s',
$appCss,
);
$this->assertFileExists(resource_path('js/motion.js')); $this->assertFileExists(resource_path('js/motion.js'));
$this->assertStringContainsString("import './motion.js'", $appJs); $this->assertStringContainsString("import './motion.js'", $appJs);
$this->assertStringContainsString('prefers-reduced-motion', (string) file_get_contents(resource_path('js/motion.js'))); $this->assertStringContainsString('prefers-reduced-motion', (string) file_get_contents(resource_path('js/motion.js')));