Repository navigation
feat: animation quality defaults and ui_validate keyframes/monorepo token fixes - #124
Merged
owjs3901 merged 6 commits intoNov 10, 2026
Conversation
…rance-motion rules
runtime-value no longer fires on the documented same-file constant pattern
(css({ animationName: spin }) with spin a keyframes() const): module-level
consts holding a literal, a keyframes()/css() name, or a template/arithmetic
over those resolve as build-time values. Imports, parameters, shadowed names,
let bindings, call results and object constants the file mutates stay errors.
paths and files modes check each source against the nearest devup.json above
it, so one call over a repository of several apps uses each app's theme.
tokens.scopes lists every theme with its file/reference counts, files no
theme governs are named in themeGuardrail.unverifiedPaths, and
themeAvailable/unknownTokenCheckRan are true only when every file was checked.
A single project theme no longer crosses into another package.
New warnings proven from source alone: ssr-hidden-reveal for always-rendered
motion elements starting at opacity 0, reveal-replays for whileInView without
a one-shot viewport, reveal-moves-background, and (multi-file modes)
motion-without-reduced-motion.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After the intermediate and final frames, a reveal check scrolls the target out of view and back. Content re-hidden while away, a replay on re-entry, or a target that cannot leave the viewport at that size (re-entry unobserved) fails instead of passing on the first entrance alone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…t item Adds the devfive-frontend animation reference (survey and propose by role, one-shot visible scroll reveals, fixed backgrounds, synced progress lines, no SSR/hydration flash, no-JS/reduced-motion/focus, and start/middle/end frame plus trigger-timing verification at mobile and desktop), links it from SKILL.md, and carries the binding summary in delivery-quality, which the usage guide ships to clients without installed skills. The development contract, section-entrance candidates and bootstrap instructions name the default; the contract renames two ids instead of adding any to stay inside the 15 KiB export reply budget. Includes the changepack. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…cenarios A reveal check now scrolls toward the target in steps and records how much of it was on screen when the entrance began; starting more than 1px off screen, or only after 400ms of full visibility, fails. Opt-in scenarios run from a fresh load: fast-scroll, reload, history, image-delay, image-failure, focus and progress-sync (items appear as the progress line reaches them). A scenario that cannot be observed fails, and those not requested are listed in measured.scenariosNotRun. The plan validates scenario names and selectors. The shipped motion runtime animated sections that were already on screen when its observer attached, hiding visible content to replay an entrance on first load, reload and back/forward. Such sections now stay as rendered. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…motion rules A single tsx has no path, so at a repository root with several themes its tokens stayed unverified. A relative sourceName now selects the nearest devup.json above that location (named in tokens.devupJson); the file itself is never opened, and a path leaving the project or a bare label changes nothing. Another package's theme is still not borrowed. Motion rules read components built with motion.create()/motion(), report an in-view hook without a one-shot option at info, and add stagger-too-long for a per-item step above 150ms. Reduced-motion handling is read from the syntax tree, so a comment or an unused import no longer counts as handling it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…reply budget A behaviorPlan that is complete on its first page no longer carries the page-joining instruction, which only matters when more pages follow. That frees room in the 15 KiB export reply (headroom 31 -> 104 bytes after this change) for the contract to name one-shot-reveals-without-ssr-flash and the motion-trigger-reentry-history-and-image-failure evidence item. The animation reference documents the measurable scenarios and what remains a judgement. Co-Authored-By: Claude Opus 5.5 <[email protected]>
owjs3901
marked this pull request as ready for review
November 10, 2026 08:27
owjs3901
deleted the
devfive/3099154426bffbeef8b62999b80bb038/animation-quality
branch
November 10, 2026 08:38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes restrained entrance/transition motion a default delivery item (requirements 1-4) and fixes two
devup_ui_validateproblems (requirement 5): a falseruntime-valueon the documented same-file keyframes constant, and token validation in repositories with several apps.Rules that can be proven from source or measured in a browser are checks. What is left as guidance is only what is a judgement (whether motion suits a block, whether it is restrained).
What changed, per requirement
1. Propose and implement restrained motion by default; the user's design wins
devfive-frontend/references/animation.md: survey sections and repeated content, choose motion by role (table of defaults, including "none" for first viewport, body copy, forms, navigation), tell the user the proposal, then implement. A stated design always wins.SKILL.mdlinks it up front and in References.delivery-quality.mdgains a binding summary section; that file is embedded indevup://guide/usage, so clients without installed skills receive it too.developmentContractv3 names the default (default-motion-and-safe-progressive-enhancement,one-shot-reveals-without-ssr-flash); bootstrap instructions rule 6 mentions default motion (1490 bytes, under the 1500 budget).behaviorPlansection-entrance candidates point at the rules and list the wider verification set.2. Reveal when visible, once; staggered groups; fixed backgrounds; synced progress
devup_ui_validatewarnings:reveal-replays(whileInViewwithoutviewport={{ once: true }};useInViewwithout a one-shot option is the same finding at info),reveal-moves-background,stagger-too-long(per-item step above 150ms).devup_runtime_auditreveal check: records the trigger position (measured.trigger) and fails an entrance that starts more than 1px off screen or only after 400ms of full visibility; then scrolls away and back and fails a re-hide, a replay, or an unobservable re-entry.progress-syncscenario: each item must appear within 200ms of the progress line reaching it.3. No flash or blank during SSR/hydration/reload/navigation; no-JS, reduced motion, focus
ssr-hidden-revealcovers an always-renderedmotion.*element (or a component built withmotion.create()/motion()in the same file) whoseinitialisopacity: 0. Elements insideAnimatePresenceor a conditional render are exempt; a variant declared elsewhere is not followed.motion-without-reduced-motion(paths/files modes): entrance motion while no validated source, norprojectRoot/srcin paths mode, handles reduced motion. Read from the syntax tree, so a comment or an unused import does not count.motion-runtime.tsanimated sections that were already on screen when its observer attached, hiding visible content to replay an entrance on first load, reload and back/forward. Those sections now stay as rendered.reload,historyandfocusmeasure this.4. Verify start/middle/end frames and trigger timing on mobile and desktop
motion.scenariosaddsfast-scroll,reload,history,image-delay,image-failure,focus,progress-sync, each from a fresh load. A scenario that cannot be observed fails, and scenarios not requested are listed inmeasured.scenariosNotRun.animation.mdsection 4 maps each evidence item to its scenario; contract evidence namescomputed-style-and-animation-start-middle-endandmotion-trigger-reentry-history-and-image-failure.5a. Same-file static keyframes reference is not
runtime-valuecss()/globalCss()/keyframes()was treated as a runtime value.static_consts.rsresolves module-levelconsts of the same file that hold a literal, akeyframes()/css()name, or a template/arithmetic/object/array built from those (css({ animationName: spin })).let, undeclared names, consts declared inside a function, call results, anything built from those, and object/array consts the file mutates.5b. Per-app
devup.json; unverified scope is never a passpathsandfilesmodes check each source against the nearestdevup.jsonabove it.tokens.scopeslists each theme with file/reference counts;tokens.verificationisverified,partialorskipped.themeGuardrail.unverifiedPaths, andthemeAvailable/tokens.unknownTokenCheckRanare true only when every file was checked.okReasonis nevercleanwhen unverified references exist.devup.jsonfiles is matched per directory instead of being refused.tsxis located by a relativesourceName(apps/front/src/Hero.tsx): the nearestdevup.jsonabove it is used and named intokens.devupJson. The file is never opened; a bare label or a path leaving the project changes nothing.Reply budget
export_context_summarizes_a_large_project_under_fifteen_kibhad 31 bytes of headroom. AbehaviorPlanthat is complete on its first page no longer carries the page-joining instruction (it only matters when more pages follow), which frees room for the two new contract ids. Headroom after this PR: 104 bytes. The budget itself is unchanged.Tests
cargo fmt --all -- --check,cargo clippy --locked --workspace --all-targets --all-features -- -D warnings: clean.cargo insta test --workspace --all-features --check: 2286 passed, 0 failed, 2 ignored; no snapshot changes.devup.json, package boundary, multi-theme bundle, locatedsourceName), 8 motion-rule. Extended: contract, plan validation, paging and reply-budget tests.explore_script_behavior.mjs,withdraw.test.mjs,checks.test.mjs) 82 pass, 1 skipped.harness/runtime-audit/test/motion.test.mjs4 pass in Chromium: the shipped controller passes every page scenario; fixtures that break one rule each (early trigger, late trigger, replay, re-hide, hydration flash on reload and history, stuck after fast scroll, animating a focused control, image-gated reveal, unsynced progress line) fail on that rule; blocks with nothing to observe fail as unobserved.One existing assertion changed:
fetch_tests.rscounted 6 installed frontend documents and now counts 7, because a reference was added.What remains
useInView. Other animation libraries (GSAP, AOS, hand-written observers) are covered only by the runtime audit, which measures the rendered result whatever produced it.🤖 Generated with Claude Code