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 @@ Run itRun it 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, ),