Skip to content

Commit d5fb581

Browse files
committed
Overhaul website showcase and bottom nav styling
1 parent 8ef186a commit d5fb581

3 files changed

Lines changed: 2309 additions & 218 deletions

File tree

docs/assets/main.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,81 @@ for (const marquee of document.querySelectorAll(".marquee")) {
9292
for (const item of items) track.appendChild(item.cloneNode(true));
9393
}
9494
clone.innerHTML = track.innerHTML;
95+
96+
// duration scales with track width so every row moves at the same speed
97+
const duration = `${track.scrollWidth / 40}s`;
98+
track.style.animationDuration = duration;
99+
clone.style.animationDuration = duration;
95100
}
96101

102+
// ---- liquid-glass nav: lens-style edge refraction.
103+
// Needs backdrop-filter: url(), so it's enabled only where that works (Chromium);
104+
// other browsers keep the plain frosted-blur look. ----
105+
(() => {
106+
const nav = document.querySelector(".pill-nav");
107+
const map = document.getElementById("lg-map");
108+
if (!nav || !map) return;
109+
110+
const isWebkit =
111+
/Safari/.test(navigator.userAgent) &&
112+
!/Chrome/.test(navigator.userAgent);
113+
const isFirefox = /Firefox/.test(navigator.userAgent);
114+
const probe = document.createElement("div");
115+
probe.style.backdropFilter = "url(#liquid-glass)";
116+
if (isWebkit || isFirefox || probe.style.backdropFilter === "") return;
117+
118+
// displacement map: R drives x, G drives y. 50% gray (0.5) means "no shift",
119+
// so both gradients hold 0.5 through the middle and ramp smoothly only near
120+
// the rim — a continuous profile (no steps), otherwise the backdrop doubles
121+
const updateMap = () => {
122+
const { width, height } = nav.getBoundingClientRect();
123+
if (!width || !height) return;
124+
// rasterize at device resolution — at fractional/high DPR the map is
125+
// otherwise misaligned with the backdrop and parts of the pill stay sharp
126+
const dpr = window.devicePixelRatio || 1;
127+
// same physical rim width on every side, so all four edges bend equally
128+
const ramp = Math.min(width, height) * 0.35;
129+
const rx = ((ramp / width) * 100).toFixed(2);
130+
const ry = ((ramp / height) * 100).toFixed(2);
131+
const svg =
132+
`<svg width="${width * dpr}" height="${height * dpr}" viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">` +
133+
`<defs>` +
134+
`<linearGradient id="lg-x" x1="0%" y1="0%" x2="100%" y2="0%">` +
135+
`<stop offset="0%" stop-color="#ff0000"/>` +
136+
`<stop offset="${rx}%" stop-color="#800000"/>` +
137+
`<stop offset="${100 - rx}%" stop-color="#800000"/>` +
138+
`<stop offset="100%" stop-color="#000000"/>` +
139+
`</linearGradient>` +
140+
`<linearGradient id="lg-y" x1="0%" y1="0%" x2="0%" y2="100%">` +
141+
`<stop offset="0%" stop-color="#00ff00"/>` +
142+
`<stop offset="${ry}%" stop-color="#008000"/>` +
143+
`<stop offset="${100 - ry}%" stop-color="#008000"/>` +
144+
`<stop offset="100%" stop-color="#000000"/>` +
145+
`</linearGradient>` +
146+
`</defs>` +
147+
`<rect width="${width}" height="${height}" fill="url(#lg-x)"/>` +
148+
`<rect width="${width}" height="${height}" fill="url(#lg-y)" style="mix-blend-mode:screen"/>` +
149+
`</svg>`;
150+
// pin the map to the element box in user units — the filter region is
151+
// larger than the element, so percentage sizing would misalign it
152+
map.setAttribute("x", "0");
153+
map.setAttribute("y", "0");
154+
map.setAttribute("width", `${width}`);
155+
map.setAttribute("height", `${height}`);
156+
map.setAttribute(
157+
"href",
158+
`data:image/svg+xml,${encodeURIComponent(svg)}`,
159+
);
160+
};
161+
162+
// single displacement pass: subtle lens bend at the rim, no color fringing
163+
document.getElementById("lg-lens")?.setAttribute("scale", "-35");
164+
165+
updateMap();
166+
new ResizeObserver(updateMap).observe(nav);
167+
nav.classList.add("glass-active");
168+
})();
169+
97170
// ---- cursor spotlight on feature cards ----
98171
if (window.matchMedia("(hover: hover)").matches) {
99172
for (const card of document.querySelectorAll(".feature-card")) {
@@ -136,6 +209,8 @@ function countUp(el, target) {
136209
el.textContent = formatStat(target);
137210
return;
138211
}
212+
// reserve the final width up front so the row doesn't shift while counting
213+
el.style.minWidth = `${formatStat(target).length}ch`;
139214
const duration = 1200;
140215
const start = performance.now();
141216
const tick = (now) => {

docs/assets/style.css

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ h2 {
323323
bottom: max(1rem, env(safe-area-inset-bottom));
324324
transform: translateX(-50%);
325325
z-index: 50;
326+
isolation: isolate;
326327
display: flex;
327328
align-items: center;
328329
gap: 0.25rem;
@@ -350,7 +351,30 @@ h2 {
350351
pointer-events: none;
351352
}
352353

354+
/* liquid glass: the class is added by JS only where backdrop-filter: url()
355+
works (Chromium); everywhere else the frosted look above remains */
356+
.pill-nav.glass-active {
357+
background: color-mix(in srgb, var(--bg-raised) 12%, transparent);
358+
backdrop-filter: url(#liquid-glass) saturate(1.3);
359+
-webkit-backdrop-filter: url(#liquid-glass) saturate(1.3);
360+
box-shadow:
361+
var(--shadow-nav),
362+
inset 0 0 2px 1px rgba(255, 255, 255, 0.28),
363+
inset 0 0 10px 4px rgba(255, 255, 255, 0.12);
364+
}
365+
366+
@media (prefers-color-scheme: light) {
367+
.pill-nav.glass-active {
368+
box-shadow:
369+
var(--shadow-nav),
370+
inset 0 0 2px 1px rgba(20, 30, 45, 0.14),
371+
inset 0 0 10px 4px rgba(20, 30, 45, 0.08);
372+
}
373+
}
374+
353375
.pill-nav a {
376+
position: relative;
377+
z-index: 1;
354378
display: flex;
355379
flex-direction: column;
356380
align-items: center;
@@ -511,7 +535,8 @@ h3,
511535
box-shadow:
512536
0 40px 80px rgba(0, 0, 0, 0.55),
513537
0 12px 32px rgba(29, 68, 134, 0.35),
514-
inset 0 1px 1px rgba(255, 255, 255, 0.14);
538+
inset 0 1px 1px rgba(255, 255, 255, 0.18),
539+
inset 1px 0 1px rgba(255, 255, 255, 0.05);
515540
}
516541

517542
.phone::before {
@@ -533,12 +558,25 @@ h3,
533558
position: absolute;
534559
inset: 0;
535560
border-radius: inherit;
536-
background: linear-gradient(
537-
115deg,
538-
transparent 38%,
539-
rgba(255, 255, 255, 0.025) 48%,
540-
transparent 58%
541-
);
561+
background:
562+
linear-gradient(
563+
115deg,
564+
transparent 32%,
565+
rgba(255, 255, 255, 0.045) 43%,
566+
rgba(255, 255, 255, 0.015) 50%,
567+
transparent 60%
568+
),
569+
linear-gradient(
570+
115deg,
571+
transparent 64%,
572+
rgba(255, 255, 255, 0.028) 69%,
573+
transparent 75%
574+
),
575+
radial-gradient(
576+
120% 55% at 14% 0%,
577+
rgba(255, 255, 255, 0.05),
578+
transparent 55%
579+
);
542580
pointer-events: none;
543581
}
544582

0 commit comments

Comments
 (0)