diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3dda05b9..3e626fab 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,7 +23,6 @@ concurrency:
env:
CARGO_TERM_COLOR: always
- HURL_VERSION: 8.0.1
REGISTRY_USERNAME: lovasoa
REGISTRY_IMAGE: lovasoa/sqlpage
@@ -412,36 +411,6 @@ jobs:
PATHS_OUTCOME: ${{ steps.paths.outcome }}
ALL_EXAMPLES: ${{ steps.paths.outputs.all_examples }}
MATCHED_EXAMPLES: ${{ steps.paths.outputs.changes }}
- - name: Restore Hurl archive
- id: hurl-cache
- if: steps.examples.outputs.examples != '[]'
- uses: actions/cache@v6
- with:
- path: .cache/hurl
- key: hurl-${{ runner.os }}-x86_64-unknown-linux-gnu-${{ env.HURL_VERSION }}
- - name: Download Hurl archive
- if: steps.examples.outputs.examples != '[]' && steps.hurl-cache.outputs.cache-hit != 'true'
- run: |
- set -euo pipefail
- archive="hurl-${HURL_VERSION}-x86_64-unknown-linux-gnu.tar.gz"
- base_url="https://github.com/Orange-OpenSource/hurl/releases/download/${HURL_VERSION}"
- mkdir -p .cache/hurl
- curl --fail --location --retry 5 --retry-delay 2 --output ".cache/hurl/${archive}" "${base_url}/${archive}"
- curl --fail --location --retry 5 --retry-delay 2 --output ".cache/hurl/${archive}.sha256" "${base_url}/${archive}.sha256"
- (
- cd .cache/hurl
- expected_sha="$(tr -d '[:space:]' < "${archive}.sha256")"
- actual_sha="$(sha256sum "$archive" | cut -d ' ' -f 1)"
- test "$expected_sha" = "$actual_sha"
- )
- - name: Upload Hurl archive
- if: steps.examples.outputs.examples != '[]'
- uses: actions/upload-artifact@v7
- with:
- name: hurl-${{ env.HURL_VERSION }}-x86_64-unknown-linux-gnu
- path: .cache/hurl/*
- if-no-files-found: error
- retention-days: 1
hurl:
name: hurl (${{ matrix.example }})
@@ -457,23 +426,12 @@ jobs:
example: ${{ fromJSON(needs.hurl_examples.outputs.examples) }}
steps:
- uses: actions/checkout@v7
- - name: Download Hurl archive
- uses: actions/download-artifact@v8
- with:
- name: hurl-${{ env.HURL_VERSION }}-x86_64-unknown-linux-gnu
- path: ${{ runner.temp }}/hurl
- name: Install Hurl
+ # Use Ubuntu's build so apt resolves the matching libcurl/libxml2 ABI.
run: |
- set -euo pipefail
- archive="hurl-${HURL_VERSION}-x86_64-unknown-linux-gnu.tar.gz"
- (
- cd "$RUNNER_TEMP/hurl"
- expected_sha="$(tr -d '[:space:]' < "${archive}.sha256")"
- actual_sha="$(sha256sum "$archive" | cut -d ' ' -f 1)"
- test "$expected_sha" = "$actual_sha"
- tar xzf "$archive" -C "$RUNNER_TEMP/hurl"
- )
- echo "$RUNNER_TEMP/hurl/hurl-${HURL_VERSION}-x86_64-unknown-linux-gnu/bin" >> "$GITHUB_PATH"
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends hurl
+ hurl --version
- name: Download SQLPage image
uses: actions/download-artifact@v8
with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1c98381..e4447522 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## unreleased
+- Refined the official website with muted blue accents, simpler navigation, a clearer primary action, and a level SQL ribbon with vertical separators, and a grounded studio scene with brushed metal and physical reflections. Restored visible progress in the streaming comparison animation.
+
- Restored parameter reuse for repeated variables on SQL Server, PostgreSQL, and SQLite, fixing SQL Server and PostgreSQL queries that repeat a variable-dependent expression in `SELECT` and `GROUP BY`.
## v0.47.0 (2026-10-15)
diff --git a/examples/official-site/assets/brand/brand.css b/examples/official-site/assets/brand/brand.css
index 9425a715..a3309cbe 100644
--- a/examples/official-site/assets/brand/brand.css
+++ b/examples/official-site/assets/brand/brand.css
@@ -5,14 +5,100 @@
font-weight: 100 900;
font-display: swap;
}
-:root {
- --brand-ink: #080e11;
- --brand-graphite: #18262d;
- --brand-paper: #f4f7f8;
- --brand-cyan: #58cce0;
- --brand-teal: #146575;
- --brand-steel: #9caeb5;
- --brand-font: Outfit, system-ui, sans-serif;
- --brand-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
- --tblr-font-sans-serif: var(--brand-font);
+/* Official-site overrides use Tabler's existing theme properties directly.
+ Both the standalone landing page and documentation import this file. */
+:root,
+.layout-boxed[data-bs-theme="dark"] {
+ color-scheme: dark;
+ --tblr-font-sans-serif: Outfit, system-ui, sans-serif;
+ --tblr-font-monospace:
+ "SFMono-Regular", Consolas, "Liberation Mono", monospace;
+ /* Native Tabler utility colors also serve component foregrounds. Keep their
+ defaults; use the surface properties below for dark page backgrounds. */
+ --tblr-black: #000;
+ --tblr-white: #fff;
+ --tblr-light: #f9fafb;
+ --tblr-dark: #1f2937;
+ --tblr-gray-300: #d1d5db;
+ --tblr-primary-fg: var(--tblr-white);
+
+ /* Core colors refined for better contrast */
+ --tblr-body-color: hsl(225deg 35% 86%);
+ --tblr-secondary-color: hsl(225, 15%, 80%);
+ --tblr-muted-color: hsla(225, 15%, 75%, 0.8);
+
+ /* Background system */
+ --tblr-body-bg: hsl(225deg 44% 9%);
+ --tblr-bg-surface: hsl(225, 47%, 10%);
+ --tblr-bg-surface-secondary: hsl(225, 47%, 12%);
+ --tblr-bg-surface-tertiary: hsl(225, 47%, 14%);
+
+ /* Border colors */
+ --tblr-border-color: hsl(225deg, 26%, 19%);
+ --tblr-border-color-translucent: hsla(225deg 27% 19% / 0.7);
+
+ /* Text secondary RGB */
+ --tblr-text-secondary-rgb:
+ 204, 209, 217; /* RGB equivalent of hsl(225, 15%, 80%) */
+
+ /* Code colors */
+ --tblr-code-color: hsl(
+ 225deg 45.4% 76.93%
+ ); /* Light code text for dark theme */
+ --tblr-code-bg: hsla(225, 47%, 15%, 0.5); /* Subtle dark background */
+ --tblr-active-bg: var(--tblr-code-bg);
+
+ /* Ethereal accent colors */
+ --tblr-blue-rgb: 84, 151, 213;
+ --tblr-blue: rgb(var(--tblr-blue-rgb));
+ --tblr-blue-lt-rgb: 21, 31, 53;
+ --tblr-blue-lt: rgb(var(--tblr-blue-lt-rgb));
+ --tblr-primary-rgb: 139, 184, 232;
+ --tblr-primary: rgb(var(--tblr-primary-rgb));
+ --tblr-secondary: hsla(247, 60%, 94%, 0.7); /* Nebula purple */
+
+ /* Status + accent colors aligned with the brand palette */
+ --tblr-success-rgb: 16, 132, 86; /* Deep green for white text */
+ --tblr-success: rgb(var(--tblr-success-rgb));
+ --tblr-warning-rgb: 197, 124, 0; /* Deep amber */
+ --tblr-warning: rgb(var(--tblr-warning-rgb));
+ --tblr-danger-rgb: 196, 68, 68; /* Deep crimson */
+ --tblr-danger: rgb(var(--tblr-danger-rgb));
+ --tblr-purple-rgb: 118, 82, 200; /* Deep violet */
+ --tblr-purple: rgb(var(--tblr-purple-rgb));
+ --tblr-cyan-rgb: 0, 149, 168; /* Deep cyan */
+ --tblr-cyan: rgb(var(--tblr-cyan-rgb));
+ --tblr-indigo-rgb: 96, 113, 215; /* Deep indigo */
+ --tblr-indigo: rgb(var(--tblr-indigo-rgb));
+ --tblr-teal: #267d63;
+ --tblr-teal-fg: #20050b;
+
+ --tblr-green: var(--tblr-success);
+ --tblr-yellow: var(--tblr-warning);
+ --tblr-red: var(--tblr-danger);
+
+ /* Luminous links */
+ --tblr-link-color: hsl(212, 70%, 75%); /* Star glow */
+ --tblr-link-hover-color: hsl(212, 70%, 85%); /* Supernova */
+ --tblr-carousel-caption-color: var(--tblr-muted-color);
+
+ /* Ethereal shadows */
+ --tblr-box-shadow:
+ 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 15px rgba(66, 153, 225, 0.15);
+ --tblr-box-shadow-dropdown:
+ 0px 8px 24px rgba(0, 0, 0, 0.25), 1px 9px 20px rgba(174, 62, 201, 0.15);
+
+ /* Pure white emphasis */
+ --tblr-emphasis-color: #ffffff;
+ --tblr-heading-color: #ffffff;
+
+ /* Syntax highlighting colors */
+ --hljs-cosmic-comment: hsl(290, 10%, 60%); /* Distant star */
+ --hljs-cosmic-punctuation: hsl(60, 40%, 85%); /* Stardust */
+ --hljs-cosmic-property: hsl(340, 60%, 65%); /* Red giant */
+ --hljs-cosmic-number: hsl(0, 50%, 75%); /* Solar flare */
+ --hljs-cosmic-boolean: hsl(270, 100%, 75%); /* Purple nebula */
+ --hljs-cosmic-string: hsl(120, 40%, 75%); /* Green aurora */
+ --hljs-cosmic-operator: hsl(0, 0%, 95%); /* White dwarf */
+ --hljs-cosmic-keyword: hsl(210, 100%, 75%); /* Blue giant */
}
diff --git a/examples/official-site/assets/brand/logo-horizontal-light.svg b/examples/official-site/assets/brand/logo-horizontal-light.svg
index 45132597..8923e69c 100644
--- a/examples/official-site/assets/brand/logo-horizontal-light.svg
+++ b/examples/official-site/assets/brand/logo-horizontal-light.svg
@@ -1,11 +1,11 @@
diff --git a/examples/official-site/assets/highlightjs-and-tabler-theme.css b/examples/official-site/assets/highlightjs-and-tabler-theme.css
index 231b903d..db0fdd00 100644
--- a/examples/official-site/assets/highlightjs-and-tabler-theme.css
+++ b/examples/official-site/assets/highlightjs-and-tabler-theme.css
@@ -1,91 +1,6 @@
@charset "utf-8";
@import url("./brand/brand.css");
-:root,
-.layout-boxed[data-bs-theme="dark"] {
- color-scheme: dark;
-
- /* Core colors refined for better contrast */
- --tblr-body-color: hsl(225deg 35% 86%);
- --tblr-secondary-color: hsl(225, 15%, 80%);
- --tblr-muted-color: hsla(225, 15%, 75%, 0.8);
-
- /* Background system */
- --tblr-body-bg: hsl(225deg 44% 9%);
- --tblr-bg-surface: hsl(225, 47%, 10%);
- --tblr-bg-surface-secondary: hsl(225, 47%, 12%);
- --tblr-bg-surface-tertiary: hsl(225, 47%, 14%);
-
- /* Border colors */
- --tblr-border-color: hsl(225deg, 26%, 19%);
- --tblr-border-color-translucent: hsla(225deg 27% 19% / 0.7);
-
- /* Text secondary RGB */
- --tblr-text-secondary-rgb:
- 204, 209, 217; /* RGB equivalent of hsl(225, 15%, 80%) */
-
- /* Code colors */
- --tblr-code-color: hsl(
- 225deg 45.4% 76.93%
- ); /* Light code text for dark theme */
- --tblr-code-bg: hsla(225, 47%, 15%, 0.5); /* Subtle dark background */
- --tblr-active-bg: var(--tblr-code-bg);
-
- /* Ethereal accent colors */
- --tblr-blue-rgb: 84, 151, 213;
- --tblr-blue: rgb(var(--tblr-blue-rgb));
- --tblr-blue-lt-rgb: 21, 31, 53;
- --tblr-blue-lt: rgb(var(--tblr-blue-lt-rgb));
- --tblr-primary-rgb: 95, 132, 169;
- --tblr-primary: rgb(var(--tblr-primary-rgb));
- --tblr-secondary: hsla(247, 60%, 94%, 0.7); /* Nebula purple */
-
- /* Status + accent colors aligned with the brand palette */
- --tblr-success-rgb: 16, 132, 86; /* Deep green for white text */
- --tblr-success: rgb(var(--tblr-success-rgb));
- --tblr-warning-rgb: 197, 124, 0; /* Deep amber */
- --tblr-warning: rgb(var(--tblr-warning-rgb));
- --tblr-danger-rgb: 196, 68, 68; /* Deep crimson */
- --tblr-danger: rgb(var(--tblr-danger-rgb));
- --tblr-purple-rgb: 118, 82, 200; /* Deep violet */
- --tblr-purple: rgb(var(--tblr-purple-rgb));
- --tblr-cyan-rgb: 0, 149, 168; /* Deep cyan */
- --tblr-cyan: rgb(var(--tblr-cyan-rgb));
- --tblr-indigo-rgb: 96, 113, 215; /* Deep indigo */
- --tblr-indigo: rgb(var(--tblr-indigo-rgb));
- --tblr-teal: #267d63;
- --tblr-teal-fg: #20050b;
-
- --tblr-green: var(--tblr-success);
- --tblr-yellow: var(--tblr-warning);
- --tblr-red: var(--tblr-danger);
-
- /* Luminous links */
- --tblr-link-color: hsl(212, 70%, 75%); /* Star glow */
- --tblr-link-hover-color: hsl(212, 70%, 85%); /* Supernova */
- --tblr-carousel-caption-color: var(--tblr-muted-color);
-
- /* Ethereal shadows */
- --tblr-box-shadow:
- 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 15px rgba(66, 153, 225, 0.15);
- --tblr-box-shadow-dropdown:
- 0px 8px 24px rgba(0, 0, 0, 0.25), 1px 9px 20px rgba(174, 62, 201, 0.15);
-
- /* Pure white emphasis */
- --tblr-emphasis-color: #ffffff;
- --tblr-heading-color: #ffffff;
-
- /* Syntax highlighting colors */
- --hljs-cosmic-comment: hsl(290, 10%, 60%); /* Distant star */
- --hljs-cosmic-punctuation: hsl(60, 40%, 85%); /* Stardust */
- --hljs-cosmic-property: hsl(340, 60%, 65%); /* Red giant */
- --hljs-cosmic-number: hsl(0, 50%, 75%); /* Solar flare */
- --hljs-cosmic-boolean: hsl(270, 100%, 75%); /* Purple nebula */
- --hljs-cosmic-string: hsl(120, 40%, 75%); /* Green aurora */
- --hljs-cosmic-operator: hsl(0, 0%, 95%); /* White dwarf */
- --hljs-cosmic-keyword: hsl(210, 100%, 75%); /* Blue giant */
-}
-
.btn-primary {
--tblr-btn-border-color: var(--tblr-secondary);
--tblr-btn-active-border-color: transparent;
@@ -249,11 +164,8 @@ pre:has(code) {
}
/* Self-hosted official-site typography; retain the established documentation theme. */
-:root {
- --tblr-font-sans-serif: var(--brand-font);
-}
.layout-boxed {
- font-family: var(--brand-font);
+ font-family: var(--tblr-font-sans-serif);
}
.navbar-brand {
font-weight: 600;
diff --git a/examples/official-site/assets/landing/assets/scene-first-frame.webp b/examples/official-site/assets/landing/assets/scene-first-frame.webp
index f6834229..9d3acb64 100644
Binary files a/examples/official-site/assets/landing/assets/scene-first-frame.webp and b/examples/official-site/assets/landing/assets/scene-first-frame.webp differ
diff --git a/examples/official-site/assets/landing/assets/sculpture-turns.avif b/examples/official-site/assets/landing/assets/sculpture-turns.avif
index 83c8a59e..e95bac6e 100644
Binary files a/examples/official-site/assets/landing/assets/sculpture-turns.avif and b/examples/official-site/assets/landing/assets/sculpture-turns.avif differ
diff --git a/examples/official-site/assets/landing/js/comparison.js b/examples/official-site/assets/landing/js/comparison.js
index 507037d8..f1135526 100644
--- a/examples/official-site/assets/landing/js/comparison.js
+++ b/examples/official-site/assets/landing/js/comparison.js
@@ -7,6 +7,7 @@ export function initComparison(root, signal) {
const next = section.querySelector("[data-comparison-next]");
const status = section.querySelector("[data-comparison-status]");
const motion = matchMedia("(prefers-reduced-motion: reduce)");
+ let destination = null;
function update() {
const offsets = cards.map((card) => card.offsetLeft);
@@ -27,8 +28,12 @@ export function initComparison(root, signal) {
}
function goTo(left) {
+ destination = Math.max(
+ 0,
+ Math.min(left, track.scrollWidth - track.clientWidth),
+ );
track.scrollTo({
- left,
+ left: destination,
behavior: motion.matches ? "instant" : "smooth",
});
}
@@ -36,16 +41,28 @@ export function initComparison(root, signal) {
function move(direction) {
// Equal-width grid columns snap one card at a time; native scrolling clamps the ends.
const step = cards[1].offsetLeft;
+ // Repeated clicks advance from the requested card, even while a previous
+ // smooth scroll is still exposing a partial card and updating the status.
const index =
- direction > 0
- ? Math.floor((track.scrollLeft + 2) / step) + 1
- : Math.ceil((track.scrollLeft - 2) / step) - 1;
+ Math.round((destination ?? track.scrollLeft) / step) + direction;
goTo(index * step);
}
previous.addEventListener("click", () => move(-1), { signal });
next.addEventListener("click", () => move(1), { signal });
track.addEventListener("scroll", update, { passive: true, signal });
+ const followNativeScroll = () => {
+ destination = null;
+ };
+ track.addEventListener("scrollend", followNativeScroll, { signal });
+ track.addEventListener("pointerdown", followNativeScroll, {
+ passive: true,
+ signal,
+ });
+ track.addEventListener("wheel", followNativeScroll, {
+ passive: true,
+ signal,
+ });
const keys = {
ArrowLeft: () => move(-1),
ArrowRight: () => move(1),
@@ -61,7 +78,10 @@ export function initComparison(root, signal) {
},
{ signal },
);
- const observer = new ResizeObserver(update);
+ const observer = new ResizeObserver(() => {
+ destination = null;
+ update();
+ });
observer.observe(track);
section.querySelector(".comparison-controls").hidden = false;
update();
diff --git a/examples/official-site/assets/landing/js/scene/database-scene.js b/examples/official-site/assets/landing/js/scene/database-scene.js
index f1380eaa..93672cbf 100644
--- a/examples/official-site/assets/landing/js/scene/database-scene.js
+++ b/examples/official-site/assets/landing/js/scene/database-scene.js
@@ -1,14 +1,14 @@
/**
* Owns one square renderer and its resource lifetime. Scroll supplies placement;
- * controls supply local rotation. Materials, decoration and glow stay independent.
+ * controls supply local rotation. Materials, studio surfaces and glow stay independent.
*/
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.186.1/+esm";
+import { RectAreaLightUniformsLib } from "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/lights/RectAreaLightUniformsLib.js/+esm";
import { GLTFLoader } from "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/loaders/GLTFLoader.js/+esm";
import { createFiniteJewelLight } from "./finite-jewel-light.js";
import { JEWEL_EDGES } from "./jewel-edges.js";
import { MODEL_URL } from "./sculpture-assets.js";
import { createSculptureControls } from "./sculpture-controls.js";
-import { createSculptureDecoration } from "./sculpture-decoration.js";
import {
CAMERA_DISTANCE,
cameraForSculpture,
@@ -19,6 +19,7 @@ import {
finishMetal,
loadMetalEnvironment,
} from "./sculpture-materials.js";
+import { createSculptureStudio } from "./sculpture-studio.js";
import { createSelectiveGlow } from "./selective-glow.js";
import { loadSculptureShaders } from "./shader-sources.js";
@@ -88,6 +89,9 @@ export function createDatabaseScene({
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.95;
renderer.setClearColor(0x000000, 0);
+ renderer.shadowMap.enabled = true;
+ renderer.shadowMap.autoUpdate = false;
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
let canvas = renderer.domElement;
const canvasStyle =
"position:absolute;left:0;top:0;width:1000px;height:1000px;transform-origin:0 0";
@@ -127,6 +131,8 @@ export function createDatabaseScene({
renderer.domElement.removeEventListener("webglcontextlost", onLost);
resetBudget();
glow?.dispose();
+ studioSet?.dispose();
+ key.shadow.dispose();
environment?.dispose();
renderer.dispose();
// This scene owns the context; losing it releases all its GPU allocations.
@@ -136,7 +142,7 @@ export function createDatabaseScene({
releaseGraphics();
baked = new Image();
const replacement = document.createElement("canvas");
- replacement.width = 320;
+ replacement.width = 400;
replacement.height = 400;
context = replacement.getContext("2d");
if (!context) {
@@ -146,13 +152,6 @@ export function createDatabaseScene({
canvas.replaceWith(replacement);
canvas = replacement;
canvas.style.cssText = canvasStyle;
- // Keep the square layout while rasterizing only the cropped sculpture.
- Object.assign(canvas.style, {
- boxSizing: "content-box",
- width: "700px",
- height: "850px",
- padding: "75px 150px",
- });
root.dataset.renderer = "baked";
root.dataset.scene = "loading";
loaded = readySent = false;
@@ -189,6 +188,17 @@ export function createDatabaseScene({
let glow, jewelReflection;
const key = new THREE.DirectionalLight(0xe5edf1, 1.7);
key.position.set(-3.5, 5, 5);
+ key.castShadow = true;
+ key.shadow.mapSize.set(512, 512);
+ Object.assign(key.shadow.camera, {
+ left: -4,
+ right: 4,
+ top: 4,
+ bottom: -4,
+ near: 0.5,
+ far: 18,
+ });
+ key.shadow.normalBias = 0.025;
scene.add(key);
const rim = new THREE.DirectionalLight(0xa1bac7, 0.8);
rim.position.set(4, 2, -2);
@@ -197,6 +207,17 @@ export function createDatabaseScene({
fill.position.set(-5, -0.5, -2);
scene.add(fill);
+ RectAreaLightUniformsLib.init();
+ const softbox = new THREE.RectAreaLight(0xe5edf1, 7, 2.5, 6);
+ softbox.position.set(-3.5, 1.5, 4);
+ softbox.lookAt(0, 0, 0);
+ scene.add(softbox);
+ const edgeSoftbox = new THREE.RectAreaLight(0xa1bac7, 4, 1.2, 5);
+ edgeSoftbox.position.set(3.5, 1.5, 2);
+ edgeSoftbox.lookAt(0, 0, 0);
+ scene.add(edgeSoftbox);
+ scene.add(new THREE.HemisphereLight(0xa1bac7, 0x18262d, 0.55));
+
const parallaxPivot = new THREE.Group();
scene.add(parallaxPivot);
const scrollPivot = new THREE.Group();
@@ -207,7 +228,7 @@ export function createDatabaseScene({
let model;
let wireMaterial;
const modelCenter = new THREE.Vector3();
- let decoration;
+ let studioSet;
assets
.then(async ([gltf, shaders, studio]) => {
if (disposed) return;
@@ -221,8 +242,8 @@ export function createDatabaseScene({
);
glow.setSize(1000, 1000, 1);
jewelReflection = createFiniteJewelLight(JEWEL_EDGES, shaders);
- decoration = createSculptureDecoration(shaders);
- parallaxPivot.add(decoration.group);
+ studioSet = createSculptureStudio();
+ scene.add(studioSet.group);
performance.mark("sqlpage:assets-ready");
const bounds = new THREE.Box3().setFromObject(gltf.scene);
bounds.getCenter(modelCenter);
@@ -241,8 +262,10 @@ export function createDatabaseScene({
if (
material.name.includes("Black") ||
material.name.includes("Gunmetal")
- )
+ ) {
finishMetal(material, studio, shaders);
+ child.castShadow = true;
+ }
if (material.name.includes("Mirror Top"))
jewelReflection.apply(material);
if (material.name.includes("Separator"))
@@ -410,7 +433,7 @@ export function createDatabaseScene({
) {
if (baked) {
syncHitArea();
- // 64 full-material/glow views, cropped from the original 1000px square.
+ // 64 square views preserve the complete studio floor and its soft edges.
const turn =
(((state.turn ?? 0) +
yaw.setFromQuaternion(dragQuaternion, "YXZ").y) /
@@ -419,20 +442,20 @@ export function createDatabaseScene({
if (turn !== lastTurn) {
const whole = Math.floor(turn),
blend = turn - whole;
- context.clearRect(0, 0, 320, 400);
+ context.clearRect(0, 0, 400, 400);
for (let i = 0; i < 2; i++) {
const index = (((whole + i) % 64) + 64) % 64;
context.globalAlpha = i ? blend : 1 - blend;
context.globalCompositeOperation = i ? "lighter" : "source-over";
context.drawImage(
baked,
- (index % 8) * 320,
+ (index % 8) * 400,
Math.floor(index / 8) * 400,
- 320,
+ 400,
400,
0,
0,
- 320,
+ 400,
400,
);
}
@@ -440,7 +463,6 @@ export function createDatabaseScene({
}
} else {
// Prepare GPU-only animation only when a new live frame can be drawn.
- decoration?.update(time, state.motion);
liquidClock.value = time;
if (diamond) {
diamond.position.y = Math.sin(time * 0.62) * 0.012;
@@ -455,14 +477,17 @@ export function createDatabaseScene({
interior.uInteriorPulse.value = pulse;
interiorLight.intensity = (0.6 * pulse) / 14;
scene.updateMatrixWorld(true);
+ studioSet.update(model);
+ scene.updateMatrixWorld(true);
interiorLight.getWorldPosition(interior.uInteriorPosition.value);
interiorLight.getWorldScale(interiorScale);
interior.uInteriorRadius.value = Math.abs(interiorScale.x) * 1.15;
if (diamond && wireMaterial)
jewelReflection.update(diamond, camera, wireMaterial);
syncHitArea();
+ renderer.shadowMap.needsUpdate = true;
renderer.render(scene, camera);
- glow.render(model, [decoration.group]);
+ glow.render(model, [studioSet.group]);
fence = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
submittedAt = performance.now();
}
diff --git a/examples/official-site/assets/landing/js/scene/sculpture-decoration.js b/examples/official-site/assets/landing/js/scene/sculpture-decoration.js
deleted file mode 100644
index 95c276dc..00000000
--- a/examples/official-site/assets/landing/js/scene/sculpture-decoration.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/** Deterministic stars and orbital tracks; resources are owned by the scene. */
-import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.186.1/+esm";
-
-export function createSculptureDecoration(shaders) {
- const decoration = new THREE.Group();
- const orbMaterial = new THREE.MeshBasicMaterial({ color: 0x377e94 });
- const orbGeometry = new THREE.SphereGeometry(0.015, 10, 8);
- const orbs = new THREE.InstancedMesh(orbGeometry, orbMaterial, 8);
- orbs.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
- // All eight moving nodes stay inside the orbital tracks.
- orbs.boundingSphere = new THREE.Sphere(new THREE.Vector3(), 4);
- decoration.add(orbs);
- const transform = new THREE.Object3D();
- for (let i = 0; i < 4; i++) {
- const rx = 1.65 + i * 0.46,
- rz = 1.24 + i * 0.22,
- y = -1.3 - i * 0.18;
- const points = Array.from({ length: 192 }, (_, j) => {
- const a = (j / 192) * Math.PI * 2;
- return new THREE.Vector3(
- Math.cos(a) * rx,
- y + Math.sin(a) * (0.07 + i * 0.07),
- Math.sin(a) * rz,
- );
- });
- const geometry = new THREE.BufferGeometry().setFromPoints(points);
- const material = new THREE.LineBasicMaterial({
- color: i === 1 ? 0x3e7f90 : 0x22424f,
- transparent: true,
- opacity: i === 1 ? 0.24 : 0.14,
- depthWrite: false,
- });
- decoration.add(new THREE.LineLoop(geometry, material));
- }
-
- const starsGeometry = new THREE.BufferGeometry();
- const positions = [],
- sizes = [],
- phases = [];
- let seed = 7331;
- const random = () => {
- seed = (seed * 1664525 + 1013904223) >>> 0;
- return seed / 4294967296;
- };
- for (let i = 0; i < 65; i++) {
- positions.push(
- (random() - 0.5) * 8,
- (random() - 0.42) * 5.6,
- (random() - 0.5) * 5.5,
- );
- sizes.push(i % 11 === 0 ? 3 : 1 + random() * 1.4);
- phases.push(random() * Math.PI * 2);
- }
- starsGeometry.setAttribute(
- "position",
- new THREE.Float32BufferAttribute(positions, 3),
- );
- starsGeometry.setAttribute(
- "pointSize",
- new THREE.Float32BufferAttribute(sizes, 1),
- );
- starsGeometry.setAttribute(
- "phase",
- new THREE.Float32BufferAttribute(phases, 1),
- );
- const starsMaterial = new THREE.ShaderMaterial({
- uniforms: {
- uTime: { value: 0 },
- },
- vertexShader: shaders.starsVertex,
- fragmentShader: shaders.starsFragment,
- transparent: true,
- depthWrite: false,
- blending: THREE.AdditiveBlending,
- });
- const stars = new THREE.Points(starsGeometry, starsMaterial);
- decoration.add(stars);
- return {
- group: decoration,
- update(time, motion) {
- if (motion) decoration.rotation.y = time * 0.014;
- starsMaterial.uniforms.uTime.value = time;
- for (let i = 0; i < 8; i++) {
- const ring = Math.floor(i / 2);
- const a =
- ring * 1.34 + (i % 2) * Math.PI + time * (0.09 + ring * 0.013);
- transform.scale.setScalar(i % 2 === 0 ? 1 : 0.66);
- transform.position.set(
- Math.cos(a) * (1.65 + ring * 0.46),
- -1.3 - ring * 0.18 + Math.sin(a) * 0.07,
- Math.sin(a) * (1.24 + ring * 0.22),
- );
- transform.updateMatrix();
- orbs.setMatrixAt(i, transform.matrix);
- }
- orbs.instanceMatrix.needsUpdate = true;
- },
- };
-}
diff --git a/examples/official-site/assets/landing/js/scene/sculpture-materials.js b/examples/official-site/assets/landing/js/scene/sculpture-materials.js
index 7f19ced9..87c4c2bc 100644
--- a/examples/official-site/assets/landing/js/scene/sculpture-materials.js
+++ b/examples/official-site/assets/landing/js/scene/sculpture-materials.js
@@ -32,13 +32,13 @@ function localPosition(shader, varying) {
export function finishMetal(material, environment, shaders) {
const lid = material.name.includes("Mirror Top");
const rim = material.name.includes("Gunmetal");
- material.color.set(lid ? "#23303a" : rim ? "#3a4651" : "#252c33");
- material.metalness = 0.96;
+ material.color.set(lid ? "#384651" : rim ? "#adb8c2" : "#9ca8b2");
+ material.metalness = lid ? 0.96 : 0.9;
// Keep the cap polished: the satin shell roughness blurs the reflected wires.
- material.roughness = lid ? 0.09 : rim ? 0.23 : 0.34;
+ material.roughness = lid ? 0.09 : rim ? 0.24 : 0.3;
material.envMap = environment;
material.envMapIntensity =
- (lid ? 0.62 : rim ? 0.76 : 0.68) * STUDIO_RADIANCE_SCALE;
+ (lid ? 0.82 : rim ? 1.15 : 1.08) * STUDIO_RADIANCE_SCALE;
material.defines = { ...material.defines, METAL_LID: lid ? 1 : 0 };
material.onBeforeCompile = (shader) => {
localPosition(shader, "vMetalPosition");
@@ -48,7 +48,7 @@ export function finishMetal(material, environment, shaders) {
);
};
material.customProgramCacheKey = () =>
- `sqlpage-black-metal-${lid ? "lid" : "shell"}-v4`;
+ `sqlpage-black-metal-${lid ? "lid" : "shell"}-v5`;
}
export function finishLiquid(material, clock, environment, interior, shaders) {
diff --git a/examples/official-site/assets/landing/js/scene/sculpture-studio.js b/examples/official-site/assets/landing/js/scene/sculpture-studio.js
new file mode 100644
index 00000000..8f6ddb1b
--- /dev/null
+++ b/examples/official-site/assets/landing/js/scene/sculpture-studio.js
@@ -0,0 +1,94 @@
+/** A matte studio sweep catches real light and the sculpture's shadow. */
+import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.186.1/+esm";
+
+export function createSculptureStudio() {
+ const group = new THREE.Group();
+ const profile = [
+ [5, 0],
+ [-1.8, 0],
+ ];
+ for (let i = 1; i <= 20; i++) {
+ const angle = ((i / 20) * Math.PI) / 2;
+ profile.push([-1.8 - Math.sin(angle) * 1.2, 1.2 - Math.cos(angle) * 1.2]);
+ }
+ profile.push([-3, 7]);
+ const positions = [],
+ indices = [];
+ profile.forEach(([z, y], i) => {
+ positions.push(-6, y, z, 6, y, z);
+ if (i) {
+ const n = i * 2;
+ indices.push(n - 2, n - 1, n, n - 1, n + 1, n);
+ }
+ });
+ const geometry = new THREE.BufferGeometry();
+ geometry.setAttribute(
+ "position",
+ new THREE.Float32BufferAttribute(positions, 3),
+ );
+ geometry.setIndex(indices);
+ geometry.computeVertexNormals();
+ const theme = getComputedStyle(document.documentElement);
+ const material = new THREE.MeshStandardMaterial({
+ color: theme.getPropertyValue("--tblr-bg-surface-tertiary").trim(),
+ roughness: 0.86,
+ metalness: 0.12,
+ transparent: true,
+ depthWrite: false,
+ opacity: 0.65,
+ });
+ material.onBeforeCompile = (shader) => {
+ shader.vertexShader = shader.vertexShader
+ .replace(
+ "#include ",
+ "#include \nvarying vec3 vStudioPosition;",
+ )
+ .replace(
+ "#include ",
+ "#include \nvStudioPosition = position;",
+ );
+ shader.fragmentShader = shader.fragmentShader
+ .replace(
+ "#include ",
+ "#include \nvarying vec3 vStudioPosition;",
+ )
+ .replace(
+ "#include ",
+ `#include
+ float edge = 1.0 - smoothstep(1.2, 2.8, abs(vStudioPosition.x));
+ float wall = 1.0 - smoothstep(1.8, 5.0, vStudioPosition.y);
+ float floorEdge = 1.0 - smoothstep(1.0, 3.8, vStudioPosition.z);
+ float grain = fract(sin(dot(floor(vStudioPosition.xz * 800.0), vec2(12.9898, 78.233))) * 43758.5453);
+ diffuseColor.rgb *= 0.94 + grain * 0.12;
+ diffuseColor.a *= edge * wall * floorEdge;
+ `,
+ );
+ };
+ material.customProgramCacheKey = () => "sqlpage-studio-sweep-v1";
+ const sweep = new THREE.Mesh(geometry, material);
+ sweep.receiveShadow = true;
+ group.add(sweep);
+ const spill = new THREE.PointLight(
+ theme.getPropertyValue("--tblr-primary").trim(),
+ 2.4,
+ 5,
+ 2,
+ );
+ spill.position.set(0.3, 0.85, -0.6);
+ group.add(spill);
+ const bounds = new THREE.Box3();
+ return {
+ group,
+ dispose() {
+ geometry.dispose();
+ material.dispose();
+ group.removeFromParent();
+ },
+ update(model) {
+ // Follow the lowest point of the tilted/dragged model without rotating
+ // the room with it. The shadow remains attached to the contact surface.
+ bounds.setFromObject(model);
+ group.position.y = bounds.min.y - 0.025;
+ },
+ };
+}
diff --git a/examples/official-site/assets/landing/js/scene/shader-sources.js b/examples/official-site/assets/landing/js/scene/shader-sources.js
index 91e813a3..d8ba4e19 100644
--- a/examples/official-site/assets/landing/js/scene/shader-sources.js
+++ b/examples/official-site/assets/landing/js/scene/shader-sources.js
@@ -12,8 +12,6 @@ const files = {
liquidFinish: "liquid-finish.glsl",
liquidFlow: "liquid-flow.glsl",
metalFinish: "metal-finish.glsl",
- starsFragment: "stars-fragment.glsl",
- starsVertex: "stars-vertex.glsl",
};
let sources;
diff --git a/examples/official-site/assets/landing/js/scene/shaders/metal-finish.glsl b/examples/official-site/assets/landing/js/scene/shaders/metal-finish.glsl
index f343105f..091a3501 100644
--- a/examples/official-site/assets/landing/js/scene/shaders/metal-finish.glsl
+++ b/examples/official-site/assets/landing/js/scene/shaders/metal-finish.glsl
@@ -7,8 +7,8 @@
const float metalMinimumRoughness = .06;
#else
float metalToolCoordinate = vMetalPosition.y;
- const float metalMachining = .012;
- const float metalGrainStrength = .016;
+ const float metalMachining = .02;
+ const float metalGrainStrength = .026;
const float metalMinimumRoughness = .15;
#endif
float metalGrain = fract(sin(dot(
diff --git a/examples/official-site/assets/landing/js/scene/shaders/stars-fragment.glsl b/examples/official-site/assets/landing/js/scene/shaders/stars-fragment.glsl
deleted file mode 100644
index 09ec8ec5..00000000
--- a/examples/official-site/assets/landing/js/scene/shaders/stars-fragment.glsl
+++ /dev/null
@@ -1,8 +0,0 @@
-// Soft circular point sprites; no star image or texture fetch is needed.
-varying float brightness;
-void main() {
- float r = length(gl_PointCoord - .5);
- if (r > .5) discard;
- float a = pow(1.0 - r * 2.0, 2.0) * brightness * .45;
- gl_FragColor = vec4(vec3(.13, .39, .48), a);
-}
diff --git a/examples/official-site/assets/landing/js/scene/shaders/stars-vertex.glsl b/examples/official-site/assets/landing/js/scene/shaders/stars-vertex.glsl
deleted file mode 100644
index 30da85ac..00000000
--- a/examples/official-site/assets/landing/js/scene/shaders/stars-vertex.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-// Deterministic point sizes and phases are supplied by sculpture-decoration.
-attribute float pointSize;
-attribute float phase;
-varying float brightness;
-uniform float uTime;
-void main() {
- vec3 p = position;
- p.y += sin(uTime * .12 + phase) * .06;
- vec4 mv = modelViewMatrix * vec4(p, 1.0);
- gl_Position = projectionMatrix * mv;
- gl_PointSize = clamp(pointSize * (7.0 / -mv.z), 1.0, 12.0);
- brightness = .5 + .35 * sin(phase + uTime * .35);
-}
diff --git a/examples/official-site/assets/landing/styles/base.css b/examples/official-site/assets/landing/styles/base.css
index 86e0706b..f337a75f 100644
--- a/examples/official-site/assets/landing/styles/base.css
+++ b/examples/official-site/assets/landing/styles/base.css
@@ -1,10 +1,5 @@
/* Shared tokens and native element defaults. */
-:root {
- --foreground: #f4f7f8;
- --cyan: #58cce0;
- --ink: #080e11;
- --mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
-}
+
[hidden] {
display: none !important;
}
@@ -25,13 +20,13 @@
html {
color-scheme: dark;
scrollbar-width: thin;
- scrollbar-color: #345a62 #080e11;
+ scrollbar-color: var(--tblr-dark) var(--tblr-body-bg);
}
body {
margin: 0;
- background: var(--ink);
- color: var(--foreground);
- font-family: var(--brand-font);
+ background: var(--tblr-body-bg);
+ color: var(--tblr-body-color);
+ font-family: var(--tblr-font-sans-serif);
-webkit-font-smoothing: antialiased;
}
button,
@@ -54,12 +49,12 @@ a {
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
- outline: 2px solid var(--cyan);
+ outline: 2px solid var(--tblr-primary);
outline-offset: 6px;
}
::selection {
- background: #5bcce1;
- color: #081015;
+ background: var(--tblr-primary);
+ color: var(--tblr-body-bg);
}
.skip-link {
position: fixed;
@@ -67,9 +62,9 @@ button:focus-visible,
left: 20px;
transform: translateY(-180%);
z-index: 100;
- background: var(--cyan);
+ background: var(--tblr-primary);
padding: 12px 16px;
- color: var(--ink);
+ color: var(--tblr-body-bg);
}
.skip-link:focus {
transform: none;
diff --git a/examples/official-site/assets/landing/styles/comparison.css b/examples/official-site/assets/landing/styles/comparison.css
index 8853a8ec..870cbb0d 100644
--- a/examples/official-site/assets/landing/styles/comparison.css
+++ b/examples/official-site/assets/landing/styles/comparison.css
@@ -6,20 +6,24 @@
}
/* Day 100: a fixed SQLPage example beside a natively scrollable set of alternatives. */
.comparison-section {
- --section-color: var(--brand-paper);
- --comparison-border: #dbe2e7;
- --comparison-copy: #49545f;
- --comparison-muted: #626f7c;
+ --section-color: var(--tblr-light);
+ --comparison-border: var(--tblr-body-color);
+ --comparison-copy: var(--tblr-dark);
+ --comparison-muted: color-mix(
+ in srgb,
+ var(--tblr-dark),
+ var(--tblr-white) 40%
+ );
background: var(--section-color);
- color: #0b1020;
+ color: var(--tblr-bg-surface);
color-scheme: light;
padding-bottom: calc(var(--wave-height) + 64px);
}
.comparison-section .eyebrow {
- color: #146575;
+ color: var(--tblr-blue);
}
.comparison-section h2 em {
- color: #0e8292;
+ color: var(--tblr-blue);
}
.comparison-section .section-heading {
grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.95fr) minmax(0, 0.27fr);
@@ -40,7 +44,7 @@
grid-template-columns: minmax(0, 1.3fr) 36px minmax(0, 2fr);
gap: 18px;
padding-top: 34px;
- border-top: 1px solid #cbd4da;
+ border-top: 1px solid var(--tblr-gray-300);
}
.comparison-card {
min-width: 0;
@@ -69,21 +73,21 @@
.comparison-card-heading p {
margin: 5px 0 0;
color: var(--comparison-muted);
- font: 11px / 1.5 var(--mono);
+ font: 11px / 1.5 var(--tblr-font-monospace);
}
.comparison-versus {
flex-direction: column;
gap: 20px;
margin: 76px 0 115px;
- color: #8a96a2;
- font: 500 26px var(--brand-font);
+ color: var(--tblr-muted-color);
+ font: 500 26px var(--tblr-font-sans-serif);
}
.comparison-versus::before,
.comparison-versus::after {
content: "";
width: 1px;
flex: 1;
- background: #cbd4da;
+ background: var(--tblr-gray-300);
}
.comparison-alternatives {
min-width: 0;
@@ -98,7 +102,7 @@
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
scrollbar-width: thin;
- scrollbar-color: #9caeb5 transparent;
+ scrollbar-color: var(--tblr-muted-color) transparent;
padding-bottom: 15px;
}
.comparison-track > .comparison-card {
@@ -111,10 +115,11 @@
height: 326px;
border: 1px solid var(--comparison-border);
border-radius: 12px;
- background: #fff;
- color: #242b32;
+ background: var(--tblr-white);
+ color: var(--tblr-bg-surface-tertiary);
overflow: hidden;
- box-shadow: 0 12px 24px #18262d0a;
+ box-shadow: 0 12px 24px
+ color-mix(in srgb, var(--tblr-bg-surface-secondary) 3.92%, transparent);
}
.comparison-window svg {
width: 15px;
@@ -123,25 +128,25 @@
vertical-align: middle;
}
.comparison-code {
- border-color: #202739;
- background: #080d19;
- color: #eef1f7;
+ border-color: var(--tblr-bg-surface-tertiary);
+ background: var(--tblr-body-bg);
+ color: var(--tblr-light);
}
.comparison-window-bar {
height: 36px;
justify-content: space-between;
gap: 10px;
padding: 0 14px;
- background: #f6f8fa;
+ background: var(--tblr-light);
border-bottom: 1px solid var(--comparison-border);
- font: 11px / 1.4 var(--mono);
+ font: 11px / 1.4 var(--tblr-font-monospace);
}
.comparison-window-bar > span:last-child:not(:first-child) {
opacity: 0.55;
}
.comparison-code .comparison-window-bar {
- background: #171d29;
- border-color: #303746;
+ background: var(--tblr-bg-surface-secondary);
+ border-color: var(--tblr-border-color);
}
.comparison-window-body {
display: flex;
@@ -158,33 +163,42 @@
.comparison-diff {
flex-wrap: wrap;
margin: 0 0 12px;
- color: #97a8c3;
- font: 11px / 1.4 var(--mono);
+ color: var(--tblr-muted-color);
+ font: 11px / 1.4 var(--tblr-font-monospace);
}
.comparison-added {
- color: #5cde93;
+ color: color-mix(in srgb, var(--tblr-success), var(--tblr-white) 35%);
}
.comparison-removed {
- color: #ff797e;
+ color: color-mix(in srgb, var(--tblr-danger), var(--tblr-white) 30%);
}
.comparison-diff i {
width: 56px;
height: 8px;
background:
- repeating-linear-gradient(90deg, transparent 0 7px, #080d19 7px 9px),
- linear-gradient(90deg, #48bb70 82%, #f15b62 82%);
+ repeating-linear-gradient(
+ 90deg,
+ transparent 0 7px,
+ var(--tblr-body-bg) 7px 9px
+ ),
+ linear-gradient(
+ 90deg,
+ color-mix(in srgb, var(--tblr-success), var(--tblr-white) 35%) 82%,
+ var(--tblr-danger) 82%
+ );
}
.comparison-files {
margin: 0;
padding: 0;
list-style: none;
- font: 11px / 1.5 var(--mono);
+ font: 11px / 1.5 var(--tblr-font-monospace);
}
.comparison-files li {
align-items: normal;
justify-content: space-between;
padding: 7px 0;
- border-top: 1px solid #ffffff1c;
+ border-top: 1px solid
+ color-mix(in srgb, var(--tblr-white) 10.98%, transparent);
}
.comparison-files li > span:first-child {
gap: 7px;
@@ -204,32 +218,33 @@
gap: 10px;
margin: auto 0 0;
padding-top: 14px;
- border-top: 1px solid #ffffff1c;
- font: 12px / 1.4 var(--mono);
+ border-top: 1px solid
+ color-mix(in srgb, var(--tblr-white) 10.98%, transparent);
+ font: 12px / 1.4 var(--tblr-font-monospace);
}
.comparison-deploy svg {
- color: #29d6df;
+ color: var(--tblr-blue);
width: 18px;
height: 18px;
}
.comparison-deploy span {
- color: #97a8c3;
+ color: var(--tblr-muted-color);
}
.comparison-more {
margin: 5px 0;
- font: 11px / 1.5 var(--mono);
- color: #97a8b8;
+ font: 11px / 1.5 var(--tblr-font-monospace);
+ color: var(--tblr-muted-color);
}
.comparison-checks {
margin-top: auto;
- font: 11px / 1.5 var(--mono);
+ font: 11px / 1.5 var(--tblr-font-monospace);
}
.comparison-checks p {
margin: 4px 0 0;
- color: #5cde93;
+ color: color-mix(in srgb, var(--tblr-success), var(--tblr-white) 35%);
}
.comparison-checks p:first-child {
- color: #f5c457;
+ color: color-mix(in srgb, var(--tblr-warning), var(--tblr-white) 30%);
}
.comparison-card .comparison-verdict {
margin: 18px 0 8px;
@@ -261,7 +276,7 @@
}
.comparison-sheet-labels {
padding: 12px 0 8px;
- font: 10px var(--mono);
+ font: 10px var(--tblr-font-monospace);
color: var(--comparison-muted);
}
.comparison-sheet-files {
@@ -274,7 +289,7 @@
padding: 9px 12px;
}
.comparison-sheet-files li:nth-child(4) {
- background: #eaf1ff;
+ background: var(--tblr-light);
}
.comparison-sheet-files b {
display: grid;
@@ -282,40 +297,40 @@
width: 16px;
height: 18px;
border-radius: 3px;
- background: #217747;
+ background: var(--tblr-success);
color: white;
font-size: 11px;
}
.comparison-sheet-files small {
- font: 10px var(--mono);
+ font: 10px var(--tblr-font-monospace);
color: var(--comparison-muted);
white-space: nowrap;
}
.comparison-lock {
margin: auto 0 0;
padding: 10px;
- border: 1px solid #eed3a0;
+ border: 1px solid var(--hljs-cosmic-punctuation);
border-radius: 7px;
- background: #fbf5e9;
- color: #795100;
+ background: color-mix(in srgb, var(--tblr-warning) 8%, var(--tblr-white));
+ color: var(--tblr-warning);
font-size: 12px;
}
.comparison-terminal {
padding-top: 20px;
- font: 11px / 1.75 var(--mono);
+ font: 11px / 1.75 var(--tblr-font-monospace);
}
.comparison-terminal p {
margin: 0;
overflow-wrap: anywhere;
}
.comparison-terminal p > span:not(.comparison-more) {
- color: #5cde93;
+ color: color-mix(in srgb, var(--tblr-success), var(--tblr-white) 35%);
}
.comparison-cursor {
display: inline-block;
width: 7px;
height: 13px;
- background: #97a8b8;
+ background: var(--tblr-muted-color);
vertical-align: middle;
}
.comparison-metric {
@@ -340,8 +355,8 @@
border-radius: 5px;
align-self: start;
position: relative;
- color: #6e7c89;
- background: #f6f8fa;
+ color: var(--tblr-muted-color);
+ background: var(--tblr-light);
font-size: 12px;
}
.comparison-metric .comparison-view-only small {
@@ -351,7 +366,7 @@
white-space: nowrap;
border-radius: 4px;
padding: 6px 8px;
- background: #242b32;
+ background: var(--tblr-bg-surface-tertiary);
color: white;
font-size: 10px;
}
@@ -367,7 +382,7 @@
.comparison-bars i {
flex: 1;
height: 42%;
- background: #adc1ff;
+ background: var(--tblr-link-hover-color);
border-radius: 2px 2px 0 0;
}
.comparison-bars i:nth-child(3n) {
@@ -387,7 +402,7 @@
}
.comparison-bars i:last-child {
height: 100%;
- background: #326cff;
+ background: var(--tblr-link-color);
}
.comparison-chat {
align-items: start;
@@ -401,28 +416,28 @@
width: 24px;
height: 24px;
border-radius: 50%;
- background: #326cff;
+ background: var(--tblr-link-color);
color: white;
font-size: 12px;
}
.comparison-lowcode .comparison-window-body {
position: relative;
- background: #e0e4e8;
+ background: var(--tblr-body-color);
}
.comparison-lowcode table {
border-collapse: collapse;
font-size: 12px;
width: 100%;
- color: #626b74;
+ color: color-mix(in srgb, var(--tblr-dark), var(--tblr-white) 40%);
}
.comparison-lowcode th {
- font: 10px var(--mono);
+ font: 10px var(--tblr-font-monospace);
text-align: left;
}
.comparison-lowcode td,
.comparison-lowcode th {
padding: 7px 0;
- border-bottom: 1px solid #cbd4da;
+ border-bottom: 1px solid var(--tblr-gray-300);
}
.comparison-lowcode td:last-child,
.comparison-lowcode th:last-child {
@@ -432,7 +447,11 @@
content: "";
position: absolute;
inset: 0;
- background: #18262d40;
+ background: color-mix(
+ in srgb,
+ var(--tblr-bg-surface-secondary) 25.10%,
+ transparent
+ );
}
.comparison-upgrade {
position: absolute;
@@ -443,8 +462,9 @@
transform: translateY(-50%);
padding: 20px 16px;
border-radius: 12px;
- background: #fff;
- box-shadow: 0 15px 30px #18262d40;
+ background: var(--tblr-white);
+ box-shadow: 0 15px 30px
+ color-mix(in srgb, var(--tblr-bg-surface-secondary) 25.10%, transparent);
font-size: 16px;
}
.comparison-upgrade p {
@@ -462,7 +482,7 @@
.comparison-upgrade > div span:last-child {
border-radius: 6px;
padding: 8px 12px;
- background: #6844df;
+ background: var(--tblr-purple);
color: white;
}
.comparison-footer {
@@ -490,7 +510,7 @@
.comparison-controls p {
margin: 0 10px 0 0;
color: var(--comparison-muted);
- font: 11px / 1.5 var(--mono);
+ font: 11px / 1.5 var(--tblr-font-monospace);
}
.comparison-controls button {
display: grid;
@@ -498,17 +518,17 @@
width: 44px;
height: 44px;
padding: 0;
- border: 1px solid #a8b5bf;
+ border: 1px solid var(--tblr-secondary-color);
border-radius: 8px;
background: transparent;
- color: #0b1020;
+ color: var(--tblr-bg-surface);
transition:
background 200ms,
border-color 200ms;
}
.comparison-controls button:not(:disabled):hover {
- background: #e1ecef;
- border-color: #146575;
+ background: var(--tblr-light);
+ border-color: var(--tblr-blue);
}
.comparison-controls svg {
width: 20px;
diff --git a/examples/official-site/assets/landing/styles/component-demos.css b/examples/official-site/assets/landing/styles/component-demos.css
index e29f6e9c..3c7ab349 100644
--- a/examples/official-site/assets/landing/styles/component-demos.css
+++ b/examples/official-site/assets/landing/styles/component-demos.css
@@ -5,19 +5,20 @@
}
.sql-editor {
margin-top: 18px;
- border: 1px solid #293246;
+ border: 1px solid var(--tblr-bg-surface-tertiary);
border-radius: 12px;
- background: #080e1b;
- box-shadow: 0 14px 28px #07111d20;
+ background: var(--tblr-body-bg);
+ box-shadow: 0 14px 28px
+ color-mix(in srgb, var(--tblr-body-bg) 12.55%, transparent);
overflow: hidden;
}
.sql-editor-header {
display: flex;
align-items: center;
justify-content: space-between;
- border-bottom: 1px solid #1c2537;
- font: 12px var(--mono);
- color: #c3cbd9;
+ border-bottom: 1px solid var(--tblr-bg-surface-tertiary);
+ font: 12px var(--tblr-font-monospace);
+ color: var(--tblr-gray-300);
}
.sql-editor-header button {
display: inline-flex;
@@ -26,11 +27,11 @@
padding: 8px 16px;
border: 0;
background: transparent;
- color: #94a9be;
+ color: var(--tblr-muted-color);
font: inherit;
}
.sql-editor-header button:hover {
- color: #64d5df;
+ color: var(--tblr-blue);
}
.source-tabs {
display: flex;
@@ -39,17 +40,17 @@
.sql-editor-header .source-tabs button {
border-radius: 0;
border: 0;
- border-right: 1px solid #1b2337;
+ border-right: 1px solid var(--tblr-bg-surface-tertiary);
padding: 12px 18px;
background: transparent;
- color: #8898b2;
- font: 12px var(--mono);
+ color: var(--tblr-muted-color);
+ font: 12px var(--tblr-font-monospace);
box-shadow: inset 0 2px transparent;
}
.sql-editor-header .source-tabs button[aria-selected="true"] {
- color: #dbe0ec;
- background: #111a2b;
- box-shadow: inset 0 2px #7aaef0;
+ color: var(--tblr-body-color);
+ background: var(--tblr-bg-surface-secondary);
+ box-shadow: inset 0 2px var(--tblr-primary);
}
.sql-editor-header svg {
width: 15px;
@@ -63,7 +64,7 @@
overflow-x: auto;
}
#demo-source {
- color: #bac9d9;
+ color: var(--tblr-secondary-color);
display: block;
white-space: normal;
counter-reset: sql-line;
@@ -80,7 +81,7 @@
position: absolute;
left: 0;
width: 1.4em;
- color: #4c5e78;
+ color: var(--tblr-blue);
text-align: right;
user-select: none;
}
@@ -120,7 +121,7 @@
min-width: 0;
}
.component-picker strong {
- font: bold 18px var(--mono);
+ font: bold 18px var(--tblr-font-monospace);
display: block;
}
.component-picker small {
@@ -130,17 +131,18 @@
}
.component-picker button:hover {
transform: translateX(3px);
- background: #29c8d710;
+ background: color-mix(in srgb, var(--tblr-blue) 6.27%, transparent);
}
.component-picker button[aria-selected="true"] {
- background: #07111d;
+ background: var(--tblr-body-bg);
color: white;
- border-color: #202c3e;
- border-left: 3px solid #74b6e9;
- box-shadow: 0 12px 28px #07111d30;
+ border-color: var(--tblr-bg-surface-tertiary);
+ border-left: 3px solid var(--tblr-primary);
+ box-shadow: 0 12px 28px
+ color-mix(in srgb, var(--tblr-body-bg) 18.82%, transparent);
}
.component-picker button[aria-selected="true"] svg {
- color: #29c8d7;
+ color: var(--tblr-blue);
}
#demo-panel {
position: relative;
@@ -151,7 +153,7 @@
top: -86px;
left: 48%;
margin: 0;
- color: #52737d;
+ color: color-mix(in srgb, var(--tblr-dark), var(--tblr-white) 40%);
font: italic 17px / 1.35 cursive;
transform: rotate(-6deg);
pointer-events: none;
@@ -172,20 +174,21 @@
display: block;
width: 100%;
height: 100%;
- border: 1px solid #2b344c;
+ border: 1px solid var(--tblr-bg-surface-tertiary);
border-radius: 18px;
- background: #0d1224;
- box-shadow: 0 18px 30px #07111d18;
+ background: var(--tblr-bg-surface);
+ box-shadow: 0 18px 30px
+ color-mix(in srgb, var(--tblr-body-bg) 9.41%, transparent);
}
#demo-panel iframe[aria-hidden="true"] {
visibility: hidden;
pointer-events: none;
}
.code-panel {
- background: #080e1b;
- color: #b3c5d4;
- font: 13px / 1.8 var(--mono);
- border: 1px solid #28c8d6;
+ background: var(--tblr-body-bg);
+ color: var(--tblr-secondary-color);
+ font: 13px / 1.8 var(--tblr-font-monospace);
+ border: 1px solid var(--tblr-blue);
border-radius: 12px;
padding: 22px;
white-space: pre-wrap;
@@ -199,8 +202,8 @@
display: inline-flex;
align-items: center;
gap: 6px;
- font: 12px var(--mono);
- color: #217c8a;
+ font: 12px var(--tblr-font-monospace);
+ color: var(--tblr-blue);
}
.demo-docs:hover {
text-decoration: underline;
@@ -274,20 +277,20 @@
#demo-source .hljs-keyword,
#demo-source .hljs-literal {
- color: #b5a4f5;
+ color: var(--hljs-cosmic-boolean);
}
#demo-source .hljs-string {
- color: #64d5bf;
+ color: var(--hljs-cosmic-string);
}
#demo-source .hljs-number {
- color: #efc58c;
+ color: var(--hljs-cosmic-punctuation);
}
#demo-source .hljs-comment {
- color: #708598;
+ color: var(--tblr-muted-color);
}
#demo-source .hljs-built_in,
#demo-source .hljs-type {
- color: #6cd6e8;
+ color: var(--tblr-blue);
}
#demo-panel[data-demo="catalog"] .sql-editor,
#demo-panel[data-demo="catalog"] .demo-annotation,
diff --git a/examples/official-site/assets/landing/styles/demo.css b/examples/official-site/assets/landing/styles/demo.css
index 82523924..a080b386 100644
--- a/examples/official-site/assets/landing/styles/demo.css
+++ b/examples/official-site/assets/landing/styles/demo.css
@@ -1,13 +1,7 @@
@import url("../../brand/brand.css");
/* Standalone SQLPage demos: content-sized shells and compact native components. */
-:root {
- --tblr-body-bg: #0d1224;
- --tblr-bg-surface: #0d1224;
- --tblr-border-color: #273149;
- --tblr-primary: #28c8d6;
- --tblr-body-color: #dbe0ec;
-}
+
html,
body,
.page,
@@ -17,7 +11,7 @@ body,
}
body {
font-size: 13px;
- background: #0d1224;
+ background: var(--tblr-bg-surface);
}
.page,
.page-wrapper {
@@ -71,7 +65,7 @@ body {
}
.table {
font-size: 14px;
- --tblr-table-border-color: #1b2337;
+ --tblr-table-border-color: var(--tblr-bg-surface-tertiary);
}
.table td,
.table th {
@@ -79,7 +73,7 @@ body {
}
.table th {
font-size: 11px;
- color: #9caac4;
+ color: var(--tblr-muted-color);
background: transparent;
}
#landing-customers .table th {
@@ -89,7 +83,7 @@ body {
border-radius: 8px;
}
.table-sort {
- color: #94a3bb;
+ color: var(--tblr-muted-color);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.12em;
@@ -103,23 +97,29 @@ body {
margin: 0;
padding: 3px 10px;
border-radius: 5px;
- background: #123737;
- color: #5ed4b6;
+ background: var(
+ --tblr-success-lt,
+ color-mix(in srgb, var(--tblr-success) 20%, var(--tblr-body-bg))
+ );
+ color: var(--hljs-cosmic-string);
font-size: 11px;
font-weight: 600;
}
.table tr.trial td._col_status p {
- background: #123459;
- color: #8bbcf5;
+ background: var(--tblr-bg-surface-tertiary);
+ color: var(--tblr-primary);
}
.table tr.late td._col_status p {
- background: #4b3526;
- color: #ecb56b;
+ background: var(
+ --tblr-warning-lt,
+ color-mix(in srgb, var(--tblr-warning) 20%, var(--tblr-body-bg))
+ );
+ color: color-mix(in srgb, var(--tblr-warning), var(--tblr-white) 30%);
}
input.form-control {
min-height: 38px;
- background: #090e1d;
- border-color: #273149;
+ background: var(--tblr-body-bg);
+ border-color: var(--tblr-bg-surface-tertiary);
font-size: 12px;
border-radius: 8px;
padding: 8px 12px;
@@ -146,7 +146,7 @@ form.mt-3 {
font-size: 12px;
font-weight: 500;
margin-bottom: 7px;
- color: #bac5d6;
+ color: var(--tblr-secondary-color);
}
.form-fieldset .mb-3 {
margin-bottom: 16px !important;
@@ -155,13 +155,13 @@ form.mt-3 {
font-size: 13px;
padding: 9px 18px;
border-radius: 8px;
- box-shadow: 0 3px 14px #20c8d518;
- background: #27c4d2;
- border-color: #27c4d2;
- color: #06121b;
+ box-shadow: 0 3px 14px color-mix(in srgb, var(--tblr-blue) 9.41%, transparent);
+ background: var(--tblr-blue);
+ border-color: var(--tblr-blue);
+ color: var(--tblr-dark);
}
.apexcharts-tooltip {
- color: #0d1224;
+ color: var(--tblr-bg-surface);
}
#landing-kpis {
display: grid;
@@ -178,13 +178,24 @@ form.mt-3 {
}
#landing-kpis .card {
height: 100%;
- border: 1px solid #294055;
+ border: 1px solid var(--tblr-border-color);
border-radius: 10px;
- background: linear-gradient(145deg, #17263a, #0d1729);
+ background: linear-gradient(
+ 145deg,
+ var(--tblr-bg-surface-tertiary),
+ var(--tblr-bg-surface-secondary)
+ );
overflow: hidden;
}
#landing-kpis .bg-cyan-lt {
- background: linear-gradient(120deg, #0d2a36, #122333) !important;
+ background: linear-gradient(
+ 120deg,
+ var(
+ --tblr-success-lt,
+ color-mix(in srgb, var(--tblr-success) 20%, var(--tblr-body-bg))
+ ),
+ var(--tblr-bg-surface-secondary)
+ ) !important;
}
#landing-kpis .card-body {
gap: 8px;
@@ -193,7 +204,7 @@ form.mt-3 {
#landing-kpis .subheader {
font-size: 10px;
letter-spacing: 0.08em;
- color: #9badc5;
+ color: var(--tblr-muted-color);
}
#landing-kpis .h1 {
font-size: clamp(38px, 6vw, 56px);
@@ -205,13 +216,13 @@ form.mt-3 {
#landing-kpis .d-flex.flex-wrap {
margin: 0 !important;
font-size: 12px;
- color: #a9bacf;
+ color: var(--tblr-secondary-color);
}
#landing-kpis .text-truncate {
white-space: normal;
}
#landing-kpis .text-cyan {
- color: #43ccd7 !important;
+ color: var(--tblr-blue) !important;
}
@media (max-width: 480px) {
#sqlpage_main_wrapper {
@@ -246,9 +257,13 @@ form.mt-3 {
/* Native form controls retain validation, icons, and accessible labels. */
#landing-profile {
padding: 22px;
- border: 1px solid #26344b;
+ border: 1px solid var(--tblr-bg-surface-tertiary);
border-radius: 14px;
- background: linear-gradient(135deg, #152339, #0d1224 65%);
+ background: linear-gradient(
+ 135deg,
+ var(--tblr-bg-surface-tertiary),
+ var(--tblr-bg-surface) 65%
+ );
}
#landing-profile h2 {
font-size: 23px;
@@ -256,9 +271,9 @@ form.mt-3 {
font-weight: 600;
}
#landing-profile .input-group-text {
- background: #111c2e;
- border-color: #273149;
- color: #73a4b7;
+ background: var(--tblr-bg-surface-secondary);
+ border-color: var(--tblr-bg-surface-tertiary);
+ color: var(--tblr-muted-color);
}
#landing-profile .input-group-text svg {
width: 17px;
@@ -272,12 +287,12 @@ form.mt-3 {
box-shadow 180ms;
}
#landing-profile .form-control:focus {
- border-color: #28c8d6;
- box-shadow: 0 0 0 3px #28c8d61a;
+ border-color: var(--tblr-blue);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--tblr-blue) 10.20%, transparent);
}
#landing-profile .form-hint {
font-size: 11px;
- color: #8597b0;
+ color: var(--tblr-muted-color);
line-height: 1.5;
}
#landing-profile .btn {
@@ -290,12 +305,19 @@ form.mt-3 {
font-size: 13px;
}
#landing-kpis .bg-teal-lt {
- background: linear-gradient(145deg, #102e30, #0d1b29) !important;
+ background: linear-gradient(
+ 145deg,
+ var(
+ --tblr-success-lt,
+ color-mix(in srgb, var(--tblr-success) 20%, var(--tblr-body-bg))
+ ),
+ var(--tblr-bg-surface-secondary)
+ ) !important;
}
#landing-kpis .progress {
height: 4px;
margin-top: 10px;
- background: #ffffff0d;
+ background: color-mix(in srgb, var(--tblr-white) 5.10%, transparent);
}
.component-catalog {
display: grid;
@@ -307,9 +329,9 @@ form.mt-3 {
align-items: center;
gap: 10px;
padding: 16px 12px;
- border: 1px solid #26344b;
+ border: 1px solid var(--tblr-bg-surface-tertiary);
border-radius: 9px;
- color: #dbe0ec;
+ color: var(--tblr-body-color);
text-decoration: none;
font-size: 12px;
transition:
@@ -319,15 +341,15 @@ form.mt-3 {
}
.component-catalog a:hover,
.component-catalog a:focus-visible {
- color: #5ddddf;
- background: #122636;
- border-color: #28c8d6;
+ color: var(--tblr-blue);
+ background: var(--tblr-bg-surface-tertiary);
+ border-color: var(--tblr-blue);
}
.component-catalog svg {
width: 19px;
height: 19px;
flex: none;
- color: #67b9c7;
+ color: var(--tblr-blue);
}
.component-catalog svg:last-child {
margin-left: auto;
@@ -369,9 +391,6 @@ form.mt-3 {
}
/* Match the official-site typeface without changing the demo composition. */
-:root {
- --tblr-font-sans-serif: var(--brand-font);
-}
body {
- font-family: var(--brand-font);
+ font-family: var(--tblr-font-sans-serif);
}
diff --git a/examples/official-site/assets/landing/styles/dialog.css b/examples/official-site/assets/landing/styles/dialog.css
index 96eda8c3..3468f2f9 100644
--- a/examples/official-site/assets/landing/styles/dialog.css
+++ b/examples/official-site/assets/landing/styles/dialog.css
@@ -4,15 +4,19 @@
width: min(90vw, 440px);
max-height: 85svh;
overflow: auto;
- color: var(--foreground);
- background: #0b141bec;
- border: 1px solid #355766;
+ color: var(--tblr-body-color);
+ background: color-mix(in srgb, var(--tblr-body-bg) 92.55%, transparent);
+ border: 1px solid var(--tblr-body-bg);
border-radius: 12px;
padding: 30px;
backdrop-filter: blur(24px);
}
.navigation-dialog::backdrop {
- background: #02090de0;
+ background: color-mix(
+ in srgb,
+ color-mix(in srgb, var(--tblr-body-bg), var(--tblr-black) 70%) 87.84%,
+ transparent
+ );
backdrop-filter: blur(10px);
}
.navigation-dialog h2 {
@@ -21,7 +25,7 @@
letter-spacing: -1px;
}
.navigation-dialog p {
- color: #8b9fa5;
+ color: var(--tblr-muted-color);
}
.navigation-dialog nav {
display: flex;
@@ -31,10 +35,11 @@
.navigation-dialog nav a {
font-size: 20px;
padding: 17px 0;
- border-top: 1px solid #43626a44;
+ border-top: 1px solid
+ color-mix(in srgb, var(--tblr-body-bg) 26.67%, transparent);
}
.navigation-dialog nav a:hover {
- color: var(--cyan);
+ color: var(--tblr-primary);
}
.dialog-close {
min-width: 44px;
diff --git a/examples/official-site/assets/landing/styles/frontend.css b/examples/official-site/assets/landing/styles/frontend.css
index 4998ee65..5153dc7c 100644
--- a/examples/official-site/assets/landing/styles/frontend.css
+++ b/examples/official-site/assets/landing/styles/frontend.css
@@ -1,11 +1,11 @@
/* Four frontend views pair concise copy with diagrams and deployment links. */
.deployment-section {
- --section-color: #2bc5cf;
+ --section-color: var(--tblr-blue);
/* Match the solid wave at the edge, then retain the section's diagonal gradient. */
background:
linear-gradient(180deg, var(--section-color), transparent 180px),
- linear-gradient(125deg, #29d6df, #2ba4b4);
- color: #071322;
+ linear-gradient(125deg, var(--tblr-blue), var(--tblr-primary));
+ color: var(--tblr-bg-surface);
padding-top: 65px;
padding-bottom: 150px;
}
@@ -15,7 +15,7 @@
gap: 25px;
}
.deployment-section .eyebrow {
- color: #104759;
+ color: var(--tblr-bg-surface-tertiary);
}
.deployment-section h2 {
font-size: clamp(70px, 9vw, 125px);
@@ -43,8 +43,9 @@
gap: 16px;
padding: 25px;
color: inherit;
- background: #07132208;
- border: 1px solid #10475966;
+ background: color-mix(in srgb, var(--tblr-bg-surface) 3.14%, transparent);
+ border: 1px solid
+ color-mix(in srgb, var(--tblr-bg-surface-tertiary) 40.00%, transparent);
border-radius: 15px;
text-align: left;
cursor: pointer;
@@ -56,13 +57,14 @@
}
.frontend-tabs button:hover {
transform: translateY(-3px);
- background: #07132216;
+ background: color-mix(in srgb, var(--tblr-bg-surface) 8.63%, transparent);
}
.frontend-tabs button[aria-selected="true"] {
- background: #080d19;
- color: #f6f8fa;
- border-color: #080d19;
- box-shadow: 0 12px 25px #07132225;
+ background: var(--tblr-body-bg);
+ color: var(--tblr-light);
+ border-color: var(--tblr-body-bg);
+ box-shadow: 0 12px 25px
+ color-mix(in srgb, var(--tblr-bg-surface) 14.51%, transparent);
}
.frontend-tabs svg {
width: 24px;
@@ -80,15 +82,16 @@
.frontend-tabs small {
display: block;
margin-top: 5px;
- font: 13px / 1.4 var(--mono);
+ font: 13px / 1.4 var(--tblr-font-monospace);
opacity: 0.75;
}
.frontend-panels {
border-radius: 20px;
padding: 55px 45px;
- background: #080d19;
- color: #eef1f7;
- box-shadow: 0 25px 45px #07132220;
+ background: var(--tblr-body-bg);
+ color: var(--tblr-light);
+ box-shadow: 0 25px 45px
+ color-mix(in srgb, var(--tblr-bg-surface) 12.55%, transparent);
}
.frontend-panel {
display: grid;
@@ -107,12 +110,12 @@
.frontend-copy p {
font-size: 17px;
line-height: 1.5;
- color: #a6afc3;
+ color: var(--tblr-secondary-color);
margin: 0 0 18px;
}
.frontend-copy a {
- color: #79b6e8;
- font: 14px / 1.5 var(--mono);
+ color: var(--tblr-primary);
+ font: 14px / 1.5 var(--tblr-font-monospace);
text-underline-offset: 5px;
}
.frontend-copy a svg {
@@ -122,23 +125,23 @@
}
.feature-node {
padding: 18px 20px;
- background: #101524;
- border: 1px solid #ffffff24;
+ background: var(--tblr-bg-surface);
+ border: 1px solid color-mix(in srgb, var(--tblr-white) 14.12%, transparent);
border-radius: 12px;
- font: 19px / 1.35 var(--mono);
- color: #dce1ef;
+ font: 19px / 1.35 var(--tblr-font-monospace);
+ color: var(--tblr-body-color);
text-decoration: none;
}
.feature-node small {
display: block;
- color: #889bbd;
+ color: var(--tblr-muted-color);
font-size: 14px;
margin-top: 7px;
}
.feature-result {
- background: linear-gradient(120deg, #29d6df, #2ba4b4);
- color: #071322;
- font: 700 20px / 1.2 var(--brand-font);
+ background: linear-gradient(120deg, var(--tblr-blue), var(--tblr-primary));
+ color: var(--tblr-bg-surface);
+ font: 700 20px / 1.2 var(--tblr-font-sans-serif);
}
.customize-diagram,
.ship-diagram {
@@ -150,8 +153,8 @@
}
.customize-diagram b,
.ship-diagram b {
- color: #889bbd;
- font: 16px var(--mono);
+ color: var(--tblr-muted-color);
+ font: 16px var(--tblr-font-monospace);
}
.customize-diagram .feature-node {
padding: 18px 16px;
@@ -168,12 +171,12 @@
}
.ship-options a:hover,
.ship-options a:focus-visible {
- border-color: #79b6e8;
- background: #79b6e810;
+ border-color: var(--tblr-primary);
+ background: color-mix(in srgb, var(--tblr-primary) 6.27%, transparent);
}
.frontend-panel .frontend-footnote {
- color: #889bbd;
- font: 13px / 1.5 var(--mono);
+ color: var(--tblr-muted-color);
+ font: 13px / 1.5 var(--tblr-font-monospace);
margin: 10px 0 0;
}
#ship-panel > .frontend-footnote {
@@ -181,8 +184,8 @@
}
.instant-diagram {
position: relative;
- font: 14px / 1.5 var(--mono);
- color: #889bbd;
+ font: 14px / 1.5 var(--tblr-font-monospace);
+ color: var(--tblr-muted-color);
}
.instant-diagram p {
margin: 0 0 12px;
@@ -196,21 +199,21 @@
border-radius: 7px;
}
.render-timeline > span {
- --stage-color: #27314b;
+ --stage-color: var(--tblr-primary);
position: relative;
- background: var(--stage-color);
+ background: color-mix(in srgb, var(--stage-color) 45%, var(--tblr-body-bg));
padding: 10px 0;
text-align: center;
- color: #b2bbd0;
+ color: var(--tblr-secondary-color);
}
.render-timeline > span:nth-child(2) {
- --stage-color: #314e64;
+ --stage-color: var(--tblr-blue);
}
.render-timeline > span:nth-child(3) {
- --stage-color: #3c4863;
+ --stage-color: var(--tblr-indigo);
}
.render-timeline > span:nth-child(4) {
- --stage-color: #4a4e5e;
+ --stage-color: var(--tblr-primary);
}
.render-timeline > span > span,
.stream-timeline > span {
@@ -225,7 +228,7 @@
pointer-events: none;
}
.timeline-fill {
- background: var(--stage-color);
+ background: color-mix(in srgb, var(--stage-color) 45%, var(--tblr-body-bg));
}
.stream-timeline {
position: relative;
@@ -236,21 +239,30 @@
overflow: hidden;
display: grid;
place-items: center;
- color: #081b2b;
+ color: var(--tblr-bg-surface-secondary);
font-size: 13px;
white-space: nowrap;
- background: linear-gradient(90deg, #408fa2, #6c9ec3);
+ background: linear-gradient(90deg, var(--tblr-primary), var(--tblr-blue));
}
.stream-fill {
- background: linear-gradient(90deg, #39abc0, #79b6e8);
+ background: linear-gradient(90deg, var(--tblr-primary), var(--tblr-primary));
}
.stream-sheen {
width: 35%;
- background: linear-gradient(90deg, transparent, #ffffff50, transparent);
+ background: linear-gradient(
+ 90deg,
+ transparent,
+ color-mix(in srgb, var(--tblr-white) 31.37%, transparent),
+ transparent
+ );
opacity: 0;
}
.instant-diagram[data-stream-motion] .render-timeline > span {
- background: color-mix(in srgb, var(--stage-color) 30%, transparent);
+ background: var(--tblr-bg-surface-tertiary);
+}
+/* Keep the rail distinct from its fill; labels remain readable at every stage. */
+.instant-diagram[data-stream-motion] .stream-timeline {
+ background: color-mix(in srgb, var(--tblr-primary) 35%, var(--tblr-light));
}
.timeline-toggle {
position: absolute;
@@ -261,18 +273,18 @@
width: 28px;
height: 28px;
padding: 0;
- border: 1px solid #ffffff24;
+ border: 1px solid color-mix(in srgb, var(--tblr-white) 14.12%, transparent);
border-radius: 50%;
background: transparent;
- color: #889bbd;
+ color: var(--tblr-muted-color);
cursor: pointer;
}
.timeline-toggle:hover {
- color: #eef1f7;
- border-color: #79b6e8;
+ color: var(--tblr-light);
+ border-color: var(--tblr-primary);
}
.timeline-toggle:focus-visible {
- outline: 2px solid #79b6e8;
+ outline: 2px solid var(--tblr-primary);
outline-offset: 3px;
}
.timeline-toggle svg {
@@ -298,10 +310,10 @@
margin-top: 24px;
}
.render-labels span {
- border: 1px solid #ffffff24;
+ border: 1px solid color-mix(in srgb, var(--tblr-white) 14.12%, transparent);
border-radius: 12px;
padding: 10px 14px;
- color: #dce1ef;
+ color: var(--tblr-body-color);
font-size: 12px;
}
.safety-diagram {
@@ -318,8 +330,8 @@
display: flex;
align-items: center;
gap: 14px;
- font: 14px var(--mono);
- color: #4fd2b0;
+ font: 14px var(--tblr-font-monospace);
+ color: var(--hljs-cosmic-string);
}
.attack-list .feature-node {
width: 160px;
@@ -329,12 +341,13 @@
.attack-list i {
flex: 1;
min-width: 15px;
- border-top: 2px dashed #ffffff40;
+ border-top: 2px dashed
+ color-mix(in srgb, var(--tblr-white) 25.10%, transparent);
}
.safety-diagram > svg {
width: 105px;
height: 130px;
- color: #79b6e8;
+ color: var(--tblr-primary);
}
@keyframes frontend-enter {
from {
diff --git a/examples/official-site/assets/landing/styles/hero.css b/examples/official-site/assets/landing/styles/hero.css
index f10dc8e5..b9b3240d 100644
--- a/examples/official-site/assets/landing/styles/hero.css
+++ b/examples/official-site/assets/landing/styles/hero.css
@@ -3,7 +3,7 @@
position: absolute;
right: calc(var(--composition-left) + var(--composition-width) * 0.045);
top: 16%;
- z-index: 15;
+ z-index: 21;
width: clamp(248px, 18vw, 330px);
opacity: var(--intro-opacity);
transform: translateX(calc(var(--progress) * 30px));
@@ -13,22 +13,28 @@
position: relative;
overflow: hidden;
padding: 19px 24px 20px;
- border: 0;
- border-radius: 3px;
+ border: 1px solid color-mix(in srgb, var(--tblr-primary) 20.00%, transparent);
+ border-radius: 6px;
background:
repeating-linear-gradient(
0deg,
- #b7d0da01 0,
- #b7d0da01 1px,
+ color-mix(in srgb, var(--tblr-gray-300) 0.39%, transparent) 0,
+ color-mix(in srgb, var(--tblr-gray-300) 0.39%, transparent) 1px,
transparent 1px,
transparent 3px
),
- linear-gradient(135deg, #020609, #0a1217 72%, #0b1419);
+ linear-gradient(
+ 135deg,
+ color-mix(in srgb, var(--tblr-body-bg), var(--tblr-black) 70%),
+ var(--tblr-body-bg) 72%,
+ var(--tblr-body-bg)
+ );
box-shadow:
- inset 0 5px 9px #000b,
- inset 3px 0 6px #0007,
- inset -1px -1px 1px #93b0be15,
- 0 1px 0 #7895a51b;
+ inset 0 5px 9px color-mix(in srgb, var(--tblr-black) 73.33%, transparent),
+ inset 3px 0 6px color-mix(in srgb, var(--tblr-black) 46.67%, transparent),
+ inset -1px -1px 1px
+ color-mix(in srgb, var(--tblr-muted-color) 8.24%, transparent),
+ 0 1px 0 color-mix(in srgb, var(--tblr-muted-color) 10.59%, transparent);
}
.metal-recess:before {
content: "";
@@ -36,8 +42,9 @@
inset: 0;
border-radius: inherit;
box-shadow:
- inset 0 1px 1px #000,
- inset 0 -1px 0 #afc8d313;
+ inset 0 1px 1px var(--tblr-black),
+ inset 0 -1px 0
+ color-mix(in srgb, var(--tblr-secondary-color) 7.45%, transparent);
pointer-events: none;
}
.metal-recess:after {
@@ -45,26 +52,29 @@
position: absolute;
inset: 2px;
border-radius: 2px;
- border-top: 1px solid #0007;
- border-left: 1px solid #0003;
+ border-top: 1px solid
+ color-mix(in srgb, var(--tblr-black) 46.67%, transparent);
+ border-left: 1px solid
+ color-mix(in srgb, var(--tblr-black) 20.00%, transparent);
pointer-events: none;
}
.metal-recess code {
position: relative;
display: grid;
gap: 6px;
- color: #83969f;
- font: clamp(13px, 0.82vw, 16px) / 1.65 var(--mono);
+ color: var(--tblr-muted-color);
+ font: clamp(13px, 0.82vw, 16px) / 1.65 var(--tblr-font-monospace);
letter-spacing: -0.025em;
text-shadow:
- 0 -1px 1px #000,
- 0 1px 0 #bfd7e01b;
+ 0 -1px 1px var(--tblr-black),
+ 0 1px 0 color-mix(in srgb, var(--tblr-gray-300) 10.59%, transparent);
}
.metal-recess code b {
font-weight: 400;
- color: #607781;
+ color: color-mix(in srgb, var(--tblr-dark), var(--tblr-white) 40%);
}
.hero-copy {
+ z-index: 21;
position: absolute;
top: 13%;
left: calc(var(--composition-left) + var(--composition-width) * 0.042);
@@ -107,9 +117,14 @@
}
.title-select,
.title-from {
- color: #e9e8e7;
+ color: var(--tblr-light);
text-shadow: none;
- background: linear-gradient(180deg, #fafafa 0%, #eceeef 42%, #bfc5c8 100%);
+ background: linear-gradient(
+ 180deg,
+ var(--tblr-light) 0%,
+ var(--tblr-light) 42%,
+ var(--tblr-secondary-color) 100%
+ );
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
@@ -118,11 +133,18 @@
font-size: 0.985em;
letter-spacing: 0;
padding: 4px 0 9px;
- background: linear-gradient(180deg, #83deea 0%, #58c8da 48%, #39adc4 100%);
+ background: linear-gradient(
+ 180deg,
+ var(--tblr-link-hover-color) 0%,
+ var(--tblr-primary) 48%,
+ var(--tblr-muted-color) 100%
+ );
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
- filter: drop-shadow(0 2px 2px #030a0f8c);
+ filter: drop-shadow(
+ 0 2px 2px color-mix(in srgb, var(--tblr-body-bg) 54.90%, transparent)
+ );
}
.title-from {
font-size: 0.985em;
@@ -134,7 +156,7 @@
top: 72%;
width: fit-content;
max-width: calc(var(--composition-width) * 0.5);
- z-index: 10;
+ z-index: 21;
display: flex;
align-items: center;
gap: clamp(28px, 3vw, 60px);
@@ -145,91 +167,34 @@
.intro-row p {
font-size: clamp(17px, 1.5vw, 27px);
line-height: 1.35;
- font-weight: 550;
+ font-weight: 450;
letter-spacing: -0.01em;
margin: 0;
flex: 1;
- color: #d5d7d9;
+ color: var(--tblr-secondary-color);
white-space: nowrap;
- text-shadow: 0 2px 4px #0009;
+ text-shadow: 0 2px 4px
+ color-mix(in srgb, var(--tblr-black) 60.00%, transparent);
}
.run-link {
- display: flex;
+ display: inline-flex;
align-items: center;
- gap: 17px;
- color: #5fcfe2;
- font-size: clamp(20px, 1.8vw, 29px);
+ justify-content: center;
+ gap: 16px;
+ padding: 16px 24px;
+ border: 1px solid
+ color-mix(in srgb, var(--tblr-link-hover-color) 33.33%, transparent);
+ border-radius: 6px;
+ background: var(--tblr-primary);
+ color: var(--tblr-bg-surface);
+ font-size: clamp(18px, 1.4vw, 23px);
+ font-weight: 600;
white-space: nowrap;
- transition: color 0.25s;
- letter-spacing: -0.01em;
-}
-.run-orbit {
- width: 70px;
- height: 70px;
- position: relative;
- display: grid;
- place-items: center;
- border: 1px solid #4fb6ca70;
- border-radius: 50%;
- transition: box-shadow 0.3s;
-}
-.run-orbit > span {
- position: absolute;
- inset: 6px;
- border: 5px solid #56bfd0;
- border-radius: 50%;
- display: grid;
- place-items: center;
- box-shadow:
- inset 0 0 12px #42daf31a,
- 0 0 13px #42daf31a;
- transition:
- background 0.3s,
- color 0.3s;
-}
-.run-orbit > span .icon {
- margin-left: 3px;
- --icon-size: 21px;
-}
-.run-orbit > i {
- position: absolute;
- inset: -6px;
- border-top: 2px solid #57c5db;
- border-right: 2px solid transparent;
- border-bottom: 2px solid #57c5db;
- border-left: 2px solid transparent;
- border-radius: 50%;
- transform: rotate(25deg);
- transition: transform 0.55s;
-}
-.run-orbit > i:before,
-.run-orbit > i:after {
- content: "";
- position: absolute;
- width: 4px;
- height: 4px;
- top: 10px;
- right: 9px;
- background: #6bd7e9;
- box-shadow: 0 0 8px #43c9ed;
- border-radius: 50%;
-}
-.run-orbit > i:after {
- top: auto;
- right: auto;
- bottom: 10px;
- left: 9px;
+ transition: background-color 0.2s;
}
.run-link:hover {
- color: #b7f2ff;
-}
-.run-link:hover .run-orbit {
- box-shadow: 0 0 32px #59d7ed22;
-}
-.run-link:hover .run-orbit > i {
- transform: rotate(100deg);
+ background: var(--tblr-link-hover-color);
}
-.run-link:hover .run-orbit > span {
- background: #51c1d0;
- color: #07171c;
+.run-link .icon {
+ --icon-size: 22px;
}
diff --git a/examples/official-site/assets/landing/styles/interaction.css b/examples/official-site/assets/landing/styles/interaction.css
index 6c269858..8af27d96 100644
--- a/examples/official-site/assets/landing/styles/interaction.css
+++ b/examples/official-site/assets/landing/styles/interaction.css
@@ -24,7 +24,7 @@
cursor: grabbing;
}
.model-interaction:focus-visible {
- outline: 1px dashed #5ed7df77;
+ outline: 1px dashed color-mix(in srgb, var(--tblr-blue) 46.67%, transparent);
}
.scene-error {
position: absolute;
@@ -32,12 +32,12 @@
top: 53%;
z-index: 16;
text-align: center;
- color: #9fb6be;
- font: 12px var(--mono);
+ color: var(--tblr-muted-color);
+ font: 12px var(--tblr-font-monospace);
max-width: 290px;
padding: 20px;
- background: #080f14ed;
- border: 1px solid #26444e;
+ background: color-mix(in srgb, var(--tblr-body-bg) 92.94%, transparent);
+ border: 1px solid var(--tblr-border-color);
border-radius: 8px;
line-height: 1.6;
}
@@ -46,12 +46,15 @@
}
.scene-error p:first-child {
font-size: 16px;
- color: #deecee;
+ color: var(--tblr-body-color);
}
.scene-error button {
- border: 1px solid #458897;
- color: #70c8da;
- background: #15313c;
+ border: 1px solid var(--tblr-blue);
+ color: var(--tblr-blue);
+ background: var(
+ --tblr-success-lt,
+ color-mix(in srgb, var(--tblr-success) 20%, var(--tblr-body-bg))
+ );
padding: 8px 17px;
border-radius: 50px;
}
diff --git a/examples/official-site/assets/landing/styles/navigation.css b/examples/official-site/assets/landing/styles/navigation.css
index 3392e5b6..4551e4c9 100644
--- a/examples/official-site/assets/landing/styles/navigation.css
+++ b/examples/official-site/assets/landing/styles/navigation.css
@@ -18,149 +18,46 @@
display: flex;
align-items: center;
gap: 12px;
- text-shadow: 0 2px 3px #0009;
+ text-shadow: 0 2px 3px
+ color-mix(in srgb, var(--tblr-black) 60.00%, transparent);
}
.brand-symbol {
display: block;
width: 72px;
height: auto;
flex-shrink: 0;
- filter: drop-shadow(0 0 10px #37e5ef1a);
+ filter: drop-shadow(
+ 0 0 10px color-mix(in srgb, var(--tblr-blue) 10.20%, transparent)
+ );
}
-/* Thin, smoked glass with a polished edge. */
+/* Quiet navigation leaves the sculpture as the page's reflective surface. */
.desktop-nav {
display: flex;
align-items: center;
gap: 18px;
}
.desktop-nav .glass-button {
- --glass-x: 50%;
- --glass-y: 20%;
- position: relative;
- isolation: isolate;
display: inline-flex;
align-items: center;
justify-content: center;
- overflow: hidden;
min-height: 46px;
padding: 12px 22px;
- border: 1px solid transparent;
- border-radius: 999px;
+ border-radius: 6px;
font-size: clamp(14px, 1.32vw, 21px);
- color: #d7e0e4;
- background:
- radial-gradient(
- ellipse at var(--glass-x) var(--glass-y),
- #9cdef715,
- transparent 68%
- ),
- linear-gradient(118deg, #b4e9fa0b, transparent 38%, #74c4de08),
- linear-gradient(180deg, #263c482b 0%, #0711178c 54%, #18333d55 100%);
- backdrop-filter: blur(17px) saturate(1.22) brightness(1.08);
- -webkit-backdrop-filter: blur(17px) saturate(1.22) brightness(1.08);
- box-shadow:
- inset 0 1px 1px #e3f6ff19,
- inset 1px 0 1px #a7eaff0c,
- inset 0 -1.5px 1px #62b8cc30,
- inset 0 -5px 9px #56cae508,
- 0 5px 16px #0005,
- 0 1px 2px #0008;
+ color: var(--tblr-gray-300);
transition:
- box-shadow 0.32s,
- color 0.25s,
- transform 0.25s;
-}
-.desktop-nav .glass-button:before {
- content: "";
- position: absolute;
- inset: 0;
- border-radius: inherit;
- padding: 1px;
- background: linear-gradient(
- 135deg,
- #a5d9e638 0%,
- #5c9daf38 22%,
- #142f3f14 46%,
- #46a0b16b 76%,
- #b2edf24d 100%
- );
- -webkit-mask:
- linear-gradient(#000 0 0) content-box,
- linear-gradient(#000 0 0);
- -webkit-mask-composite: xor;
- mask:
- linear-gradient(#000 0 0) content-box,
- linear-gradient(#000 0 0);
- mask-composite: exclude;
- pointer-events: none;
-}
-.desktop-nav .glass-button:after {
- content: "";
- position: absolute;
- top: 1px;
- left: 12%;
- right: 12%;
- height: 42%;
- border-radius: 50% 50% 45% 45% / 15% 15% 75% 75%;
- background: linear-gradient(165deg, #dcf4ff0f, transparent 62%);
- border-top: 1px solid #e9fbff19;
- pointer-events: none;
- opacity: 0.65;
- transition: opacity 0.3s;
-}
-.desktop-nav .glass-button > span {
- position: relative;
- z-index: 1;
- letter-spacing: -0.015em;
- text-shadow:
- 0 1px 2px #000b,
- 0 -1px 0 #ffffff08;
-}
-.desktop-nav .glass-button:hover:after,
-.desktop-nav .glass-button:focus-visible:after {
- opacity: 1;
-}
-.desktop-nav .glass-button:active {
- transform: translateY(0.5px);
- box-shadow:
- inset 0 2px 7px #0005,
- inset 0 -1px 1px #8cdded50,
- 0 1px 7px #0005;
+ color 0.2s,
+ background-color 0.2s;
}
.desktop-nav .glass-button:hover,
.desktop-nav .glass-button:focus-visible {
- color: #f0f9fc;
- transform: translateY(-1px);
- border-color: #67d9ea99;
- background:
- radial-gradient(
- ellipse at var(--glass-x) var(--glass-y),
- #a6e7fa24,
- transparent 72%
- ),
- linear-gradient(180deg, #47718345, #15334163 52%, #21566950);
- box-shadow:
- inset 0 1px 1px #e5f8ff29,
- inset 0 -1.5px 2px #90e8f378,
- inset 0 -6px 12px #75d7ee18,
- 0 5px 17px #0004,
- 0 2px 16px #52d7f12b;
-}
-.desktop-nav .glass-button:hover:before,
-.desktop-nav .glass-button:focus-visible:before {
- background: linear-gradient(
- 137deg,
- #c7f4ff5c,
- #4591a464 25%,
- #2350673b 45%,
- #74e0f6b3 76%,
- #caf8ff8f
- );
+ color: var(--tblr-body-color);
+ background: color-mix(in srgb, var(--tblr-primary) 6.27%, transparent);
}
.mobile-menu {
display: none;
background: none;
border: 0;
- color: #d3e3e6;
+ color: var(--tblr-body-color);
padding: 10px;
}
diff --git a/examples/official-site/assets/landing/styles/responsive.css b/examples/official-site/assets/landing/styles/responsive.css
index 36c7573d..8ea2eb25 100644
--- a/examples/official-site/assets/landing/styles/responsive.css
+++ b/examples/official-site/assets/landing/styles/responsive.css
@@ -8,10 +8,6 @@
.viewport-footer {
bottom: 30px;
}
- .run-orbit {
- height: 82px;
- width: 82px;
- }
}
@media (max-width: 1150px) {
.site-header {
@@ -50,16 +46,6 @@
font-size: 22px;
gap: 12px;
}
- .run-orbit {
- width: 57px;
- height: 57px;
- }
- .run-orbit > span {
- border-width: 4px;
- }
- .run-orbit > span .icon {
- --icon-size: 18px;
- }
.ribbon-sequence code {
font-size: 17px;
}
@@ -106,14 +92,6 @@
font-size: 20px;
gap: 11px;
}
- .run-orbit {
- width: 50px;
- height: 50px;
- }
- .run-orbit > span {
- inset: 5px;
- border-width: 3px;
- }
.scene-error {
right: 6%;
font-size: 12px;
@@ -124,10 +102,7 @@
animation: none;
}
a,
- button,
- .run-orbit,
- .run-orbit > span,
- .run-orbit > i {
+ button {
transition: none;
}
}
@@ -175,9 +150,9 @@
padding: 12px 10px;
border-radius: 5px;
box-shadow:
- inset 0 2px 8px #000b,
- 0 0 0 1px #38c9e52b,
- 0 0 10px #38c9e511;
+ inset 0 2px 8px color-mix(in srgb, var(--tblr-black) 73.33%, transparent),
+ 0 0 0 1px color-mix(in srgb, var(--tblr-blue) 16.86%, transparent),
+ 0 0 10px color-mix(in srgb, var(--tblr-blue) 6.67%, transparent);
}
.metal-recess code {
grid-template-columns: 1fr;
@@ -225,7 +200,8 @@
line-height: 1.35;
white-space: normal;
letter-spacing: 0;
- text-shadow: 0 2px 10px #0009;
+ text-shadow: 0 2px 10px
+ color-mix(in srgb, var(--tblr-black) 60.00%, transparent);
}
.desktop-break {
display: none;
@@ -234,24 +210,13 @@
font-size: 20px;
gap: 13px;
}
- .run-orbit {
- width: clamp(46px, 13vw, 70px);
- height: clamp(46px, 13vw, 70px);
- }
- .run-orbit > span {
- inset: 5px;
- border-width: 3px;
- }
- .run-orbit > span .icon {
- --icon-size: 18px;
- }
.viewport .sql-ribbon {
top: var(--portrait-ribbon-top, 78%);
bottom: auto;
height: 49px;
- transform: rotate(-4deg);
- width: 112%;
- left: -6%;
+ transform: none;
+ width: 92%;
+ left: 4%;
}
.ribbon-sequence a {
gap: 11px;
@@ -264,7 +229,7 @@
.ribbon-sequence .icon {
--icon-size: 17px;
}
- .ribbon-dot {
+ .ribbon-separator {
margin-left: 23px;
}
.viewport-footer {
@@ -283,10 +248,19 @@
.cinematic-shade {
background: linear-gradient(
180deg,
- #05090d33,
+ color-mix(
+ in srgb,
+ color-mix(in srgb, var(--tblr-body-bg), var(--tblr-black) 70%) 20.00%,
+ transparent
+ ),
transparent 30%,
- #04070b22 85%,
- #080e1188
+ color-mix(
+ in srgb,
+ color-mix(in srgb, var(--tblr-body-bg), var(--tblr-black) 70%) 13.33%,
+ transparent
+ )
+ 85%,
+ color-mix(in srgb, var(--tblr-body-bg) 53.33%, transparent)
);
}
}
@@ -310,13 +284,18 @@
height: 44px;
padding: 0;
flex-shrink: 0;
- border: 1px solid #8acbdd35;
+ border: 1px solid
+ color-mix(in srgb, var(--tblr-primary) 20.78%, transparent);
border-radius: 50%;
- background: linear-gradient(150deg, #b2e6fa14, #06121d55);
+ background: linear-gradient(
+ 150deg,
+ color-mix(in srgb, var(--tblr-link-hover-color) 7.84%, transparent),
+ color-mix(in srgb, var(--tblr-body-bg) 33.33%, transparent)
+ );
box-shadow:
- inset 0 1px 1px #d9f5ff21,
- inset 0 -1px 1px #57bdcc4d,
- 0 4px 12px #0005;
+ inset 0 1px 1px color-mix(in srgb, var(--tblr-light) 12.94%, transparent),
+ inset 0 -1px 1px color-mix(in srgb, var(--tblr-blue) 30.20%, transparent),
+ 0 4px 12px color-mix(in srgb, var(--tblr-black) 33.33%, transparent);
backdrop-filter: blur(15px);
}
}
@@ -356,10 +335,6 @@
.intro-row p {
font-size: 17px;
}
- .run-orbit {
- width: 44px;
- height: 44px;
- }
.viewport .sql-ribbon {
height: 48px;
bottom: 7%;
diff --git a/examples/official-site/assets/landing/styles/ribbon.css b/examples/official-site/assets/landing/styles/ribbon.css
index ddb8f5d3..a3b184a9 100644
--- a/examples/official-site/assets/landing/styles/ribbon.css
+++ b/examples/official-site/assets/landing/styles/ribbon.css
@@ -2,22 +2,22 @@
.sql-ribbon {
position: absolute;
bottom: 4.5%;
- left: -4%;
- width: 110%;
- z-index: 12;
+ left: 4%;
+ width: 92%;
+ z-index: 21;
height: clamp(54px, 5.6svh, 76px);
display: flex;
align-items: center;
- transform: rotate(-4deg);
+ transform: none;
transform-origin: center;
- border-top: 1px solid #4195a5;
- border-bottom: 1px solid #23515e;
- background: linear-gradient(180deg, #15252cc7, #091118ed 45%, #0a1117f5);
- box-shadow:
- 0 -6px 23px #41bbd513,
- inset 0 1px 0 #66cfe12b,
- inset 0 7px 13px #0006,
- 0 8px 24px #0005;
+ border: 1px solid color-mix(in srgb, var(--tblr-primary) 18.82%, transparent);
+ border-radius: 8px;
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--tblr-bg-surface-secondary) 78.04%, transparent),
+ color-mix(in srgb, var(--tblr-body-bg) 92.94%, transparent) 45%,
+ color-mix(in srgb, var(--tblr-body-bg) 96.08%, transparent)
+ );
opacity: var(--ribbon-opacity);
overflow: hidden;
}
@@ -36,24 +36,26 @@
align-items: center;
gap: 15px;
padding: 16px 0 16px 38px;
- color: #8b9eaa;
+ color: var(--tblr-muted-color);
text-shadow:
- 0 -1px 2px #000,
- 0 1px 0 #dfedf51a;
+ 0 -1px 2px var(--tblr-black),
+ 0 1px 0 color-mix(in srgb, var(--tblr-light) 10.20%, transparent);
transition: color 0.3s;
white-space: nowrap;
}
.ribbon-sequence code {
- font: 600 clamp(15px, 1.37vw, 22px) var(--mono);
+ font: 600 clamp(15px, 1.37vw, 22px) var(--tblr-font-monospace);
letter-spacing: -0.5px;
}
-.ribbon-dot {
+.ribbon-separator {
margin: 0 0 0 33px;
- font-size: 20px;
- color: #89bac7;
+ width: 1px;
+ height: 28px;
+ flex-shrink: 0;
+ background: var(--tblr-border-color);
}
.ribbon-sequence a:hover {
- color: #87d6e8;
+ color: var(--tblr-primary);
}
.viewport-footer {
position: absolute;
@@ -65,11 +67,11 @@
align-items: center;
justify-content: space-between;
gap: 20px;
- font: 12px var(--mono);
- color: #809da7;
+ font: 12px var(--tblr-font-monospace);
+ color: var(--tblr-muted-color);
}
.viewport-footer > a:hover {
- color: var(--cyan);
+ color: var(--tblr-primary);
}
[data-enhanced] .ribbon-track {
animation: ribbon-travel 44s linear infinite;
diff --git a/examples/official-site/assets/landing/styles/scene.css b/examples/official-site/assets/landing/styles/scene.css
index 917173a2..04577017 100644
--- a/examples/official-site/assets/landing/styles/scene.css
+++ b/examples/official-site/assets/landing/styles/scene.css
@@ -2,7 +2,7 @@
.sqlpage-world {
position: relative;
overflow-x: clip;
- background: var(--ink);
+ background: var(--tblr-body-bg);
}
.experience {
position: relative;
@@ -26,7 +26,12 @@
overflow: hidden;
isolation: isolate;
background:
- radial-gradient(ellipse at 79% 26%, #183d4755, transparent 57%), #080e11;
+ radial-gradient(
+ ellipse at 79% 26%,
+ color-mix(in srgb, var(--tblr-border-color) 33.33%, transparent),
+ transparent 57%
+ ),
+ var(--tblr-body-bg);
}
/* One sculpture follows the authored section anchors. */
.scene-layer {
@@ -85,8 +90,25 @@
z-index: -2;
pointer-events: none;
background:
- linear-gradient(90deg, #05090da6, transparent 66%),
- radial-gradient(ellipse at 53% 49%, transparent 39%, #04070b66 100%);
+ linear-gradient(
+ 90deg,
+ color-mix(
+ in srgb,
+ color-mix(in srgb, var(--tblr-body-bg), var(--tblr-black) 70%) 65.10%,
+ transparent
+ ),
+ transparent 66%
+ ),
+ radial-gradient(
+ ellipse at 53% 49%,
+ transparent 39%,
+ color-mix(
+ in srgb,
+ color-mix(in srgb, var(--tblr-body-bg), var(--tblr-black) 70%) 40.00%,
+ transparent
+ )
+ 100%
+ );
opacity: calc(0.9 - var(--progress) * 0.3);
}
.surface-finish {
@@ -97,8 +119,8 @@
opacity: 0.2;
background: repeating-linear-gradient(
0deg,
- #a9ccd203 0,
- #a9ccd203 1px,
+ color-mix(in srgb, var(--tblr-secondary-color) 1.18%, transparent) 0,
+ color-mix(in srgb, var(--tblr-secondary-color) 1.18%, transparent) 1px,
transparent 1px,
transparent 3px
);
diff --git a/examples/official-site/assets/landing/styles/section-motion.css b/examples/official-site/assets/landing/styles/section-motion.css
index b5e80867..b3008c71 100644
--- a/examples/official-site/assets/landing/styles/section-motion.css
+++ b/examples/official-site/assets/landing/styles/section-motion.css
@@ -16,7 +16,7 @@
position: absolute;
inset: 0;
z-index: 2;
- background: #030817;
+ background: var(--tblr-body-bg);
opacity: calc(var(--section-cover, 0) * 0.16);
pointer-events: none;
}
diff --git a/examples/official-site/assets/landing/styles/sections.css b/examples/official-site/assets/landing/styles/sections.css
index 6710501a..be3ed6ae 100644
--- a/examples/official-site/assets/landing/styles/sections.css
+++ b/examples/official-site/assets/landing/styles/sections.css
@@ -1,6 +1,6 @@
/* Section composition and shared content primitives; section-motion.css owns scroll poses. */
.landing-section {
- --section-color: #080d19;
+ --section-color: var(--tblr-body-bg);
position: relative;
margin-top: -1px;
padding: 110px 5.5% 100px;
@@ -19,11 +19,11 @@
}
.landing-section h2 em {
font-style: normal;
- color: #29c8d7;
+ color: var(--tblr-blue);
}
.eyebrow {
- font: 13px / 1.65 var(--mono);
- color: #5b9ba9;
+ font: 13px / 1.65 var(--tblr-font-monospace);
+ color: var(--tblr-primary);
}
.section-heading {
display: grid;
@@ -58,15 +58,15 @@
}
.demo-section {
padding-top: 70px;
- --section-color: #f6f8fa;
- background: #f6f8fa;
- color: #0b1020;
+ --section-color: var(--tblr-light);
+ background: var(--tblr-light);
+ color: var(--tblr-bg-surface);
padding-bottom: 85px;
color-scheme: light;
overflow-anchor: none;
}
.demo-section .eyebrow {
- color: #3f7480;
+ color: var(--tblr-blue);
}
.demo-section h2 {
font-size: clamp(60px, 7.7vw, 106px);
@@ -75,8 +75,8 @@
margin-top: 30px;
}
.purpose-section {
- --section-color: #101324;
- background: #101324;
+ --section-color: var(--tblr-bg-surface);
+ background: var(--tblr-bg-surface);
padding-bottom: 105px;
}
.purpose-section h2 {
@@ -90,7 +90,7 @@
margin: 0 0 40px;
}
.purpose-intro > p:first-child {
- color: #a6abbb;
+ color: var(--tblr-muted-color);
font-size: 21px;
line-height: 1.45;
}
@@ -114,8 +114,8 @@
.segmented button {
border: 0;
border-radius: 7px;
- background: #080d1c;
- color: #d6dfeb;
+ background: var(--tblr-body-bg);
+ color: var(--tblr-body-color);
padding: 12px 16px;
font-weight: 600;
font-size: 13px;
@@ -125,8 +125,8 @@
}
.segmented button[aria-pressed="true"],
.segmented button[aria-selected="true"] {
- background: #29c8d7;
- color: #07121d;
+ background: var(--tblr-blue);
+ color: var(--tblr-dark);
}
.stack-list {
list-style: none;
@@ -141,7 +141,7 @@
gap: 10px;
padding: 20px 16px;
border: 1px solid transparent;
- background: #262f49;
+ background: var(--tblr-bg-surface-tertiary);
border-radius: 8px;
font-size: 15px;
transition:
@@ -149,12 +149,12 @@
background 0.4s;
}
.stack-list small {
- font: 11px var(--mono);
- color: #a9b5c7;
+ font: 11px var(--tblr-font-monospace);
+ color: var(--tblr-secondary-color);
}
.stack-list .database-layer {
- border-color: #29c8d7;
- background: #0b1121;
+ border-color: var(--tblr-blue);
+ background: var(--tblr-bg-surface);
}
.stack-list .stack-detail {
display: grid;
@@ -173,7 +173,7 @@
.stack-detail [data-stack-copy="sqlpage"] {
opacity: 0;
transform: translateY(8px);
- color: #63d6df;
+ color: var(--tblr-blue);
}
.stack-list li:nth-child(2) {
--stack-delay: 45ms;
@@ -185,8 +185,8 @@
--stack-delay: 135ms;
}
.stack-comparison[data-stack="sqlpage"] [data-stack-layer] {
- border-color: #29c8d750;
- background: #102333;
+ border-color: color-mix(in srgb, var(--tblr-blue) 31.37%, transparent);
+ background: var(--tblr-bg-surface-secondary);
}
.stack-comparison[data-stack="sqlpage"] [data-stack-copy="typical"] {
opacity: 0;
@@ -202,16 +202,16 @@
gap: 18px;
}
.benefit {
- border: 1px solid #303950;
+ border: 1px solid var(--tblr-border-color);
border-radius: 15px;
padding: 24px;
- background: #080d19;
+ background: var(--tblr-body-bg);
transition:
border-color 0.2s,
transform 0.2s;
}
.benefit:hover {
- border-color: #29c8d7;
+ border-color: var(--tblr-blue);
transform: translateY(-3px);
}
.benefit h3 {
@@ -223,41 +223,44 @@
.benefit p {
font-size: 16px;
line-height: 1.45;
- color: #a6b0c2;
+ color: var(--tblr-secondary-color);
}
.benefit small {
- font: bold 12px var(--mono);
+ font: bold 12px var(--tblr-font-monospace);
}
.benefit.featured {
- background: linear-gradient(120deg, #28d4dd, #2ba6b6);
- color: #071120;
+ background: linear-gradient(120deg, var(--tblr-blue), var(--tblr-primary));
+ color: var(--tblr-bg-surface);
border-color: transparent;
}
.benefit.featured p {
- color: #13384a;
+ color: var(
+ --tblr-success-lt,
+ color-mix(in srgb, var(--tblr-success) 20%, var(--tblr-dark))
+ );
}
.chip {
display: inline-flex;
align-items: center;
gap: 5px;
- border: 1px solid #2c8193;
+ border: 1px solid var(--tblr-blue);
padding: 5px 9px;
margin: 3px 0;
border-radius: 999px;
- font: 11px / 1.3 var(--mono);
- color: #41c6d6;
+ font: 11px / 1.3 var(--tblr-font-monospace);
+ color: var(--tblr-blue);
}
a.chip:hover {
- background: #29c8d7;
- color: #07121d;
+ background: var(--tblr-blue);
+ color: var(--tblr-dark);
}
.chip svg {
width: 14px;
height: 14px;
}
.databases-section {
- --section-color: #27314b;
- background: #27314b;
+ --section-color: var(--tblr-bg-surface-tertiary);
+ background: var(--tblr-bg-surface-tertiary);
}
.database-grid {
display: grid;
@@ -266,12 +269,12 @@ a.chip:hover {
margin-top: 120px;
}
.database-grid a {
- border: 1px solid #2acbda;
+ border: 1px solid var(--tblr-blue);
border-radius: 12px;
- background: #08111e;
- color: #32c9d9;
+ background: var(--tblr-body-bg);
+ color: var(--tblr-blue);
padding: 28px 22px;
- font: 24px var(--mono);
+ font: 24px var(--tblr-font-monospace);
transform: rotate(-2deg);
transition:
transform 0.2s,
@@ -284,14 +287,15 @@ a.chip:hover {
.database-grid a:hover,
.database-grid a:focus-visible {
transform: rotate(0) translateY(-5px);
- background: #102337;
- box-shadow: 0 5px 25px #23c8d522;
+ background: var(--tblr-bg-surface-tertiary);
+ box-shadow: 0 5px 25px
+ color-mix(in srgb, var(--tblr-blue) 13.33%, transparent);
}
.database-grid small {
display: block;
margin-top: 9px;
font-size: 11px;
- color: #588a9c;
+ color: var(--tblr-primary);
}
.emoji {
font-family:
@@ -304,7 +308,7 @@ a.chip:hover {
height: 12px;
}
.commit-section {
- background: #080d19;
+ background: var(--tblr-body-bg);
display: flex;
}
/* Keep the footer at the end of the final cover, even on tall screens. */
@@ -314,7 +318,7 @@ a.chip:hover {
flex-direction: column;
}
.commit-section h2 {
- color: #2fc7d4;
+ color: var(--tblr-blue);
}
.commit-action {
display: flex;
@@ -324,8 +328,8 @@ a.chip:hover {
}
.primary-button {
display: inline-block;
- background: linear-gradient(110deg, #2bd5df, #289cac);
- color: #071221;
+ background: linear-gradient(110deg, var(--tblr-blue), var(--tblr-primary));
+ color: var(--tblr-bg-surface);
border-radius: 8px;
padding: 22px 36px;
font-size: 24px;
@@ -339,11 +343,12 @@ a.chip:hover {
}
.primary-button:hover {
transform: translateY(-3px);
- box-shadow: 0 6px 28px #2acddb44;
+ box-shadow: 0 6px 28px
+ color-mix(in srgb, var(--tblr-blue) 26.67%, transparent);
}
.landing-footer {
margin-top: auto;
- border-top: 1px solid #202739;
+ border-top: 1px solid var(--tblr-bg-surface-tertiary);
padding-top: 38px;
display: flex;
justify-content: space-between;
@@ -359,10 +364,10 @@ a.chip:hover {
flex-wrap: wrap;
gap: 22px;
font-size: 12px;
- color: #90a8bd;
+ color: var(--tblr-muted-color);
}
.landing-footer nav a:hover {
- color: #32c9d9;
+ color: var(--tblr-blue);
text-decoration: underline;
}
@media (max-width: 900px) {
diff --git a/examples/official-site/assets/landing/styles/waves.css b/examples/official-site/assets/landing/styles/waves.css
index 5d487f3b..b9c85103 100644
--- a/examples/official-site/assets/landing/styles/waves.css
+++ b/examples/official-site/assets/landing/styles/waves.css
@@ -13,12 +13,12 @@
fill: var(--section-color);
}
.wave-front {
- stroke: #39d3e5;
+ stroke: var(--tblr-blue);
stroke-width: 0.7;
}
.wave-back {
opacity: 0.24;
- fill: #39d3e5;
+ fill: var(--tblr-blue);
}
@media (max-width: 900px) {
.landing-section {
@@ -30,3 +30,8 @@
--wave-height: 96px;
}
}
+
+/* Keep the first crest below the opening ribbon and footer. */
+.demo-section {
+ --wave-height: 24px;
+}
diff --git a/examples/official-site/sqlpage/templates/landing-page.handlebars b/examples/official-site/sqlpage/templates/landing-page.handlebars
index c0bb37fc..a65537d4 100644
--- a/examples/official-site/sqlpage/templates/landing-page.handlebars
+++ b/examples/official-site/sqlpage/templates/landing-page.handlebars
@@ -147,11 +147,7 @@
{{icon_img "player-play"}}Run itRun it{{icon_img "arrow-right"}}
diff --git a/tests/end-to-end/landing-comparison.spec.ts b/tests/end-to-end/landing-comparison.spec.ts
index 8aecd0af..55bd1d1c 100644
--- a/tests/end-to-end/landing-comparison.spec.ts
+++ b/tests/end-to-end/landing-comparison.spec.ts
@@ -30,6 +30,22 @@ for (const settings of [
const initialHeight = await section.evaluate(
(section) => section.clientHeight,
);
+ if (settings.reducedMotion === "no-preference") {
+ // Click again before the previous smooth scroll reaches its destination.
+ await next.evaluate((button) => {
+ if (!(button instanceof HTMLButtonElement))
+ throw new Error("Missing next button");
+ button.click();
+ button.click();
+ button.click();
+ });
+ await expect(status).toHaveText("4–5 of 5 alternatives");
+ await expect(next).toBeDisabled();
+ await track.focus();
+ await track.press("Home");
+ await expect(previous).toBeDisabled();
+ await expect(status).toHaveText("1–2 of 5 alternatives");
+ }
for (let index = 1; index <= 5 - settings.visible; index++) {
await next.click();
await expect(status).toHaveText(
diff --git a/tests/end-to-end/landing-streaming.spec.ts b/tests/end-to-end/landing-streaming.spec.ts
index 71e6f4c3..0282893a 100644
--- a/tests/end-to-end/landing-streaming.spec.ts
+++ b/tests/end-to-end/landing-streaming.spec.ts
@@ -11,6 +11,23 @@ test("streaming comparison starts early, pauses and restarts with its tab", asyn
diagram.getByText("streaming HTML", { exact: true }),
).toBeVisible();
const bounds = await diagram.boundingBox();
+ // Filling a bar must visibly differ from both its rail and the panel surface.
+ expect(
+ await diagram.evaluate((element) => {
+ const panel = getComputedStyle(
+ element.closest(".frontend-panels")!,
+ ).backgroundColor;
+ const fills = [...element.querySelectorAll(".timeline-fill")];
+ const stream = element.querySelector(".stream-timeline")!;
+ return (
+ fills.every(
+ (fill) => getComputedStyle(fill).backgroundColor !== panel,
+ ) &&
+ getComputedStyle(stream.querySelector(".stream-fill")!)
+ .backgroundColor !== getComputedStyle(stream).backgroundColor
+ );
+ }),
+ ).toBe(true);
// Seek the real animations to the moment SQLPage has streamed HTML while
// the illustrative app is still loading JavaScript, before its API/render.
diff --git a/tests/end-to-end/landing.spec.ts b/tests/end-to-end/landing.spec.ts
index a1f828ac..b46049e8 100644
--- a/tests/end-to-end/landing.spec.ts
+++ b/tests/end-to-end/landing.spec.ts
@@ -89,7 +89,7 @@ for (const viewport of [
const canvas = page.locator("canvas");
await expect(canvas).toHaveJSProperty(
"width",
- viewport.width === 390 ? 320 : 1000,
+ viewport.width === 390 ? 400 : 1000,
);
if (viewport.width === 390) {
await expect(page.locator(".sqlpage-world")).toHaveAttribute(
@@ -100,10 +100,10 @@ for (const viewport of [
expect.stringMatching(/\/sculpture-turns\.avif$/),
]);
} else {
- // Keep the full raster while batching nodes and cropping only empty glow.
- await expect(page.locator("html")).toHaveAttribute(
+ // The grounded studio has no instanced orbital dots.
+ await expect(page.locator("html")).not.toHaveAttribute(
"data-test-orb-instances",
- "8",
+ /.+/,
);
await expect(page.locator("html")).toHaveAttribute(
"data-test-glow-pixels",
@@ -161,7 +161,7 @@ for (const viewport of [
y: rect.y + scrollY,
size: rect.width,
turn: Number(canvas.dataset.scrollTurn),
- pixels: canvas.width === 320 ? canvas.toDataURL() : null,
+ pixels: canvas.width === 400 ? canvas.toDataURL() : null,
opacity: Number(
getComputedStyle(canvas.closest(".scene-layer")!).opacity,
),