From b48262660eb5b4e58f8978e93d99493ce4251e68 Mon Sep 17 00:00:00 2001 From: Erik Cardoso Date: Sun, 30 Aug 2026 13:13:27 -0300 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20public-readiness=20infrastructure?= =?UTF-8?q?=20=E2=80=94=20catalog,=20E2E,=20honest=20messaging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds observable truth layer (attribute grammar, generated catalog, compatibility/benchmarks pages), Playwright E2E in CI, docs alignment to channels v2, WordPress examples, CSS dist publish prep, and honest showcase footer copy (zero animation runtime JS). Co-authored-by: Cursor --- .github/workflows/ci.yml | 26 + .gitignore | 2 + AGENTS.md | 22 +- README.md | 32 +- apps/docs/src/pages/agents.astro | 5 +- apps/docs/src/pages/attributes.astro | 351 +- apps/docs/src/pages/effects.astro | 37 +- apps/docs/src/pages/index.astro | 15 +- apps/docs/src/pages/motion-conditions.astro | 6 +- apps/docs/src/pages/skills.astro | 6 +- apps/docs/src/pages/tokens.astro | 2 +- apps/showcase/config/showcase-coverage.mjs | 17 +- apps/showcase/config/template-registry.mjs | 3 + apps/showcase/index.html | 4 +- apps/showcase/output/contract-checklist.md | 23 +- apps/showcase/pages/core/archive.html | 4 +- apps/showcase/pages/core/benchmarks.html | 294 ++ apps/showcase/pages/core/compatibility.html | 302 ++ apps/showcase/pages/core/core.html | 276 +- apps/showcase/pages/core/elements.html | 4 +- apps/showcase/pages/core/hosts.html | 51 +- apps/showcase/pages/core/skins.html | 4 +- .../pages/motion/api-motion-catalog.html | 29 +- apps/showcase/pages/motion/motion-lab.html | 38 +- apps/showcase/pages/motion/playground.html | 304 ++ .../showcase/pages/scenes/scene-timeline.html | 4 +- apps/showcase/public/css/01-motion-tokens.css | 53 +- apps/showcase/public/css/03-motion.css | 120 +- .../public/css/03b-motion-extended.css | 4500 ++++++++--------- apps/showcase/public/css/03c-scene-engine.css | 94 +- apps/showcase/public/css/scene-recipes.css | 583 ++- .../public/css/showcase-reference-pages.css | 1370 ++++- .../showcase/public/css/showcase-tw-demos.css | 2 +- apps/showcase/public/data/benchmarks.json | 88 + .../showcase/public/data/catalog-summary.json | 21 + .../public/data/compatibility-matrix.json | 130 + apps/showcase/public/data/presets-full.json | 2396 +++++++++ apps/showcase/public/data/presets-index.json | 943 ++++ .../public/js/showcase-api-motion-catalog.js | 62 +- .../showcase/public/js/showcase-benchmarks.js | 50 + .../public/js/showcase-catalog-registry.js | 99 + .../public/js/showcase-compatibility.js | 53 + .../js/showcase-motion-lab-inspector.js | 239 + .../showcase/public/js/showcase-playground.js | 211 + .../scripts/validate-showcase-contract.mjs | 3 +- docs/AGENTS-VELORA.md | 941 ++++ docs/README.md | 4 + docs/audits/showcase-readiness-2026.md | 363 ++ docs/project/CONTRACT.md | 31 +- docs/project/PUBLISH.md | 57 + docs/project/SCENE_SYSTEM.md | 12 +- docs/project/SHOWCASE_PAGE_PLAYBOOK.md | 112 +- docs/project/TIMELINE_ENGINE.md | 4 +- docs/spec/attribute-grammar.md | 570 +++ .../plans/2026-08-27-core-continuous-track.md | 52 + .../plans/2026-08-27-core-release-desk.md | 35 + ...2026-08-27-core-continuous-track-design.md | 49 + .../2026-08-27-core-opera-scenes-design.md | 22 + .../2026-08-27-core-release-desk-design.md | 129 + docs/validation/reduced-motion-checklist.md | 68 + examples/README.md | 1 + examples/wordpress/README.md | 28 + examples/wordpress/elementor/README.md | 36 + .../wordpress/gutenberg/block-pattern.php | 27 + .../php-template/functions-snippet.php | 36 + package.json | 9 +- packages/catalog/README.md | 27 + packages/catalog/attributes.json | 91 + packages/catalog/catalog-summary.json | 21 + packages/catalog/package.json | 17 + packages/catalog/presets.json | 2398 +++++++++ packages/catalog/scripts/generate-catalog.mjs | 447 ++ .../compiler/output/motion-compiler-report.md | 191 +- packages/css/README.md | 56 + packages/css/package.json | 10 +- packages/css/scripts/build-dist.mjs | 168 + packages/css/scripts/check-dist.mjs | 26 + packages/css/src/01-motion-tokens.css | 53 +- packages/css/src/03-motion.css | 120 +- packages/css/src/03b-motion-extended.css | 4500 ++++++++--------- packages/css/src/03c-scene-engine.css | 94 +- packages/css/src/scene-recipes.css | 583 ++- pnpm-lock.yaml | 40 + tests/README.md | 41 + tests/e2e/catalog-data.spec.mjs | 35 + tests/e2e/playwright.config.mjs | 36 + tests/e2e/showcase.reduced-motion.spec.mjs | 38 + tests/e2e/showcase.smoke.spec.mjs | 67 + 88 files changed, 18743 insertions(+), 5780 deletions(-) create mode 100644 apps/showcase/pages/core/benchmarks.html create mode 100644 apps/showcase/pages/core/compatibility.html create mode 100644 apps/showcase/pages/motion/playground.html create mode 100644 apps/showcase/public/data/benchmarks.json create mode 100644 apps/showcase/public/data/catalog-summary.json create mode 100644 apps/showcase/public/data/compatibility-matrix.json create mode 100644 apps/showcase/public/data/presets-full.json create mode 100644 apps/showcase/public/data/presets-index.json create mode 100644 apps/showcase/public/js/showcase-benchmarks.js create mode 100644 apps/showcase/public/js/showcase-catalog-registry.js create mode 100644 apps/showcase/public/js/showcase-compatibility.js create mode 100644 apps/showcase/public/js/showcase-motion-lab-inspector.js create mode 100644 apps/showcase/public/js/showcase-playground.js create mode 100644 docs/AGENTS-VELORA.md create mode 100644 docs/audits/showcase-readiness-2026.md create mode 100644 docs/project/PUBLISH.md create mode 100644 docs/spec/attribute-grammar.md create mode 100644 docs/superpowers/plans/2026-08-27-core-continuous-track.md create mode 100644 docs/superpowers/plans/2026-08-27-core-release-desk.md create mode 100644 docs/superpowers/specs/2026-08-27-core-continuous-track-design.md create mode 100644 docs/superpowers/specs/2026-08-27-core-opera-scenes-design.md create mode 100644 docs/superpowers/specs/2026-08-27-core-release-desk-design.md create mode 100644 docs/validation/reduced-motion-checklist.md create mode 100644 examples/wordpress/README.md create mode 100644 examples/wordpress/elementor/README.md create mode 100644 examples/wordpress/gutenberg/block-pattern.php create mode 100644 examples/wordpress/php-template/functions-snippet.php create mode 100644 packages/catalog/README.md create mode 100644 packages/catalog/attributes.json create mode 100644 packages/catalog/catalog-summary.json create mode 100644 packages/catalog/package.json create mode 100644 packages/catalog/presets.json create mode 100644 packages/catalog/scripts/generate-catalog.mjs create mode 100644 packages/css/README.md create mode 100644 packages/css/scripts/build-dist.mjs create mode 100644 packages/css/scripts/check-dist.mjs create mode 100644 tests/README.md create mode 100644 tests/e2e/catalog-data.spec.mjs create mode 100644 tests/e2e/playwright.config.mjs create mode 100644 tests/e2e/showcase.reduced-motion.spec.mjs create mode 100644 tests/e2e/showcase.smoke.spec.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a02b258..348d07b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,3 +37,29 @@ jobs: - name: Build run: pnpm build + + e2e: + runs-on: ubuntu-latest + needs: build + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.13 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Install Playwright browsers + run: pnpm exec playwright install --with-deps chromium firefox webkit + + - name: E2E tests + run: pnpm test:e2e diff --git a/.gitignore b/.gitignore index 915e73b..3e21577 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ packages/css/dist/ **/.turbo/ **/.playwright-cli/ **/output/playwright/ +test-results/ +playwright-report/ pnpm-debug.log* # Astro diff --git a/AGENTS.md b/AGENTS.md index 20adbb5..234c6bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,25 +1,27 @@ ## Learned User Preferences - Prefer communication in Portuguese. -- Zero-JS motion is non-negotiable: never introduce JS animation runtimes; keep the CSS-only / HTML+CSS path. +- Zero animation runtime JavaScript is non-negotiable (not absolute “Zero JavaScript”); never introduce JS animation runtimes; keep the CSS-only / HTML+CSS path. - Treat Velora as a declarative CSS motion runtime and modern reference—not a utility framework; prioritize positioning, consistency, technical demos, and perceived value over feature volume. -- Prefer evaluate-then-plan against the execution roadmap before large changes; often wants page-by-page reformulation using Velora CSS and the Showcase template. +- Prefer evaluate-then-plan against `docs/AGENTS-VELORA.md` (P0/P1 public-readiness roadmap) and the showcase-readiness audit before large feature work; often wants page-by-page reformulation using Velora CSS and the Showcase template. - Showcase pages should feel cinematic and exercise the design system plus motion catalog resources, not generic or simplistic layouts. -- Prefer complex, coordinated scenes (3D stages, large type, enter/exit choreography, horizontal/stacked sections) over sparse section layouts. -- Keep showcase UI patterns consistent across pages; avoid exaggerated glow and one-off chrome that breaks the shared look. +- Prefer complex, coordinated scenes (3D stages, large type, enter/exit choreography, continuous pin+scrub opera tracks, horizontal/stacked sections) over sparse section layouts or product-form “release desk” teaching UIs. +- Keep showcase UI patterns consistent across pages; avoid exaggerated glow and one-off chrome that breaks the shared look; motion catalog should use compact DX controls covering the full contract and varied demo visuals (not repetitive chrome). - Stage 3D should be a reusable CSS-only stage contract (perspective/preserve-3d on the container; positional transforms via CSS vars on items; motion on inner content), not cube-triad-specific demos; preserve text-ring-orbit and circle-text-scroll. -- Aim for GSAP-like scene authorship in HTML/CSS (shared scene clock, relative timing, pin+scrub)—preferred product direction for the scene engine. +- Aim for GSAP-like scene authorship in HTML/CSS (shared scene clock, relative timing, pin+scrub); use `vl-in-view` for temporal viewport-gated enters and `vl-timeline="view"` (+ `vl-range`) for scrub—often hybrid on teaching pages; inside pinned scenes use chapter gates on the scene clock (`vl-act`/`vl-span`), not nested `vl-in-view` on `[vl-stage]`. - Core motion must stay host-agnostic and work with any UI (Tailwind and others); **Skins** is the product name for the Velora design-system layer (named themes via `data-editorial-theme`); Showcase is the cinematic reference UI, not the motion contract. - Prefer modern CSS capabilities (e.g. `if()`, typed `attr()`, `sibling-index()`) as progressive enhancement for scene choreography. -- Prefer fixing contract/consistency before expanding demos; prefer a lean public Showcase that archives surplus pages in-repo rather than deleting Skins/DS work; reuse existing motion examples rather than inventing parallel systems. +- Prefer hardening Scene Engine + `vl-in-view` contract/consistency before expanding demos; expand motion inventory before freezing core/catalog; prefer a lean public Showcase that archives surplus pages in-repo rather than deleting Skins/DS work; reuse existing motion examples rather than inventing parallel systems. ## Learned Workspace Facts - Monorepo layout: canonical CSS in `packages/css/src/`; Vite playground in `apps/showcase/`; Astro docs app in `apps/docs/`; markdown source of truth in repo-root `docs/`; plus `design-system/`, `examples/`, `experiments/`, and `starters/html-css-minimal/`. - Always edit CSS in `packages/css/src/` and sync to showcase (`pnpm sync:showcase-css`); `apps/showcase/public/css/` is derived and must not be edited as source. -- Motion is attribute-driven (`vl-effect`, `vl-timeline`, `vl-range`, `vl-scene`, related `vl-*`); rules live in ordered `@layer velora.*` (reset → tokens → layout → motion → components → transitions → utilities → overrides). +- Motion is attribute-driven (`vl-effect`, `vl-timeline`, `vl-range`, `vl-scene`, `vl-in-view`, related `vl-*`); `vl-in-view` is a temporal viewport gate (not scrub) that replays descendant motion on re-entry and must not pause scroll-driven timelines; do not nest `vl-in-view` on `[vl-scene][vl-timeline="view"] [vl-stage]`—use chapter gates on the scene clock for in-view-like enter/replay/exit inside pin; view-clock stages compose via named `--vl-scene` / `--vl-scroll-timeline`; rules live in ordered `@layer velora.*` (reset → tokens → layout → motion → components → transitions → utilities → overrides). - Product surfaces split: Velora core = scene/motion engine; Showcase = cinematic reference UI/DS; docs site documents the framework once the API is stable. -- `@velora/css` ships separable entrypoints: `motion-core` (host-agnostic engine) vs `theme` / full bundle (visual Skins); editorial skins use `html[data-editorial-theme]` (e.g. noir, earth, aethel, meridian). +- `@velora/css` ships separable entrypoints: `motion-core` (host-agnostic engine) vs `theme` / full bundle (visual Skins); editorial skins use `html[data-editorial-theme]` (e.g. noir, earth, aethel, meridian); cube-triad demos live in `scene-recipes.css`, not the core 3D stage contract; package is monorepo-local—not yet published to npm (document honestly until P1.4). - Lean Showcase restarts should snapshot retired pages under a dated in-repo `archive/` (outside live Vite registry and contract checks), not delete recoverable DS/skin work. -- Workspace tooling is pnpm + Turborepo; common checks include `pnpm verify:contract` and showcase CSS drift checks. -- Root `AGENTS.md` holds learned memory only; agent operating instructions live under `docs/agents/AGENTS.md` and must not be mixed into the learned-memory file. +- Workspace tooling is pnpm + Turborepo; local Showcase via `pnpm dev` (or `pnpm --filter showcase dev`); common checks include `pnpm verify:contract`, showcase CSS drift checks, and `pnpm generate:catalog` / `pnpm check:catalog`. +- Root `AGENTS.md` holds learned memory only; agent operating instructions live under `docs/agents/AGENTS.md`; `docs/AGENTS-VELORA.md` is the product/public-readiness roadmap (P0/P1)—keep all three distinct. +- Canonical public spec chain: `docs/spec/attribute-grammar.md` + generated `packages/catalog/` (`pnpm generate:catalog`) + operational matrix in `docs/project/CONTRACT.md`. +- Showcase motion authorship (gate vs scrub vs scene, anti-patterns) lives in `docs/project/SHOWCASE_PAGE_PLAYBOOK.md` §4; bussola section IDs must compose `view-timeline-name` with `--vl-scene` (e.g. `--vl-scene, --tl-N`), and unlayered `.scene-tl-stage { position: relative }` breaks sticky pin on `[vl-stage]`. diff --git a/README.md b/README.md index f00fd59..cc01ae5 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Velora -**Motion-native CSS. Zero JavaScript. Cinematic interfaces.** +**Motion-native CSS. Zero animation runtime JavaScript. Cinematic interfaces.** ![License: ISC](https://img.shields.io/badge/license-ISC-blue.svg) Velora is a **declarative CSS motion engine**. HTML attributes describe intent; the browser executes it. No JavaScript animation runtime. Works with Tailwind, Relume, or any UI. -The optional **Skins** layer provides complete design-system models. The cinematic **Showcase** proves how Core behavior and Skin DNA combine; neither is required to turn motion on. +The optional **Skins** layer provides complete design-system models. The cinematic **Showcase** proves how Core behavior and Skin DNA combine; neither is required to turn motion on. ## Features @@ -16,9 +16,9 @@ The optional **Skins** layer provides complete design-system models. The cinemat 📐 **8-Layer Cascade Architecture** — Structured specificity via CSS `@layer` (reset → tokens → layout → motion → components → transitions → utilities → overrides) -🔄 **Element + Scroll Motion** — Native entry/exit, intrinsic-size, top-layer, path and scroll-driven motion +🔄 **Element + Scroll Motion** — Native entry/exit, intrinsic-size, top-layer, path and scroll-driven motion -📦 **Declarative Attribute Grammar** — Motion via HTML attributes (`vl-effect`, `vl-timeline`, `vl-children`, `vl-scene`) instead of CSS classes +📦 **Declarative Attribute Grammar** — Motion via HTML attributes (`vl-enter`, `vl-scroll`, `vl-scene`, `vl-timeline`, …) instead of CSS classes ⚡ **Container Queries & Density Modes** — Responsive components with default, compact, and presentation layout modes @@ -30,6 +30,14 @@ The optional **Skins** layer provides complete design-system models. The cinemat pnpm add @velora/css ``` +> **Note:** `@velora/css` is **publish-ready** (`dist/` + manifest) but **not yet on npm**. Use workspace, `pnpm pack:css`, or CDN URLs from [PUBLISH.md](docs/project/PUBLISH.md) after release. + +### CDN (after publish) + +```html + +``` + ### 2. Import Host-agnostic (any UI — Tailwind, etc.): @@ -50,7 +58,7 @@ Full Velora look (theme + components + motion): ```html
-

Zero-JS motion

+

Declarative motion

Same attributes on Tailwind or Velora UI.

@@ -83,9 +91,9 @@ It includes: - `@velora/css/premium` — premium components - `@velora/css/overrides` — last-mile override layer -Proofs: `examples/tailwind-host/` (motion-core + Tailwind) · `apps/showcase` (six primary areas: Home, Core, Elements, Scenes, Skins, Catalog). Hosts and Archive remain secondary references. Older Showcase pages: `archive/showcase-2026-08/`. - -**Skins** are named design-system models (`html[data-editorial-theme]`) covering typography, grid, density, geometry, materials, media and motion voice—not only color. They are optional; motion works without them. +Proofs: `examples/tailwind-host/` (motion-core + Tailwind) · `apps/showcase` (six primary areas: Home, Core, Elements, Scenes, Skins, Catalog). Hosts and Archive remain secondary references. Older Showcase pages: `archive/showcase-2026-08/`. + +**Skins** are named design-system models (`html[data-editorial-theme]`) covering typography, grid, density, geometry, materials, media and motion voice—not only color. They are optional; motion works without them. ## Motion Channels (v2) @@ -147,7 +155,9 @@ Apply a preset with a single attribute: ``` -No JavaScript required. +No animation runtime JavaScript required for cross-document MPA transitions. + +**Spec:** [`docs/spec/attribute-grammar.md`](docs/spec/attribute-grammar.md) · **Preset registry:** `packages/catalog/` (136 presets, `pnpm generate:catalog`) ## Workspace Layout @@ -158,8 +168,8 @@ velora/ ├── docs/ # Markdown source of truth (contract, workspace, agents) ├── packages/ │ ├── css/ # @velora/css — canonical framework source -│ ├── pages/ # legacy/reference HTML pages used by showcase tooling -│ └── velora-components/ # component HTML catalog and source material +│ ├── catalog/ # Generated preset registry (pnpm generate:catalog) +│ └── compiler/ # Grammar validator and motion scanner ├── apps/ │ ├── showcase/ # Vite showcase (primary interactive app + API catalogs) │ └── docs/ # Astro documentation site (published guides) diff --git a/apps/docs/src/pages/agents.astro b/apps/docs/src/pages/agents.astro index 2c41b5f..d709078 100644 --- a/apps/docs/src/pages/agents.astro +++ b/apps/docs/src/pages/agents.astro @@ -28,11 +28,10 @@ import Base from "../layouts/Base.astro";
<section
   vl-scene="hero"
   vl-timeline="view"
-  vl-effect="scene-hero-reveal"
   vl-range="entry 0% cover 70%"
   vl-pin>
-  <h1>Build motion-rich interfaces with HTML and CSS</h1>
-  <p>No animation libraries. No framework runtime.</p>
+  <h1 vl-enter="fade-up">Build motion-rich interfaces with HTML and CSS</h1>
+  <p vl-enter="fade-up" vl-delay="120ms">No animation libraries. No animation runtime JS.</p>
 </section>

diff --git a/apps/docs/src/pages/attributes.astro b/apps/docs/src/pages/attributes.astro index 5d4a779..e4d32ee 100644 --- a/apps/docs/src/pages/attributes.astro +++ b/apps/docs/src/pages/attributes.astro @@ -2,13 +2,14 @@ import Base from "../layouts/Base.astro"; --- - + Reference

Attribute Reference

- Complete reference for every vl-* HTML attribute. Attributes are Velora's - declarative grammar — they express intent, not implementation. + Velora motion is declared with vl-* HTML attributes. Prefer channels + (vl-enter, vl-scroll, …) for new work. Canonical spec: + attribute-grammar.md.

Quick Reference

@@ -17,328 +18,94 @@ import Base from "../layouts/Base.astro"; AttributeTypeBrief - vl-effectenumNamed motion preset - vl-timelineenumAnimation progress model - vl-rangeenumScroll-driven animation range - vl-scenestringScene container marker - vl-childrenenumChild choreography mode - vl-staggerdurationStagger interval override - vl-depthnumberMotion distance multiplier - vl-speedenumDuration category override - vl-pinbooleanSticky positioning - vl-scrubbooleanLinear scroll-linked timing - vl-oncebooleanOne-shot animation - vl-targetsselectorDescendant target hint - vl-page-transitionenumCross-document page transition preset - vl-transitionstringShared element / local transition + vl-entertokensEntrance preset (preferred) + vl-exittokensExit preset + vl-scrolltokensScroll-linked motion + vl-hovertokensHover / focus interaction + vl-loop / vl-loop-effectcount + tokensAmbient loops + vl-stateenumNative state transitions + vl-effecttokensLegacy / composite preset escape hatch + vl-timelineenumProgress model: view, scroll, auto, hover, state + vl-in-viewbooleanViewport gate for temporal descendant motion + vl-scene / vl-stagetrack + stageScene engine container + vl-act / vl-spannumberScene beat index and span + vl-pin / vl-scrubnumber / booleanPin+scrub track (view clock) + vl-children / vl-staggerenum / timeChild choreography + vl-range / vl-speed / vl-durationenum / enum / timeTiming overrides + vl-motionenumSubtree motion mode (standard, subtle, cinematic, still) + vl-page-transitionenumMPA view transition preset on <html> -
- -

vl-effect

-

- Declares the named motion preset to apply to this element. See the - Effects reference for full - descriptions of each value. -

- - - - - - - - - - - - - -
CategoryValues
Entrancefade-in fade-up fade-down slide-left slide-right scale-in blur-in reveal-3d flow-in clip-rise tilt-in
Interactionhover-lift hover-glow underline-expand icon-shift
Stateaccordion drawer panel-swap
Continuousshimmer float glow-breathe wobble orbit rock morph elastic-in
Texttext-reveal text-reveal-up text-reveal-down
Cinemacinema-zoom parallax typewriter
Scene presetsscene-hero-reveal scene-feature-flow scene-story-pin scene-layer-stack
-
<div vl-effect="fade-up">...</div>
-<a href="/page" vl-effect="underline-expand">Link</a>
-<section vl-scene="hero" vl-effect="scene-hero-reveal">...</section>
- -
- -

vl-timeline

-

- Controls when the animation defined by vl-effect plays. Without a timeline, - effects play immediately on page load. -

- - - - - - - - - - - - - - - - - - - - - - - - - - -
ValueMechanismUse case
viewanimation-timeline: view(block)Reveal as element scrolls into viewport. Most common scroll-driven pattern.
scrollanimation-timeline: scroll(root)Progress tied to root scroll position (0% = top, 100% = bottom).
autoanimation-timeline: autoBrowser default (time-based). Lets you use a custom timeline via CSS.
hoverPauses animation, runs on :hoverPlay animation on hover — good for preview effects.
-
<!-- Scroll-driven reveal -->
-<div vl-effect="blur-in" vl-timeline="view">...</div>
-
-<!-- Plays on hover -->
-<div vl-effect="scale-in" vl-timeline="hover">...</div>
- -
- -

vl-range

-

- Sets the animation-range for scroll-driven effects. Only effective when used with - vl-timeline="view" or vl-timeline="scroll". -

- - - - - - - - - - - - -
Valueanimation-rangeFeel
entryentry 0% entry 100%Plays only while entering the viewport
entry-shortentry 15% cover 35%Quick reveal in the upper viewport zone
entry-longentry 0% cover 70%Extended reveal range — default for scenes
covercover 0% cover 100%Plays the full time element covers the viewport
containcontain 0% contain 100%Plays while element is fully contained in viewport
customvar(--vl-range)Use --vl-range inline for full control
-
<!-- Slow reveal across the full scroll zone -->
-<div vl-effect="flow-in" vl-timeline="view" vl-range="entry-long">...</div>
-
-<!-- Fully custom range -->
-<div
-  vl-effect="fade-up"
-  vl-timeline="view"
-  vl-range="custom"
-  style="--vl-range: entry 10% cover 60%;">
-  Custom range
-</div>
- -
- -

vl-scene

-

- Marks a container as a scene host. Applies CSS containment and enables scene-specific - child choreography. Value is a semantic label. -

- - - - - - - - - - -
ValueIntent
heroFull-width hero section with primary CTA
storyNarrative / storytelling pin section
featuresFeature grid or card collection
any stringCustom scene label for your own patterns
-
<section vl-scene="hero" vl-effect="scene-hero-reveal">...</section>
-<section vl-scene="features" vl-effect="scene-feature-flow" vl-timeline="view">...</section>
- -
- -

vl-children

-

- Applies staggered animations to all direct children. Do not combine with a scene preset — use one - or the other. -

- - - - - - - - - -
ValueEffectMax children
staggerfade-up with incremental delays12
cascadeflow-in with incremental delays8
sequencefade-up one-after-another (full duration gaps)6
-
<ul vl-children="stagger" vl-stagger="80ms" vl-timeline="view">
-  <li>Item 1</li>
-  <li>Item 2</li>
-  <li>Item 3</li>
-</ul>
+

Deprecated: vl-typevl-timeline; vl-easing → tokens; vl-transitionvl-page-transition.


-

vl-stagger

+

Channels (preferred)

+
<article
+  vl-enter="fade-up"
+  vl-scroll="parallax"
+  vl-hover="underline-expand"
+  vl-timeline="view">
+  ...
+</article>

- Overrides the default stagger step (--vl-stagger-step: 45ms) for vl-children. + Each channel owns one behavior. See the + Motion Catalog + and generated registry (pnpm generate:catalog) for the full preset list.

- - - - - - - - - - - - -
ValueDelay per child
60ms60ms
80ms80ms
100ms100ms
120ms120ms
150ms150ms
200ms200ms

-

vl-depth

+

Scene engine

+
<section vl-scene vl-timeline="view" vl-pin="3" vl-scrub>
+  <div vl-stage>
+    <h1 vl-enter="clip-rise" vl-act="1">Title</h1>
+    <img vl-scroll="media-zoom" vl-act="2" vl-span="2" alt="">
+  </div>
+</section>

- Multiplies the translation distance (--vl-motion-distance) for entrance effects. - Affects fade-up, fade-down, slide-left, slide-right, - flow-in, and other translate-based effects. + Place channel attributes on direct [vl-stage] children. + Do not nest vl-in-view inside a pin+scrub stage — use vl-act / vl-span instead.

- - - - - - - - - - -
Value--vl-motion-distance
10.75rem — subtle
21.5rem — moderate
32.5rem — pronounced
44rem — dramatic
-
<div vl-effect="fade-up" vl-depth="4">Deep dramatic reveal</div>

-

vl-speed

-

Overrides the animation duration category:

+

vl-timeline

- + - - - + + + + +
ValueDuration
ValueUse case
fast150ms
normal300ms
slow500ms
viewElement or scene scroll progress (most common)
scrollRoot/container scroll timeline
autoTime-based (scene acts without pin height)
hoverIntent-gated playback
stateNative state transition clock
-
<div vl-effect="scale-in" vl-speed="fast">Quick snap in</div>

-

vl-pin

+

vl-in-view

- Boolean attribute. Makes the element sticky (position: sticky; top: 0) at - z-index: 200. Use on scene sections that should remain fixed during scroll. + Temporal viewport gate: descendant enter/exit/loop/hover/state motion replays on re-entry. + Does not pause vl-scroll. Use outside pin+scrub scenes.

-
<section vl-scene="hero" vl-pin>
-  <!-- Stays at top of viewport while parent scrolls -->
-</section>
- -
- -

vl-scrub

-

- Boolean attribute. Switches timing to linear (animation-timing-function: linear) and - sets animation-fill-mode: both. Use with scroll-driven effects where you want - direct scroll-to-animation scrubbing without easing curves. -

-
<div vl-effect="parallax" vl-scrub>Directly scrubbed parallax</div>
- -
- -

vl-once

-

- Boolean attribute. Sets animation-iteration-count: 1 and - animation-fill-mode: forwards. The element animates once and stays in its final state. -

-
<div vl-effect="clip-rise" vl-timeline="view" vl-once>
-  Reveals once, stays visible
-</div>
- -
- -

vl-targets

-

- A CSS selector string that hints which descendants should be targeted by the scene or children - system. CSS-only support is limited — primary use is for documentation and future runtime hooks. -

-
<section vl-scene="features" vl-targets=".vl-card">
-  <div>Non-card child (not targeted)</div>
-  <div class="vl-card">Card (targeted)</div>
-  <div class="vl-card">Card (targeted)</div>
-</section>

vl-page-transition

-

- Set on <html> to select the cross-document page transition preset. - See Page Transitions for full details. -

- - - - - - - - - - - - -
ValueEffect
(omitted)Default Velora signature: blur + scale
wipeClip-path curtain wipe
glideHorizontal slide
irisCircular iris open/close
cinemaDramatic blur + brightness + clip
snapQuick spring-based snap
+

Set on <html> for cross-document MPA transitions. See Page Transitions.

<html lang="en" vl-page-transition="cinema">

-

vl-transition

-

- Reserved for shared element and local element transition hints. Intended for future - Velora runtime integration and CSS-level patterns. -

-
<!-- Future: cover transition pattern -->
-<div vl-transition="cover">...</div>
- -
- -

Attribute Composition

-

Attributes compose cleanly — combine them to express complex motion intent:

-
<!-- Cinematic feature section -->
-<section
-  vl-scene="features"
-  vl-effect="scene-feature-flow"
-  vl-timeline="view"
-  vl-range="entry-long">
-  ...
-</section>
-
-<!-- Staggered grid with custom pacing -->
-<div
-  vl-children="stagger"
-  vl-stagger="120ms"
-  vl-timeline="view"
-  vl-once>
-  ...
-</div>
-
-<!-- Deep, slow hero heading -->
-<h1
-  vl-effect="clip-rise"
-  vl-timeline="view"
-  vl-depth="3"
-  vl-speed="slow"
-  vl-once>
-  Premium motion starts here
-</h1>
+

Further reading

+ diff --git a/apps/docs/src/pages/effects.astro b/apps/docs/src/pages/effects.astro index 6c51924..464165f 100644 --- a/apps/docs/src/pages/effects.astro +++ b/apps/docs/src/pages/effects.astro @@ -2,38 +2,35 @@ import Base from "../layouts/Base.astro"; --- - + Motion

Motion Effects

- Named motion presets applied via the vl-effect attribute. All effects are pure CSS — - no JavaScript involved. + Prefer channels v2: vl-enter, vl-scroll, vl-hover, etc. + vl-effect remains a legacy/composite escape hatch. All motion is CSS-only — zero animation runtime JavaScript. + See Attribute Reference.

-

How Effects Work

+

How channels work

- Add vl-effect to any element to apply a preset. Combine with - vl-timeline to control when the animation plays: + Pick the channel that owns the behavior, then add vl-timeline when you need a progress model:

-
<!-- Plays immediately on page load -->
-<h1 vl-effect="fade-up">Heading</h1>
+  
<!-- Entrance (preferred) -->
+<h1 vl-enter="fade-up">Heading</h1>
 
-<!-- Plays when element enters the viewport -->
-<div vl-effect="blur-in" vl-timeline="view">Content</div>
+<!-- Viewport-gated enter -->
+<div vl-in-view>
+  <p vl-enter="fade-up" vl-timeline="view">Content</p>
+</div>
 
-<!-- Plays once, stays visible after -->
-<div vl-effect="clip-rise" vl-timeline="view" vl-once>One-shot reveal</div>
+<!-- Scroll-linked -->
+<img vl-scroll="media-zoom" vl-timeline="view" alt="" />
 
-<!-- Custom depth and speed -->
-<div
-  vl-effect="fade-up"
-  vl-timeline="view"
-  style="--vl-motion-distance: 2.5rem; --vl-motion-duration: 700ms;">
-  Deep, slow reveal
-</div>
+<!-- Legacy composite (avoid for new work) --> +<div vl-effect="blur-in" vl-timeline="view">…</div>
-

Entrance Primitives

+

Entrance presets (vl-enter)

Affect the element itself. Best combined with vl-timeline="view" for scroll-driven reveals.

diff --git a/apps/docs/src/pages/index.astro b/apps/docs/src/pages/index.astro index 661a47a..2bb4dc9 100644 --- a/apps/docs/src/pages/index.astro +++ b/apps/docs/src/pages/index.astro @@ -15,10 +15,21 @@ import Base from "../layouts/Base.astro";

Installation

-

Install the package from the npm registry:

+

+ @velora/css is developed in this monorepo. It is not yet published to npm. + Use workspace:*, a path import, or pnpm pack from packages/css after pnpm build. +

pnpm add @velora/css
 # or
 npm install @velora/css
+

+ Public spec: Attribute grammar + · Live playground: Motion Playground + · Publish: PUBLISH.md +

+ +

CDN (after npm publish)

+
<link rel="stylesheet" href="https://unpkg.com/@velora/css@1.0.0/dist/motion-core.css" />

Choose an import

@@ -66,7 +77,7 @@ npm install @velora/css

What Velora is

diff --git a/apps/docs/src/pages/motion-conditions.astro b/apps/docs/src/pages/motion-conditions.astro index 2173c80..705279e 100644 --- a/apps/docs/src/pages/motion-conditions.astro +++ b/apps/docs/src/pages/motion-conditions.astro @@ -13,12 +13,12 @@ import Base from "../layouts/Base.astro"; A single declarative attribute — vl-motion — selects a motion mode for an element and its subtree. It does not name a preset; instead it re-scales the shared engine tokens that every preset already reads, so one attribute adapts timing, travel, depth, blur and easing - across the whole subtree. Zero JavaScript. + across the whole subtree. Zero animation runtime JavaScript.

The Modes

- Add vl-motion to any element. Descendant vl-effect / vl-enter + Add vl-motion to any element. Descendant vl-enter / vl-scroll reveals inherit the mode automatically.

@@ -52,7 +52,7 @@ import Base from "../layouts/Base.astro";
<!-- One attribute governs the whole subtree -->
 <section vl-motion="cinematic">
   <h2 vl-enter="reveal-cinematic" vl-timeline="view">Grand & deep</h2>
-  <p vl-effect="fade-up" vl-timeline="view">Slower, deeper, blurred entrance.</p>
+  <p vl-enter="fade-up" vl-timeline="view">Slower, deeper, blurred entrance.</p>
 </section>
 
 <section vl-motion="subtle">...quieter, faster motion...</section>
diff --git a/apps/docs/src/pages/skills.astro b/apps/docs/src/pages/skills.astro
index 66ad0ae..b9754d9 100644
--- a/apps/docs/src/pages/skills.astro
+++ b/apps/docs/src/pages/skills.astro
@@ -18,7 +18,7 @@ import Base from "../layouts/Base.astro";
   
  • Semantic HTML first
  • Modern CSS as the primary rendering engine
  • -
  • Zero JavaScript for animation logic
  • +
  • Zero animation runtime JavaScript for motion logic
  • Minimal JavaScript only for progressive enhancement or accessibility where strictly needed
  • No React, Vue, GSAP, Framer Motion, Locomotive Scroll, Swup, or equivalent runtime in core
  • Strict namespace: .vl-*, --vl-*, vl-*
  • @@ -63,11 +63,11 @@ import Base from "../layouts/Base.astro";

    Motion Systems Engineer

    Builds and refines Velora's motion primitives and choreography system.

    -

    Scope: vl-effect, vl-timeline, vl-range, vl-scrub, motion tokens, reveal systems, hover systems, stagger systems.

    +

    Scope: vl-enter, vl-scroll, vl-hover, legacy vl-effect, vl-timeline, vl-range, vl-scrub, motion tokens, reveal systems, hover systems, stagger systems.

    Allowed: add new motion presets, refactor motion declarations into variables/tokens, create scroll-linked CSS patterns, define primitive vs scene motion distinctions.

    Forbidden: using JS to drive animations that modern CSS can handle, mixing scene orchestration into primitive effect definitions without need, introducing unregistered effect names casually.

    Ideal prompt

    -
    Implement vl-effect="flow-in" and vl-timeline="view" using layered CSS only. No JS. Use tokenized values and reduced-motion support.
    +
    Implement vl-enter="flow-in" and vl-timeline="view" using layered CSS only. No animation runtime JS. Use tokenized values and reduced-motion support.

    diff --git a/apps/docs/src/pages/tokens.astro b/apps/docs/src/pages/tokens.astro index 4193a24..58f2d81 100644 --- a/apps/docs/src/pages/tokens.astro +++ b/apps/docs/src/pages/tokens.astro @@ -279,7 +279,7 @@ import Base from "../layouts/Base.astro";
<!-- Override per element -->
 <div
-  vl-effect="fade-up"
+  vl-enter="fade-up"
   vl-timeline="view"
   style="--vl-motion-distance: 2rem; --vl-motion-duration: 600ms;">
   Custom depth and speed
diff --git a/apps/showcase/config/showcase-coverage.mjs b/apps/showcase/config/showcase-coverage.mjs
index 4d29ff2..b37d568 100644
--- a/apps/showcase/config/showcase-coverage.mjs
+++ b/apps/showcase/config/showcase-coverage.mjs
@@ -28,7 +28,22 @@ export const SHOWCASE_COVERAGE = {
     "pages/motion/api-motion-catalog.html": {
       name: "Catalog",
       role: "Provide a concise public decision guide for channels, scenes, timelines, support and fallbacks.",
-      signature: "Intent-first reference with canonical recipes and a separate exhaustive Motion Lab",
+      signature: "Intent-first reference with canonical recipes, generated preset registry and a separate exhaustive Motion Lab",
+    },
+    "pages/core/compatibility.html": {
+      name: "Compatibility",
+      role: "Document primitive support tiers, browser matrix, fallbacks and reduced-motion policy.",
+      signature: "Generated compatibility matrix linked to packages/catalog",
+    },
+    "pages/core/benchmarks.html": {
+      name: "Benchmarks",
+      role: "Publish reproducible integration evidence — not unverified FPS marketing.",
+      signature: "Objective metrics table, methodology draft and automated bench roadmap",
+    },
+    "pages/motion/playground.html": {
+      name: "Playground",
+      role: "Compose public vl-* attributes from the registry, preview CSS-only motion, copy markup.",
+      signature: "Playground UI uses JavaScript; preview motion uses Velora/CSS",
     },
   },
   componentGroups: {
diff --git a/apps/showcase/config/template-registry.mjs b/apps/showcase/config/template-registry.mjs
index 0d1e6e6..a74476d 100644
--- a/apps/showcase/config/template-registry.mjs
+++ b/apps/showcase/config/template-registry.mjs
@@ -22,8 +22,11 @@ export const TEMPLATE_REGISTRY = {
       ],
       secondary: [
         "pages/core/hosts.html",
+        "pages/core/compatibility.html",
+        "pages/core/benchmarks.html",
         "pages/core/archive.html",
         "pages/motion/motion-lab.html",
+        "pages/motion/playground.html",
       ],
     },
   },
diff --git a/apps/showcase/index.html b/apps/showcase/index.html
index 68fda6a..c98a17a 100644
--- a/apps/showcase/index.html
+++ b/apps/showcase/index.html
@@ -1174,7 +1174,7 @@ 

Six focused areas. One engine for the whole - + + diff --git a/apps/showcase/pages/motion/motion-lab.html b/apps/showcase/pages/motion/motion-lab.html index 7500233..44b14b1 100644 --- a/apps/showcase/pages/motion/motion-lab.html +++ b/apps/showcase/pages/motion/motion-lab.html @@ -19,7 +19,7 @@ Skip to content

Entrance presets (vl-enter)

+

+ {enterPresets.length} presets from the generated catalog ({generatedLabel}). Prefer channels for new markup — not legacy vl-effect. +

Affect the element itself. Best combined with vl-timeline="view" for scroll-driven reveals.

- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {enterPresets.map((preset) => ( + + + + + + ))}
ValueDescriptionDefault duration
ValueStatusReduced motion
fade-inSimple opacity 0 → 1 entrance--vl-duration-slow (500ms)
fade-upOpacity + translateY upward. Workhorse reveal effect.500ms
fade-downOpacity + translateY downward. Good for dropdowns and menus.500ms
slide-leftOpacity + slide in from the right. Uses --vl-ease-out-soft.500ms
slide-rightOpacity + slide in from the left. Uses --vl-ease-out-soft.500ms
scale-inScales from 0.94 with a subtle upward nudge. Uses spring easing.500ms
blur-inCinematic blur (14px) dissolve entrance. Dramatic and modern.--vl-duration-slower (800ms)
reveal-3dPerspective rotateX entry — page flip feel.500ms
flow-inBlur + translate + scale — cinematic combination. Premium look.800ms
clip-riseClip-path reveal from bottom. Text feels like it emerges from the page.800ms
tilt-inPerspective rotateY entry. Great for cards arriving from the side.800ms
{preset.name}{preset.status}{preset.reducedMotion ?? "static"}
@@ -108,51 +104,56 @@ import Base from "../layouts/Base.astro"; Custom entrance </div>
-

Live Demo

-
-
-
fade-up
-

Opacity + translate Y.

-
-
-
blur-in
-

Cinematic blur dissolve.

-
-
-
reveal-3d
-

Perspective rotateX entry.

+

Live Demo — entrance

+ +
+
+
fade-up
+

Opacity + translate Y.

+
+
+
blur-in
+

Cinematic blur dissolve.

+
+
+
reveal-3d
+

Perspective rotateX entry.

+
-
+ -

Interaction Effects

+

Interaction presets (vl-hover)

- Applied to interactive elements. These respond to user hover — no timeline needed. + Applied via vl-hover on interactive elements — no timeline needed. + {hoverPresets.length} presets in the generated catalog.

+ +
+
+
hover-lift
+

Lift + shadow

+
+
+
hover-glow
+

Primary glow ring

+
+ + underline-expand + +
+
- + - - - - - - - - - - - - - - - - - - - - + {hoverPresets.map((preset) => ( + + + + + + ))}
ValueTriggerDescription
ValueStatusReduced motion
hover-lift:hoverTranslates up 4px + floating shadow. Classic card hover.
hover-glow:hoverAdds a primary-colored glow shadow ring. Great for media embeds.
underline-expand:hoverExpanding underline grows from center. Elegant nav link effect.
icon-shift:hoverLast child (icon/arrow) shifts 4px on hover. Use on inline elements with an icon sibling.
{preset.name}{preset.status}{preset.reducedMotion ?? "static"}
@@ -174,6 +175,44 @@ import Base from "../layouts/Base.astro"; Touch devices: Hover effects automatically disable via @media (hover: none).

+

Scroll presets (vl-scroll)

+

+ Scroll-linked transforms. {scrollPresets.length} presets — usually paired with vl-timeline="view" unless the preset is inherently scroll-driven. +

+ + + + + + {scrollPresets.map((preset) => ( + + + + + + ))} + +
ValueStatusReduced motion
{preset.name}{preset.status}{preset.reducedMotion ?? "static"}
+ +

Loop presets (vl-loop / vl-loop-effect)

+

+ Continuous ambient motion. {loopPresets.length} presets — loops stop under prefers-reduced-motion. +

+ + + + + + {loopPresets.map((preset) => ( + + + + + + ))} + +
ValueStatusReduced motion
{preset.name}{preset.status}{preset.reducedMotion ?? "static"}
+

State Effects

Used with toggled elements like accordions, drawers, and panels. Typically paired with @@ -206,26 +245,8 @@ import Base from "../layouts/Base.astro";

Continuous / Ambient Effects

- Loop indefinitely. Use to add life to decorative elements, loading states, or ambient backgrounds. - Always respect prefers-reduced-motion — wrap with the media query if adding ambient - effects to content-critical elements. + See the generated loop preset table above. Examples below use legacy vl-effect — prefer vl-loop / vl-loop-effect for new markup.

- - - - - - - - - - - - - - -
ValueLoop durationDescription
shimmer2.5sBackground shimmer sweep. Use on skeleton loaders or accent lines. Requires background-size: 200% auto and a gradient background.
float5.5sGentle vertical bob. Great for hero illustrations.
glow-breathe3.5sPulsing box-shadow. Use on live indicators or accent dots.
wobble2.8sTilt oscillation (±3deg). Attention-grabbing but subtle.
orbit14sCircular orbit — full 360deg spin. For orbital icons or decorative rings.
rock4sWider tilt oscillation (±6deg). More playful than wobble.
morph8sOrganic border-radius morphing. Use on blobs and abstract shapes.
elastic-in0.7sSpringy scale-in entrance. One-shot spring pop, not a loop.
-
<!-- Floating hero illustration -->
 <img src="hero-visual.png" alt="" vl-effect="float" />
 
diff --git a/apps/docs/src/pages/hosts.astro b/apps/docs/src/pages/hosts.astro
index 20d0125..2c80aa6 100644
--- a/apps/docs/src/pages/hosts.astro
+++ b/apps/docs/src/pages/hosts.astro
@@ -1,43 +1,40 @@
 ---
 import Base from "../layouts/Base.astro";
+import DocsLive from "../components/DocsLive.astro";
 ---
 
-
+
   Interoperability
 
-  

Hosts (Tailwind & others)

+

Hosts (Tailwind, WordPress & others)

- Velora is a motion engine. Your host owns layout and visual styling. + Velora is a motion engine. Your host owns layout and visual styling — motion attributes still work.

+ +
+

+ Host utility classes +

+

No .vl-card required

+

+ Only vl-* attributes drive motion +

+
+
+

Minimal import

@import "@velora/css/motion-core";
-/* optional page transitions */
-@import "@velora/css/transitions";
- -

Do not require @velora/css/theme or component classes for scenes to work.

- -

Tailwind example

-
<section vl-scene vl-timeline="view" vl-pin="3" vl-scrub>
-  <div vl-stage class="min-h-svh grid place-items-center gap-6 px-6">
-    <p class="text-sm uppercase tracking-widest" vl-enter="fade-up" vl-act="1">Kicker</p>
-    <h1 class="text-5xl font-semibold" vl-enter="clip-rise" vl-act="1">Title</h1>
-    <img class="w-full max-w-3xl rounded-2xl" vl-scroll="media-zoom" vl-act="2" vl-span="2" alt="" />
-  </div>
-</section>
- -

- Repo proof: examples/tailwind-host/. Showcase proves the Velora cinematic UI; - examples prove the host-agnostic path. -

+@import "@velora/css/transitions"; /* optional */
-

Layers

+

WordPress

- Velora uses @layer velora.*. Keep host utilities outside or after those layers so - Tailwind wins on appearance while vl-* attributes still drive animation. + Same attributes in PHP templates, Gutenberg, or Elementor — + examples/wordpress/.

- -

Optional skin

-
@import "@velora/css/motion-core";
-@import "@velora/css/theme"; /* brand tokens + scene recipes */
diff --git a/apps/docs/src/pages/index.astro b/apps/docs/src/pages/index.astro index 2bb4dc9..8b2cb6c 100644 --- a/apps/docs/src/pages/index.astro +++ b/apps/docs/src/pages/index.astro @@ -1,66 +1,87 @@ --- import Base from "../layouts/Base.astro"; +import DocsLive from "../components/DocsLive.astro"; +import { SITE, showcaseUrl } from "../lib/site"; --- - + Documentation

Getting Started

A declarative motion language for HTML. Attributes describe intent; CSS executes it. - Works with Tailwind, Relume, or any UI. Showcase is the optional Velora look - (Skins — named models via data-editorial-theme). - The public Showcase is six pages; the previous demos live in - archive/showcase-2026-08/. + Every section below includes a Test here frame — scroll or interact on the page.

+ Declarative motion

'} + > +

Declarative motion

+

+ Same public API as Showcase — test in the frame, copy into your project. +

+
+ +

Explore

+ +

Installation

- @velora/css is developed in this monorepo. It is not yet published to npm. - Use workspace:*, a path import, or pnpm pack from packages/css after pnpm build. + @velora/css is developed in this monorepo. It is not published to npm yet — + we are finishing Showcase and documentation before release.

-
pnpm add @velora/css
-# or
-npm install @velora/css
+
# After npm publish (coming soon)
+pnpm add @velora/css
+
+# Today: monorepo workspace or local pack
+pnpm --filter @velora/css build
+pnpm pack:css

- Public spec: Attribute grammar - · Live playground: Motion Playground - · Publish: PUBLISH.md + Canonical spec: + Attribute grammar + · Live catalog: + Motion catalog + · Playground: + Motion Playground

-

CDN (after npm publish)

-
<link rel="stylesheet" href="https://unpkg.com/@velora/css@1.0.0/dist/motion-core.css" />
- -

Choose an import

- - - - - - - - - - - - - - - - - - -
ImportUse when
@velora/css/motion-coreHost UI already provides look (Tailwind, custom DS). Motion + scene engine only.
@velora/css/themeOptional Velora visual tokens + named scene recipes.
@velora/css / fullConvenience: theme + components + motion (Showcase / docs style).
- -
/* Host-agnostic (recommended for Tailwind projects) */
-@import "@velora/css/motion-core";
-@import "@velora/css/transitions"; /* optional */
+  

Quick Start — scene engine

+

Track + stage + acts: one shared clock, pin, scrub — no animation runtime JavaScript.

-/* Or full Velora skin */ -@import "@velora/css";
+ …'} + > +
+
+

Act 1

+

Scene clock

+

Scrub progress in the frame

+
+
+
-

Quick Start — scene engine

-

Track + stage + acts: one shared clock, pin, scrub — no JavaScript.

<section vl-scene vl-timeline="view" vl-pin="3" vl-scrub>
   <div vl-stage>
     <p vl-enter="fade-up" vl-act="1">Kicker</p>
@@ -69,15 +90,11 @@ npm install @velora/css
</div> </section> -

- Style with your own classes. Motion does not require .vl-card or brand tokens. - See Hosts (Tailwind) and Scenes. -

-

What Velora is

  • Motion language — not a utility framework requirement
  • -
  • Zero animation runtime JavaScript — scroll timelines, view transitions, CSS only (Showcase tooling JS is separate)
  • -
  • Showcase — cinematic reference UI / design system skin
  • +
  • Zero animation runtime JavaScript — scroll timelines, view transitions, CSS only
  • +
  • Host-agnosticmotion-core without Skins
  • +
  • Skins — switch editorial theme in the header (Noir, Earth, Aethel, Meridian)
diff --git a/apps/docs/src/pages/motion-conditions.astro b/apps/docs/src/pages/motion-conditions.astro index 705279e..f3c76eb 100644 --- a/apps/docs/src/pages/motion-conditions.astro +++ b/apps/docs/src/pages/motion-conditions.astro @@ -1,5 +1,7 @@ --- import Base from "../layouts/Base.astro"; +import DocsLive from "../components/DocsLive.astro"; +import { SITE } from "../lib/site"; --- +

Test here — compare modes

+ …'}> +
+
+

cinematic

+

Grand & deep

+
+
+

subtle

+

Quiet & fast

+
+
+
+
<!-- One attribute governs the whole subtree -->
 <section vl-motion="cinematic">
   <h2 vl-enter="reveal-cinematic" vl-timeline="view">Grand & deep</h2>
@@ -144,6 +160,6 @@ import Base from "../layouts/Base.astro";
     The vl-motion modes themselves work in all modern browsers. The if()-based
     refinement requires a browser with CSS if() support; everywhere else the baseline modes
     are used. See the live comparison on the
-    Zero-JS Motion showcase page.
+    Zero-JS Motion showcase page.
   

diff --git a/apps/docs/src/pages/scenes.astro b/apps/docs/src/pages/scenes.astro index 7f1bfca..b4c91f0 100644 --- a/apps/docs/src/pages/scenes.astro +++ b/apps/docs/src/pages/scenes.astro @@ -1,5 +1,6 @@ --- import Base from "../layouts/Base.astro"; +import DocsLive from "../components/DocsLive.astro"; --- @@ -7,52 +8,60 @@ import Base from "../layouts/Base.astro";

Scenes

- A scene is a shared clock for children — not a look. Layout and chrome come from the host UI - (or optional Velora theme recipes). + A scene is a shared clock for children — not a look. Scroll the frame to scrub acts on the track.

-

Track + stage + acts

-
<section vl-scene vl-timeline="view" vl-pin="3" vl-scrub>
-  <div vl-stage>
-    <p vl-enter="fade-up" vl-act="1">Kicker</p>
-    <h1 vl-enter="clip-rise" vl-act="1">Title</h1>
-    <img vl-scroll="media-zoom" vl-act="2" vl-span="2" alt="" />
-    <a vl-enter="fade-up" vl-act="5">CTA</a>
-  </div>
-</section>
+ +
+

Kicker

+

Title

+
+`} + > +
+
+

Kicker · act 1

+

Title · act 1

+

CTA · act 3–4

+ Finale · act 5 +
+
+
+

Track + stage + acts

- + - - + - - - +
AttributeRole
vl-sceneTrack — owns --vl-scene view-timeline and optional pin height
vl-sceneTrack — owns --vl-scene and pin height
vl-stageSticky viewport stage (required for pin+scrub)
vl-timelineview (scroll film) or auto (time film)
vl-pinTrack height in viewport multiples (e.g. 3)
vl-pinTrack height in viewport multiples
vl-scrubLinear fill — progress glued to scroll
vl-actBeat index (same act = overlap / GSAP <)
vl-spanHow many beats the tween spans
vl-rangeEscape hatch — overrides act-derived ranges
vl-act / vl-spanBeat index and duration on the clock

Why track ≠ stage

- A sticky element freezes its own anonymous view() progress. Children bind to the - named track timeline (--vl-scene) while the stage stays pinned. -

- -

Named presets (skin recipes)

-

- Values like vl-scene="cinematic-hero" remain for compatibility. They ship in - scene-recipes.css via @velora/css/theme / full — they mix look + motion. - Prefer track/stage/acts for portable hosts. + Children bind to the named track timeline (--vl-scene) while + [vl-stage] stays pinned in the scrollport.

Channels on children

-

- Use existing channels on stage children: vl-enter, vl-scroll, - vl-exit, etc. The scene clock drives when they play. -

+ +
+
+

vl-enter

+

On stage child

+
+
+

vl-scroll

+

Scroll channel

+
+
+
diff --git a/apps/docs/src/pages/skills.astro b/apps/docs/src/pages/skills.astro index b9754d9..1f49fd9 100644 --- a/apps/docs/src/pages/skills.astro +++ b/apps/docs/src/pages/skills.astro @@ -1,5 +1,6 @@ --- import Base from "../layouts/Base.astro"; +import DocsLive from "../components/DocsLive.astro"; --- @@ -7,11 +8,17 @@ import Base from "../layouts/Base.astro";

Agent Skill Sets

- These are project-specific operating profiles for AI coding agents — Cursor, GitHub Copilot, and - similar tools. Each skill set defines a mission, scope, allowed and forbidden actions, and ideal - prompts. Use them individually or in sequence. + Operating profiles for AI agents. Validate motion against live Test here frames + on Attributes, Effects, and Scenes.

+ +
+

No animation runtime JS

+

CSS-only motion in every public demo

+
+
+

Core Rule Shared by All Skill Sets

diff --git a/apps/docs/src/pages/timelines.astro b/apps/docs/src/pages/timelines.astro index c789373..252f246 100644 --- a/apps/docs/src/pages/timelines.astro +++ b/apps/docs/src/pages/timelines.astro @@ -1,5 +1,6 @@ --- import Base from "../layouts/Base.astro"; +import DocsLive from "../components/DocsLive.astro"; --- @@ -7,44 +8,52 @@ import Base from "../layouts/Base.astro";

Timelines

- Timelines decide what drives progress. Effects decide what moves. - The scene engine shares one clock across stage children. + Timelines decide what drives progress. Channels decide what moves. + Test each model in the frames below.

-

vl-timeline values

+

vl-timeline="view"

+ +

View progress

+

+ Linked to visibility in the scrollport +

+
+ +

vl-timeline="auto"

+ +

Auto timeline

+

Time-based, no scroll needed

+
+ +

vl-timeline="hover"

+ +
+

Hover to play

+

Progress gated by intent

+
+
+ +

Scene clock + scrub

+ +
+
+

Shared clock

+

Scrub acts

+
+
+
+ +

Reference

- - + + - +
ValueBehavior
viewProgress from element / scene visibility (view() or named --vl-scene)
scrollProgress from root scroll
viewElement / scene visibility (view() or --vl-scene)
scrollRoot scroll progress
autoTime-based CSS animation
hoverPaused until hover
hoverPaused until hover / focus
- -

Scene clock

-

- On [vl-scene][vl-timeline="view"], stage children use - animation-timeline: --vl-scene and ranges derived from - vl-act / vl-span / --vl-beats (default 8). -

-

- With vl-timeline="auto", the same acts map to - animation-delay: (act - 1) * --vl-beat. Pin/scrub are no-ops. -

- -

Ranges

-

- Presets via vl-range (entry, entry-short, cover, …) - still apply to standalone elements. Inside a pinned scene, prefer acts; use - vl-range only as an escape hatch. -

- -

Scrub

-
<section vl-scene vl-timeline="view" vl-pin="3" vl-scrub>
-  …
-</section>
-

vl-scrub forces linear timing and fill both on scene-driven children.

diff --git a/apps/docs/src/pages/tokens.astro b/apps/docs/src/pages/tokens.astro index 58f2d81..ff4aa3b 100644 --- a/apps/docs/src/pages/tokens.astro +++ b/apps/docs/src/pages/tokens.astro @@ -1,5 +1,6 @@ --- import Base from "../layouts/Base.astro"; +import DocsLive from "../components/DocsLive.astro"; --- @@ -13,6 +14,18 @@ import Base from "../layouts/Base.astro";

Color System

+ +
+ --vl-color-primary + --vl-bg-surface + --vl-text-muted +
+
+

Skin-driven

+

Noir · Earth · Aethel · Meridian

+
+
+

Brand Palette

Velora's brand uses a natural, earthy palette built on hex values for precision:

diff --git a/apps/docs/src/pages/transitions.astro b/apps/docs/src/pages/transitions.astro index e56bad7..41533fd 100644 --- a/apps/docs/src/pages/transitions.astro +++ b/apps/docs/src/pages/transitions.astro @@ -1,5 +1,6 @@ --- import Base from "../layouts/Base.astro"; +import DocsLive from "../components/DocsLive.astro"; --- @@ -7,10 +8,27 @@ import Base from "../layouts/Base.astro";

Page Transitions

- Native cross-document View Transitions — no router, no JavaScript. Just CSS and the - vl-page-transition attribute. + Native cross-document View Transitions — no animation runtime JavaScript. CSS and the + vl-page-transition attribute only.

+

Test here — page transitions

+ +

Cross-document demo

+

+ This site runs vl-page-transition on <html>. + Jump to Attributes or Effects to see the preset. +

+ +
+

How to Enable

View Transitions require the browser's navigation opt-in. Velora ships this in its main CSS entry: diff --git a/apps/docs/src/styles/docs-shell.css b/apps/docs/src/styles/docs-shell.css new file mode 100644 index 0000000..a9a7fc7 --- /dev/null +++ b/apps/docs/src/styles/docs-shell.css @@ -0,0 +1,663 @@ +/** + * Velora Docs shell — Skins-aware layout on top of @velora/css full bundle. + * Uses editorial tokens (data-editorial-theme) + shared DS chrome patterns. + */ + +.docs-page-shell { + --docs-gutter: clamp(1rem, 3vw, 2rem); + --docs-sidebar-width: 16.5rem; + --docs-content-max: min(48rem, 100%); + background: var(--vl-bg-main); + color: var(--vl-text-primary); + scroll-padding-top: 5rem; +} + +/* Header skin controls (aligned with Showcase) */ +.ds-vt-select, +.ds-editorial-select { + appearance: none; + background: color-mix(in oklch, var(--vl-bg-surface) 60%, transparent); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 70%, transparent); + border-radius: var(--vl-radius-sm); + color: var(--vl-text-secondary); + font-family: var(--vl-font-family-mono, monospace); + font-size: 0.7rem; + letter-spacing: 0.04em; + padding: 0.3rem 1.6rem 0.3rem 0.6rem; + cursor: pointer; + transition: + border-color var(--vl-transition-fast), + color var(--vl-transition-fast); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%237f8668' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0.5rem center; +} + +.ds-editorial-select { + max-width: 7.5rem; +} + +.ds-vt-select:hover, +.ds-vt-select:focus-visible, +.ds-editorial-select:hover, +.ds-editorial-select:focus-visible { + border-color: var(--vl-color-primary); + color: var(--vl-text-primary); + outline: none; +} + +.ds-theme-toggle { + display: flex; + align-items: center; + gap: 1px; + background: color-mix(in oklch, var(--vl-bg-surface) 55%, transparent); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 60%, transparent); + border-radius: var(--vl-radius-sm); + padding: 2px; +} + +.ds-theme-btn { + background: transparent; + border: none; + border-radius: calc(var(--vl-radius-sm) - 2px); + color: var(--vl-text-muted); + cursor: pointer; + font-size: 0.75rem; + line-height: 1; + padding: 0.25rem 0.4rem; + transition: + background var(--vl-transition-fast), + color var(--vl-transition-fast); +} + +.ds-theme-btn:hover { + background: color-mix(in oklch, var(--vl-color-primary) 15%, transparent); + color: var(--vl-text-primary); +} + +.ds-theme-btn.is-active { + background: var(--vl-color-primary); + color: var(--vl-bg-main); +} + +.ds-theme-btn:focus-visible { + outline: 2px solid var(--vl-focus-ring-color); + outline-offset: 1px; +} + +.docs-page-shell .vl-header__brand img { + block-size: 1.35rem; + inline-size: auto; +} + +html:not([data-theme="light"]) .docs-page-shell .vl-header__brand img { + filter: brightness(0) invert(1); + opacity: 0.92; +} + +html[data-editorial-theme="earth"] .docs-page-shell .vl-header__brand img { + filter: none; + opacity: 1; +} + +.docs-header-link { + display: inline-flex; + align-items: center; + min-height: 2rem; + padding: 0.35rem 0.75rem; + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 70%, transparent); + border-radius: var(--vl-radius-sm); + color: var(--vl-text-secondary); + font-size: var(--vl-font-size-xs); + font-weight: var(--vl-font-weight-semibold); + letter-spacing: 0.06em; + text-decoration: none; + text-transform: uppercase; + transition: + border-color var(--vl-transition-fast), + color var(--vl-transition-fast), + background var(--vl-transition-fast); +} + +.docs-header-link:hover { + border-color: color-mix(in oklch, var(--vl-color-primary) 44%, var(--vl-border-subtle)); + color: var(--vl-text-primary); + background: color-mix(in oklch, var(--vl-color-primary) 8%, transparent); +} + +/* Layout */ +.docs-layout { + display: grid; + grid-template-columns: var(--docs-sidebar-width) minmax(0, 1fr); + gap: clamp(1rem, 2.5vw, 2rem); + max-width: min(112rem, calc(100vw - (var(--docs-gutter) * 2))); + margin-inline: auto; + padding: var(--vl-space-lg) var(--docs-gutter) var(--vl-space-3xl); +} + +.docs-sidebar { + position: sticky; + top: 5.5rem; + align-self: start; + max-height: calc(100dvh - 6.5rem); + overflow: auto; + padding: var(--vl-space-md); + border-radius: var(--vl-radius-lg); +} + +.docs-sidebar nav { + display: flex; + flex-direction: column; + gap: 2px; +} + +.docs-nav-section { + font-size: var(--vl-font-size-xs); + font-weight: var(--vl-font-weight-semibold); + text-transform: uppercase; + letter-spacing: var(--vl-tracking-display); + color: var(--vl-text-muted); + padding-inline: var(--vl-space-sm); + margin-block: var(--vl-space-md) var(--vl-space-2xs); +} + +.docs-nav-section:first-of-type { + margin-top: 0; +} + +.docs-sidebar a { + display: block; + padding: var(--vl-space-xs) var(--vl-space-sm); + border-radius: var(--vl-radius-md); + color: var(--vl-text-muted); + text-decoration: none; + font-size: var(--vl-font-size-sm); + font-weight: var(--vl-font-weight-medium); + transition: + background var(--vl-transition-fast), + color var(--vl-transition-fast); +} + +.docs-sidebar a:hover { + background: color-mix(in oklch, var(--vl-color-primary) 10%, transparent); + color: var(--vl-text-secondary); +} + +.docs-sidebar a[aria-current="page"] { + background: color-mix(in oklch, var(--vl-color-primary) 14%, transparent); + color: var(--vl-color-primary); + font-weight: var(--vl-font-weight-semibold); +} + +.docs-main { + min-width: 0; + max-width: var(--docs-content-max); +} + +.docs-main > :first-child { + margin-top: 0; +} + +.docs-main > :where(p, ul, ol, blockquote) { + max-width: var(--vl-measure-prose, 65ch); +} + +/* Typography — showcase-ref rhythm, docs scale */ +.docs-page-title { + margin-top: var(--vl-space-sm); + margin-bottom: var(--vl-space-md); + font-family: var(--vl-font-family-display); + font-size: clamp(2rem, 4.5vw, var(--vl-font-size-4xl)); + font-weight: var(--vl-font-weight-bold); + letter-spacing: var(--vl-tracking-tight); + line-height: var(--vl-leading-tight); + color: var(--vl-text-primary); + text-wrap: balance; +} + +.docs-page-lead { + font-size: var(--vl-font-size-lg); + color: var(--vl-text-secondary); + line-height: var(--vl-leading-relaxed); + margin-bottom: var(--vl-space-xl); +} + +.docs-page-lead--compact { + font-size: var(--vl-font-size-md); +} + +.docs-page-lead--narrow { + max-width: 44ch; +} + +.docs-link-primary { + color: var(--vl-color-primary); + text-underline-offset: 0.2em; +} + +.docs-main h2 { + margin-block: var(--vl-space-3xl) var(--vl-space-md); + padding: var(--vl-space-sm) var(--vl-space-md); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 72%, transparent); + border-radius: var(--vl-radius-lg); + background: + linear-gradient( + 180deg, + color-mix(in oklch, var(--vl-bg-surface-elevated) 74%, transparent), + color-mix(in oklch, var(--vl-bg-surface) 94%, transparent) + ); + box-shadow: var(--vl-shadow-sm); + font-family: var(--vl-font-family-display); + font-size: var(--vl-font-size-2xl); + font-weight: var(--vl-font-weight-semibold); + letter-spacing: var(--vl-tracking-tight); + color: var(--vl-text-primary); + scroll-margin-top: 6rem; +} + +.docs-main h3 { + margin-block: var(--vl-space-lg) var(--vl-space-sm); + font-family: var(--vl-font-family-display); + font-size: var(--vl-font-size-lg); + font-weight: var(--vl-font-weight-semibold); + color: var(--vl-text-primary); +} + +.docs-main h4 { + margin-block: var(--vl-space-md) var(--vl-space-xs); + font-size: var(--vl-font-size-sm); + font-weight: var(--vl-font-weight-semibold); + letter-spacing: var(--vl-tracking-wide); + text-transform: uppercase; + color: var(--vl-text-muted); +} + +.docs-main p { + color: var(--vl-text-secondary); + line-height: var(--vl-leading-relaxed); + margin-bottom: var(--vl-space-md); + text-wrap: pretty; +} + +.docs-main a { + color: color-mix(in oklch, var(--vl-color-primary) 86%, white 8%); + text-underline-offset: 0.2em; +} + +.docs-main a:hover { + color: var(--vl-color-primary); +} + +.docs-main ul, +.docs-main ol { + color: var(--vl-text-secondary); + line-height: var(--vl-leading-relaxed); + padding-left: var(--vl-space-lg); + margin-bottom: var(--vl-space-md); +} + +.docs-main li { + margin-bottom: var(--vl-space-xs); +} + +.docs-main code { + font-family: var(--vl-font-family-mono); + font-size: 0.85em; + background: color-mix(in oklch, var(--vl-color-primary) 12%, var(--vl-bg-surface)); + color: var(--vl-color-primary); + padding: 0.1em 0.4em; + border-radius: var(--vl-radius-sm); + border: 1px solid color-mix(in oklch, var(--vl-color-primary) 20%, transparent); +} + +.docs-main pre { + background: var(--vl-bg-inset); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 72%, transparent); + border-radius: var(--vl-radius-lg); + padding: var(--vl-space-lg); + overflow-x: auto; + margin-block: var(--vl-space-lg); + box-shadow: var(--vl-shadow-sm); +} + +.docs-main pre code { + background: none; + border: none; + color: var(--vl-text-secondary); + padding: 0; + font-size: var(--vl-font-size-sm); + line-height: 1.8; +} + +.docs-main td code { + font-size: 0.85em; +} + +.docs-catalog-panel { + margin-block: var(--vl-space-xl); + padding: var(--vl-space-lg); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 72%, transparent); + border-radius: var(--vl-radius-xl); + background: color-mix(in oklch, var(--vl-bg-surface) 92%, transparent); + box-shadow: var(--vl-shadow-sm); +} + +.docs-catalog-panel__kicker { + margin: 0 0 var(--vl-space-sm); + font-family: var(--vl-font-family-mono, monospace); + font-size: var(--vl-font-size-xs); + letter-spacing: var(--vl-tracking-wide); + text-transform: uppercase; + color: var(--vl-text-muted); +} + +.docs-catalog-panel__channels { + margin: var(--vl-space-sm) 0 0; + font-size: var(--vl-font-size-sm); + color: var(--vl-text-secondary); + line-height: 1.6; +} + +.docs-catalog-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--vl-space-sm); +} + +.docs-catalog-stat { + display: grid; + gap: 0.15rem; + padding: var(--vl-space-sm) var(--vl-space-md); + border-radius: var(--vl-radius-lg); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 65%, transparent); + background: var(--vl-bg-inset); +} + +.docs-catalog-stat strong { + font-family: var(--vl-font-family-display); + font-size: clamp(1.35rem, 3vw, 1.75rem); + line-height: 1; + color: var(--vl-text-primary); +} + +.docs-catalog-stat span { + font-size: var(--vl-font-size-xs); + color: var(--vl-text-muted); + text-transform: uppercase; + letter-spacing: var(--vl-tracking-wide); +} + +.docs-main table { + width: 100%; + border-collapse: collapse; + margin-block: var(--vl-space-lg); + font-size: var(--vl-font-size-sm); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 72%, transparent); + border-radius: var(--vl-radius-lg); + overflow: hidden; + background: color-mix(in oklch, var(--vl-bg-surface) 90%, transparent); + box-shadow: var(--vl-shadow-sm); +} + +.docs-main th { + text-align: left; + padding: var(--vl-space-sm) var(--vl-space-md); + font-weight: var(--vl-font-weight-semibold); + font-size: var(--vl-font-size-xs); + text-transform: uppercase; + letter-spacing: var(--vl-tracking-wide); + color: var(--vl-text-muted); + background: var(--vl-bg-inset); + border-bottom: 1px solid var(--vl-border-subtle); +} + +.docs-main td { + padding: var(--vl-space-sm) var(--vl-space-md); + border-bottom: 1px solid color-mix(in oklch, var(--vl-border-subtle) 50%, transparent); + color: var(--vl-text-secondary); + vertical-align: top; +} + +.docs-main tbody tr:nth-child(even) { + background: color-mix(in oklch, var(--vl-bg-inset) 28%, transparent); +} + +.docs-main tr:last-child td { + border-bottom: none; +} + +.docs-main blockquote { + border-left: 3px solid var(--vl-color-primary); + padding: var(--vl-space-md) var(--vl-space-lg); + background: color-mix(in oklch, var(--vl-color-primary) 6%, transparent); + border-radius: 0 var(--vl-radius-md) var(--vl-radius-md) 0; + color: var(--vl-text-secondary); + margin-block: var(--vl-space-lg); + font-style: italic; +} + +.docs-main hr { + border: none; + border-top: 1px solid var(--vl-border-subtle); + margin-block: var(--vl-space-2xl); +} + +.docs-grid-cards { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); + gap: var(--vl-space-md); + margin-top: var(--vl-space-lg); +} + +.docs-grid-3 { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--vl-space-md); +} + +.docs-section-block { + margin-block: var(--vl-space-lg); +} + +.docs-card-link { + text-decoration: none; + display: block; + color: inherit; +} + +.docs-swatch { + display: inline-block; + width: 1rem; + height: 1rem; + border-radius: var(--vl-radius-sm); + vertical-align: middle; +} + +@media (max-width: 48rem) { + .docs-layout { + grid-template-columns: 1fr; + } + + .docs-grid-3 { + grid-template-columns: 1fr; + } + + .docs-sidebar { + position: static; + max-height: none; + } + + .docs-page-shell .vl-header__actions .ds-vt-select, + .docs-page-shell .vl-header__actions .ds-editorial-select, + .docs-page-shell .vl-header__actions .ds-theme-toggle { + display: none; + } +} + +/* ----------------------------------------------------------------------- + Live demos — scrollport drives view timelines inside the docs page + ----------------------------------------------------------------------- */ +.docs-live { + margin-block: var(--vl-space-lg); + border: 1px solid color-mix(in oklch, var(--vl-border-subtle) 72%, transparent); + border-radius: var(--vl-radius-lg); + background: + linear-gradient( + 180deg, + color-mix(in oklch, var(--vl-bg-surface-elevated) 70%, transparent), + color-mix(in oklch, var(--vl-bg-surface) 94%, transparent) + ); + box-shadow: var(--vl-shadow-sm); + overflow: hidden; +} + +.docs-live__bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--vl-space-sm); + padding: var(--vl-space-sm) var(--vl-space-md); + border-bottom: 1px solid color-mix(in oklch, var(--vl-border-subtle) 55%, transparent); + background: color-mix(in oklch, var(--vl-bg-inset) 40%, transparent); +} + +.docs-live__kicker { + margin: 0; +} + +.docs-live__replay { + cursor: pointer; + border: 1px solid color-mix(in oklch, var(--vl-color-primary) 35%, transparent); + background: color-mix(in oklch, var(--vl-color-primary) 10%, transparent); + color: var(--vl-color-primary); + font-family: var(--vl-font-family-mono); + font-size: var(--vl-font-size-xs); + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.docs-live__replay:hover { + background: color-mix(in oklch, var(--vl-color-primary) 18%, transparent); +} + +.docs-live__hint { + margin: 0; + padding: var(--vl-space-xs) var(--vl-space-md); + font-size: var(--vl-font-size-xs); + color: var(--vl-text-muted); + border-bottom: 1px solid color-mix(in oklch, var(--vl-border-subtle) 40%, transparent); +} + +.docs-live__scroll { + max-height: 16rem; + overflow: auto; + overscroll-behavior: contain; + scroll-behavior: smooth; + background: var(--vl-bg-inset); +} + +.docs-live--tall .docs-live__scroll { + max-height: 22rem; +} + +.docs-live--scene .docs-live__scroll { + max-height: 26rem; +} + +.docs-live__pad { + block-size: 7rem; + pointer-events: none; +} + +.docs-live--tall .docs-live__pad { + block-size: 9rem; +} + +.docs-live--scene .docs-live__pad--top { + block-size: 3rem; +} + +.docs-live--scene .docs-live__pad--bottom { + block-size: 14rem; +} + +.docs-live__content { + padding: var(--vl-space-md); + display: grid; + gap: var(--vl-space-md); + place-items: center; + min-height: 8rem; +} + +.docs-live__stage { + padding: var(--vl-space-lg); + display: grid; + gap: var(--vl-space-md); + place-items: center; + min-height: 10rem; + background: var(--vl-bg-inset); +} + +.docs-live__code { + margin: 0; + border-radius: 0; + border: none; + border-top: 1px solid color-mix(in oklch, var(--vl-border-subtle) 55%, transparent); + font-size: var(--vl-font-size-xs); +} + +.docs-live .vl-card { + inline-size: min(100%, 14rem); +} + +.docs-live__scene-track { + inline-size: 100%; + min-block-size: 140%; +} + +.docs-live__scene-track [vl-stage] { + min-block-size: 12rem; + display: grid; + place-items: center; + gap: var(--vl-space-sm); + padding: var(--vl-space-md); + text-align: center; +} + +.docs-live__demo-title { + margin: 0; + font-family: var(--vl-font-family-display); + font-size: var(--vl-font-size-xl); + font-weight: var(--vl-font-weight-bold); +} + +.docs-live__demo-copy { + margin: 0; + color: var(--vl-text-secondary); + font-size: var(--vl-font-size-sm); +} + +.docs-live__demo-chip { + display: inline-flex; + padding: 0.35rem 0.75rem; + border-radius: var(--vl-radius-full); + border: 1px solid color-mix(in oklch, var(--vl-color-primary) 30%, transparent); + font-size: var(--vl-font-size-xs); + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--vl-text-muted); +} + +.docs-live__row { + display: flex; + flex-wrap: wrap; + gap: var(--vl-space-md); + justify-content: center; + inline-size: 100%; +} + +@media (max-width: 48rem) { + .docs-live__scroll { + max-height: 14rem; + } +} diff --git a/apps/docs/vercel.json b/apps/docs/vercel.json new file mode 100644 index 0000000..bb67885 --- /dev/null +++ b/apps/docs/vercel.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "installCommand": "pnpm install", + "buildCommand": "pnpm generate:catalog && pnpm --filter docs build", + "outputDirectory": "dist", + "framework": "astro" +} diff --git a/apps/showcase/index.html b/apps/showcase/index.html index c98a17a..245ac40 100644 --- a/apps/showcase/index.html +++ b/apps/showcase/index.html @@ -21,7 +21,7 @@ - + Skip to content

+
@@ -112,7 +113,7 @@
- + @@ -160,33 +161,42 @@
-
-
-
-

Archive // frozen Showcase

-

The previous ~50 pages are kept, not served.

-

- Snapshot: archive/showcase-2026-08/ in the repo (outside Vite and verify:contract). - Restore a page by copying it into apps/showcase/pages/ and adding it to the template registry. -

-

Paths below are archive-relative. They are not live routes.

-
    -
  • index.html — cinematic home (pre-lean)
  • -
  • pages/scenes/scene-hero.html — hero scene recipe
  • -
  • pages/scenes/scene-story.html — pinned story
  • -
  • pages/scenes/scene-features.html — feature flow
  • -
  • pages/scenes/scene-creator.html — scene creator
  • -
  • pages/components/buttons.html — button kit
  • -
  • pages/components/forms.html — forms
  • -
  • pages/color/design-tokens.html — token reference
  • -
  • pages/typography/typography.html — type samples
  • -
  • pages/tools/architecture.html — architecture
  • -
  • pages/library/gallery.html — library gallery
  • -
  • pages/motion/3d-explorer.html — 3D explorer
  • -
  • Full list: archive/showcase-2026-08/MANIFEST.md
  • -
-
-
+
+
+
+

Archive // frozen Showcase

+

The previous ~50 pages are kept, not served.

+

+ Snapshot: archive/showcase-2026-08/ in the repo (outside Vite and verify:contract). + Restore a page by copying it into apps/showcase/pages/ and adding it to the template registry. +

+

Paths below are archive-relative — not live routes. Prefer the 12-page live registry (Home → Core → Scenes) for public proof.

+
# restore a frozen page to live Showcase
+cp archive/showcase-2026-08/pages/scenes/scene-hero.html \
+   apps/showcase/pages/scenes/
+# then register in apps/showcase/vite.config + contract checklist
+

Archive index — open files from repo, not the dev server:

+
    +
  • index.html — cinematic home (pre-lean)
  • +
  • pages/scenes/scene-hero.html — hero scene recipe
  • +
  • pages/scenes/scene-story.html — pinned story
  • +
  • pages/scenes/scene-features.html — feature flow
  • +
  • pages/scenes/scene-creator.html — scene creator
  • +
  • pages/components/buttons.html — button kit
  • +
  • pages/components/forms.html — forms
  • +
  • pages/color/design-tokens.html — token reference
  • +
  • pages/typography/typography.html — type samples
  • +
  • pages/tools/architecture.html — architecture
  • +
  • pages/library/gallery.html — library gallery
  • +
  • pages/motion/3d-explorer.html — 3D explorer
  • +
  • Full list: archive/showcase-2026-08/MANIFEST.md
  • +
+ +
+
@@ -215,6 +225,17 @@

The previous ~50 pages are kept, not ser + +

@@ -205,6 +208,9 @@

Objective claims

Methodology (draft)

Before any FPS claim

+

Integration cost is measurable today; frame-time studies need the pinned scenario checklist below.

+
@import "@velora/css/motion-core";
+/* shipped pages: vl-* only — no animation runtime JS */
Required

Hardware profile, browser + version, display refresh rate, pinned scenario URL, scroll distance, warm-up passes, and trace export (Performance panel).

Compare

Same DOM scenario across Velora CSS, vanilla CSS, IntersectionObserver choreography and GSAP + ScrollTrigger — identical content, identical scroll length.

@@ -218,9 +224,11 @@

Before any FPS claim

Distribution + harness

Generated evidence

Metrics load from /data/benchmarks.json (built with @velora/css dist).

+

Reproduce locally: pnpm verify:contract && pnpm test:e2e — then inspect packages/css/dist/manifest.json for bundle sizes.

CDN snippet (after npm publish):

-
<link rel="stylesheet" href="…" />
-

Publish checklist: docs/project/PUBLISH.md · Run: pnpm verify:contract && pnpm test:e2e

+
<link rel="stylesheet" href="https://cdn.example/@velora/css/motion-core.css" />
+<!-- optional: @velora/css/theme for Skins -->
+

Publish checklist: docs/project/PUBLISH.md · Run: pnpm verify:contract && pnpm test:e2e

@@ -251,6 +259,17 @@

Generated evidence

+ +
@@ -219,7 +222,10 @@

The effect may disappear. The interface must not.

Reduced motion

Readable composed state always wins.

-

prefers-reduced-motion: reduce collapses travel, disables loops and scroll-linked animation. vl-motion="still" forces rest regardless of OS preference. No essential information is conveyed only through motion. Checklist: docs/validation/reduced-motion-checklist.md

+

prefers-reduced-motion: reduce collapses travel, disables loops and scroll-linked animation. vl-motion="still" forces rest regardless of OS preference. No essential information is conveyed only through motion. Checklist: docs/validation/reduced-motion-checklist.md

+
<section vl-motion="still">
+  <!-- loops off · scroll scrub off · hierarchy preserved -->
+</section>
@@ -259,6 +265,17 @@

Order without travel

+
+