diff --git a/src/components/HeroStrands.astro b/src/components/HeroStrands.astro new file mode 100644 index 0000000..5fc7b99 --- /dev/null +++ b/src/components/HeroStrands.astro @@ -0,0 +1,129 @@ +--- +/* + * A slow, luminous ribbon field behind the hero. + * + * Inspired by React Bits' "Strands", but written from scratch on a 2D canvas + * rather than pulling in ogl and a WebGL shader. That library is ~25kB gzipped; + * this is a couple of hundred bytes, on a page whose entire JS budget is about + * 2kB. The look is the point, not the implementation. + * + * Deliberately restrained: brand blues only, low alpha, and slow enough that it + * reads as ambient rather than as something demanding attention. + */ +--- + + + + diff --git a/src/components/PageHeader.astro b/src/components/PageHeader.astro index 1893f96..4846e2b 100644 --- a/src/components/PageHeader.astro +++ b/src/components/PageHeader.astro @@ -1,4 +1,6 @@ --- +import HeroStrands from './HeroStrands.astro'; + interface Props { title: string; lead?: string; @@ -6,8 +8,10 @@ interface Props { const { title, lead } = Astro.props; --- -
-
+
+ + {/* Above the canvas, which is absolutely positioned across this container. */} +

{title}

{lead &&

{lead}

} diff --git a/src/components/ShimmerBorder.astro b/src/components/ShimmerBorder.astro new file mode 100644 index 0000000..e8a4005 --- /dev/null +++ b/src/components/ShimmerBorder.astro @@ -0,0 +1,84 @@ +--- +/* + * A shimmer that travels around the edge of its parent. + * + * A conic gradient is rotated behind the card and masked down to just the + * border ring, so a single highlight runs continuously around the whole + * perimeter -- corners included. + * + * This is not React Bits' "Star Border", which slides two radial gradients + * along the top and bottom edges. That reads as two streaks passing by rather + * than one shimmer going around, which is the wrong feel for a card. + * + * Drop it inside a `position: relative` element. Decorative, so aria-hidden and + * no pointer events. It masks itself rather than clipping the parent, so + * anything overhanging the card -- a badge, say -- is unaffected. + */ +interface Props { + /** One full lap. */ + speed?: string; + /** Ring thickness. */ + thickness?: string; +} +const { speed = '5s', thickness = '1.5px' } = Astro.props; +--- + + + + diff --git a/src/pages/404.astro b/src/pages/404.astro index bfb72a5..1ece16e 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@layouts/BaseLayout.astro'; +import HeroStrands from '@components/HeroStrands.astro'; import { LINKS } from '@data/site'; // NOTE: this is the org site's 404, so GitHub Pages serves it for any @@ -13,7 +14,9 @@ import { LINKS } from '@data/site'; path="/404" noindex > -
+
+ +

404

We couldn't find that page. @@ -51,4 +54,5 @@ import { LINKS } from '@data/site'; >.

+
diff --git a/src/pages/index.astro b/src/pages/index.astro index 824a55c..e79fa41 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -2,6 +2,7 @@ import { Image } from 'astro:assets'; import BaseLayout from '@layouts/BaseLayout.astro'; import StoreButton from '@components/StoreButton.astro'; +import HeroStrands from '@components/HeroStrands.astro'; import { SITE, LINKS } from '@data/site'; import { STORES } from '@data/stores'; import devices from '@assets/devices.png'; @@ -14,6 +15,8 @@ import devices from '@assets/devices.png'; path="/" >
+ + {/* Ambient brand wash -- the modern descendant of the old #bg-circle. */}