6403d0a9a5
The TUI's ambient life — fish, jellyfish, bubbles, the whale cameo — stays out of scope. Two orders of magnitude separate web-native "ocean motion" from what this product has ever shipped, and this page quotes the product's restraint doctrine in its own copy. The whole site now has four keyframes; these are two of them. M1, the column breath: one overlay on `.ocean-column` animating OPACITY ONLY between 0 and 0.045 on a 90s cycle — the TUI's authored 0.018–0.055 phase bias as the same fraction of a mix. Opacity is the only property that cannot cause layout work on an element four bands tall. No position change, no hue shift, no filter. M2, the caustic: ~1.3s of ice light crossing the footer whale, parked off-canvas for the remaining ~2.7s, peak 0.33, sine-in-out — `ambient_life.rs` at its literal amplitude and cadence. One whale only; two caustics is chrome. It is clipped to the mark, so it lands on the whale and never on a rectangle of footer behind it, and `Whale` takes it as an opt-in prop. The clip sits on a static <g> with the highlight moving inside it. Clipping the moving rect instead slides the whale-shaped window along with the light, which is a very quiet way to render nothing at all — caught by screenshotting a frozen frame rather than by reading the markup. Both effects are gated behind `prefers-reduced-motion: no-preference` rather than overridden under `reduce`: an override can be defeated by specificity, a gate cannot. Verified under emulated `reduce` — the breath overlay does not exist, the caustic has no animation and stays parked, and the column's field is still there. Reduced motion freezes the field; it never removes it. No new strings. Verified: npx tsc --noEmit, npm test (235), npm run lint, by eye at /en 1440px including a frozen mid-sweep frame.
68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
/** Static Signal Current mark from the managed Codewhale product contract. */
|
|
const WHALE_BODY =
|
|
"M7 57c9-13 21-15 25-25 3-7-1-12-10-16 6-1 11 1 14 6 3-6 10-11 19-13-1 10-5 17-12 21-4 3-5 8-8 14-5 9-15 14-28 13Z";
|
|
const WHALE_CURRENT = "M28 58c10-8 15-15 17-26 4 8 2 18-3 26H28Z";
|
|
|
|
export function Whale({
|
|
size = 36,
|
|
className = "",
|
|
caustic = false,
|
|
}: {
|
|
size?: number;
|
|
className?: string;
|
|
/**
|
|
* Ambient light passing over the mark — `ambient_life.rs`'s caustic at its
|
|
* literal amplitude and cadence. Exactly one whale on the page may carry it
|
|
* (the footer's); two caustics is chrome. The fixed gradient/clip ids are
|
|
* safe for the same reason.
|
|
*/
|
|
caustic?: boolean;
|
|
}) {
|
|
return (
|
|
<svg
|
|
viewBox="0 0 64 64"
|
|
width={size}
|
|
height={size}
|
|
className={`codewhale-mark ${className}`}
|
|
aria-hidden="true"
|
|
fill="none"
|
|
>
|
|
{caustic ? (
|
|
<defs>
|
|
<clipPath id="codewhale-caustic-clip">
|
|
<path d={WHALE_BODY} />
|
|
<path d={WHALE_CURRENT} />
|
|
</clipPath>
|
|
<linearGradient id="codewhale-caustic-light" x1="0" y1="0" x2="1" y2="0">
|
|
<stop offset="0%" stopColor="#d1ebf4" stopOpacity="0" />
|
|
<stop offset="50%" stopColor="#d1ebf4" stopOpacity="0.33" />
|
|
<stop offset="100%" stopColor="#d1ebf4" stopOpacity="0" />
|
|
</linearGradient>
|
|
</defs>
|
|
) : null}
|
|
|
|
<path className="codewhale-mark-primary" d={WHALE_BODY} />
|
|
<path className="codewhale-mark-current" d={WHALE_CURRENT} />
|
|
|
|
{/* The light is clipped to the mark itself, so it lands on the whale and
|
|
never on a rectangle of footer behind it. The clip lives on a STATIC
|
|
group and the highlight moves inside it — put the clip on the moving
|
|
rect and the whale-shaped window slides along with the light, which
|
|
is a very quiet way to render nothing at all. Parked off-canvas at
|
|
rest, which is also where reduced motion leaves it. */}
|
|
{caustic ? (
|
|
<g clipPath="url(#codewhale-caustic-clip)">
|
|
<rect
|
|
className="codewhale-caustic"
|
|
x="-56"
|
|
y="0"
|
|
width="48"
|
|
height="64"
|
|
fill="url(#codewhale-caustic-light)"
|
|
/>
|
|
</g>
|
|
) : null}
|
|
</svg>
|
|
);
|
|
}
|