vl-enter
+fade-up on view
+diff --git a/.cursor/hooks/state/continual-learning-index.json b/.cursor/hooks/state/continual-learning-index.json
index 34b2117..2571d5e 100644
--- a/.cursor/hooks/state/continual-learning-index.json
+++ b/.cursor/hooks/state/continual-learning-index.json
@@ -1,93 +1,157 @@
{
"version": 1,
"transcripts": {
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/18618295-bdf2-46e6-b561-c3a2775071d4.jsonl": {
- "mtimeMs": 1778184725699,
- "mtimeSec": 1778184725
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/ed1cd72f-1fd4-49a1-92ef-18325437cf3e/ed1cd72f-1fd4-49a1-92ef-18325437cf3e.jsonl": {
+ "mtimeMs": 1777753610368.671,
+ "mtimeSec": 1777753610
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/subagents/0f44cf87-7461-4845-9f03-338f5dc902fa.jsonl": {
- "mtimeMs": 1778184539161,
- "mtimeSec": 1778184539
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/5257044c-1ee3-4a8a-8bcc-e832c1098b75.jsonl": {
+ "mtimeMs": 1778186072210.1665,
+ "mtimeSec": 1778186072
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/subagents/3a538d77-2c55-4b14-9d9e-7cc1bc4a3e78.jsonl": {
- "mtimeMs": 1778184550203,
- "mtimeSec": 1778184550
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/71601be8-1824-4ff6-bff6-10979b3b9d1c.jsonl": {
+ "mtimeMs": 1778185707953.2952,
+ "mtimeSec": 1778185707
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/subagents/e7c111ca-606d-4a7c-b6ed-6f15fd594ad7.jsonl": {
- "mtimeMs": 1778184530873,
- "mtimeSec": 1778184530
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/5f1fbb68-c2a6-4653-bc18-39fd65714456.jsonl": {
+ "mtimeMs": 1778185807372.7222,
+ "mtimeSec": 1778185807
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/1a0acb75-2678-41d9-a434-61d34aa5db6e/1a0acb75-2678-41d9-a434-61d34aa5db6e.jsonl": {
- "mtimeMs": 1777455389418,
- "mtimeSec": 1777455389
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/3421c42d-30dc-4c0a-8862-3e7bef09293c.jsonl": {
+ "mtimeMs": 1778185139365.1401,
+ "mtimeSec": 1778185139
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/4784ec19-7f3e-4ce7-84bb-485c708340f8/4784ec19-7f3e-4ce7-84bb-485c708340f8.jsonl": {
- "mtimeMs": 1778189613797,
- "mtimeSec": 1778189613
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/8cdb1bbe-17af-4e99-99d4-753cd306a954.jsonl": {
+ "mtimeMs": 1778185806433.3477,
+ "mtimeSec": 1778185806
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/6607f714-8ca0-4f9c-8438-37e472087f00.jsonl": {
+ "mtimeMs": 1778186899932.858,
+ "mtimeSec": 1778186899
},
"/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/558abbbe-b072-48ec-82e2-2b80941fbe18/558abbbe-b072-48ec-82e2-2b80941fbe18.jsonl": {
- "mtimeMs": 1777456066747,
+ "mtimeMs": 1777456066747.9126,
"mtimeSec": 1777456066
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/6607f714-8ca0-4f9c-8438-37e472087f00.jsonl": {
- "mtimeMs": 1778186899932,
- "mtimeSec": 1778186899
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/9dbf47df-d1ff-4804-94ec-bcd6cc7a408f/9dbf47df-d1ff-4804-94ec-bcd6cc7a408f.jsonl": {
+ "mtimeMs": 1778167194799.754,
+ "mtimeSec": 1778167194
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/3421c42d-30dc-4c0a-8862-3e7bef09293c.jsonl": {
- "mtimeMs": 1778185139365,
- "mtimeSec": 1778185139
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/e4897ba2-a6c9-4962-a6eb-182c160d18da/e4897ba2-a6c9-4962-a6eb-182c160d18da.jsonl": {
+ "mtimeMs": 1787316784737.6025,
+ "mtimeSec": 1787316784
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/5257044c-1ee3-4a8a-8bcc-e832c1098b75.jsonl": {
- "mtimeMs": 1778186072210,
- "mtimeSec": 1778186072
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/1a0acb75-2678-41d9-a434-61d34aa5db6e/1a0acb75-2678-41d9-a434-61d34aa5db6e.jsonl": {
+ "mtimeMs": 1777455389418.547,
+ "mtimeSec": 1777455389
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/5f1fbb68-c2a6-4653-bc18-39fd65714456.jsonl": {
- "mtimeMs": 1778185807372,
- "mtimeSec": 1778185807
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/f189cdc9-ce87-420b-a111-cc8b68b661d2/subagents/bb3ca315-3e7b-4179-bc25-84887e4252d7.jsonl": {
+ "mtimeMs": 1787858041666.5142,
+ "mtimeSec": 1787858041
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/71601be8-1824-4ff6-bff6-10979b3b9d1c.jsonl": {
- "mtimeMs": 1778185707953,
- "mtimeSec": 1778185707
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/f189cdc9-ce87-420b-a111-cc8b68b661d2/subagents/23444bac-61e9-417f-a159-5b17c6f4bfb1.jsonl": {
+ "mtimeMs": 1787840620627.6536,
+ "mtimeSec": 1787840620
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6607f714-8ca0-4f9c-8438-37e472087f00/subagents/8cdb1bbe-17af-4e99-99d4-753cd306a954.jsonl": {
- "mtimeMs": 1778185806433,
- "mtimeSec": 1778185806
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/f189cdc9-ce87-420b-a111-cc8b68b661d2/f189cdc9-ce87-420b-a111-cc8b68b661d2.jsonl": {
+ "mtimeMs": 1787860483908.09,
+ "mtimeSec": 1787860483
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6a780cd3-579a-44fb-8723-104e3be1c936/6a780cd3-579a-44fb-8723-104e3be1c936.jsonl": {
- "mtimeMs": 1777826388114,
- "mtimeSec": 1777826388
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/4bd9dae4-d6bf-49bf-8e43-76c77fc4b56a/4bd9dae4-d6bf-49bf-8e43-76c77fc4b56a.jsonl": {
+ "mtimeMs": 1788108888571.2524,
+ "mtimeSec": 1788108888
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/70831a21-356c-4321-aab2-c64a327f61b0/70831a21-356c-4321-aab2-c64a327f61b0.jsonl": {
- "mtimeMs": 1787600611440,
- "mtimeSec": 1787600611
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/4bd9dae4-d6bf-49bf-8e43-76c77fc4b56a/subagents/5acf18e4-aebb-48d8-9119-ba8b99d96ff4.jsonl": {
+ "mtimeMs": 1788088034726.202,
+ "mtimeSec": 1788088034
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/4bd9dae4-d6bf-49bf-8e43-76c77fc4b56a/subagents/40f6206c-6a36-4701-b11a-98ac1302a332.jsonl": {
+ "mtimeMs": 1788087714970.336,
+ "mtimeSec": 1788087714
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/4bd9dae4-d6bf-49bf-8e43-76c77fc4b56a/subagents/1e9cc255-7eab-448a-a2c8-b973310eb608.jsonl": {
+ "mtimeMs": 1788107655785.1824,
+ "mtimeSec": 1788107655
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/4bd9dae4-d6bf-49bf-8e43-76c77fc4b56a/subagents/ef1b1794-65fe-4378-8364-20bcf45d5713.jsonl": {
+ "mtimeMs": 1788087712932.7095,
+ "mtimeSec": 1788087712
},
"/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/70831a21-356c-4321-aab2-c64a327f61b0/subagents/4392098b-6f8c-46d5-8890-ba2c6014af90.jsonl": {
- "mtimeMs": 1787600620087,
- "mtimeSec": 1787600620
+ "mtimeMs": 1787600653398.6538,
+ "mtimeSec": 1787600653
},
"/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/70831a21-356c-4321-aab2-c64a327f61b0/subagents/43e4de2c-1f21-4226-92bf-d7a8a53125ed.jsonl": {
- "mtimeMs": 1787582184721,
+ "mtimeMs": 1787582184721.024,
"mtimeSec": 1787582184
},
"/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/70831a21-356c-4321-aab2-c64a327f61b0/subagents/cd35309d-843b-4b29-b69d-cd2ac29eee1e.jsonl": {
- "mtimeMs": 1787572666994,
+ "mtimeMs": 1787572666994.6475,
"mtimeSec": 1787572666
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/9dbf47df-d1ff-4804-94ec-bcd6cc7a408f/9dbf47df-d1ff-4804-94ec-bcd6cc7a408f.jsonl": {
- "mtimeMs": 1778167194799,
- "mtimeSec": 1778167194
- },
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/e4897ba2-a6c9-4962-a6eb-182c160d18da/e4897ba2-a6c9-4962-a6eb-182c160d18da.jsonl": {
- "mtimeMs": 1787316784737,
- "mtimeSec": 1787316784
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/70831a21-356c-4321-aab2-c64a327f61b0/70831a21-356c-4321-aab2-c64a327f61b0.jsonl": {
+ "mtimeMs": 1787601340071.9563,
+ "mtimeSec": 1787601340
},
- "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/ed1cd72f-1fd4-49a1-92ef-18325437cf3e/ed1cd72f-1fd4-49a1-92ef-18325437cf3e.jsonl": {
- "mtimeMs": 1777753610368,
- "mtimeSec": 1777753610
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/6a780cd3-579a-44fb-8723-104e3be1c936/6a780cd3-579a-44fb-8723-104e3be1c936.jsonl": {
+ "mtimeMs": 1777826388114.6313,
+ "mtimeSec": 1777826388
},
"/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/f903c4dc-dba1-4bb0-bbc0-2916acc9cfb3/f903c4dc-dba1-4bb0-bbc0-2916acc9cfb3.jsonl": {
- "mtimeMs": 1777633041954,
+ "mtimeMs": 1777633041954.3208,
"mtimeSec": 1777633041
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/subagents/e7c111ca-606d-4a7c-b6ed-6f15fd594ad7.jsonl": {
+ "mtimeMs": 1778184530873.8816,
+ "mtimeSec": 1778184530
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/subagents/3a538d77-2c55-4b14-9d9e-7cc1bc4a3e78.jsonl": {
+ "mtimeMs": 1778184550203.0469,
+ "mtimeSec": 1778184550
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/subagents/0f44cf87-7461-4845-9f03-338f5dc902fa.jsonl": {
+ "mtimeMs": 1778184539161.252,
+ "mtimeSec": 1778184539
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/18618295-bdf2-46e6-b561-c3a2775071d4/18618295-bdf2-46e6-b561-c3a2775071d4.jsonl": {
+ "mtimeMs": 1778184725699.5852,
+ "mtimeSec": 1778184725
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/68448bf6-9ece-4087-a506-8f4c4a1a73a0/subagents/6e97d7a8-752f-4d93-9a3d-93f27764207f.jsonl": {
+ "mtimeMs": 1787838000625.8323,
+ "mtimeSec": 1787838000
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/68448bf6-9ece-4087-a506-8f4c4a1a73a0/subagents/74ee04ac-21a7-42ab-9ab4-87df0e8b0dc5.jsonl": {
+ "mtimeMs": 1787837937201.3765,
+ "mtimeSec": 1787837937
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/68448bf6-9ece-4087-a506-8f4c4a1a73a0/68448bf6-9ece-4087-a506-8f4c4a1a73a0.jsonl": {
+ "mtimeMs": 1787839699778.3687,
+ "mtimeSec": 1787839699
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/67747426-9b14-4a8f-b653-9fefecfaac9e/67747426-9b14-4a8f-b653-9fefecfaac9e.jsonl": {
+ "mtimeMs": 1787623893941.035,
+ "mtimeSec": 1787623893
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/4784ec19-7f3e-4ce7-84bb-485c708340f8/4784ec19-7f3e-4ce7-84bb-485c708340f8.jsonl": {
+ "mtimeMs": 1778189613797.1755,
+ "mtimeSec": 1778189613
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/a4b519cd-43c8-42b2-97d8-a59ab53ce6a5/subagents/1ee0945b-ef60-46f8-9f1c-ee37a300bc3d.jsonl": {
+ "mtimeMs": 1788109413532.8337,
+ "mtimeSec": 1788109413
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/a4b519cd-43c8-42b2-97d8-a59ab53ce6a5/subagents/0c31c8df-636a-4d1c-b7ff-61303a8f8ce2.jsonl": {
+ "mtimeMs": 1788178851242.3418,
+ "mtimeSec": 1788178851
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/a4b519cd-43c8-42b2-97d8-a59ab53ce6a5/subagents/2ca383e1-b55b-4dd0-9f75-2d24a92a0c3c.jsonl": {
+ "mtimeMs": 1788109429360.8635,
+ "mtimeSec": 1788109429
+ },
+ "/Users/erikassiscardoso/.cursor/projects/Users-erikassiscardoso-Local-Sites-velora/agent-transcripts/a4b519cd-43c8-42b2-97d8-a59ab53ce6a5/a4b519cd-43c8-42b2-97d8-a59ab53ce6a5.jsonl": {
+ "mtimeMs": 1788178836494.3508,
+ "mtimeSec": 1788178836
}
}
}
diff --git a/.cursor/hooks/state/continual-learning.json b/.cursor/hooks/state/continual-learning.json
index b53db30..f2b030c 100644
--- a/.cursor/hooks/state/continual-learning.json
+++ b/.cursor/hooks/state/continual-learning.json
@@ -1,8 +1,8 @@
{
"version": 1,
- "lastRunAtMs": 1787600605625,
- "turnsSinceLastRun": 1,
- "lastTranscriptMtimeMs": 1787600605478.6738,
- "lastProcessedGenerationId": "0851e63a-0490-413f-8bf7-d36a295dfdfc",
+ "lastRunAtMs": 1788178825203,
+ "turnsSinceLastRun": 6,
+ "lastTranscriptMtimeMs": 1788178825079.6353,
+ "lastProcessedGenerationId": "1d881672-78d7-49df-a1c5-450aec8c7c26",
"trialStartedAtMs": null
}
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/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 1db47d4..61ca100 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -1,5 +1,8 @@
name: Deploy Showcase to GitHub Pages
+# Fallback deploy for apps/showcase — primary production URL is https://veloracss.io (Vercel).
+# See docs/project/DEPLOY.md for DNS (GoDaddy) and docs.veloracss.io on a second Vercel project.
+
on:
push:
branches:
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
new file mode 100644
index 0000000..7a2e385
--- /dev/null
+++ b/.github/workflows/publish-npm.yml
@@ -0,0 +1,45 @@
+name: Publish @velora/css
+
+on:
+ workflow_dispatch:
+ push:
+ tags:
+ - "css-v*"
+
+permissions:
+ contents: read
+ id-token: write
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+
+ 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
+ registry-url: https://registry.npmjs.org
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Verify contract
+ run: pnpm verify:contract
+
+ - name: Build CSS dist
+ run: pnpm --filter @velora/css build
+
+ - name: Check dist
+ run: pnpm --filter @velora/css check:dist
+
+ - name: Publish to npm
+ working-directory: packages/css
+ run: npm publish --provenance --access public
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..16479c6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,25 +1,29 @@
## 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.
-- 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 evaluate-then-plan against `docs/AGENTS-VELORA.md` (P0/P1 public-readiness roadmap) and the showcase-readiness audit before large feature work or public/npm release; often proceed autonomously through roadmap items without pausing for approval; 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; copy must be practical show-don't-tell (state scroll/hover/tab actions, copyable snippets beside demos, metrics only from generated catalog)—not generic layouts or abstract marketing copy.
+- 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); avoid overused narrative hooks (e.g. "Release 07 / 72%") outside Home idx-clock and Skins—follow `docs/project/SHOWCASE_COPY_GUIDE.md`.
- 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.
-- 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.
+- 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 proves interactively (cinematic demos); Docs teach narratively and consume `@velora/catalog` at build—don't duplicate manual preset tables.
- 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).
-- 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).
-- 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.
+- 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).
+- Public deploy layout: Showcase `veloracss.io` + Docs `docs.veloracss.io` on Vercel; Hostinger VPS `examples.veloracss.io` for WordPress/host demos; GitHub Pages as showcase fallback; GoDaddy DNS only—`docs/project/DEPLOY.md`; canonical URLs in `apps/docs/src/lib/site.ts`.
+- `@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; defer publish until showcase and docs are ready (document honestly until P1.4).
+- Showcase contract registry: 12 live pages (primary + reference); lean restarts 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; local Showcase via `pnpm dev` (or `pnpm --filter showcase dev`); docs site via `pnpm dev:docs` (Astro, http://localhost:4321); common checks include `pnpm verify:contract`, showcase CSS drift checks, and `pnpm generate:catalog` / `pnpm check:catalog`.
+- Canonical GitHub remote: `https://github.com/erikconnect/Velora` (not legacy `velora-css/velora` URLs).
+- P0 observability infrastructure (showcase audit, attribute grammar, generated catalog, compatibility matrix, Playwright E2E) is largely complete; npm publish remains deferred.
+- 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; copy tone and page jobs in `docs/project/SHOWCASE_COPY_GUIDE.md`; 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.**

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
Same attributes on Tailwind or Velora UI. {hint}Zero-JS motion
+ Declarative motion
+ )}
+{code}
- This is the operational guide for AI coding agents working on Velora — including GitHub Copilot,
- Cursor, and similar tools. It defines the product thesis, non-negotiable rules, attribute system
- contract, and safe prompting patterns that all agents must follow.
+ Operational guide for AI agents on Velora. Demos on other pages use the same public
+ vl-* API — test there, do not invent showcase-only motion.
+ If the docs frame can replay it, it is a public Velora feature. +
+<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>
vl-scene"hero", "story", "features"vl-effect"fade-up", "flow-in", "scene-hero-reveal"vl-timeline"view", "scroll", "auto", "hover"vl-enter"fade-up", "clip-rise", "blur-in"vl-scroll"media-zoom", "parallax"vl-hover"hover-lift", "underline-expand"vl-effect"fade-up", "flow-in"vl-scenevl-stagevl-act / vl-spanvl-act="2", vl-span="3"vl-in-viewvl-timeline"view", "scroll", "auto", "hover"vl-range"entry 0% cover 40%"vl-pinvl-scrubvl-oncevl-pinvl-pin="3"vl-scrubvl-oncevl-children"stagger", "cascade"vl-stagger"fast", "slow", numericvl-depth"shallow", "deep"vl-speed"slow", "fast"vl-targetsvl-page-transition"fade", "slide", "morph"vl-transition"cover"vl-motion"standard", "cinematic", "still"vl-page-transition<html>"cinema", "wipe", "glide"This distinction is mandatory.
- A primitive effect affects the element itself: + A channel preset affects the element itself:
-<div vl-effect="fade-up"></div>
+ <div vl-enter="fade-up" vl-timeline="view"></div>
- A scene preset coordinates the element and its descendants: + A scene coordinates children on a shared clock:
-<section vl-scene="hero" vl-effect="scene-hero-reveal"></section>
+ <section vl-scene vl-timeline="view" vl-pin="3" vl-scrub>
+ <div vl-stage>
+ <h1 vl-enter="clip-rise" vl-act="1">Title</h1>
+ </div>
+</section>
Agents must not blur these concepts.
- 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
+ for new work. Each section includes a live frame — Test here.
| Attribute | Type | Brief |
|---|---|---|
vl-effect | enum | Named motion preset |
vl-timeline | enum | Animation progress model |
vl-range | enum | Scroll-driven animation range |
vl-scene | string | Scene container marker |
vl-children | enum | Child choreography mode |
vl-stagger | duration | Stagger interval override |
vl-depth | number | Motion distance multiplier |
vl-speed | enum | Duration category override |
vl-pin | boolean | Sticky positioning |
vl-scrub | boolean | Linear scroll-linked timing |
vl-once | boolean | One-shot animation |
vl-targets | selector | Descendant target hint |
vl-page-transition | enum | Cross-document page transition preset |
vl-transition | string | Shared element / local transition |
- Declares the named motion preset to apply to this element. See the - Effects reference for full - descriptions of each value. -
-| Category | Values |
|---|---|
| Entrance | fade-in fade-up fade-down slide-left slide-right scale-in blur-in reveal-3d flow-in clip-rise tilt-in |
| Interaction | hover-lift hover-glow underline-expand icon-shift |
| State | accordion drawer panel-swap |
| Continuous | shimmer float glow-breathe wobble orbit rock morph elastic-in |
| Text | text-reveal text-reveal-up text-reveal-down |
| Cinema | cinema-zoom parallax typewriter |
| Scene presets | scene-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>
-
-
- Controls when the animation defined by vl-effect plays. Without a timeline,
- effects play immediately on page load.
-
| Value | Mechanism | Use case |
|---|---|---|
view |
- animation-timeline: view(block) |
- Reveal as element scrolls into viewport. Most common scroll-driven pattern. | -
scroll |
- animation-timeline: scroll(root) |
- Progress tied to root scroll position (0% = top, 100% = bottom). | -
auto |
- animation-timeline: auto |
- Browser default (time-based). Lets you use a custom timeline via CSS. | -
hover |
- Pauses animation, runs on :hover |
- Play 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>
-
-
- Sets the animation-range for scroll-driven effects. Only effective when used with
- vl-timeline="view" or vl-timeline="scroll".
-
| Value | animation-range | Feel |
|---|---|---|
entry | entry 0% entry 100% | Plays only while entering the viewport |
entry-short | entry 15% cover 35% | Quick reveal in the upper viewport zone |
entry-long | entry 0% cover 70% | Extended reveal range — default for scenes |
cover | cover 0% cover 100% | Plays the full time element covers the viewport |
contain | contain 0% contain 100% | Plays while element is fully contained in viewport |
custom | var(--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>
-
- - Marks a container as a scene host. Applies CSS containment and enables scene-specific - child choreography. Value is a semantic label. -
-| Value | Intent |
|---|---|
hero | Full-width hero section with primary CTA |
story | Narrative / storytelling pin section |
features | Feature grid or card collection |
| any string | Custom 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>
-
- - Applies staggered animations to all direct children. Do not combine with a scene preset — use one - or the other. -
-| Value | Effect | Max children |
|---|---|---|
stagger | fade-up with incremental delays | 12 |
cascade | flow-in with incremental delays | 8 |
sequence | fade-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>
-
-
- Overrides the default stagger step (--vl-stagger-step: 45ms) for vl-children.
-
| Value | Delay per child |
|---|---|
60ms | 60ms |
80ms | 80ms |
100ms | 100ms |
120ms | 120ms |
150ms | 150ms |
200ms | 200ms |
fade-up on view
+Hover this card
+
- 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.
+ Temporal gate — descendant motion replays when the gate re-enters the scrollport.
+ Requires container-type: style (Chrome 128+, Safari 18+); otherwise
+ vl-in-view is inert and children animate on their own timelines.
| Value | --vl-motion-distance |
|---|---|
1 | 0.75rem — subtle |
2 | 1.5rem — moderate |
3 | 2.5rem — pronounced |
4 | 4rem — dramatic |
<div vl-effect="fade-up" vl-depth="4">Deep dramatic reveal</div>
-
- Overrides the animation duration category:
-| Value | Duration |
|---|---|
fast | 150ms |
normal | 300ms |
slow | 500ms |
<div vl-effect="scale-in" vl-speed="fast">Quick snap in</div>
+ …
'}> +Replay on re-entry
++ Scroll away, scroll back, or press Replay +
+
- Boolean attribute. Makes the element sticky (position: sticky; top: 0) at
- z-index: 200. Use on scene sections that should remain fixed during scroll.
-
<section vl-scene="hero" vl-pin>
- <!-- Stays at top of viewport while parent scrolls -->
-</section>
-
-
- 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>
-
-
- 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>
-
- - 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>
-
-
- Set on <html> to select the cross-document page transition preset.
- See Page Transitions for full details.
-
| Value | Effect | |
|---|---|---|
| Attribute | Type | Brief |
| (omitted) | Default Velora signature: blur + scale | |
wipe | Clip-path curtain wipe | |
glide | Horizontal slide | |
iris | Circular iris open/close | |
cinema | Dramatic blur + brightness + clip | |
snap | Quick spring-based snap | |
vl-enter | tokens | Entrance preset (preferred) |
vl-exit | tokens | Exit preset |
vl-scroll | tokens | Scroll-linked motion |
vl-hover | tokens | Hover / focus interaction |
vl-effect | tokens | Legacy / composite escape hatch |
vl-timeline | enum | view, scroll, auto, hover, state |
vl-in-view | boolean | Viewport gate for descendant motion |
vl-scene / vl-stage | track + stage | Scene engine |
vl-act / vl-span | number | Scene beat index and span |
vl-pin / vl-scrub | number / boolean | Pin+scrub track |
vl-motion | enum | Subtree motion mode |
vl-page-transition | enum | MPA transition on <html> |
<html lang="en" vl-page-transition="cinema">
-
- - 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>
-
- 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>
+ Scroll-linked child
+
+ Support levels come from packages/catalog/compatibility-matrix.json — generated with the preset registry ({generatedLabel}), not marketing copy.
+
Registry · {summary.presetCount} presets
++ Baseline targets: Chrome {matrix.baseline.chrome}, Safari {matrix.baseline.safari}, Firefox {matrix.baseline.firefox}. + {matrix.baseline.note} +
+Reduced motion safe
++ Enable Reduce motion in OS settings — travel collapses, loops stop +
+| Level | Meaning |
|---|---|
| {level} | +{meaning} | +
Primitive × browser status from the generated matrix. Interactive version lives on the Showcase.
+| Primitive | Status | Chrome | Safari | Firefox | Fallback |
|---|---|---|---|---|---|
| {row.name} | +{row.status} | +{row.chrome} | +{row.safari} | +{row.firefox} | +{row.fallback} | +
- 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.
Generated registry · {generatedLabel}
++ {Object.entries(summary.byChannel) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([channel, count]) => `${formatChannelLabel(channel)} (${count})`) + .join(" · ")} +
+
+ Full searchable registry:
+ Showcase catalog
+ · source packages/catalog/ via pnpm generate:catalog
+
Channel-first
++ Prefer vl-enter, vl-scroll, vl-hover — not legacy vl-effect +
+
- 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>
- 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.
| Value | Description | Default duration |
|---|---|---|
| Value | Status | Reduced motion |
fade-in |
- Simple opacity 0 → 1 entrance | ---vl-duration-slow (500ms) |
-
fade-up |
- Opacity + translateY upward. Workhorse reveal effect. | -500ms | -
fade-down |
- Opacity + translateY downward. Good for dropdowns and menus. | -500ms | -
slide-left |
- Opacity + slide in from the right. Uses --vl-ease-out-soft. |
- 500ms | -
slide-right |
- Opacity + slide in from the left. Uses --vl-ease-out-soft. |
- 500ms | -
scale-in |
- Scales from 0.94 with a subtle upward nudge. Uses spring easing. | -500ms | -
blur-in |
- Cinematic blur (14px) dissolve entrance. Dramatic and modern. | ---vl-duration-slower (800ms) |
-
reveal-3d |
- Perspective rotateX entry — page flip feel. | -500ms | -
flow-in |
- Blur + translate + scale — cinematic combination. Premium look. | -800ms | -
clip-rise |
- Clip-path reveal from bottom. Text feels like it emerges from the page. | -800ms | -
tilt-in |
- Perspective rotateY entry. Great for cards arriving from the side. | -800ms | -
{preset.name} |
+ {preset.status} | +{preset.reducedMotion ?? "static"} | +
Opacity + translate Y.
-Cinematic blur dissolve.
+Opacity + translate Y.
+Cinematic blur dissolve.
+Perspective rotateX entry.
+Perspective rotateX entry.
-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.
Lift + shadow
+Primary glow ring
+| Value | Trigger | Description |
|---|---|---|
| Value | Status | Reduced motion |
hover-lift |
- :hover |
- Translates up 4px + floating shadow. Classic card hover. | -
hover-glow |
- :hover |
- Adds a primary-colored glow shadow ring. Great for media embeds. | -
underline-expand |
- :hover |
- Expanding underline grows from center. Elegant nav link effect. | -
icon-shift |
- :hover |
- Last child (icon/arrow) shifts 4px on hover. Use on inline elements with an icon sibling. | -
{preset.name} |
+ {preset.status} | +{preset.reducedMotion ?? "static"} | +
@media (hover: none).
+ vl-scroll)
+ Scroll-linked transforms. {scrollPresets.length} presets — usually paired with vl-timeline="view" unless the preset is inherently scroll-driven.
+
| Value | Status | Reduced motion |
|---|---|---|
{preset.name} |
+ {preset.status} | +{preset.reducedMotion ?? "static"} | +
vl-loop / vl-loop-effect)
+ Continuous ambient motion. {loopPresets.length} presets — loops stop under prefers-reduced-motion.
+
| Value | Status | Reduced motion |
|---|---|---|
{preset.name} |
+ {preset.status} | +{preset.reducedMotion ?? "static"} | +
Used with toggled elements like accordions, drawers, and panels. Typically paired with @@ -209,26 +245,8 @@ import Base from "../layouts/Base.astro";
- 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.
| Value | Loop duration | Description |
|---|---|---|
shimmer | 2.5s | Background shimmer sweep. Use on skeleton loaders or accent lines. Requires background-size: 200% auto and a gradient background. |
float | 5.5s | Gentle vertical bob. Great for hero illustrations. |
glow-breathe | 3.5s | Pulsing box-shadow. Use on live indicators or accent dots. |
wobble | 2.8s | Tilt oscillation (±3deg). Attention-grabbing but subtle. |
orbit | 14s | Circular orbit — full 360deg spin. For orbital icons or decorative rings. |
rock | 4s | Wider tilt oscillation (±6deg). More playful than wobble. |
morph | 8s | Organic border-radius morphing. Use on blobs and abstract shapes. |
elastic-in | 0.7s | Springy 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 */
-
- 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/.
@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 661a47a..8b2cb6c 100644
--- a/apps/docs/src/pages/index.astro
+++ b/apps/docs/src/pages/index.astro
@@ -1,55 +1,87 @@
---
import Base from "../layouts/Base.astro";
+import DocsLive from "../components/DocsLive.astro";
+import { SITE, showcaseUrl } from "../lib/site";
---
-
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.
Install the package from the npm registry:
-pnpm add @velora/css
-# or
-npm install @velora/css
+ Declarative motion
++ Same public API as Showcase — test in the frame, copy into your project. +
+| Import | Use when |
|---|---|
@velora/css/motion-core |
- Host UI already provides look (Tailwind, custom DS). Motion + scene engine only. | -
@velora/css/theme |
- Optional Velora visual tokens + named scene recipes. | -
@velora/css / full |
- Convenience: theme + components + motion (Showcase / docs style). | -
Channels v2 grammar and scene engine controls.
+Track, stage, acts — pin and scrub without JS.
+Stable vs experimental — observable truth layer.
+/* Host-agnostic (recommended for Tailwind projects) */
-@import "@velora/css/motion-core";
-@import "@velora/css/transitions"; /* optional */
+ Installation
+
+ @velora/css is developed in this monorepo. It is not published to npm yet —
+ we are finishing Showcase and documentation before release.
+
+ # After npm publish (coming soon)
+pnpm add @velora/css
-/* Or full Velora skin */
-@import "@velora/css";
+# Today: monorepo workspace or local pack
+pnpm --filter @velora/css build
+pnpm pack:css
+ + Canonical spec: + Attribute grammar + · Live catalog: + Motion catalog + · Playground: + Motion Playground +
Track + stage + acts: one shared clock, pin, scrub — no JavaScript.
+Track + stage + acts: one shared clock, pin, scrub — no animation runtime JavaScript.
+ +Act 1
+Scrub progress in the frame
+<section vl-scene vl-timeline="view" vl-pin="3" vl-scrub>
<div vl-stage>
<p vl-enter="fade-up" vl-act="1">Kicker</p>
@@ -58,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.
-
motion-core without Skins
- 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.
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>
- <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>
@@ -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
Attribute Role
- 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 66ad0ae..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,18 +8,24 @@ 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
- 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 +70,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/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
Value Behavior
- 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 4193a24..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:
@@ -279,7 +292,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/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.
+
+
+
+ → Scenes
+
+
+ → Tokens
+
+
+
+
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/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..245ac40 100644
--- a/apps/showcase/index.html
+++ b/apps/showcase/index.html
@@ -21,7 +21,7 @@
-
+
Skip to content
Act 01 · Core directs · Skins give voice · Showcase proves
+Act 01 · declare intent in HTML
- Declare intent in semantic HTML. Modern CSS orchestrates depth, type, scroll, state and handoffs.
- Change the Skin — not the product structure — to create an entirely new visual world.
+ Add vl-* attributes to semantic HTML. CSS maps intent to view timelines, scroll progress and native state — no animation runtime JavaScript.
+ Hover the stage. Scroll the acts below. Copy the snippets.
Try the header: switch Skin, theme, or page transition — then navigate. Shared elements persist across routes.
Act 02 · one shared clock
-Five beats, one release decision. The Core controls time; the Skin controls atmosphere.
+Act 02 · pin + scrub teaching clock
+Scroll slowly through this section. The stage pins; each card is a beat on the shared CSS clock (vl-scene + vl-act).
Context · Release 07
-Evidence arrives second: 72% team alignment.
-The evidence holds while the stage stays pinned and the story advances.
-The decision lands last: ship the cinematic voice.
+Beat 01 · frame
+vl-pin="2" holds the viewport while scroll advances --vl-scene.
Beat 03 · cue — descendants use vl-act / vl-enter, not nested view gates on the stage.
Beat 05 · cut — open the full scene timeline with five acts.
- Channel: enter. Type scale and reveal are engine; the font stack is a Skin. Copy from Catalog.
- See catalog.
+ Scroll this section — vl-enter="clip-rise" + vl-range tune the view window; vl-effect="gradient-text-color" on the hero word.
+ Catalog → enter.
- The attributes below are not decorative samples. Together they stage discovery, evidence, interaction and atmosphere — directly in semantic HTML. + Four live demos below — scroll into each, hover the fan, watch the grid wave. Every attribute is copyable from the Catalog.
+Interact first: path follows scroll · fan responds to hover · grid-wave orchestrates children · loops respect reduced motion.
Hover each card — preview uses the attribute on the chip below.
Scene 01
+Scene 01 · pin+scrub
Launch-value surfaces with staged copy, brand media, CTAs, and immediate narrative focus.
- vl-scene="cinematic-hero" +Pin the stage, scrub acts — vl-act choreographs title, evidence and handoff on one view clock.