hyperframes lint, hyperframes check and the @hyperframes/lint package report findings with a stable code. This page lists every code with its severity, what it catches, and how to fix it. The codes come straight from the rule set in packages/lint/src/rules/ plus the project-level probes that run as part of a project lint. Severities:
  • Error — the composition is broken or the render will fail or visibly diverge. Fails hyperframes check by default.
  • Warning — a quality, determinism or maintainability hazard. Does not fail hyperframes check unless you pass --strict; in render, warnings abort only under --strict-all.
  • Info — a heads-up that needs no action. Shown by hyperframes lint --verbose.
Looking for a code from a render or check log? Codes appear in the output of npx hyperframes lint [dir] — add --json to script over them. check additionally runs runtime, layout and motion audits beyond these lint rules; see Troubleshooting.

Structure and timeline

Source: packages/lint/src/rules/structure.ts. The five codes marked Warning or error are warnings when the CLI lints a project, and errors when the browser editor lints the file it is editing.

nested_structure_needs_subcomposition

Warning or error. A top-level timeline element contains another nested structure instead of a sub-composition mount. The timeline draws one row per top-level element, and the root composition is built only from sub-compositions. Fix: Move the element and its contents into a sub-composition file and mount it with data-composition-src.

timeline_element_missing_timing

Warning or error. A timeline element has no data-duration, so the timeline cannot draw where it ends. Fix: Add data-duration (in seconds) to the element.

caption_track_kind_missing

Warning or error. An element looks like a caption row but is not marked as one, so the timeline cannot group it on the caption track. Fix: Add data-track-kind="captions" to the element.

multiple_caption_tracks

Warning or error. Captions are spread across several timeline tracks instead of one caption lane. Fix: Give every caption element the same data-track-index.

multiple_scene_templates

Warning. A file has more than one <template> holding a composition root and no composition root outside them. Only the first one renders; the message names each template. Fix: Keep one composition root per file, or move the other scenes into their own files.

clip_ends_past_root_duration

Warning. A clip runs past the root composition’s data-duration, so it is cut off in previews, posters and renders. Fix: Extend the root data-duration, or make the clip end at or before it.

nested_clip_never_visible

Warning or error. A timed clip sits inside another timed element, and its time window never overlaps that element’s, so it never appears. A nested clip’s data-start counts from the composition’s start, not from the element around it. Runs in sub-composition files too. Fix: Give the clip a data-start inside its parent’s window, in composition time, or move it out of the parent.

Composition contract

Source: packages/lint/src/rules/composition.ts.

invalid_composition_variables_declaration

Error. data-composition-variables is not valid JSON or not an array of variable declarations. Fix: Provide a JSON array such as data-composition-variables='[{"id":"title","type":"string","label":"Title","default":"Hello"}]' — single-quote the attribute, double-quote the JSON.

invalid_variable_values_json

Error. data-variable-values is not valid JSON. Fix: Single-quote the attribute and double-quote the JSON keys and values, e.g. data-variable-values='{"title":"Hello"}'.

unknown_variable_binding

Warning. An element binds a variable that is not declared in data-composition-variables, so the binding silently keeps the authored fallback. Fix: Declare the variable on the composition root (<html>, or the data-composition-id root of a template or fragment composition).

unloadable_media_variable_default

Error. A variable’s default is a URL the runtime refuses to load, so bound elements render their authored fallback src while the render still exits 0. Fix: Use a relative path, http(s), blob:, or a data:image/* URI. Copy the file into the project and reference it relatively.

deprecated_data_end

Error. A legacy data-end is used without data-duration, or a stale data-end disagrees with the data-duration next to it. Fix: Replace data-end with data-duration. The compiler generates data-end from data-duration automatically.

deprecated_data_layer

Error. The legacy data-layer attribute is used. Fix: Replace it with data-track-index, the canonical name the browser editor and the linter read.

duplicate_composition_id

Error. The same data-composition-id appears on several elements in one file — often a <meta> tag carrying the same id as the root <div>. Fix: Keep data-composition-id on exactly one element: the composition root. Remove it from metadata or duplicate hosts.

invalid_parent_traversal_in_asset_path

Error. Asset paths use ../ to traverse above the project root; render and preview clamp or misresolve them. Fix: Point the path at a file inside the project — from compositions/scene.html, both ../assets/x.png and assets/x.png reach the project’s assets folder.

composition_exceeds_inspection_viewport_cap

Warning. The composition is larger than the 4096px viewport cap that check, validate, snapshot, compare and layout audits clamp to. render is unaffected, but a clean inspection result no longer covers the whole canvas. Fix: Keep the authored size if the output needs it, and verify the region past the cap from a render rather than from inspection commands.

composition_file_too_large

Warning. The HTML composition file has grown past a size where it stays easy to read, iterate on, and diff. Fix: Split it into sub-compositions and mount them from the parent with data-composition-src.

composition_heavy_overlay_count_high

Warning. Many elements carry heavy overlay CSS (filter: blur, radial-gradient, clip-path), which can cap capture performance — including hidden ones. Fix: Split the composition and concat the pieces so each capture sees a small subset of heavy overlays; remove overlays that are not visible anyway.

missing_data_no_timeline

Warning. A data-composition-id element (or the root) has neither a matching window.__timelines registration nor data-no-timeline, so the producer polls up to 45 seconds before timing out. Fix: Register the timeline, mount the composition with data-composition-src, or add data-no-timeline for genuinely static sections.

negative_z_index

Warning. z-index below 0 paints the element behind its nearest stacking context’s content; with no stacking-context ancestor that is the composition root, so an opaque background there hides the element entirely. Fix: Give the element’s parent a stacking context (isolation: isolate is the cheapest; transform, filter, opacity below 1, contain: paint, will-change also work).

requestanimationframe_in_composition

Error. requestAnimationFrame runs on wall-clock time, not the GSAP timeline, so it desyncs from frame capture and can flicker or drop frames in renders. Fix: Use GSAP tweens or timeline onUpdate callbacks for animation logic instead.

root_composition_duration_derived

Warning. The root has no data-duration and no GSAP timeline, so its length is inferred from its clips. Fix: Add data-duration="<seconds>" to the root element to set the length yourself.

root_composition_missing_duration_source

Error. The root has no data-duration, no GSAP timeline, and no CSS, WAAPI, Lottie or Three.js animation to infer a duration from, so the render engine cannot determine how long to capture and fails. Fix: Add data-duration="<seconds>" to the root, or add a paused GSAP timeline registered on window.__timelines.

root_composition_missing_html_wrapper

Error. An index.html carrying data-composition-id is a bare fragment without <!DOCTYPE html>, <html> or <body>, so browsers quirks-mode it and the preview server mis-serves it. Fix: Wrap the composition in <!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>...</body></html>.

root_zoom_rescales_a_fixed_canvas

Error. CSS zoom on the canvas rescales the painted composition inside a capture frame that does not rescale with it — the frame is sized from the root’s data-width and data-height in layout pixels. Fix: Author at the real size: set data-width and data-height (and the root’s width and height) and drop the canvas-level zoom. To scale the output without changing layout, pass --output-resolution to render. zoom on descendants is fine.

split_data_attribute_selector

Error. A CSS selector combines two attributes inside one [...] block, which browsers reject — GSAP timelines or querySelector calls fail before they register. Fix: Use separate attribute selectors: [data-composition-id="x"][data-track-index="1"].

standalone_composition_wrapped_in_template

Error. The root index.html is wrapped in a <template> tag, so the runtime cannot play it — only sub-compositions loaded via data-composition-src use <template> wrappers. Fix: Remove the <template> wrapper and serve the composition as a real document.

subcomposition_blanks_before_host

Warning. A mounted sub-composition is blank for part of its host’s window because its own data-duration is shorter than the slot it fills. Fix: Set the sub-composition’s data-duration to fill the host window, or cover the gap with another clip.

subcomposition_linked_style_names_root_id

Warning. A stylesheet linked from a sub-composition has a rule on #<root id>, the id of the sub-composition’s root. When the composition is compiled the root is merged into its host, so that selector matches nothing. The same rule in an inline <style> is rewritten and works. Fix: Move the rule into a <style> inside the <template>, or select a child element of the root in the linked file instead of the root itself.

subcomposition_root_styled_by_class

Error. A sub-composition’s root element is styled through class selectors, but at render every rule is scoped to [data-composition-id=...], so the class-keyed styles stop applying. Fix: Give the root id="root" and style it with #root plus plain descendant selectors.

subcomposition_style_outside_template

Warning. A sub-composition file with a <template> has a <style>, or a <link rel="stylesheet"> outside <head>, that sits outside the template. Mounting keeps only the template’s content and the stylesheet links in <head>, so those styles never apply in preview, snapshot or render. Fix: Move it inside <template>, next to the composition root.

template_literal_selector

Error. querySelector is called with a template-literal variable, which the bundler’s CSS parser crashes on. Fix: Use hardcoded string selectors instead of interpolated ones.

timed_element_missing_clip_class

Warning. An element has timing attributes but no class="clip". The runtime still times it, but the browser editor and the clip-ownership rules use .clip to recognise it, so it is harder to edit and to lint. Fix: Add class="clip" to the element.

timeline_track_too_dense

Warning. One track in this file carries more than 3 timed elements (audio, video, caption cues and sub-composition mounts are not counted), which makes the timeline harder to read, iterate on, and diff. Fix: Split the elements into sub-compositions and mount them from the parent with data-composition-src.

Document, CSS and scripts

Source: packages/lint/src/rules/core.ts.

host_missing_composition_id

Error. A data-composition-src host element has no data-composition-id of its own. Fix: Set data-composition-id on every data-composition-src host element.

css_parse_error

Error. A <style> block fails to parse as CSS. Fix: Fix the reported CSS syntax error.

css_transition_used

Warning. A CSS transition declaration runs on the browser clock and cannot be seeked deterministically across render workers. transition: none and all-zero durations are allowed. Fix: Keep the class or attribute swap for state, but put the visual change on the paused GSAP timeline.

id_override_reduced_specificity

Warning. A selector sets position properties through [id=...] or :where(#id), which has class-level or zero specificity, so a compound class rule on the same element can silently win. Fix: Use the plain #id selector; id specificity beats any selector built only from classes.

id_requires_css_escape

Error / warning. An id starts with a digit, so #id selectors throw a SyntaxError in querySelector and GSAP, and CSS drops the rule. Error when such a selector is actually used in the composition, warning otherwise. Fix: Rename the id to start with a letter (recommended), or build selectors with CSS.escape(id) at runtime.

inline_style_attribute_selector

Error. A selector reads the inline style attribute ([style], [style*=...] and the other forms). GSAP writes inline styles every frame, and each write then restyles far more than the animated element. The preview slows to a crawl. Fix: Let the runtime hide hidden clips; it already hides their contents. For a state of your own, toggle a class or data attribute from your script and select on that.

invalid_inline_script_syntax

Error. An inline <script> is closed with malformed syntax. Fix: Close inline scripts with a valid </script> tag.

missing_timeline_registry

Error. The composition registers no window.__timelines registry at all. Fix: Register each composition timeline on window.__timelines[compositionId].

non_deterministic_code

Error. A script uses a non-deterministic value source (Math.random, Date.now, gsap.utils.random, "random(...)"), so renders can differ between frames or runs. Fix: Precompute the value once at build time and pass a constant, or seed and freeze it.

repeated_id_descendant_selector

Error. A selector requires an id to be nested inside another element with the same id. Ids must be unique, so the selector can never match. Fix: Remove the duplicate ancestor: change #x #x to #x.

root_dimensions_mismatch

Warning. The root’s data-width/data-height were edited directly, but the scaffold’s html, body CSS and <meta viewport> still hold the old resolution, and a stale overflow: hidden body visually clips the correctly-sized root. Fix: Update the body CSS and viewport to match, or scaffold with hyperframes init --resolution portrait.

root_missing_composition_id

Error. The root composition is missing data-composition-id. Fix: Add a stable data-composition-id to the entry composition wrapper.

root_missing_dimensions

Error. The root composition is missing data-width or data-height. Fix: Set numeric data-width and data-height on the entry composition root.

runtime_hidden_style_opacity

Error. A selector observes the runtime-owned hidden style (used to hide native video during frame copying) and forces opacity to zero, which breaks the video replacement pipeline. Fix: Remove the rule; the runtime already hides a hidden clip and its contents. To fade or hide something yourself, animate it on the timeline, or toggle a class or data attribute and select on that.

scoped_css_missing_wrapper

Warning. Scoped CSS targets a composition id for which no matching wrapper exists in this HTML. Fix: Keep the matching composition wrapper, or align the CSS scope to an existing wrapper.

studio_missing_editable_id

Warning. A timeline-visible element has no id, so the browser editor cannot target it stably for timeline and canvas edits. Fix: Add a stable, human-readable id such as id="hero-title" to elements that agents or the browser editor should edit.

timeline_id_mismatch

Error. A timeline is registered under a key for which no element carries the matching data-composition-id, so the runtime cannot auto-nest it. Fix: Make window.__timelines["<key>"] match the element’s data-composition-id, or vice versa.

unbalanced_style_tags

Error. A <style> block is never closed (following markup is parsed as CSS and disappears), or an extra </style> closes the stylesheet early and dumps CSS into the body as visible text. Fix: Keep <style> and </style> paired.

unclosed_tag_swallowed_element

Error. A tag is missing its closing > before the next <, so the following element is swallowed as bogus attribute text and never becomes a real node. Fix: Close the previous tag’s > before opening the next element.

visible_markup_comment

Error. CSS or JS block-comment syntax (/* ... */) appears in visible HTML markup, where only <!-- ... --> is a comment — so it renders as on-screen text. Fix: Remove the text or convert it to a real HTML comment; keep CSS comments inside <style> and JS comments inside <script>.

GSAP animation

Source: packages/lint/src/rules/gsap.ts.

audio_reactive_single_tween_per_group

Warning. Audio-reactive captions use a single tween per group based on peak values, which sets one static value and is not perceptible as audio reactivity. Fix: Sample the audio data at 100-200ms intervals across each group’s lifetime and create a tween per sample point.

gsap_animates_clip_element

Error. GSAP sets visibility-affecting properties (opacity, visibility, autoAlpha) directly on a clip element whose visibility the framework owns. Fix: Remove the visibility or autoAlpha tween, or move the content into a child <div> and target that instead.

gsap_callback_dom_measurement

Warning. A timeline callback reaches DOM measurement (getBoundingClientRect, getTotalLength, getComputedStyle, …). Callbacks re-fire on every seek, and a cold render worker runs them against a different layout than the sequential pass. Fix: Do all measurement and DOM setup synchronously at build time, before registering the timeline, or derive geometry from fixed composition coordinates.

gsap_cold_seek_hidden_fromto_missing_reveal

Error. An element authored hidden uses gsap.fromTo() whose destination vars omit opacity/autoAlpha, so cold render workers restore the hidden state and the element can stay invisible. Fix: Add opacity: 1 (or autoAlpha: 1) to the destination vars so every seek path establishes the visible end state.

gsap_css_transform_conflict

Error. An element has a CSS transform (for example translateX(-50%) centering) and a GSAP tween animates transform properties — GSAP overwrites the full CSS transform. Fix: Remove the transform from CSS and express it in GSAP properties instead; tl.fromTo with explicit start values is exempt.

gsap_from_opacity_noop

Error. An element has CSS opacity: 0 and a gsap.from() that also sets opacity to 0, so it animates from 0 to 0 and never appears. Fix: Remove opacity: 0 from the CSS or inline style and let the gsap.from() own the initial hidden state.

gsap_fullscreen_overlay_starts_visible

Error. A full-frame overlay starts visible before its first GSAP opacity tween, so it covers earlier render frames — often as a blank or white video. Fix: Author the overlay hidden in CSS, or add an immediate gsap.set(..., { opacity: 0 }) before the reveal tween.

gsap_function_value_hazard

Error / warning. A function-valued tween var reads transform-sensitive geometry, measures layout at tween init, or accesses its first parameter (which is a number, not the element). Error for transform-sensitive reads and bad member access, warning otherwise. Fix: Compute the value once at build time and pass a constant; for per-element logic use the second parameter: (index, target) => ....

gsap_group_selector_keyframes

Warning. A GSAP tween targets several elements with shared keyframes, so editing one element’s keyframes in the browser editor changes all of them. Fix: Replace the group selector with individual tl.to() calls per element.

gsap_infinite_repeat

Error / warning. A GSAP tween or timeline uses repeat: -1. Warning when the composition declares a finite data-duration (seeking and export are clipped to it), error when nothing bounds the timeline. Fix: Declare a finite data-duration, or replace repeat: -1 with a finite count such as repeat: Math.max(0, Math.floor(totalDuration / cycleDuration) - 1).

gsap_non_transform_motion

Error. A GSAP tween animates layout properties (left, top, width, …), text-reflow properties (fontSize, letterSpacing, …) or sets roundProps, all of which snap to integer device pixels and stutter under frame capture. Fix: Animate transforms instead: positional properties become x/y/scale; reflow properties need per-character splits or a static final value; remove roundProps.

gsap_relative_value_second_writer

Error. A relative tween value (+=...) starts while another writer is still active on the same property, so different seek paths init the relative tween from different bases and the frame snaps. Fix: Use absolute values or a fromTo() with explicit endpoints for that property. A relative value with a single writer is safe.

gsap_repeat_ceil_overshoot

Warning. A repeat count computed with Math.ceil can overshoot the composition duration, extending the animation past it. Fix: Use Math.floor in the repeat calculation: repeat: Math.max(0, Math.floor(totalDuration / cycleDuration) - 1).

gsap_repeat_floor_unclamped

Warning. A repeat calculation can evaluate to -1 when the composition is shorter than one cycle, which GSAP interprets as an infinite repeat. Fix: Clamp the count at zero: repeat: Math.max(0, Math.floor(totalDuration / cycleDuration) - 1).

gsap_repeat_refresh_relative_value

Error. repeatRefresh: true combined with a relative value accumulates per iteration; a cold render worker seeking into iteration N never performed the earlier accumulation, so its result differs. Fix: Remove repeatRefresh: true, or replace the relative value with absolute endpoints (a fromTo()).

gsap_repeated_fromto_without_baseline

Warning. Several tl.fromTo() calls target the same element with no stable baseline, so the last-authored “from” values become the resting state for any seek before the first tween runs. Fix: Add immediateRender: false to every fromTo except the earliest-positioned one.

gsap_timeline_not_registered

Error. A GSAP timeline is created but never registered on window.__timelines, so the runtime never discovers it and the animation does not play in preview or render. Fix: Add window.__timelines = window.__timelines || {}; and assign the timeline under the composition’s data-composition-id.

gsap_timeline_registered_before_async_build

Error. window.__timelines is assigned before the timeline is built inside document.fonts.ready, so an empty timeline registers first and can nest empty in sub-compositions. Fix: Move the window.__timelines[id] = tl assignment to the end of the async build callback, after the tweens are added.

gsap_timeline_return_used_as_tween

Error. Code captures the return of tl.to()/tl.from()/tl.set() expecting the tween, but those methods return the timeline itself — so a later kill() or overwrite hits the whole timeline. Fix: Put the group of tweens in a nested timeline (gsap.timeline() added with tl.add(nested, 0)) and kill that instead.

gsap_timeline_set_initial_hide

Warning. The initial hidden state is set with tl.set(..., 0) inside the paused timeline; a zero-duration set at position 0 does not render while the playhead sits exactly at 0, so frame 0 shows the un-hidden state. Fix: Use gsap.set(...) outside the timeline, or author the hidden state directly in CSS or attributes.

gsap_undefined_css_variable

Warning. GSAP animates a var(--...) CSS variable that no static CSS or composition-variable declaration defines, so the computed color may become invalid or transparent. Fix: Define the variable in CSS, declare it in data-composition-variables, or add a var(--x, #fff) fallback.

missing_gsap_plugin

Error. A tween uses a plugin-specific property such as motionPath, but the plugin is not loaded and registered, so core GSAP ignores the property. Fix: Load the plugin before the animation script and register it with gsap.registerPlugin(...), or express the motion with core x/y tweens.

missing_gsap_script

Error. The composition uses GSAP but no GSAP script is loaded, so the animation will not run. Fix: Add the GSAP <script> tag before the animation script.

overlapping_gsap_tweens

Warning. Two GSAP tweens overlap on the same target for the same properties, so the later one overwrites the earlier mid-flight. Fix: Shorten the earlier tween, move the later one, or add overwrite: "auto".

svg_drawon_css_dasharray_conflict

Error. GSAP writes strokeDasharray on an element whose CSS declares a multi-component stroke-dasharray; GSAP merges dash lists per component, so the CSS gap survives and the draw-on breaks. Fix: Remove the CSS stroke-dasharray (decorative dashes belong on a separate element), or set the full two-component value in GSAP.

svg_round_cap_hidden_dash

Warning. A round or square capped path is hidden with dasharray = gap = pathLength and dashoffset = dash. Chrome can leave a zero-length sliver at the path end there, and the cap paints it as a dot on some curves. Fix: Make the gap longer than the path (stroke-dasharray="100 120", stroke-dashoffset="110"). Or keep the element visibility: hidden until its draw tween starts.

svg_measure_before_path_d

Error / warning. getTotalLength() is called on a path with no static d attribute, so it returns 0 and the dash animation dies. Error when no d assignment exists anywhere, warning when d is only assigned inside a function body. Fix: Assign the path’s d synchronously at build time (top level, before measuring), or author a static d attribute in the HTML.

Media elements

Source: packages/lint/src/rules/media.ts.

audio_attribute_unreadable

Error. A data-automation or data-fx-chain is not one the render can parse: not JSON, no "version": 1, no lanes / nodes array, a lane without target or points, or a node without type. The audio mix fails on it, locally and in the cloud. Fix: Write it as {"version": 1, "lanes": [...]} (automation) or {"version": 1, "nodes": [...]} (effect chain), or remove the attribute.

audio_carve_ungrouped_sources

Warning. A voiceover carve names its member clips directly instead of carving an <hf-audio-group>, so the list silently rots when clips change. Fix: Group the voice clips and carve against the group.

audio_group_carve_attr

Warning. An <hf-audio-group> carries data-fx-carve (or a carve-derived chain), but a carve belongs on the bed clip, not the bus. Fix: Remove data-fx-carve from the group and carve the bed clip instead.

audio_group_holds_elements

Warning. Elements sit inside an <hf-audio-group>. Most often the group was written self-closed (<hf-audio-group id="sfx" />), which HTML does not close on a custom element, so every later sibling lands inside it. Being inside routes no audio through the bus, and while the group has data-hidden those elements are hidden and silenced in preview and render. Fix: Close the group with </hf-audio-group> right after its opening tag, and give clips meant for the bus data-audio-group.

audio_group_no_members

Error. An audio group exists that no clip joins, so its fader, effects and automation are silently dropped. Fix: Add data-audio-group="<id>" to the clips the bus is for, or delete the bus.

audio_group_timing_attrs

Warning. An <hf-audio-group> carries timing attributes (data-start, data-duration, data-track-index). A group has no clip window of its own; its members carry the timing. Fix: Remove the timing attributes from the group and set them on the member clips.

audio_volume_double_automation

Warning. An element has both a volume automation lane and a GSAP tween on volume; the lane wins and the tween is ignored in preview and render. Fix: Keep one: delete the lane to tween, or drop the tween and shape the level in the lane.

audio_volume_tween_overrides_gain

Warning. An element has data-volume gain and a GSAP tween on volume; tween values are absolute and replace the gain rather than scale it. Fix: Write tween targets as absolute gains that already include the gain, or reset data-volume to 1 and let the tween carry the level.

base64_media_prohibited

Error. Inline base64 audio or video is present. Base64 media bloats file size and breaks rendering. Fix: Use a relative path or HTTPS URL for the src — never embed media as base64.

color_grading_invalid_json

Error. data-color-grading on a <video> or <img> starts with { or [ but is not valid JSON, so the grade does not render. Fix: Use valid JSON, for example data-color-grading='{"preset":"skin-soft","intensity":0.6,"adjust":{"highlights":-0.08}}' — single-quote the attribute, double-quote the JSON.

color_grading_invalid_structure

Error. data-color-grading parses as JSON but breaks the grading contract — an unsupported key, a value of the wrong type, or a number out of range. The runtime silently ignores unknown keys, so a control can look applied in the HTML yet never render. One finding is reported per problem, naming the offending path. Fix: Correct or remove the value the finding names, following the Color grading reference.

color_grading_non_media

Error. data-color-grading is set on an element other than <video>, <img>, <canvas> or <hf-color-group>, such as a wrapper or an element with a CSS background. The shader runtime only grades real <video>, <img> and <canvas> elements, plus the members of a grade group, so the attribute has no effect. Fix: Move the attribute to the <video>, <img> or <canvas> element itself.

duplicate_media_discovery_risk

Warning. Several matching media entries share the same source, start and duration, so the compiler can discover the same media twice. Fix: Remove the duplicated media nodes so discovery sees one.

duplicate_media_id

Error. A media id is defined on multiple elements, so preview and the producer can build different media graphs. Fix: Give each media element a unique id.

imperative_media_control

Error. An inline script controls managed media imperatively (play, pause, currentTime, …). HyperFrames must own playback to keep preview, timeline and renders deterministic. Fix: Express timing with data-start/data-duration and media offsets such as data-media-start instead.

media_crossorigin_breaks_preview

Error. The element has crossorigin, which forces a CORS-checked fetch; if the host omits CORS headers, the load fails silently in the browser editor preview (blank media) while renders still work. Fix: Remove crossorigin unless you read the media back through canvas, WebGL or WebAudio from a host known to send CORS headers.

media_missing_data_start

Error. A media element has src but no data-start, so HyperFrames cannot own its playback and preview and render can diverge. Fix: Add data-start="0" (or the intended start) and data-duration if the clip should stop before the source ends.

media_missing_id

Error. A timed media element has no id, so the renderer cannot discover it — the audio goes silent or the video freezes. Fix: Add a unique id attribute to the element.

media_missing_src

Error. A timed media element has no src (on the element or a <source> child), so the renderer cannot load it. Fix: Add src to the element, or a <source src="..."> child.

media_preload_none

Warning. The element has preload="none", which prevents the renderer from loading the media (the compiler strips it for renders, but preview can misbehave). Fix: Remove preload="none" or change it to preload="auto".

media_runtime_src_mutation

Warning. An inline script mutates the src of existing managed media, which races media discovery and extraction. Fix: Author the final static src, or bind data-var-src to a declared variable so the source is applied before discovery.

media_src_kind_mismatch

Error. A media URL’s kind does not match its element — for example a still image on <video> or a video URL on <img>, so the element renders nothing. Fix: Match the element to the media: <img> for stills, <video> for video URLs, <audio> for sound.

media_variable_src_no_fallback

Warning. The element relies on data-var-src with no fallback src, so renders without that variable value cannot load the media, and audio extraction reads the authored src. Fix: Add a fallback src the composition can render with when the variable is not provided.

nested_media_start_basis_ambiguous

Warning. A nested media element’s data-start is ambiguous: composition-local or a legacy root-global timestamp, and the two read as different frames. Fix: Keep data-start composition-local, or mark the legacy basis with data-hf-media-start-basis="global"; otherwise convert to local time.

placeholder_media_url

Error. The element uses a placeholder media URL that will 404 at render time. Fix: Replace it with a real, reachable media URL.

self_closing_media_tag

Error. A self-closing media tag (<video ... />) is invalid HTML: the browser leaves the tag open and swallows all subsequent elements as invisible fallback content, making the composition invisible. Fix: Write explicit closing tags: <video ...></video>. Media elements must never be self-closed.

speed_ramp_on_non_media

Warning. A speed-ramp lane sits on a non-media element; only <video> and <audio> clips can be retimed, so the lane does nothing. Fix: Move the rate lane onto the video or audio clip, or retime an animation with a GSAP timeline.

video_audio_double_source

Error. An <audio> element and a <video> element point at the same source at the same time; the unmuted video already carries the sound, so playback echoes. Fix: Let the video carry its own sound (recommended), or mute the video and keep the separate <audio> when picture and sound must be cut independently.

video_missing_muted

Error. A timed <video> declares neither muted nor data-has-audio="true", so the linter cannot tell silent footage from sound-carrying footage. Fix: Add data-has-audio="true" when the file has sound, or muted when it is silent.

video_muted_with_declared_audio

Error. A <video> declares data-has-audio="true" but also carries muted, so the browser editor preview silences the audio the declaration promises. Fix: Remove muted if the video should be audible, or drop data-has-audio and use data-volume="0" for silent visual video.

video_nested_in_timed_element

Error. A <video> with data-start is nested inside another timed element; the frame extractor resolves the video’s start without the wrapper’s offset while visibility uses the wrapper’s window, so the clip shows wrong frames and disappears partway. Fix: Time the wrapper or the video, never both: remove data-start from the wrapper, or move the <video> up to be a direct child of the stage.

Captions

Source: packages/lint/src/rules/captions.ts.

caption_container_relative_position

Error. A caption selector uses position: relative, which causes overflow and breaks caption stacking. Fix: Use position: absolute for all caption elements.

caption_exit_missing_hard_kill

Error. Caption exit animations (tl.to with opacity: 0) have no hard tl.set kill, so conflicting karaoke tweens can leave captions stuck on screen. Fix: Add tl.set(groupEl, { opacity: 0, visibility: "hidden" }, group.end) after every exit tween as a deterministic kill.

caption_fittext_scale_mismatch

Warning. fitTextFontSize uses a maxWidth that leaves no headroom for emphasis words scaled above 1x, so the effective width can overflow the composition. Fix: Reduce maxWidth to leave headroom for the scale factor.

caption_overflow_clips_scaled_words

Error. A caption container has overflow: hidden while GSAP scales caption words above 1x, so scaled emphasis words and their glow are clipped. Fix: Use overflow: visible on caption containers and prevent overflow with fitTextFontSize and a reduced maxWidth instead.

caption_text_overflow_risk

Warning. A caption selector has white-space: nowrap but no max-width, so long phrases clip off-screen. Fix: Add max-width: 1600px (landscape) or 900px (portrait), keeping overflow visible.

caption_textshadow_on_group_container

Warning. textShadow is tweened on a caption group container; with semi-transparent children the glow renders as a visible rectangle behind the whole group. Fix: Apply textShadow to individual active word elements and use scale on the group for pulsing.

caption_transcript_not_inline

Error. A captions composition loads its transcript with fetch(), but the browser editor caption editor needs an inline var TRANSCRIPT = [...] array to detect and edit captions. Fix: Embed the transcript inline as var TRANSCRIPT = [...] with JSON-quoted keys.

Fonts

Source: packages/lint/src/rules/fonts.ts.

font_family_without_font_face

Error. A @font-face-undeclared font family is used, so the renderer cannot supply it and text falls back to a default face. Fix: Declare each family with @font-face pointing at the captured .woff2 files, or bundle the font under capture/assets/fonts/. The declaration may use a quoted or unquoted family name. If font-family is the last descriptor in the block, its final semicolon is optional. Families named in a font shorthand (font: 500 16px/1 "PingFang SC", sans-serif) count the same as font-family.

font_face_local_only

Warning. An @font-face names only local() sources. A local render draws the font installed on this machine; a cloud render, or another machine without it, falls back to a different font. Fix: Add a url() source pointing at a .woff2 file inside the project, or use a Google Fonts family.

system_font_will_alias

Warning. A system font family is used without a matching @font-face; in distributed rendering system-font capture is disabled, so the font is substituted at render time and text can reflow. Only reported when the linter runs with the distributed: true option (linting for distributed or Lambda renders); a local render captures the system font as designed, so it is not flagged there. Fix: Bundle the font with @font-face and a project-local file instead of relying on OS fonts.

Textures

Source: packages/lint/src/rules/textures.ts.

texture_class_missing_base

Warning. A texture material class is used without the base hf-texture-text class, so the texture pipeline does not engage. Fix: Add the base class alongside the material class, e.g. class="hf-texture-text hf-texture-lava".

texture_class_unknown

Error. A texture material class is not defined by any local CSS, so the text renders untextured. Fix: Paste the Texture Mask Text component’s <style> block into the composition, or fix the class typo.

texture_drop_shadow_on_text

Warning. A drop shadow is applied directly to textured text, which shadows the mask rather than the glyphs. Fix: Wrap the textured text and apply filter: drop-shadow(...) to the wrapper, not the hf-texture-text element.

texture_text_missing_mask

Warning. hf-texture-text is used without a texture material class or a custom mask-image, so there is nothing to reveal. Fix: Add a material class such as hf-texture-lava, or set mask-image and -webkit-mask-image on the element.

Slideshows

Source: packages/lint/src/rules/slideshow.ts.

slideshow_invalid

Error. A slideshow island contains invalid JSON or a structure that does not match the manifest schema. Fix: Make the <script type="application/hyperframes-slideshow+json"> block valid JSON matching the SlideshowManifest schema. If supplied, startTime and endTime must be finite JSON numbers. Quoted numbers such as "0", null, and overflowing values such as 1e400 are invalid.

slideshow_unresolved_ref

Error. The slideshow manifest references a sceneId with no matching scene element, or a scene with no explicit window. Fix: Make every sceneId match a scene element’s data-composition-id, or provide explicit startTime/endTime.

Adapters: Lottie and Three.js

Source: packages/lint/src/rules/adapters.ts.

missing_lottie_script

Error. The composition uses Lottie but no Lottie script is loaded, so the animation will not render. Fix: Add the lottie-web <script> tag before the Lottie code.

missing_three_script

Error. The composition uses Three.js but no Three.js script is loaded, so the 3D scene will not render. Fix: Add the Three.js <script> tag before the Three.js code.

Linked clips

Source: packages/lint/src/rules/linkedClips.ts.

linked_clip_orphan

Warning. A clip carries data-link whose partner clip no longer exists, so it edits as a group of one. Fix: Remove the data-link attribute, or restore the partner clip.

linked_clips_out_of_sync

Warning. Clips sharing a data-link differ in their timing attributes, but linked clips are edited as one. Fix: Give every member the same data-start, data-duration, data-media-start and data-playback-rate, or remove data-link from all of them to unlink.

Project-level checks

Source: the project lint pass in packages/lint/src/project.ts, packages/lint/src/hyperframeLinter.ts and packages/lint/src/hevcPreviewLint.ts.

multiple_root_compositions

Error. Several root-level HTML files carry data-composition-id, so the runtime may discover multiple entry points and duplicate audio playback. Fix: Keep exactly one root index.html with data-composition-id; remove or rename the extra files.

blank_root_with_standalone_composition

Error. The default index.html has no renderable content while another file holds a standalone timed composition, so default commands open an empty video. Fix: Move the composition into index.html, or mount it with data-composition-src; if the separate file is intentional, render it explicitly.

missing_or_empty_sub_composition

Error. A data-composition-src points at a folder, an empty file, or a missing file, so the mount is unusable and the render pre-flight rejects it. Fix: Point data-composition-src at the HTML file (such as <folder>/index.html) and make sure it renders content.

missing_local_asset

Error. An element references local files that do not exist in the project, so the renderer silently skips them and the video ships with missing visuals. Fix: Add the file to the project directory, or update the src to an existing file. A common cause is renamed captured assets.

audio_file_without_element

Warning. The project contains audio files but no <audio> element in any composition, so the rendered video will be silent. Fix: Add an <audio> element referencing the file.

audio_src_not_found

Error. An <audio> element references files that are not in the project, so the rendered video will be silent. Fix: Add the missing file(s) to the project directory, or update the src to point at existing files.

duplicate_audio_track

Warning. Two audible elements overlap on the same track index, so their audio doubles up. Fix: Use non-overlapping time windows or different track indices.

texture_mask_asset_not_found

Error. A CSS mask-image or -webkit-mask-image declaration references files that are not in the project, so the texture mask fails to load. In valid CSS, comments and text inside other declarations, such as content strings, are ignored. Fix: Add the missing mask file(s) to the project, or update the mask-image URL.

missing_stylesheet_asset

Warning. A url() in a linked local stylesheet does not resolve beside that stylesheet. Browsers resolve url() from the stylesheet’s own folder, so the font or image 404s and the render falls back silently. When the file exists in a parent folder, the fix hint names the working path. Fix: Change the url() to be relative to the stylesheet (for example ../assets/fonts/x.ttf from styles/main.css), or add the file.

inaccessible_media_url

Error. A remote media URL did not answer a HEAD request with a 2xx status (or the request failed or timed out), so the media cannot load. Reported only by the async lintMediaUrls() export of @hyperframes/lint; hyperframes lint and hyperframes check do not run it. Fix: Replace it with a valid, reachable media URL.

hevc_preview_codec

Info. A video file uses the HEVC/H.265 codec. Renders are unaffected (FFmpeg pre-decodes), and preview automatically falls back to a cached H.264 proxy when the browser cannot decode HEVC. Fix: Nothing required. If preview playback still fails, run hyperframes doctor and check that media auto-proxying is enabled.

video_media_start_at_or_past_eof

Warning. A video’s data-media-start sits at or past the source’s playable duration, so the video holds its final frame for the whole slot. Fix: Trim data-media-start below the source duration if the final-frame hold is unintended.