Files
kjol/go/cmd/kjol-web/css/app.css

200 lines
9.3 KiB
CSS

@import "tailwindcss";
/* ---------------------------------------------------------------------------
Lora, vendored from Google Fonts — self-hosted, not linked.
---------------------------------------------------------------------------
The files live in wwwroot/fonts (served by the dev server straight out of
./wwwroot), so the app pulls no third-party request at runtime: no CDN to be
blocked, no extra DNS round trip, and no dependency on fonts.gstatic.com being
up. It is a VARIABLE font, so ONE file per style covers weights 400-700 —
hence "font-weight: 400 700" rather than a file per weight.
Only the latin and latin-ext subsets are vendored (~120 KB in total). The
unicode-range on each face is what lets the browser skip downloading latin-ext
entirely unless the page actually uses a character from it.
To refresh: fetch
https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700
and re-download the woff2 URLs it names.
--------------------------------------------------------------------------- */
@font-face {
font-family: "Lora";
font-style: italic;
font-weight: 400 700; /* a variable font: one file covers the whole range */
font-display: swap;
src: url("/fonts/lora-latin-ext-italic.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Lora";
font-style: italic;
font-weight: 400 700; /* a variable font: one file covers the whole range */
font-display: swap;
src: url("/fonts/lora-latin-italic.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Lora";
font-style: normal;
font-weight: 400 700; /* a variable font: one file covers the whole range */
font-display: swap;
src: url("/fonts/lora-latin-ext-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Lora";
font-style: normal;
font-weight: 400 700; /* a variable font: one file covers the whole range */
font-display: swap;
src: url("/fonts/lora-latin-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* ---------------------------------------------------------------------------
Dark mode: `dark:` as a CLASS, not a media query.
---------------------------------------------------------------------------
Tailwind's built-in dark variant follows the operating system. A site with its own
theme switch cannot use it: the OS says one thing, the switch says another, and the
media query wins — so the switch appears to do nothing.
This redefines it against a class on <html>, which webui.Theme toggles. The OS is
still respected: it is the DEFAULT (see the boot script in server/main.go), just no
longer the last word.
--------------------------------------------------------------------------- */
@custom-variant dark (&:where(.dark, .dark *));
/* App-side design tokens the webui kit references (Tailwind v4 @theme). Brand
values live with the app; the kit stays generic.
The surface/line/ink tokens are the kit's THEME CONTRACT (see webui.ThemeTokens):
components say bg-surface / border-line / text-ink and never name a colour, so the
whole kit changes theme by changing these ten values rather than by carrying a dark:
variant on four hundred class strings. */
@theme {
--radius-default: 0.375rem;
/* Sky: a cool, neutral blue. The page is mostly prose, code and tables, and the
accent's job is to mark the few things you can act on — a saturated indigo or a
primary blue competes with the content for attention instead of directing it. */
--color-primary: #0284c7; /* sky-600 — accent FILLS; they carry white text */
--color-primary-hover: #0369a1; /* sky-700 */
--color-primary-subtle: #f0f9ff; /* sky-50 — tinted panels, badges, callouts */
--color-primary-border: #bae6fd; /* sky-200 */
/* accent is for TEXT and icons. It is a separate token from primary because the two
have opposite constraints: a fill must be dark enough for white text on top of it,
and accent text must be readable ON the surface. One value cannot be both, and in
dark mode they diverge completely. */
--color-accent: #0369a1; /* sky-700 */
/* Surfaces, lines, ink — the kit's theme contract. */
--color-surface: #ffffff;
--color-surface-muted: #fafafa;
--color-surface-raised: #f5f5f5;
--color-surface-strong: #e5e5e5;
--color-line: #e5e5e5;
--color-line-strong: #d4d4d4;
--color-ink: #171717;
--color-ink-soft: #525252;
--color-ink-muted: #737373;
--color-ink-faint: #a3a3a3;
--color-text-heading: #111827;
--color-text-on-dark: #f9fafb;
--color-text-on-dark-muted: #9ca3af;
/* Lora is the body face. Declaring it as --font-sans makes it the default for
everything (Tailwind's preflight sets `font-family: var(--font-sans)` on
html), and also gives you `font-sans` as a utility. --font-serif points at it
too, so `font-serif` is not a different, unvendored face. */
--font-sans: "Lora", ui-serif, Georgia, Cambria, "Times New Roman", serif;
--font-serif: "Lora", ui-serif, Georgia, Cambria, "Times New Roman", serif;
}
/* ---------------------------------------------------------------------------
Grid background
---------------------------------------------------------------------------
Plain CSS, not a utility: Tailwind's arbitrary-value syntax cannot carry a
background-image with commas in it without becoming unreadable, and this is a
single named thing rather than a composition of atoms. kjol's Tailwind engine
passes rules it does not recognise straight through, so this lands in the output
untouched.
The grid is drawn with two 1px gradients — a vertical set and a horizontal set —
tiled at --grid-size. It is deliberately faint: it should register as texture, not
as graph paper you have to read the page through.
--------------------------------------------------------------------------- */
:root {
--grid-line: rgba(15, 23, 42, 0.055);
}
/* ---------------------------------------------------------------------------
The dark theme.
---------------------------------------------------------------------------
Only the token VALUES change. Not one component knows this block exists — they ask
for bg-surface and text-ink, and here is where those come to mean something else.
This is a plain rule, not another @theme block: @theme generates utilities, and these
are overrides of utilities that already exist.
The surfaces are not pure black. Black gives a dark UI a hard, glaring edge against
white text and makes every border invisible; a very dark grey leaves room for the
raised surfaces and lines above it to actually be seen. --------------------------- */
.dark {
--color-surface: #101013;
--color-surface-muted: #17171b;
--color-surface-raised: #1f1f24;
--color-surface-strong: #2c2c33;
--color-line: #2a2a30;
--color-line-strong: #3d3d45;
--color-ink: #f2f2f3;
--color-ink-soft: #c6c6cc;
--color-ink-muted: #9a9aa3;
--color-ink-faint: #71717a;
/* The fill stays put — white text has to remain readable on it — but accent TEXT has
to climb to stay readable on a near-black surface. This is exactly why they are two
tokens. */
--color-accent: #7dd3fc; /* sky-300 */
--color-primary-subtle: #0b2c3f;
--color-primary-border: #0e4966;
--color-text-heading: #f5f5f5;
/* The grid is drawn in ink, not in shadow, once the page is dark. */
--grid-line: rgba(226, 232, 240, 0.05);
}
/* The page's own background — painted before the app mounts, and behind it afterwards.
Without this, a dark app sits in a white window. */
html {
background-color: var(--color-surface);
color: var(--color-ink);
}
.bg-grid {
background-image:
linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
/* Written out rather than as var(--size) var(--size): the CSS minifier drops the
space between two adjacent var() calls, and while that is still legal CSS, a
background-size that depends on how a minifier tokenises is not worth the cleverness. */
background-size: 56px 56px;
background-position: center top;
}
/* Fades the grid out towards the bottom, so it frames the hero and then gets out of
the way of the content below rather than running under it the whole page. */
.grid-fade {
-webkit-mask-image: linear-gradient(to bottom, #000, #000 35%, transparent 100%);
mask-image: linear-gradient(to bottom, #000, #000 35%, transparent 100%);
}
/* (The hero glow that used to live here went with the hero. A coloured wash behind an
oversized headline is the most recognisable gesture in framework marketing, and this
page is not making that argument any more.) */