diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 88c8ce1..11ae22b 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -16,7 +16,7 @@ export default function BlogPage() { const href = documented.length === 1 ? `${documented[0].docsHref}changelog/` : '/docs/'; return (
-

Blog

+

Blog

Writing about the libraries and how they are built.

diff --git a/src/app/global.css b/src/app/global.css index 307f3c1..e60ab23 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -1,9 +1,15 @@ @import 'tailwindcss'; -@import 'fumadocs-ui/css/neutral.css'; +@import 'fumadocs-ui/css/ocean.css'; @import 'fumadocs-ui/css/preset.css'; -/* The neutral preset ships a greyscale primary, so every `text-fd-primary` on the site rendered as - plain foreground and nothing carried the brand. #0c63e9 is the blue in the logo itself. Dark mode +/* Point every `font-mono` on the site, including fumadocs' code blocks, at JetBrains Mono. The + variable is set on by next/font in layout.tsx. */ +@theme { + --font-mono: var(--font-mono-family), ui-monospace, monospace; +} + +/* Ocean tints the whole surface blue but its own primary is a near-black navy in light mode, which + is not the brand and barely reads as a link. #0c63e9 is the blue in the logo itself. Dark mode takes a lighter step of the same hue, because the logo blue does not clear the contrast floor on a near-black ground. */ :root { @@ -11,11 +17,73 @@ --color-fd-primary-foreground: #ffffff; } +/* Ocean leaves the light ground neutral and tints only the chrome, so light mode read grey next to + a navy dark mode. Dropping the ground below the card colour also puts the hierarchy back: cards + now sit above the page instead of being the bluer thing on a whiter one. + `:not(.dark)` is load-bearing. Plain `:root` ties with ocean's own `.dark` on specificity, and + this file is imported after it, so a bare `:root` here wins in dark mode too and paints the dark + page light. */ +:root:not(.dark) { + --color-fd-background: hsl(220, 50%, 97%); +} + .dark { --color-fd-primary: #5b9dff; --color-fd-primary-foreground: hsl(214 46% 7%); } +/* A field of blocks drifting down behind the whole site, faint enough to read as texture rather + than decoration. One SVG tile is listed three times at three sizes; each layer travels exactly + its own tile height per cycle, so the loop is seamless and three distances in one duration is + what gives three speeds. The staggered start positions stop the three grids agreeing. + + It lives in body's own background rather than an overlay element. That is the one layer that is + guaranteed to sit above the page colour and below every piece of content, so there is no z-index + to lose and nothing to exclude from pointer events. It also replaces ocean's dark-mode gradient + wash, which was the only non-flat surface on the site. + + The cost is that moving `background-position` repaints the viewport each frame. At this size and + speed that is cheap, and it buys a backdrop with no element, no canvas and no JS. If it ever + shows up in a profile, the upgrade is a fixed pseudo-element moved with `transform` instead. */ +:root { + --blocks: url("data:image/svg+xml,"); +} + +.dark { + --blocks: url("data:image/svg+xml,"); +} + +/* `.dark body` is named explicitly because ocean sets both `background-image` and + `background-repeat: no-repeat` on it, and a bare `body` loses to either. */ +body, +.dark body { + background-image: var(--blocks), var(--blocks), var(--blocks); + background-repeat: repeat; + background-size: + 320px 533px, + 210px 350px, + 470px 783px; + background-position: + 0 0, + 70px 0, + 160px 0; +} + +@keyframes blockfall { + to { + background-position: + 0 533px, + 70px 350px, + 160px 783px; + } +} + +@media (prefers-reduced-motion: no-preference) { + body { + animation: blockfall 40s linear infinite; + } +} + html { scrollbar-gutter: stable; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e8e44b4..a9acb25 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,10 +2,15 @@ import { Provider } from '@/components/provider'; import { SiteFooter } from '@/components/site-footer'; import './global.css'; import type { Metadata } from 'next'; -import { Inter } from 'next/font/google'; +import { Chakra_Petch, JetBrains_Mono } from 'next/font/google'; import { appLongName, appName, appTagline } from '@/lib/shared'; -const inter = Inter({ subsets: ['latin'] }); +// Chakra Petch has no variable cut, so every weight the site uses has to be asked for by name. +const sans = Chakra_Petch({ subsets: ['latin'], weight: ['300', '400', '500', '600', '700'] }); +// Variable, so one file covers the medium/semibold/bold the panel mockup and badges ask for. +// It feeds Tailwind's `--font-mono` in global.css, which is what fumadocs' code blocks and +// every `font-mono` on the site read. +const mono = JetBrains_Mono({ subsets: ['latin'], variable: '--font-mono-family' }); export const metadata: Metadata = { // Every absolute URL in the metadata (OG and Twitter images, canonicals) is resolved against @@ -20,7 +25,7 @@ export const metadata: Metadata = { export default function Layout({ children }: LayoutProps<'/'>) { return ( - + {children} diff --git a/src/components/hero.tsx b/src/components/hero.tsx index 380dfe8..c57f6aa 100644 --- a/src/components/hero.tsx +++ b/src/components/hero.tsx @@ -16,7 +16,7 @@ export function Hero() { return (
-

+

{words.map(({ word, accent }, i) => (
-

+

Libraries

diff --git a/src/components/principles.tsx b/src/components/principles.tsx index 6d11a7c..60fcd4e 100644 --- a/src/components/principles.tsx +++ b/src/components/principles.tsx @@ -6,7 +6,7 @@ export function Principles() {

-

+

{content.principles.title}

{content.principles.body}