Rework the CRT raster, glow and flicker effects
The three effects had drifted apart from each other and from the token system. Consolidate them behind --crt-* tokens and fix what was broken. - The animated text-shadow silently overrode the static one, so reduce-motion users saw a 5px white glow and everyone else a 3px currentColor glow. Both now come from one declaration, with the aberration offset factored out into --crt-shift. - Step the aberration instead of tweening it. text-shadow is not compositable and this one inherits to every glyph, so the smooth version repainted the whole document at refresh rate. - The flicker was invisible: a near-black tint at an effective alpha of 0.008-0.096 over a black background, a peak lift of under 2/255. Retint with the accent at ~3%. - Anchor the vignette to the viewport. It was sized to body's box, which is document-height, so on long pages the bloom sat off-screen. - Pin the overlays with position: fixed rather than painting document-sized layers, and give the grille hard stops so it is three stripes rather than a smooth R->G->B ramp. - Scale the raster pitch down at >=2dppx, where a 2px scanline is 4 device px and resamples into moire at fractional DPR. - Raise --color-mg to oklch(0.62): at 0.5 it was 3.5:1 on the background and 1.7:1 over the vignette centre. Halve the vignette alpha via its own token, keeping --color-accent-transparent at 50% for ProjectCard's glow. Collapses the two keyframe blocks from 193 lines to 40. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
89cfe7bdec
commit
0e6d352dca
2 changed files with 158 additions and 166 deletions
|
|
@ -2,6 +2,20 @@
|
|||
@import "reset.css" layer(reset);
|
||||
@import "theme.css" layer(theme);
|
||||
|
||||
/* The chromatic-aberration offset, as a 0..1 multiplier of
|
||||
--crt-aberration-max. Registering it gives it a typed initial value, so the
|
||||
calc() in body's text-shadow stays valid when the animation is not running.
|
||||
Note the var(--crt-shift, 0) fallbacks at the use sites: without one, an
|
||||
unresolved --crt-shift makes the calc() invalid at computed-value time,
|
||||
which drops the *whole* text-shadow — glow included — not just the smear.
|
||||
The animation itself does not depend on this rule, because step-end never
|
||||
interpolates; it only ever substitutes a literal keyframe value. */
|
||||
@property --crt-shift {
|
||||
syntax: "<number>";
|
||||
inherits: true;
|
||||
initial-value: 0;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "unscii16";
|
||||
src:
|
||||
|
|
@ -45,13 +59,37 @@
|
|||
:root {
|
||||
/* Color */
|
||||
--color-fg: oklch(1 0 0);
|
||||
--color-mg: oklch(0.5 0 0);
|
||||
/* 0.62, not 0.5: at 0.5 this was 3.5:1 on the background, under AA, and
|
||||
1.7:1 against the centre of the CRT vignette. 0.62 gives 5.8:1 / 4.4:1. */
|
||||
--color-mg: oklch(0.62 0 0);
|
||||
--color-bg: oklch(0 0 0);
|
||||
--color-accent: oklch(0.6 0.18 142.5);
|
||||
--color-accent-bright: oklch(0.8 0.18 142.5);
|
||||
--color-glow: oklch(0.8328 0 0);
|
||||
--color-accent-transparent: oklch(0.6 0.18 142.5 / 50%);
|
||||
|
||||
/* CRT
|
||||
The raster (scanline + aperture grille) and the phosphor (vignette,
|
||||
glow, chromatic aberration). Every value the effect uses lives here so
|
||||
the whole look can be retuned without touching the keyframes. */
|
||||
--crt-scanline-pitch: 2px;
|
||||
--crt-grille-pitch: 3px;
|
||||
--crt-scanline-color: rgb(0 0 0 / 0.25);
|
||||
--crt-grille-r: rgb(255 0 0 / 0.06);
|
||||
--crt-grille-g: rgb(0 255 0 / 0.02);
|
||||
--crt-grille-b: rgb(0 0 255 / 0.06);
|
||||
/* Deliberately not --color-accent-transparent, which ProjectCard uses for
|
||||
its box-shadow glow and wants at 50%. As a full-screen wash behind text,
|
||||
50% costs too much contrast; 25% keeps --color-mg above 4.5:1. */
|
||||
--crt-vignette-core: oklch(0.6 0.18 142.5 / 25%);
|
||||
--crt-vignette-edge: var(--color-bg);
|
||||
--crt-shadow-cool: rgb(0 30 255 / 0.5);
|
||||
--crt-shadow-warm: rgb(255 0 80 / 0.3);
|
||||
--crt-glow-blur: 3px;
|
||||
/* In em so the smear scales with the fluid root font-size. */
|
||||
--crt-aberration-max: 0.15em;
|
||||
--crt-flicker-base: 0.03;
|
||||
|
||||
/* Spacing */
|
||||
--gutter-large: clamp(3ch, 2.5vmax, 3.75ch);
|
||||
--stack-large: clamp(1.25ex, 2.5vmax, 1.75ex);
|
||||
|
|
@ -112,6 +150,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* The raster is specified in CSS px, so on a 2dppx panel a 2px scanline is
|
||||
4 device px — coarse, and on fractional DPR it resamples into moire.
|
||||
Tightening the pitch keeps the apparent scale roughly constant. */
|
||||
@media (min-resolution: 2dppx) {
|
||||
:root {
|
||||
--crt-scanline-pitch: 1.5px;
|
||||
--crt-grille-pitch: 2.25px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue