Docs/Reference

QA rules

npx demovie qa <slug> [--format f|all] [--strict] loads the composition exactly as the renderer does, samples it at 10 fps (calling seek() and inspect(), and taking screenshots where a rule needs pixels), runs every rule, writes qa.json next to video.json and exits 1 on any error. Rules listed in video.json qa.ignore are reported as waived; --strict turns warnings into errors. npx demovie preview <slug> draws the failures over the frames.

IDSeverityRuleMeasurementFix
DM-T01errorText stays on screen long enough to readEach distinct non-UI text block is continuously visible for ≥ 0.5 + words/3 s and ≥ 1.2 shold the text longer (or cut words): give it at least 0.5 s + 1/3 s per word, and never less than 1.2 s
DM-T02errorMinimum text sizeFont size ≥ 3.0% of the frame's short side (captions ≥ 3.8%), at scale 1set the font size to at least 3% of the frame's short side (calc(var(--dm-unit) * 3)); captions need 3.8%
DM-T03errorNo text overflow or clippingscrollWidth > clientWidth, or the text box extends beyond its container or the stageshorten the copy, let it wrap (max-width in ch), or enlarge the container so no glyph is cut
DM-T04errorContrastWCAG ratio of text color vs. a sampled background ring: < 4.5 warns, < 3.0 errorsdarken/lighten the text or its background (brand fg on bg), or put the text on a solid panel
DM-T05warnToo much text at onceMore than 2 non-UI text blocks, or more than 18 words, visible at onceshow one idea at a time: at most 2 text blocks and 18 words on screen together
DM-L01errorSafe areaNon-UI text, logos and the CTA stay inside the format's safe areamove it inside the safe area: position with var(--dm-safe-top/right/bottom/left), check with preview safe guides
DM-L02errorOverlapsTwo visible non-UI text boxes intersect by more than 5% of the smaller one, or text covers a highlighted product elementgive each text block its own space, and keep text off the element you highlight
DM-L03warnOff-stage leftoversVisible elements sit fully outside the stage for more than 1 shide or remove elements once they leave the stage (set opacity 0 or end their shot)
DM-P01errorShot pacingWarn when the average shot length is below the type floor; error when more than 4 consecutive shots are under 0.8 smerge or lengthen shots: keep the average above the type's floor and never cut more than 4 times in a row under 0.8 s
DM-P02errorDurationWithin the type's range, and equal to video.json.duration ± 1 frameset video.json duration inside the type's range, and end the last shot exactly at the duration
DM-P03warnEnd holdThe final logo/CTA shot is visible for ≥ 1.5 send on a logo + CTA shot held for at least 1.5 s
DM-P04errorShots declaredAt least 1 shot, and the shots cover the full duration with no gap over 0.5 sdeclare shots with v.shot(id, start, end) covering the whole video
DM-P05errorSeamless loop (hero-loop only)First vs. last frame pixel diff < 2%make the end state equal the start state (camera reset, same text and positions) so the loop is invisible
DM-G01errorTruthful cursorEvery cursor click lands inside its target element's rect under the current screen transformmove the cursor to the element (cursor.moveTo(screen, id)) so it arrives before the click, and click while the element is on screen
DM-G02warnInvented vocabularyCapitalized terms in non-UI text that aren't in the glossary, the brief or captured UI textuse the product's own words (.demovie/glossary.md), or add the term to the brief/glossary if it is real
DM-G03errorProduct shots use capturesEvery kind: "product" shot contains at least one screen()show product UI only with screen(v, { capture: "", parent: shot.el }) — never draw or mock it
DM-G04warnInvented numbersNumbers in non-UI text that aren't in the brief, the glossary or captured UI textonly show numbers from the brief, seed data or captures; put the source in the brief if it is real
DM-G05errorUpscaled capturesA screen displayed above 1.0× its native pixel density warns; above 1.5× errors (blurry)zoom less, show the screen smaller, or capture at a higher deviceScaleFactor
DM-A01errorFontsDeclared brand and fallback fonts are loaded; no silent fallback to system fontscheck brand.json font files exist under .demovie/brand/fonts and use var(--dm-font-heading/body/mono)
DM-A02errorAssetsAny 4xx/5xx response, or a broken image or videofix the path (composition-relative, /brand/, /captures/, /assets/, /audio/) or remove the reference
DM-A03errorNetworkAny blocked external requestserve everything locally: copy the file into the composition folder or npx demovie add it
DM-A04errorAudio provenanceAn audio file that has no provenance.json entry from a demovie generator, a configured provider, or add --licensedgenerate audio with npx demovie audio music|voice|mix, or import it with npx demovie add <file> --licensed
DM-R01errorDeterminism5 sampled frames, rendered at full size on two fresh pages in different seek orders (the second time right after the frame before each), look the same: at most 0.01% of pixels differ visibly (pixelmatch threshold 0.1)derive everything from t: no Date/timers/real randomness, put tweens in v.timeline, make onSeek pure, and avoid will-change on animated elements
DM-R02warnBlank framesFrames that are over 98% one color for more than 0.3 s, outside declared transitionsstart content earlier, overlap shots with a transition, or add a background element
DM-R03errorStray GSAP tweensTweens outside v.timelineadd every tween to v.timeline (v.timeline.to/from/fromTo(..., at)), never gsap.to() directly
DM-R04warnUnregistered CSS animationsRunning CSS animations not registered via v.cssdrive CSS keyframes with v.css(el, keyframes, { start, duration }) so they follow the timeline
DM-R05errorTimers after readysetTimeout/setInterval called after ready()don't use timers for animation: schedule on v.timeline or compute state in v.onSeek(t)
DM-S01errorLoudnessIntegrated loudness outside target ± 2 LU warns; true peak above −1.0 dBTP errorsre-run npx demovie audio mix <slug> (two-pass loudnorm), or lower the music/SFX gains
DM-S02warnVO overlapVoice lines overlap each other or run past the end of the videomove VO lines apart in voice.json/storyboard (start times) or shorten them
DM-V01warn"AI look" clichésNon-UI text that matches HUD, timecode, BPM or "frame N" patterns; generic status chips ("Done", "Saved", "Success") not in the glossary; more than 2 typefacesdrop decorative HUD/timecode labels and generic chips; stay with the brand's one or two typefaces
↑ ↓ to move↵ to openesc to close