Skip to content

feat: Devup UI keyframes motion, runtime motion checks, export budget headroom - #125

Merged
owjs3901 merged 4 commits into
mainfrom
devfive/3099154426bffbeef8b62999b80bb038/keyframes-motion
Nov 10, 2026
Merged

owjs3901 merged 4 commits into
mainfrom
devfive/3099154426bffbeef8b62999b80bb038/keyframes-motion

Conversation

@devfive-relay-c70423

Copy link
Copy Markdown
Contributor

Summary

Finishes the follow-ups PR #124 listed as remaining and makes Devup UI's static keyframes() the motion path (recommended, generated and validated) instead of a script-driven or framer-motion entrance. Full write-up with measurements: reports/keyframes-motion.md.

1. Devup UI keyframes as the motion path

  • API confirmed by compiling it with the versions the harness installs (@devup-ui/react 1.0.41, @devup-ui/vite-plugin 1.0.73): keyframes() returns the name, animationName/animation, animationTimeline="view()", animationRange, _motionReduce, _focusWithin and selectors all extract to static CSS; a dynamic animationDelay becomes a CSS variable.
  • Ships Reveal.tsx (new export output revealTsx, delivered with every section-entrance candidate): Reveal (scroll-linked section), RevealGroup/RevealItem (staggered group via --reveal-index), RevealLine/RevealStep (progress line and steps timed from one variable). Server code, no animation library.
  • Fallback: motion-runtime.ts no longer animates entrances. It sets data-reveal-state once, only for timed groups and browsers without view timelines, only on blocks still below the viewport, never under reduced motion. No JavaScript shows the content.
  • Guidance: animation.md rewritten keyframes-first (no bundle, no hydration flash, opacity/transform only); framer-motion only when the project already depends on it and the user asks, or for exit/gesture motion. delivery-quality.md, SKILL.md, common-patterns.md, critical-rules.md, behaviorPlan (pattern: devup-ui-static-keyframes-reveal) and the screen-plan reveal option follow.
  • Range finding: the shipped range is entry 0% entry min(100%, 240px). Measured at 390x844, a plain entry 100% leaves a 1200px section 28% opaque with 240px of it showing.
  • Static validation reads both styles (motion_keyframes.rs): ssr-hidden-reveal, reveal-replays, reveal-moves-background, stagger-too-long and motion-without-reduced-motion now read keyframes, animation props and view-timeline usage, with framer-motion detection unchanged. New: entrance-uses-motion-library (info), entrance-on-static-content, entrance-per-element (info).

2. 15 KiB export budget

  • Reply in export_context_summarizes_a_large_project_under_fifteen_kib: 15,255 -> 14,086 bytes. Headroom 105 -> 1,274 bytes, and the test now fails below 1,024.
  • Nothing dropped: standing prose (behaviorPlan policies, contract nextAction/installedReference, verificationScope explanations, the install-path note) moved to the guide, and a test asserts each moved sentence is there.
  • developmentContract v4 keeps identifiers inline. The two that feat: animation quality defaults and ui_validate keyframes/monorepo token fixes #124 renamed are restored as separate ids next to the new ones: 20 requirements, 8 evidence items, each explained in a new "Contract identifiers" section.

3. Runtime motion checks

harness/runtime-audit/reveal-checks.mjs measures an entrance whatever produced it (scroll-linked or timed): start/middle/end frames with a screenshot each, trigger position, re-entry, then twelve scenarios that run by default. Each has a passing and a failing fixture:

Check Fails on
trigger position 600px early observer; 900ms late timer; no range / plain entry 100% on a tall block
re-entry replay; re-hide while away
fast-scroll content left at opacity 0
reload, history, bfcache, client-nav replay at the restored position (bfcache is a real restore, pageshow.persisted)
image-delay, image-failure reveal gated on the image load event
focus focused block animating / part-transparent
reduced-motion, no-script preference ignored; opacity: 0 in markup
stagger 250ms steps; 1600ms items (step 150 / span 450 / total 1000ms bounds)
progress-sync steps not within 200ms of the line
  • Results are pass, fail, not-applicable or unverified. An unobserved scenario makes the check inconclusive; it is never counted as a pass in devup_runtime_audit summaries, and devup_ui_validate keeps clean-unverified-tokens for unchecked tokens.
  • Ingest returns motionEvidence: a candidate is verified only with passing checks at a mobile (<=767px) and a desktop (>=1024px) width and nothing unverified; completion applies the same rule.
  • Runner fix: a view-timeline animation was paused at its hidden first frame (blank below-fold sections in captures) and waited on for 15s; it is now cancelled to its resting style and not waited for.

4. Monorepo single-tsx validation

One tsx at a monorepo root is matched to its owning app's devup.json: sourceName as a path inside projectRoot (relative or absolute), as the tail of a path, or with no name by matching its text against project files. tokens.owner says which rule decided. Unverified only when no owner resolves, with themeGuardrail.ownerCandidates naming what was found. Tests: 9 resolved cases across apps/a and apps/b, 5 unresolved (orphan, same file under two themes, text found nowhere).

#124's limitation list

All machine-checkable limitations from #124 are resolved:

  • static rules read only framer-motion -> they read Devup UI keyframes and CSS scroll-timeline usage too;
  • reveal scenarios were opt-in -> all run by default, and what is not observed is unverified;
  • guidance-only items -> stagger, progress sync, trigger position, fast scroll, back/forward (new document, bfcache, client navigation), image delay and failure, and focus are measured, at mobile and desktop;
  • export budget -> 1,274 bytes of headroom, the two contract ids restored as separate ids, a headroom test;
  • monorepo single tsx -> owner inferred from path, name or text.

Whether an eligible content block should get motion at all is a user design choice (user-specified design wins); the tool proposes, it does not judge.

Tests

  • cargo fmt --all -- --check, cargo clippy --locked --workspace --all-targets --all-features -- -D warnings: clean.
  • cargo insta test --workspace --all-features --check: 2,310 passed, 0 failed, 2 ignored; no snapshot changes (24 new tests).
  • Node CI list: 82 pass, 1 skipped.
  • Harness browser suite (Chromium 153): 103 pass, 1 skipped. New: keyframes-motion.test.mjs (compiles the shipped templates with Devup UI and audits them at 390x844 and 1440x900) and reveal.test.mjs (6 tests of passing and failing pages).

Changed expectations: the runner template writes 14 files (was 13); the old reveal tests in motion.test.mjs moved to reveal.test.mjs.

🤖 Generated with Claude Code


Submitted via Devfive hub. Employee: ; device: 3099154426bffbeef8b62999b80bb038

leader and others added 4 commits November 10, 2026 19:05
…ingle tsx's owning app

Every entrance rule (ssr-hidden-reveal, reveal-replays, reveal-moves-background, stagger-too-long, motion-without-reduced-motion) now reads Devup UI keyframes, animation props and CSS view-timeline usage as well as framer-motion, with entrance-uses-motion-library, entrance-on-static-content and entrance-per-element added. A single tsx at a monorepo root is matched to its owning app's devup.json from an absolute or partial sourceName or by its text; unverified is reported only when no owner resolves.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
…ied is never a pass

reveal-checks.mjs measures start/middle/end frames, trigger position and re-entry, then twelve scenarios by default (fast-scroll, reload, history, real bfcache, client-nav, image delay and failure, focus, reduced-motion, no-script, stagger bounds, progress-sync), each not-applicable or unverified rather than passed when unobserved. Ingest returns motionEvidence per candidate at mobile and desktop widths and completion requires both. The runner no longer pauses a view-timeline animation at its hidden first frame. Ships the Reveal.tsx keyframes template and the marker-only runtime, with a compiled Devup UI example and failing fixtures.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
The animation guide, delivery rules, behaviorPlan and screen plan name Devup UI static keyframes as the motion path; exports deliver revealTsx. The developmentContract keeps identifiers inline (restoring the two renamed in #124 as separate ids) while standing prose moves to the guide, leaving over 1 KiB under the 15 KiB export budget with a test that asserts the margin.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
@owjs3901
owjs3901 marked this pull request as ready for review November 10, 2026 10:48
@owjs3901
owjs3901 merged commit d18686c into main Nov 10, 2026
9 checks passed
@owjs3901
owjs3901 deleted the devfive/3099154426bffbeef8b62999b80bb038/keyframes-motion branch November 10, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant