|
40 | 40 | scroll-behavior: smooth; |
41 | 41 | color-scheme: dark; |
42 | 42 | -webkit-tap-highlight-color: transparent; |
| 43 | + /* `clip`, not `hidden`: hidden would make this a scroll container and |
| 44 | + break the sticky nav. clip pairs with a visible vertical axis without |
| 45 | + coercing it, so the page still scrolls down but can never scroll |
| 46 | + sideways — whatever overhangs. The hover cards are centred on their |
| 47 | + link and a link near the right edge pushes one past the edge. */ |
| 48 | + overflow-x: clip; |
43 | 49 | } |
44 | 50 |
|
45 | 51 | body { |
|
51 | 57 | min-height: 100vh; |
52 | 58 | -webkit-font-smoothing: antialiased; |
53 | 59 | text-rendering: optimizeLegibility; |
| 60 | + /* Pools behind the page heading rather than sitting off the top edge — |
| 61 | + every route puts its title in the same place, so one gradient anchored |
| 62 | + to the top of the document lands behind all of them. The cursor glow |
| 63 | + adds to this where the pointer is, which is where it came from. */ |
54 | 64 | background-image: radial-gradient( |
55 | | - 60rem 30rem at 50% -12rem, |
56 | | - --alpha(var(--color-accent) / 8%), |
57 | | - transparent 70% |
| 65 | + 38rem 20rem at 38% 5rem, |
| 66 | + --alpha(var(--color-accent) / 14%), |
| 67 | + transparent 72% |
58 | 68 | ); |
59 | 69 | background-repeat: no-repeat; |
60 | 70 | } |
@@ -371,6 +381,40 @@ dialog[open]::backdrop { |
371 | 381 | } |
372 | 382 | } |
373 | 383 |
|
| 384 | +/* The nav's frosted panel. Solid by default so that any browser without |
| 385 | + scroll-driven animations keeps today's behaviour — legible links over a |
| 386 | + backdrop — and only browsers that can do the fade get the transparent top. |
| 387 | + Its own layer rather than a background on <nav>, because opacity is the one |
| 388 | + property that fades a backdrop-filter cleanly. */ |
| 389 | +.nav-surface { |
| 390 | + position: absolute; |
| 391 | + inset: 0; |
| 392 | + z-index: -1; |
| 393 | + background: --alpha(var(--color-background) / 80%); |
| 394 | + backdrop-filter: blur(12px) saturate(1.5); |
| 395 | +} |
| 396 | + |
| 397 | +@media (prefers-reduced-motion: no-preference) { |
| 398 | + @supports (animation-timeline: scroll()) { |
| 399 | + .nav-surface { |
| 400 | + animation: nav-surface-in linear both; |
| 401 | + animation-timeline: scroll(root block); |
| 402 | + /* fully solid within the first few rem of scrolling, so it isn't |
| 403 | + still fading in halfway down the page */ |
| 404 | + animation-range: 0 4rem; |
| 405 | + } |
| 406 | + } |
| 407 | +} |
| 408 | + |
| 409 | +@keyframes nav-surface-in { |
| 410 | + from { |
| 411 | + opacity: 0; |
| 412 | + } |
| 413 | + to { |
| 414 | + opacity: 1; |
| 415 | + } |
| 416 | +} |
| 417 | + |
374 | 418 | /* Reading position, drawn straight from the scroll offset. No JS, no scroll |
375 | 419 | listener — and in browsers without scroll-driven animations the bar simply |
376 | 420 | stays at scaleX(0) and is never seen. */ |
|
0 commit comments