/* --------------------------------------------------------------------------- kjol — shared Tailwind v4 scaffold for the JS (Solid) kit. --------------------------------------------------------------------------- The bundler PREPENDS this file to the app's own css/style.css (see jsbundler/css.go), so it is the first thing the Tailwind engine sees. That is why the `@import "tailwindcss"` lives here rather than in the app's stylesheet: an @import has to come first, and the app's file no longer is. An app adopting the shared tree therefore DROPS `@import "tailwindcss"` from its own style.css and keeps only what is genuinely its own — its brand palette, its fonts, its one-off rules. In single-tree mode (WebDir unset) this file is not prepended at all and the app's style.css stays complete, which is why cdrateline and Hotlap still import Tailwind themselves. --------------------------------------------------------------------------- */ @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, the media query wins, and the button appears to do nothing. This redefines the variant against a class on , which the Theme controller (uikit/Theme.tsx) toggles. The OS is still respected — it is the DEFAULT, applied by the boot script before first paint — just no longer the last word. --------------------------------------------------------------------------- */ @custom-variant dark (&:where(.dark, .dark *)); /* Icon family for uikit/Icons.tsx, read at runtime via getComputedStyle: --fa-style: classic → far/fas, sharp → fasr/fass; --fa-default-solid: 0|1. Kept in :root (not @theme) so Tailwind doesn't tree-shake it as unused — it is never named by a utility class, so nothing would mark it as reachable. */ :root { --fa-style: classic; --fa-default-solid: 0; } @theme { /* Custom text sizes */ --text-2xs: 0.75rem; /* Same size as Tailwind's default text-xs */ --text-xs: 0.8rem; /* Override: Tailwind default is 0.75rem */ /* Custom breakpoints (Tailwind defaults + xs + ultrawide) */ --breakpoint-xs: 375px; --breakpoint-sm: 640px; --breakpoint-md: 768px; --breakpoint-lg: 1024px; --breakpoint-xl: 1280px; --breakpoint-2xl: 1536px; --breakpoint-ultrawide: 3840px; /* Custom containers */ --container-8xl: 88rem; --container-9xl: 96rem; --container-10xl: 104rem; --container-ultrawide: 240rem; /* Custom radius */ --radius-default: var(--radius-sm); /* ---- THE THEME CONTRACT ------------------------------------------------ Every component in the kit says bg-surface / border-line / text-ink and NEVER names a colour. These are the tokens it is allowed to name. That is the whole trick. A theme is the twenty values below, not a `dark:` variant on four hundred class strings — so switching to dark is a rule that re-points these, and not one component knows it happened. The vocabulary is deliberately identical to the Go/WASM kit's (webui. ThemeTokens). Two kits, one contract: a designer changes `surface` once and both halves of the site move together. */ /* Surfaces, in ascending prominence. */ --color-surface: #ffffff; --color-surface-muted: #fafafa; --color-surface-raised: #f5f5f5; --color-surface-strong: #e5e5e5; /* Lines. */ --color-line: #e5e5e5; --color-line-strong: #d4d4d4; /* Ink, in descending prominence. */ --color-ink: #171717; --color-ink-soft: #525252; --color-ink-muted: #737373; --color-ink-faint: #a3a3a3; /* The solid neutral button. It INVERTS: a dark button on a light page, a light button on a dark one. That is why it is three tokens and not `bg-ink` — the text on top of it has to invert with it, and `text-white` would vanish the moment the fill went pale. */ --color-fill-neutral: #404040; --color-fill-neutral-hover: #262626; --color-on-fill-neutral: #ffffff; /* The accent. `primary` is a FILL — it carries white text, so it must stay dark enough for white to sit on it. Brand supplies the value; see the app's style.css. These defaults keep the kit legible before an app has themed anything, NOT so that it can skip theming. */ --color-primary: #404040; --color-primary-hover: #262626; /* ---- deliberately-dark chrome (the kit's `onDark` prop) ---------------- Not the same thing as dark MODE. Several components take onDark, for when they sit on a dark bar or panel in an otherwise light page — so these stay dark in BOTH themes and are not overridden below. They were named by the components (bg-dark, text-text-on-dark, …) and defined by nobody, so until now every one of them compiled to no colour at all and the onDark paths silently rendered unstyled. */ --color-dark: #101013; --color-dark-raised: #1f1f24; --color-text-on-dark: #f9fafb; --color-text-on-dark-muted: #9ca3af; --color-text-on-dark-faint: #6b7280; --color-border-on-dark: #2a2a30; /* State colours. These DO name a colour, because they mean one: an alert that is not red is not an alert. Their tinted BACKGROUNDS are the one place the kit still carries a `dark:` variant — a red-50 wash is invisible on a near black surface, and no re-pointed token can fix that. */ --color-info: var(--color-sky-500); --color-on-info: var(--color-white); --color-success: var(--color-green-500); --color-on-success: var(--color-white); --color-warning: var(--color-amber-500); --color-on-warning: var(--color-white); --color-danger: var(--color-red-500); --color-on-danger: var(--color-white); } /* --------------------------------------------------------------------------- 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 the 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; /* Inverted, as promised above: pale fill, dark text. */ --color-fill-neutral: #e5e5e5; --color-fill-neutral-hover: #ffffff; --color-on-fill-neutral: #171717; } /* The page's own background — painted before anything 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); }