Boxes and flex
Section titled “Boxes and flex”import { Layer } from "framefields";
Layer.flex({ width: 1920, height: 1080, dir: "column", // "row" | "column" justify: "center", // start | center | end | space-between | space-around align: "center", // start | center | end | stretch gap: 24, padding: 80, wrap: false, background: "#0d0f12", children: [ Layer.box({ padding: 16, background: "#1e232d", borderRadius: 20, children: [/* … */] }), ],});| Prop | Notes |
|---|---|
width, height |
px, "auto" (intrinsic), "fit" (shrink-wrap) or "fill" (parent extent) |
position: "absolute" + x, y |
take the layer out of flow; x/y alone also make it absolute |
background |
a color or a CSS gradient string |
borderRadius, borderColor, borderWidth |
no CSS border shorthand |
overflow: "hidden" |
clip children to the box |
opacity, scale, rotation |
static or animated |
startFrame, durationFrames |
when the layer is on screen |
id |
stable name; used by lint/QA messages and chart children |
Shapes
Section titled “Shapes”Layer.shape("circle", { width: 200, height: 200, fill: "#ff5a1f" });Layer.shape("star", { width: 240, height: 240, fill: "radial-gradient(#fff, #ffb800)" });Layer.shape("path", { d: "M0 0 L200 0 A100 100 0 0 1 0 0Z", strokeColor: "#fff", strokeWidth: 4, trimEnd: 0 }) .animate(LayerAnimation.create().fromTo("trimEnd", 0, 1, { start: 0, end: 30 })); // draw-onTypes: rect, circle, ellipse, polygon, star, arrow, path. Animate trimStart / trimEnd / trimOffset for stroke draw-ons.
There are no system fonts in headless WebGPU. Register every family before building any text layer.
import { FontManager } from "framefields";
await FontManager.register({ family: "Inter", source: "assets/fonts/Inter.ttf" });await FontManager.register({ family: "Inter", source: "assets/fonts/Inter-Bold.ttf", weight: 700 });await FontManager.registerEmojiFont("assets/fonts/NotoColorEmoji.ttf"); // emoji need a CBDT/CBLC color fontLayer.text("PRECISION MOTION", { fontFamily: "Inter", fontSize: 96, fontWeight: 700, fill: "#ffffff", letterSpacing: 2, lineHeight: 104, width: 1400, // wrap width; set it or text may clip align: "center", // "start" | "center" | "end" (follows text direction) verticalAlign: "middle", // "top" | "middle" | "bottom" inside the box height});Text is drawn by the Slug algorithm: glyph curves are evaluated per pixel in the shader, so it stays sharp at any size and under 3D camera moves. Arabic, Hebrew and Persian shape right-to-left automatically (direction: "auto"; force with "rtl" / "ltr").
Rich text
Section titled “Rich text”import { Layer, bold, color, mark, rich } from "framefields";
Layer.text( rich`Scale ${bold("10x", { fill: "#6366f1" })} faster with ${mark("120 FPS", { bg: "#10b98133" })}`, { fontFamily: "Inter", fontSize: 56, fill: "#f8fafc" },);Span helpers: span, bold, italic, color, mark, size, sub, sup. A span takes fontFamily, fontSize, fontWeight, fill, letterSpacing, baselineShift, opacity.
Measure with the real font
Section titled “Measure with the real font”const m = FontManager.measure("Hello, world", { fontFamily: "Inter", fontSize: 64 });m.width; m.height; m.lines; m.caretX(5); m.glyphs[0].x; // px, relative to the text boxUse it to place carets, underline a word, or size a pill to its label instead of guessing.
Color helpers
Section titled “Color helpers”Every helper returns a hex string.
import { Color } from "framefields";
Color.oklch(0.72, 0.16, 250); // equal L = equal perceived brightnessColor.mix("#ff5a1f", "#1f6bff", 0.5); // perceptual (OKLab) mixColor.ramp(["#3b82f6", "#8b5cf6", "#ec4899"])(0.3);Color.steps(["#0e0d0c", "#ff5a1f"], 5); // 5 swatchesColor.lighten(c, 0.1); Color.darken(c, 0.1); Color.alpha("#000", 0.4);Color.contrast(fg, bg); // WCAG ratio: ≥ 4.5 body, ≥ 3 largeColor.readableOn(bg); // black or white, whichever readsGradients
Section titled “Gradients”CSS strings, usable in any background and any shape fill. Linear, radial and conic, up to 8 stops, hard stops allowed.
import { Gradient } from "framefields";
Layer.box({ background: "linear-gradient(135deg, #ff5a1f, #ffb800 60%, #7c3aed)" });Layer.shape("circle", { fill: Gradient.radial(["#fff", "transparent"]) });Layer.box({ background: Gradient.conic(["red", "yellow", "lime", "aqua", "blue", "magenta", "red"]) });A gradient that doesn’t parse draws nothing; comp.lint() reports it as invalid_gradient.
Track mattes
Section titled “Track mattes”node.withMatte(matte, mode) shows node only where the matte is opaque ("alpha", default), transparent ("alpha-inverted"), bright ("luma") or dark ("luma-inverted"). The matte is laid out like a sibling and is never drawn itself.
// Text revealed by a sliding barLayer.text("LAUNCH", { position: "absolute", x: 560, y: 480, width: 800, height: 120, fontSize: 120 }) .withMatte( Layer.box({ position: "absolute", x: 560, y: 480, width: 800, height: 120, background: "#fff" }) .animate(LayerAnimation.keys("x", [[0, -240], [24, 560, "expo.out"]])), );
// Video inside typeLayer.video("assets/footage/surf.mp4", { position: "absolute", x: 0, y: 0, width: 1920, height: 1080, fit: "cover" }) .withMatte(Layer.text("OCEAN", { position: "absolute", x: 0, y: 340, width: 1920, fontSize: 380, align: "center", fill: "#fff" }));