group#
A positioned container whose children inherit its transform, opacity, and time window. The fundamental nesting primitive — think SVG <g>, Figma group, or AE pre-comp. Inherits common fields.
interface GroupElement extends BaseElement { type: "tok-str">'group'; elements: Element[]; "tok-cmt">// child subtree (≥ 1) clip?: boolean; "tok-cmt">// clip children to the group box (default false) border_radius?: number; "tok-cmt">// rounds the clipped box (default 0) mask?: { mode: "tok-str">'alpha' | "tok-str">'alpha-inverted' | "tok-str">'luma' | "tok-str">'luma-inverted'; elements: Element[]; "tok-cmt">// elements that compose the mask layer }; time_remap?: Keyframe[]; "tok-cmt">// keyframes that warp the subtree clock (values are warped seconds) }
Fields#
| Field | Type | Default | Description |
|---|---|---|---|
elements | Element[] | required | Child elements. Same shape as the Source's top-level elements array. |
clip | boolean | false | Render children into a layer the size of the group's box and clip anything outside it (CSS overflow: hidden). Requires explicit width/height. |
border_radius | number | 0 | Corner radius (px) of the clipped group box — rounds the clip box so children are masked to a rounded rectangle (a rounded card clipping its content). Only meaningful with clip: true; clamped to half the smaller box dimension. |
mask | { mode, elements } | — | Mask the group with another set of elements. mode is 'alpha' | 'alpha-inverted' | 'luma' | 'luma-inverted' — whether the mask layer drives content opacity by its alpha or luminance, optionally inverted. elements (≥ 1) compose the mask layer; the group's own subtree is shown only where the mask is opaque/bright. |
time_remap | Keyframe[] | — | Keyframes that warp the group subtree's clock — each keyframe's value is a warped time (in seconds) so the whole subtree can speed up, slow down, hold, or reverse together. Replaces per-child time math for the subtree as a unit. |
Semantics#
The whole concept in four rules:
- Coordinate system. A child's
x/yare in the group's local space. The group's anchor sets the local origin — by default the group's top-left corner, so childx: 0, y: 0sits at the group's top-left. - Transforms stack. Rotation, scale, and opacity multiply down the tree — a group with
opacity: 0.5containing a child withopacity: 0.8produces an effective0.4on screen. Same idea for rotation and scale. - Time is relative. A child's
timeis offset by the group'stime. A child whose time + duration exceeds the group's window is clipped (not visible after the group ends). - Layering is local. A child's
layer(integer 1–1000, unique within the group) sets paint order among its siblings — lower number draws in front, solayer: 1is on top. The group's ownlayerdecides where the whole subtree sits relative to its siblings. Layer is resolved per container, so child layers never collide with layers outside the group.
Example: animated card#
{ "type": "group", "id": "card", "x": 960, "y": 540, "x_anchor": "50%", "y_anchor": "50%", "width": 800, "height": 400, "time": 0.5, "duration": 5, "animations": [ { "type": "scale-in", "duration": 0.5, "easing": "ease-out-back" }, { "type": "fade-out", "duration": 0.3, "time": "end" } ], "elements": [ { "type": "shape", "shape": "rectangle", "x": 0, "y": 0, "width": 800, "height": 400, "fill_color": "#1e293b", "border_radius": 24 }, { "type": "text", "text": "Inside the card.", "x": 400, "y": 200, "x_anchor": "50%", "y_anchor": "50%", "font_size": 64, "font_weight": 700, "fill_color": "#ffffff" } ] }
The scale-in animates the whole card (shape + text) together, because the animation is on the group.
Example: lower third#
Reusable "lower third" subtree — title + accent bar — that you can drop anywhere on the timeline by changing the group's time:
{ "type": "group", "x": 540, "y": 950, "width": 800, "height": 120, "time": 2, "duration": 4, "animations": [ { "type": "slide-right-in", "duration": 0.4 }, { "type": "slide-left-out", "duration": 0.3, "time": "end" } ], "elements": [ { "type": "shape", "shape": "rectangle", "x": 0, "y": 0, "width": 8, "height": 120, "fill_color": "#facc15", "x_anchor": "0%" }, { "type": "text", "text": "Ian Scott · Founder", "x": 24, "y": 60, "x_anchor": "0%", "font_family": "Inter", "font_size": 36, "font_weight": 600, "fill_color": "#ffffff", "text_align": "left" } ] }
Notes#
- Nesting depth — groups can contain groups. The runtime imposes no nesting limit, but render cost scales with element count, not depth.
- Empty group —
elements: []is rejected at the schema level; at minimum one child is required. - Why "group" and not "composition"? Both terms exist in motion graphics tools. We picked the lighter one. A future
compositionelement could carry stronger semantics (nested timeline, separate frame rate, render caching) without colliding with this.