From 7e3e8acb28b7157201e1f9b97996ec6cc80928a0 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 31 Oct 2026 01:00:42 +0100 Subject: [PATCH] Give every palette colour a hex value an engine without oklch can read --- CHANGELOG.md | 6 ++++++ docs/reading-cost.md | 18 ++++++++++++++++++ public/css/styles.css | 26 ++++++++++++++------------ src/ui/stylesheet-floor.test.ts | 14 ++++++++++++++ todo.md | 9 +-------- 5 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 docs/reading-cost.md create mode 100644 src/ui/stylesheet-floor.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb066c..95b99d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ at boot. Entries start at 0.3.0. ## Unreleased +### Colours render on engines without `oklch()` + +The theme palette in `public/css/styles.css` is hex, not `oklch()`. On an engine older than +Chromium 111 (Tizen 2024, webOS TV 24) the accent token was invalid, so the primary button was white +text on no background. A plugin that overrides a palette token should use a hex value too. + ### One horizontal inset across the content pane The topbar, filter bar, pager, form page and empty-table message inset their content by `--pad-x` diff --git a/docs/reading-cost.md b/docs/reading-cost.md new file mode 100644 index 0000000..d281e8b --- /dev/null +++ b/docs/reading-cost.md @@ -0,0 +1,18 @@ +# Reading cost + +## Reading cost, chunk 57e864bce02a, finished 2026-10-30T23:59:00Z + +100624 new input tokens before the first commit. + +Read most (opens and searches): + +- `src/http/app.test.ts` 9 +- `README.md` 7 +- `src/http/app.ts` 4 +- `todo.md` 3 +- `e2e-tests/playwright.config.ts` 2 +- `AGENTS.md` 1 +- `CHANGELOG.md` 1 +- `ci.sh` 1 +- `examples/plugins/scheduling/views/shifts.ejs` 1 +- `src/auth/csrf.ts` 1 diff --git a/public/css/styles.css b/public/css/styles.css index 3250bc4..543e95c 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -21,27 +21,29 @@ widgets (date picker, scrollbars, selects). ------------------------------------------------------------- */ :root { + /* Hex, not oklch: an engine below Chromium 111 drops an oklch token and the primary button + loses its background. */ /* ---- raw LIGHT palette ---- */ --l-bg:#f3f3f4; --l-surface:#ffffff; --l-surface-2:#f6f6f7; --l-surface-3:#fbfbfc; --l-border:#e5e5e8; --l-border-2:#d6d6da; --l-text:#18181b; --l-text-muted:#6b6b73; --l-text-faint:#9a9aa2; - --l-accent:oklch(0.52 0.12 256); --l-accent-bg:oklch(0.96 0.03 256); - --l-accent-bd:oklch(0.82 0.07 256); --l-focus:oklch(0.52 0.14 256); - --l-pos:oklch(0.54 0.13 150); --l-pos-bg:oklch(0.95 0.03 150); --l-pos-bd:oklch(0.83 0.07 150); - --l-neg:oklch(0.55 0.18 25); --l-neg-bg:oklch(0.96 0.03 25); --l-neg-bd:oklch(0.85 0.08 25); - --l-warn:oklch(0.55 0.12 75); --l-warn-bg:oklch(0.96 0.04 85); --l-warn-bd:oklch(0.84 0.08 85); - --l-info:oklch(0.54 0.12 245);--l-info-bg:oklch(0.96 0.03 245);--l-info-bd:oklch(0.83 0.07 245); + --l-accent:#3669ac; --l-accent-bg:#e5f3ff; + --l-accent-bd:#a7c7f2; --l-focus:#2a68b7; + --l-pos:#258343; --l-pos-bg:#e1f5e4; --l-pos-bd:#a8d5b0; + --l-neg:#c53637; --l-neg-bg:#ffebe8; --l-neg-bd:#febab4; + --l-warn:#9a6500; --l-warn-bg:#fef0d4; --l-warn-bd:#e3c78e; + --l-info:#2274af;--l-info-bg:#e2f5ff;--l-info-bd:#a2cdf2; /* ---- raw DARK palette ---- */ --d-bg:#0d0d0f; --d-surface:#161619; --d-surface-2:#1d1d22; --d-surface-3:#1a1a1e; --d-border:#2a2a31; --d-border-2:#3a3a43; --d-text:#ededf0; --d-text-muted:#9b9ba6; --d-text-faint:#6a6a74; - --d-accent:oklch(0.70 0.12 256); --d-accent-bg:oklch(0.32 0.06 256); - --d-accent-bd:oklch(0.48 0.08 256); --d-focus:oklch(0.74 0.13 256); - --d-pos:oklch(0.74 0.14 150); --d-pos-bg:oklch(0.30 0.05 150); --d-pos-bd:oklch(0.45 0.07 150); - --d-neg:oklch(0.74 0.16 25); --d-neg-bg:oklch(0.30 0.07 25); --d-neg-bd:oklch(0.47 0.10 25); - --d-warn:oklch(0.80 0.13 85); --d-warn-bg:oklch(0.31 0.05 80); --d-warn-bd:oklch(0.48 0.07 80); - --d-info:oklch(0.74 0.12 245);--d-info-bg:oklch(0.31 0.06 245);--d-info-bd:oklch(0.47 0.08 245); + --d-accent:#6ba0e8; --d-accent-bg:#1d3351; + --d-accent-bd:#3f5f8a; --d-focus:#72adfb; + --d-pos:#62c37a; --d-pos-bg:#1a3520; --d-pos-bd:#376040; + --d-neg:#ff7e76; --d-neg-bg:#4b1d1b; --d-neg-bd:#8a423d; + --d-warn:#e4b750; --d-warn-bg:#3e2d10; --d-warn-bd:#72592c; + --d-info:#65b2f1;--d-info-bg:#12334c;--d-info-bd:#305f85; /* ---- active mapping: default = LIGHT ---- */ --bg:var(--l-bg); --surface:var(--l-surface); --surface-2:var(--l-surface-2); --surface-3:var(--l-surface-3); diff --git a/src/ui/stylesheet-floor.test.ts b/src/ui/stylesheet-floor.test.ts new file mode 100644 index 0000000..4027031 --- /dev/null +++ b/src/ui/stylesheet-floor.test.ts @@ -0,0 +1,14 @@ +import assert from "node:assert/strict"; +import { readdirSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { test } from "node:test"; +import { fileURLToPath } from "node:url"; + +const cssDir = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "public", "css"); + +test("no stylesheet declares an oklch() colour, which an engine below Chromium 111 drops", () => { + // A dropped custom property leaves var(--accent) invalid: white text on no background. + for (const file of readdirSync(cssDir).filter((f) => f.endsWith(".css"))) { + assert.ok(!/oklch\(/.test(readFileSync(join(cssDir, file), "utf8")), `${file} declares oklch()`); + } +}); diff --git a/todo.md b/todo.md index 3bc6f0a..e0d8c9e 100644 --- a/todo.md +++ b/todo.md @@ -12,7 +12,6 @@ | ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score | |---|---|---|---|---|---|---|---|---|---|---|---| -| 30 | 0.4.21 | | defect | **Give every `oklch()` colour a fallback, so primary buttons stay readable on an engine without it.** | 1 | 2 | 2 | 3 | 2 | 4 by 5 | 29.9 | | 29 | 0.4.21 | | defect | **Show the whole nav tree on an engine without `:has()`, and fold it only where `:has()` works.** | 2 | 2 | 2 | 4 | 3 | 4 by 6 | 26.9 | | 31 | 0.4.21 | | | **Delete the design-preview rules in `public/css/auth.css` that no view uses.** | 1 | 1 | 1 | 1 | 2 | code health by 2, 8 by 1 | 13.3 | | 26 | 0.4.21 | | | **Add a domain glossary and the project's intended horizon to the README.** | 1 | 2 | 1 | 2 | 3 | 9 by 3, code health by 2 | 11.9 | @@ -21,7 +20,7 @@ | 24 | 0.4.21 | | | **Retire `src/ui/shell-context.ts` and render the dashboard from `ctx.chrome`.** | 2 | 1 | 2 | 3 | 4 | code health by 3 | 9.0 | | 17 | 0.4.21 | | | **Remove the icons no caller uses from `ICON_NAMES` and the sprite.** | 1 | 1 | 1 | 1 | 1 | 8 by 1, code health by 1 | 8.8 | | 21 | 0.4.21 | | | **Fail the gate on a function that exceeds a size or complexity limit.** | 3 | 1 | 1 | 3 | 5 | code health by 4 | 7.2 | -| 32 | 0.4.21 | 29, 30 | | **Fail the gate on a stylesheet feature the browser floor lacks, unless it is guarded.** | 3 | 1 | 1 | 3 | 4 | code health by 2 | 6.0 | +| 32 | 0.4.21 | 29 | | **Fail the gate on a stylesheet feature the browser floor lacks, unless it is guarded.** | 3 | 1 | 1 | 3 | 4 | code health by 2 | 6.0 | | 27 | 0.4.21 | | | **Inset the scheduling example's lead text, shift count, alerts and call-to-action by `--pad-x`, like the bars around them.** | 1 | 2 | 1 | 2 | 1 | 9 by 2, 2 by 1 | 4.8 | | 20 | 0.4.21 | | | **Redirect an anonymous hit on a session-needing flow page to sign-in, instead of a 500.** | 2 | 2 | 2 | 4 | 3 | 2 by 4 | 4.2 | | 4 | 0.4.21 | | | **Refuse a permission save whose baseline no longer matches Keto, so concurrent edits are not silently lost.** | 2 | 2 | 2 | 4 | 4 | 2 by 4 | 2.8 | @@ -42,12 +41,6 @@ ## Details -### 30. Give every `oklch()` colour a fallback, so primary buttons stay readable on an engine without it. - -The accent, focus and status tokens in `public/css/styles.css` are `oklch()`, which needs Chromium -111. Below it (Tizen 2024, webOS TV 24) `--accent` is invalid, so `.btn-primary` is white text on -no background: the sign-in button reads as blank. - ### 29. Show the whole nav tree on an engine without `:has()`, and fold it only where `:has()` works. README → "Two browser floors": Chromium 87 must reach every page. `public/css/styles.css` hides