DOM ENGINEV0.21.3FIZLET

CSS CLOCK V0.21.3 BETA

@fzlt/core/css-clock

A page's own CSS @keyframes, on the kernel clock. The look stays in the stylesheet; Fizlet owns the time. Each animation under root is paused and reads its time from one custom property that a timeline moves through the ledger: .led { animation: blink 1s steps(2) infinite paused; animation-delay: var(--fz-t, 0s); } .bug { animation: walk 6s linear infinite paused; animation-delay: calc(var(--fz-t, 0s) - 3s); } - One shared requestAnimationFrame with every other engine on the page; off screen or in a hidden tab it asks for no frames at all. - Settles: after loops plays of span seconds it rests where it started (every animation whose duration divides span is back on its first frame) and the engine sleeps. A pointer over the root, focus inside it or coming back into view plays it again. - Reduced motion: play() lands on the rest frame at once (the still poster). - Recall: destroy() (or aborting signal) gives the root's style back as it was.

@fzlt/core/css-clock · GZIP5,368 B5.2 KB of a 5.4 KB budget

Measured on this build: minified and gzipped with everything it imports, the way npm run size checks the budget in CI.

Tap anywhere to scrub the stylesheet's time.8.6 KB gz

STATUS · what the flags meanBETA

Tested and budgeted; not yet carried by a shipped game, or its API is still moving. New since 0.20.0: its API may still move.

SINCE
v0.21.0
SHIPPED IN
  • fizlet.com (the home demo's hint and the roadmap's current phase)
MODULES · hosted on this site
import { cssClock } from "https://fizlet.com/dist/css-clock.js";
NPM · @fzlt/core
npm install @fzlt/core
import { cssClock } from "@fzlt/core/css-clock";
IN SHORT
/* CSS: the look stays in the stylesheet, paused; its time is --fz-t */
.led { animation: blink 1s steps(2) infinite paused; animation-delay: var(--fz-t, 0s); }

const c = cssClock(diagram, { span: 6, loops: 3 }); // rests on frame one, settles
pauseButton.onclick = () => c.hold(!held); // c.destroy() gives the page back

API

Every export with its signature, parameters, return type, doc comment and status flag, generated at build time from the published type declarations of v0.21.3 by the TypeScript compiler. For AI agents: the whole API as plain text.

CssClockOptions interface BETA

Options for cssClock(): the loop length, how many plays before it rests, the custom property, the frame cap, reduced motion and an abort signal.

export interface CssClockOptions {
    /** Seconds in one play: the loop length (the longest animation, or a common multiple). Default 6. */
    span?: number;
    /** Plays before it rests and the engine settles (default 3). */
    loops?: number;
    /** The custom property the animations read (default "--fz-t"). */
    prop?: string;
    /** Frame-rate cap, e.g. 30 for steps() blinks (default: none, 60 Hz). */
    fps?: number;
    /** Animate (default: yes, unless the visitor prefers reduced motion). */
    motion?: boolean;
    /** Aborting it destroys the clock (Recall included). */
    signal?: AbortSignal;
}

cssClock function BETA

Run the CSS

@keyframes under root on the kernel clock: each paused animation reads its time from one custom property a timeline moves. It settles after loops plays and wakes on pointer, focus or coming back into view; destroy() gives the root's style back.

cssClock(root: HTMLElement, o?: CssClockOptions): CssClock
root
HTMLElement
o?
CssClockOptions optional
Returns
CssClock
Declaration
export declare function cssClock(root: HTMLElement, o?: CssClockOptions): {
    engine: {
        signal: AbortSignal;
        events: import("./kernel/index.js").EventRing;
        ledger: import("./kernel/index.js").Ledger;
        random: import("./kernel/index.js").Random;
        root: Element | null;
        timing: Float64Array<ArrayBuffer>;
        dt: number;
        readonly time: number;
        readonly steps: number;
        readonly phase: number;
        readonly live: number;
        readonly running: boolean;
        readonly paused: string[];
        add<S extends import("./kernel/index.js").System>(s: S): S;
        remove(s: import("./kernel/index.js").System): void;
        pool<T>(name: string, cap: number, create: () => T): import("./kernel/index.js").Pool<T>;
        start(): void;
        busy(sec?: number): void;
        wake(): void;
        stop(): void;
        frame(now: number): void;
        advance(sec: number): void;
        stats(): import("./kernel/index.js").EngineStats;
        destroy(): void;
    };
    tl: {
        to(targets: object | ArrayLike<object>, values: Record<string, import("./timeline.js").Value>, t?: import("./timeline.js").TrackOptions): /*elided*/ any;
        label(name: string, t?: number | string): /*elided*/ any;
        play: () => /*elided*/ any;
        reverse: () => /*elided*/ any;
        pause(by?: unknown): /*elided*/ any;
        seek(t: number): /*elided*/ any;
        finish: () => /*elided*/ any;
        revert(): /*elided*/ any;
        done: () => Promise<void>;
        readonly time: number;
        readonly duration: number;
        readonly playing: boolean;
        readonly reduced: boolean;
        step(e: import("./kernel/index.js").Engine, dt: number): void;
        render(): void;
    };
    /** Play from the start (unless held). */
    play: () => void;
    /** Hold or release (a pause button): held, it keeps its frame and asks for nothing. */
    hold(on: boolean): void;
    /** Stop for good and give the page back (Recall). */
    destroy: () => void;
};

CssClock type BETA

The clock cssClock() returns.

export type CssClock = ReturnType<typeof cssClock>;