183 lines
9.0 KiB
CSS
183 lines
9.0 KiB
CSS
@import "tailwindcss";
|
||
|
||
/* ---------------------------------------------------------------------------
|
||
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;
|
||
|
||
/* Navy and red — the flag, muted. kjøl is a Norwegian word and the palette says so.
|
||
Both are dark and low-key: the page is mostly prose, code and tables, and the brand's
|
||
job is to mark the few things you can act on, not to compete with them for attention.
|
||
(The previous sky blue did the same job, but said nothing.) */
|
||
--color-primary: #1e3a63; /* muted navy — FILLS; they carry white text */
|
||
--color-primary-hover: #16294a;
|
||
--color-primary-subtle: #eef2f8; /* a navy wash — tinted panels, badges, callouts */
|
||
--color-primary-border: #c5d1e2;
|
||
|
||
/* accent is for TEXT and icons — links, the eyebrow, an active sidebar row. It is still 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. Here they are the same hue — navy — but not the same value: the accent is a
|
||
touch deeper so a navy link on white is unmistakably a link. (The red is gone; the
|
||
brand is navy throughout now. Only the flag keeps its red field.) */
|
||
--color-accent: #1c3a66; /* navy — accent TEXT */
|
||
|
||
/* 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;
|
||
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------
|
||
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(30, 58, 99, 0.06); /* the navy, at the edge of visible */
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------
|
||
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;
|
||
|
||
/* Both brand tokens move in the dark, and both for the same reason now: navy is too dark
|
||
to read on a near-black page, so each climbs to a lighter blue.
|
||
|
||
The accent (TEXT) climbs furthest — a link has to be legible at body-text weight, so it
|
||
goes to a soft sky. The fill climbs less: it only has to look like a button and still
|
||
carry white text (~7:1), so it lifts to a steel blue and stops there, well below where
|
||
the accent lands. */
|
||
--color-accent: #9fc1ec;
|
||
--color-primary: #2b4f80;
|
||
--color-primary-hover: #37619b;
|
||
--color-primary-subtle: #182234;
|
||
--color-primary-border: #2c3e5c;
|
||
|
||
--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);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------
|
||
The wordmark's logo tile: a muted Norwegian flag.
|
||
---------------------------------------------------------------------------
|
||
kjøl is a Norwegian word — the mark says so. It is the flag's Scandinavian cross: a
|
||
navy cross with an off-white outline on a red field, offset LEFT as the real flag is
|
||
(the vertical bar sits at ~38% rather than centre). Muted, not the flag's full
|
||
saturation — it is a 32px tile next to body text, not a banner.
|
||
|
||
Drawn in layered gradients over a red base rather than as an <img>, so it inherits the
|
||
tile's rounded corners and border, needs no asset request, and cannot 404. Layers paint
|
||
TOP-first, so the reading is: dim scrim, then navy cross, off-white cross, red field. Each
|
||
band is transparent outside its stripe (hard stops via doubled positions) so the layer
|
||
beneath shows through. The bands are constant across light and dark — a flag does not
|
||
change with the page theme.
|
||
|
||
The topmost layer is a flat dark scrim, and it is there so the WHITE sailboat on top of
|
||
the tile reads on its own — no outline, no shadow on the glyph. The boat is a thin white
|
||
stroke and the off-white cross is exactly the band it would vanish into; rather than trace
|
||
the boat in shadow, the whole flag is dimmed until white stands out against every band of
|
||
it, the pale cross included. A dimmer, moodier flag with a crisp white boat, on purpose.
|
||
|
||
The cross geometry, as percentages of the tile:
|
||
vertical (offset left, centre 38%): off-white 27–49%, navy 32.5–43.5%
|
||
horizontal (centred, centre 50%): off-white 39–61%, navy 44.5–55.5% */
|
||
.flag-no {
|
||
background-color: #a83f4c; /* the red field, muted (then dimmed by the scrim above) */
|
||
background-image:
|
||
linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42)), /* the dim scrim, on top */
|
||
linear-gradient(180deg, transparent 44.5%, #294c76 44.5%, #294c76 55.5%, transparent 55.5%),
|
||
linear-gradient(90deg, transparent 32.5%, #294c76 32.5%, #294c76 43.5%, transparent 43.5%),
|
||
linear-gradient(180deg, transparent 39%, #ece6da 39%, #ece6da 61%, transparent 61%),
|
||
linear-gradient(90deg, transparent 27%, #ece6da 27%, #ece6da 49%, transparent 49%);
|
||
}
|
||
|
||
.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.) */
|