diff --git a/public/assets/mru-144.gif b/public/assets/mru-144.gif new file mode 100644 index 0000000..6338a7a Binary files /dev/null and b/public/assets/mru-144.gif differ diff --git a/public/assets/mru-96.gif b/public/assets/mru-96.gif index 47eb28e..18341f4 100644 Binary files a/public/assets/mru-96.gif and b/public/assets/mru-96.gif differ diff --git a/public/assets/mru-light-144.gif b/public/assets/mru-light-144.gif new file mode 100644 index 0000000..748e5f7 Binary files /dev/null and b/public/assets/mru-light-144.gif differ diff --git a/public/assets/mru-light-96.gif b/public/assets/mru-light-96.gif index d6f953f..892871b 100644 Binary files a/public/assets/mru-light-96.gif and b/public/assets/mru-light-96.gif differ diff --git a/scripts/sync-tokens.mjs b/scripts/sync-tokens.mjs index 9efceb1..6498b8c 100644 --- a/scripts/sync-tokens.mjs +++ b/scripts/sync-tokens.mjs @@ -49,8 +49,11 @@ const FILES = [ 'ibm-plex-mono-500', ].map((f) => [`public/fonts/${f}.woff2`, `public/fonts/${f}.woff2`]), ...['jost', 'sourceserif4', 'ibmplexmono'].map((f) => [`public/fonts/OFL-${f}.txt`, `public/fonts/OFL-${f}.txt`]), - // The animated mark in the header (Mark.astro). - ...['mru', 'mru-light'].map((f) => [`public/assets/${f}.gif`, `public/assets/${f}.gif`]), + // The animated mark (Mark.astro): the originals and the header copies. + ...['mru', 'mru-light', 'mru-96', 'mru-light-96', 'mru-144', 'mru-light-144'].map((f) => [ + `public/assets/${f}.gif`, + `public/assets/${f}.gif`, + ]), ]; const check = process.argv.includes('--check'); diff --git a/src/components/DocsTop.astro b/src/components/DocsTop.astro index 3a46195..488a3ee 100644 --- a/src/components/DocsTop.astro +++ b/src/components/DocsTop.astro @@ -4,7 +4,7 @@ // On phones the bar shows the brand and a Menu button, as on mru.space; the // menu holds the docs nav, the header links, then the terms link and the // theme toggle (the sidebar's foot, which phones do not show). -import MarkSvg from './MarkSvg.astro'; +import Mark from './Mark.astro'; import DocsNav from './DocsNav.astro'; import ThemeToggle from './ThemeToggle.astro'; import { SITE } from '../data/site'; @@ -26,34 +26,7 @@ const links = [
- {/* The animated mark, as Mark.astro draws it, but from the 96 px copies - (mru-96.gif, about 140 KB, sharp at 2x) in place of the 2 MB - originals, which would hold up the page text on a slow link. */} - - - - - - - + Mru Docs diff --git a/src/components/Mark.astro b/src/components/Mark.astro index 1acc511..10f946a 100644 --- a/src/components/Mark.astro +++ b/src/components/Mark.astro @@ -2,11 +2,17 @@ // The animated mark. The GIFs carry no alpha: light art multiplies on paper, // dark art screens on ink (see README). No transform, filter, opacity or // z-index may sit on this element or any ancestor, or the blend breaks. -// Both images are lazy: only the one the theme shows gets a layout box, so -// the other never downloads. Nothing stands in while the GIF loads: the -// space stays empty, then the animation shows. The GIFs are about 2 MB, so -// they fetch at low priority: the fonts and the hero text paint first. -// Reduced motion hides both and shows the SVG. +// One holds both arts: the picks the dark GIF when the +// system prefers dark, the carries the light one. Only one downloads, +// and the request leaves with the HTML, before the stylesheet arrives. The +// script swaps the source when the viewer has chosen a theme (data-theme on +// ) and whenever that choice, or the system theme, changes. +// `small` (the header and footer brand, 48 px on the page) loads 96 px and +// 144 px copies, sharp at 2x and 3x, about 125 KB and 230 KB, at once. The +// rest load the 600 px originals, about 2 MB, lazily and at low priority: +// the fonts and the page text paint first. Nothing stands in while the GIF +// loads: the space stays empty, then the animation shows. Reduced motion +// hides the GIF and shows the SVG. import MarkSvg from './MarkSvg.astro'; interface Props { @@ -14,35 +20,31 @@ interface Props { staticSize?: number; gap?: number; alt?: string; + small?: boolean; } -const { size, staticSize, gap, alt = 'The Mru mark: a ring, a tilted orbit and an M' } = Astro.props; +const { size, staticSize, gap, alt = 'The Mru mark: a ring, a tilted orbit and an M', small = false } = Astro.props; // Inline sizes only where a page sets them, as on the canvas; otherwise the // stylesheet sizes the mark (104 px, 88 px on phones). const style = (s?: number) => [s === undefined ? '' : `width: ${s}px; height: ${s}px`, gap === undefined ? '' : `margin-bottom: ${gap}px`] .filter(Boolean) .join('; ') || undefined; +const px = small ? 96 : 600; +const darkSet = small ? '/assets/mru-96.gif 2x, /assets/mru-144.gif 3x' : '/assets/mru.gif'; +const lightSet = small ? '/assets/mru-light-96.gif 2x, /assets/mru-light-144.gif 3x' : undefined; +const light = small ? '/assets/mru-light-96.gif' : '/assets/mru-light.gif'; --- + {alt} - @@ -52,10 +54,44 @@ const style = (s?: number) => diff --git a/src/styles/shared/mru.css b/src/styles/shared/mru.css index 8bc3da8..2e13a82 100644 --- a/src/styles/shared/mru.css +++ b/src/styles/shared/mru.css @@ -476,7 +476,8 @@ padding-top: 12px; } -/* The mark */ +/* The mark. One per mark (Mark.astro): the picks the art + for the theme, so only the blend mode changes here. */ .mark-anim { width: 104px; height: 104px; @@ -487,9 +488,6 @@ height: 100%; mix-blend-mode: multiply; } -.mark-anim .on-dark { - display: none; -} @media (prefers-color-scheme: dark) { :root:not([data-theme='light']) .mark-anim img { mix-blend-mode: screen; @@ -498,22 +496,6 @@ :root[data-theme='dark'] .mark-anim img { mix-blend-mode: screen; } -@media (prefers-color-scheme: dark) { - :root:not([data-theme='light']) .mark-anim .on-light { - display: none; - } -} -:root[data-theme='dark'] .mark-anim .on-light { - display: none; -} -@media (prefers-color-scheme: dark) { - :root:not([data-theme='light']) .mark-anim .on-dark { - display: block; - } -} -:root[data-theme='dark'] .mark-anim .on-dark { - display: block; -} .mark-static { display: none; }