Files
hypit/docs/quickstart/styles.md

13 KiB
Raw Permalink Blame History

title, description
title description
SVS Stylesheets The SVS Recipe language — CSS-like stylesheets for film, caption, media, text and generation settings.

SVS (.svs) files define reusable, typed configuration values using a CSS-like syntax. They configure Film appearance, caption appearance, Media presentation and motion, text styling, generation settings, and typography choices. SVS values are called Recipes — they are immutable typed Records that consuming components validate and interpret.

Basic syntax

<?svml using="@hypit/recipe@1"?>

<sheet version="1" id="studio">
  film.vertical {
    background: #09090B;
  }

  /* Comments use CSS-style block syntax. */
  caption.primary {
    fill: #FFFFFF;
    size: 58;
  }
</sheet>
  • The processing instruction <?svml using="@hypit/recipe@1"?> selects the SVS parser.
  • The <sheet> element wraps all declarations. The id attribute becomes the top-level namespace.
  • Each block is namespace.name { ... } with ;-terminated key-value properties.
  • Comments use /* ... */.

Importing and referencing

Import an SVS file in your .svml source with a namespace prefix:

<import as="recipes" source="./recipes.svs"/>

Then reference individual Recipes via {recipes.film.vertical}, {recipes.caption.primary}, etc. The prefix comes from the as= attribute; the path comes from namespace.name in the sheet.

Film

Film appearance owns only the canvas clear color. Canvas dimensions are an explicit space:Canvas graph value, while frame rate comes from the Timeline's Clock.

film.vertical {
  background: #09090B;
}
Property Description
background Canvas clear color (hex)

Referenced by film:Film via the appearance attribute:

<space:Canvas id="vertical" width="1080" height="1920"/>
<film:Film id="main" canvas={vertical.canvas} timeline={speech.timeline} appearance={recipes.film.vertical}>

Caption Fine

The first official Caption Style family keeps planning and rendering parameters in one Recipe.

caption.dialogue {
  stack-order: 70;
  x: 0.08;
  y: 0.76;
  width: 0.84;
  size: 58;
  line-height: 0.96;
  align: center;
  fill: #FFFFFF;
  background: #09090BCC;
  padding: 16 24;
  radius: 18;
}
Property Description
stack-order Z-stacking order among all Tracks (higher = on top)
x, y Position as fraction of canvas (0–1)
width Width as fraction of canvas
size Font size in pixels
line-height Line height multiplier
align Text alignment: left, center, right
fill Text color (hex, supports alpha)
background Container background color (hex, supports alpha like #09090BCC)
padding Container padding in pixels (single value or vertical horizontal)
radius Container border radius in pixels

For reproducible rendering, select an exact installed face in the .svml source and pass that Record to the Fine Style. Family, weight and style have one source of truth on this exact font edge:

<fonts:Face id="caption-font" package="@fontsource-variable/inter" weight="600" style="normal"/>
<caption-fine:Style id="primary-caption" recipe={recipes.caption.dialogue}
  font={caption-font}/>

Per-role caption styles

Define multiple caption Recipes for different speakers:

caption.alice {
  stack-order: 70;
  x: 0.08; y: 0.76; width: 0.84;
  size: 58;
  line-height: 0.96;
  align: center;
  fill: #73FBD3;
  background: #09090BCC;
  padding: 16 24; radius: 18;
}

caption.bob {
  stack-order: 70;
  x: 0.08; y: 0.76; width: 0.84;
  size: 58;
  line-height: 0.96;
  align: center;
  fill: #FFD166;
  background: #09090BCC;
  padding: 16 24; radius: 18;
}

Then select them with timed Uses in the Track:

<fonts:Face id="caption-font" package="@fontsource-variable/inter" weight="600" style="normal"/>
<caption-fine:Style id="default-caption" recipe={recipes.caption.dialogue} font={caption-font}/>
<caption-fine:Style id="alice-caption" recipe={recipes.caption.alice} font={caption-font}/>
<caption-fine:Style id="bob-caption" recipe={recipes.caption.bob} font={caption-font}/>
<caption-fine:Caption id="captions" document={story.caption} timing={story-captions}
  timeline={speech.timeline} within={vertical.bounds}>
  <caption-fine:Use style={default-caption}/>
  <caption-fine:Use role="ALICE" style={alice-caption}/>
  <caption-fine:Use role="BOB" style={bob-caption}/>
</caption-fine:Caption>

Visual Track Clips

Visual Clips keep space, source-time, pixel treatment and local motion separate. A SpatialFrame owns position and size. z, fitting and the optional partial source-time relation are direct facts of this occurrence. A treatment Recipe may reuse image and Frame paint, while a typed Motion is reusable affine/opacity keyframes rather than a closed effect name.

visual.product {
  frame-paint: #111116;
  clip: rounded;
  radius: 28;
  padding: "0";
  border-width: 1;
  border-style: solid;
  border-color: #FFFFFF20;
  shadows: 0 10 24 0 #00000066;
}
Property Description
z Direct Z-stacking order; equal values use stable declaration and identity order
fit Direct contain, cover, fit-width, fit-height, native, scale-down, or stretch
frame-x, frame-y Alignment point inside the placement Frame
content-x, content-y Independently selected focal point inside the source
source-time / Map Reusable or inline partial mapping for timed-source coordinates
frame-paint Solid or gradient Paint behind the sampled source
clip, radius, padding Frame clipping and inset
border-*, shadows Frame-owned border and ordered shadows
motion / Pose Optional typed affine and opacity states over the Clip-local clock

Position remains an explicit graph edge:

<space:Frame id="product-frame" within={vertical.bounds}
  left="8%" top="20%" right="92%" bottom="68%"/>
<visual:Motion id="product-in">
  <visual:Pose at="start" y="80" opacity="0" easing="ease-out"/>
  <visual:Pose at="8f" y="0" opacity="1"/>
  <visual:Pose at="end" y="0" opacity="1"/>
</visual:Motion>
<visual:Clip media={product-media.media}
  during={demo} frame={product-frame}
  z="40" fit="contain"
  treatment={recipes.visual.product} motion={product-in}>
  <visual:Map/>
</visual:Clip>

If a behavior coordinates several objects, changes structure or gives a source a new visual role, author a component. Motion is the shared mathematical substrate, not a catalogue of every effect a video may ever need.

Text

Fine Text Style owns reusable typography and Paint only. The occurrence itself owns geometry, form-specific layout and absolute z, because those facts change from one use of a Style to another.

text.title {
  weight: 900;
  size: 64;
  fill: #FFFFFF;
  tracking: -1;
}
Property Description
weight Font weight
size Font size in pixels
fill Text color
tracking Letter spacing adjustment

Compiled with exact font bytes into a text:Style, then referenced by a concrete placement form:

<fonts:Face id="title-font" package="@fontsource-variable/inter" weight="900" style="normal"/>
<text:Style id="title-style" recipe={recipes.text.title} font={title-font}/>
<text:Flow id="meaning" timeline={speech.timeline} within={title-frame}
  style={title-style} z="90" align="center" during={speech.window}>
  MEANING
</text:Flow>

Speaker Text Template

The Recipe selects the prompt axes declared by the data-only speaker-v1 Text Template. Model, resolution, references and duration remain explicit inputs to seedance:ReferenceVideo; they are not hidden in this Recipe.

speaker.host {
  composition-stability: soft-locked;
  camera-motion: none;
  edit-rhythm: continuous-take;
  performance: natural-explainer;
  gesture: natural;
}
Property Description
composition-stability Camera/composition consistency: flexible-ugc, soft-locked, strict-locked
camera-motion Camera movement: none, subtle-punch-in-return
edit-rhythm Editing style: continuous-take, pause-trim-jump-cuts
performance Acting style: natural-explainer, high-energy-ugc, calm-authority, reactive-playful
gesture Gesture intensity: restrained, compact, natural, expressive
Referenced by text:Render together with the Kit's Template:
<text:Render id="hook-prompt"
  template={speaker-kit.speaker-v1} recipe={recipes.speaker.host}>
  <text:Set name="dialogue" text={story.segment.hook.dialogue}/>
  <text:Set name="action" text={hook-action}/>
</text:Render>

Generic Text Template Recipes

The same precedence is available without a domain wrapper. text:Render can read any SVS Recipe, project only properties declared by its template, and let explicit text:Param children override them. This is how the data-only Seedance B-roll, Podcast, Call, Street Interview and reference transfer Kits remain separate from Seedance execution.

broll.product-demo {
  material-mode: product-beauty;
  story-shape: process-demo;
  edit-language: insert-cutaway;
  camera-language: product-macro;
  motion-intensity: readable;
}

Street Interview, Podcast and Call use the same Recipe mechanism rather than hand-written fixed prompt prose. For example:

interview.street {
  framing: soft-handheld;
  pacing: compact;
  performance: natural-street;
  reaction: active;
  gesture: natural;
}

street-interview-v1 reads those five axes. Its per-take action carries camera changes in authored order. podcast-v1 and call-v1 read the corresponding framing, edit-language, pacing, performance, reaction and gesture axes with their own finite values. The selected Kit file is the authority for allowed values and defaults.

Model, resolution, duration and reference media are not template policy. They stay on the exact model Surface and graph edges.

Exact font declarations

SVS describes typography policy, but it does not choose or open font bytes. The official Hypit Distribution supplies the Fontsource adapter; install the selected upstream font packages in the video project's package.json. The ordinary lockfile fixes their actual versions:

<import as="fonts" from="@hypit/fontsource@1"/>
<import as="media" from="@hypit/media@1"/>

<fonts:Face id="caption-latin" package="@fontsource-variable/inter" weight="600" style="normal"/>
<fonts:Face id="caption-han" package="@fontsource-variable/noto-sans-sc" weight="600" style="normal"/>
<media:FontStack id="caption-fonts" primary={caption-latin}>
  <media:Fallback font={caption-han}/>
</media:FontStack>
Property Description
package One installed @fontsource or @fontsource-variable package
weight Exact selected face weight
style Selected style: normal or a package-supported italic

Hypit carries no finite font catalog and does not install a family during compilation. The adapter reads the selected package's metadata, CSS and font files as data, and the compiler turns those installed bytes into Resource-backed font values. A build performs no download and the Runtime never guesses a font:

<caption-fine:Style id="dialogue" recipe={recipes.caption.dialogue}
  font={caption-fonts}/>

media:FontStack emits one generic FontStackRef; its primary and fallbacks preserve their own honest metadata. The Caption Recipe does not repeat family, weight or style. CJK and Emoji can be split into several Unicode-range files while remaining one logical graph edge. Terminal Text and Fine Caption reject an omitted stack; machine-font fallback is not part of Visual IR. For a symbol with both text and Emoji presentation, write the authored Unicode Emoji sequence (for example ☎️, including VS16); no package rewrites display text to force color.

Brand and custom fonts remain explicit author assets rather than additions to a central catalog:

<import as="media" from="@hypit/media@1"/>
<media:Font id="brand" src="./assets/Brand-Semibold.woff2"
  weight="600" style="normal"/>

Combination example

A complete recipes.svs file for a four-take talking-head project:

<?svml using="@hypit/recipe@1"?>

<sheet version="1" id="studio">

  speaker.host {
    composition-stability: soft-locked;
    camera-motion: none;
    edit-rhythm: continuous-take;
    performance: natural-explainer;
    gesture: natural;
  }

  film.vertical {
    background: #09090B;
  }

  caption.primary {
    stack-order: 70;
    x: 0.08;
    y: 0.74;
    width: 0.84;
    size: 44;
    line-height: 1;
    align: center;
    fill: #FFFFFF;
    background: #09090BCC;
    padding: 14 20;
    radius: 16;
  }
</sheet>

This file is imported once in the .svml source and its values are referenced throughout:

<import as="recipes" source="./recipes.svs"/>

<text:Render id="hook-prompt" template={speaker-kit.speaker-v1}
  recipe={recipes.speaker.host}>...</text:Render>

<caption-fine:Style id="primary-caption" recipe={recipes.caption.primary} font={caption-font}/>

<space:Canvas id="vertical" width="720" height="1280"/>
<film:Film id="main" canvas={vertical.canvas} timeline={speech.timeline} appearance={recipes.film.vertical}>