From d5f9df4a198259d0a2fe7bec4ff45df74bf00d49 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 12 Aug 2026 09:27:55 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=9E=20Sync=202026-08-12=2009:27:55?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luci-theme-footstrap/Makefile | 2 +- .../htdocs/luci-static/resources/fs-router.js | 10 +++-- .../htdocs/luci-static/resources/fs-sheets.js | 23 ++++++++-- luci-theme-footstrap/styles/02-tokens.css | 18 +++++--- luci-theme-footstrap/styles/03-palettes.css | 44 ++++++++++++++----- 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile index ec406bac..f638dd5d 100644 --- a/luci-theme-footstrap/Makefile +++ b/luci-theme-footstrap/Makefile @@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap FOOTSTRAP_VERSION?= ifneq ($(FOOTSTRAP_VERSION),) PKG_VERSION:=$(FOOTSTRAP_VERSION) -PKG_RELEASE:=12 +PKG_RELEASE:=13 endif LUCI_TITLE:=Footstrap Theme diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js index 3d9db6ca..be6ef22f 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js @@ -412,9 +412,13 @@ function navigate(pathname, push, kbd) { * decided by how the user got there. * * So decline, exactly as the poisoned-document bail above does: speed is traded for correctness, - * never the other way. It costs ONE full load per such page — after it the is in the - * document, this test passes, and every later visit is a swap again (fs-sheets.js owns the sheet - * from then on and re-lights it per page). + * never the other way. The cost is one full load per ENTRY INTO A DOCUMENT that lacks the sheet, + * not one per page ever: head.ut emits the link for the DISPATCHED node only, so each full load + * starts a document carrying exactly one such sheet and discards what the previous one had + * gathered. Within that document the page is a swap from then on (fs-sheets.js owns the sheet and + * re-lights it per page) — but two `css`-bearing pages alternating are a full load every time, in + * both directions. That is the trade taken knowingly: an unstyled page is worse than a reload, and + * no in-tree node sets `css` today. * * Injecting the here instead would work and is deliberately not done: it would put the theme * in charge of fetching and ordering a foreign stylesheet, which is the job fs-sheets.js exists to diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js index 3b1dcdfb..a31185d2 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-sheets.js @@ -325,14 +325,29 @@ function outlivesPage(el) { * this module's because the answer is: a link inside #view is about to be deleted with the rest of * the view, and counting it would hand the router a sheet the next dom.content() throws away — * `luci-app-nlbwmon` returns E('link', …, L.resource('view/nlbw.css')) from render(), so that shape - * is real. Compared by PATH and by suffix: head.ut prints `{{ resource }}/{{ dispatched.css }}?v=…`, - * so neither the resource base nor the cache key is anyone's to assume. */ + * is real. + * + * WHOLE PATH, NOT A SUFFIX. head.ut prints `{{ resource }}/{{ dispatched.css }}?v=…`, and the base in + * that line is the SAME value the runtime holds: header.ut hands `resource` to `new LuCI({…})`, and + * `L.resource()` joins it back exactly, so the server's href is reconstructable rather than guessable + * — only the cache key has to come off. A suffix match is what a guess costs: anchored at nothing but + * a `/`, `custom.css` matches any sheet ending in that filename, and two in-tree apps append exactly + * that to at module eval — `luci-app-adblock` and `luci-app-banip` both add + * `L.resource('view//custom.css')`, outside #view, so outlivesPage() keeps them and this module + * disables rather than removes them: they stay for the life of the document. A third-party node + * declaring `"css": "custom.css"` would then read as already-carried the moment the user had passed + * through Adblock → Feeds, and the router would swap into a page whose stylesheet was never linked — + * the one outcome the guard exists to prevent. + * + * Equality also keeps the failure safe: L.path() drops a part that leaves its charset, so a malformed + * `css` yields the bare base and matches no href at all — a full load, which is the correct answer for + * a value nobody can serve. */ function documentCarries(path) { - const want = '/' + String(path).replace(/^\/+/, ''); + const want = L.resource(String(path)); for (const link of document.querySelectorAll('link[rel~="stylesheet"][href]')) { if (!outlivesPage(link)) continue; - if ((link.getAttribute('href') || '').split('?')[0].endsWith(want)) + if ((link.getAttribute('href') || '').split('?')[0] === want) return true; } return false; diff --git a/luci-theme-footstrap/styles/02-tokens.css b/luci-theme-footstrap/styles/02-tokens.css index d44df05a..e3b31382 100644 --- a/luci-theme-footstrap/styles/02-tokens.css +++ b/luci-theme-footstrap/styles/02-tokens.css @@ -469,14 +469,22 @@ --background-color-medium: color-mix(in srgb, var(--fs-panel) 55%, var(--fs-panel2)); --background-color-low: var(--fs-panel2); - /* --text-color-low cannot move: --fs-dim IS the muted text colour and already - * sits ON the AA floor (4.56:1 on --fs-panel2 in dark), so a fainter step would - * be an illegible grey. It stays equal to -medium — the ramp is still wide, -high - * being --fs-text — and a duplicate beats a value nobody can read. */ + /* -low used to be a DUPLICATE of -medium, both --fs-dim, on the grounds that a fainter + * step would be illegible: --fs-dim sits on the AA floor in dark (4.56:1 on --fs-panel2) + * and --fs-faint is the fainter one. That held while the two were a hair apart. Light's + * secondary tier has since moved to 11.8:1 (see 03-palettes.css), so the duplicate stopped + * being a rounding decision and started flattening the export: `spread(high, low)` fell to + * 0.082 in the default palette and 0.094 in hicontrast, under the 0.10 the ramp promises — + * an app asking for a gradation would have got one colour twice. + * + * So -low is --fs-faint, the theme's own third ink, and the illegibility that argument + * feared is what the gate below measures rather than assumes: every level is held to AA as + * text on all three surfaces AND as a fill under its --on-* ink, in both modes, across the + * whole palette x tint matrix. --fs-faint clears it everywhere. */ --text-color-highest: var(--fs-text); --text-color-high: var(--fs-text); --text-color-medium: var(--fs-dim); - --text-color-low: var(--fs-dim); + --text-color-low: var(--fs-faint); /* borders carry no text, so here -low MAY fade toward the surface: a hairline * is supposed to disappear into it. */ diff --git a/luci-theme-footstrap/styles/03-palettes.css b/luci-theme-footstrap/styles/03-palettes.css index 75680259..7f995e14 100644 --- a/luci-theme-footstrap/styles/03-palettes.css +++ b/luci-theme-footstrap/styles/03-palettes.css @@ -257,9 +257,15 @@ --fs-panel2-base: #f6f8fa; --fs-border-base: #d0d7de; --fs-text: #1f2328; - --fs-dim: #656d76; - --fs-faint: #68727e; - --fs-select-chevron: url('data:image/svg+xml;utf8,'); + /* The SECONDARY tier carries LuCI's page descriptions and every field title, and at the + * GitHub-light greys it started from (#656d76/#68727e, 5.2:1 and 4.9:1 on the card) that is + * help text a reader has to lean into — reported on the upstream PR by someone reading the + * light theme on a real router. LuCI's pages are technical and their descriptions are the + * part you read once and act on, so this tier is now 11.8:1 and 10.0:1: still a tier below + * the 15.8:1 body ink, which is what keeps a label a label, but no longer a whisper. */ + --fs-dim: #33383d; + --fs-faint: #3d4348; + --fs-select-chevron: url('data:image/svg+xml;utf8,'); --fs-accent-base: #0969da; /* a tint OF --fs-accent, so mixed FROM it. It used to restate the accent's RGB as a * literal, which goes stale silently the moment the accent is recoloured (or the @@ -315,9 +321,13 @@ --fs-panel2-base: #f6f8fb; --fs-border-base: #e4e9f1; --fs-text: #101725; - --fs-dim: #5b6675; - --fs-faint: #69727f; - --fs-select-chevron: url('data:image/svg+xml;utf8,'); + /* …and this palette's secondary tier moves with it, because the promise in the comment above + * is RELATIVE: 5.8:1 and 4.9:1 were above the default when the default was 5.2:1 and 4.9:1, + * and below it the moment that changed. 13.5:1 and 12.0:1 against the default's 11.8 and + * 10.0. */ + --fs-dim: #272f3d; + --fs-faint: #2e3745; + --fs-select-chevron: url('data:image/svg+xml;utf8,'); --fs-accent-base: #0b6fbd; --fs-accent-soft: color-mix(in srgb, var(--fs-accent) 10%, transparent); --fs-track: #eaeef4; @@ -388,10 +398,17 @@ --fs-panel-base: #ffffff; --fs-panel2-base: #f5f5f5; --fs-border-base: #cccccc; - --fs-text: #404040; - --fs-dim: #6a6a6a; - --fs-faint: #6e6e6e; - --fs-select-chevron: url('data:image/svg+xml;utf8,'); + /* THE INK IS THE ONE PLACE THIS COLOURWAY LEAVES BOOTSTRAP'S OWN VALUES BEHIND, and it is + * the same decision the default palette made: bootstrap's #404040 body and #6a6a6a muted + * tier measure 10.4:1 and 5.0:1 on white, and the muted one carries LuCI's page + * descriptions and every field title — help text you have to lean into. Matched to the + * default palette's light ramp instead, ratio for ratio: 15.7 / 11.7 / 10.0 against its + * 15.8 / 11.8 / 10.0. Neutral greys, so the colourway still reads as bootstrap's — what is + * copied is its hue, not its readability. */ + --fs-text: #232323; + --fs-dim: #383838; + --fs-faint: #424242; + --fs-select-chevron: url('data:image/svg+xml;utf8,'); /* --primary-color-medium; -high (#1976d2) measured 4.22:1 on --fs-panel2 */ --fs-accent-base: #1564c0; --fs-accent-soft: color-mix(in srgb, var(--fs-accent) 10%, transparent); @@ -417,8 +434,13 @@ --fs-panel2-base: #303030; --fs-border-base: #444444; --fs-text: #bfbfbf; + /* faint == dim here, and that is the deviation this palette allows itself: bootstrap's dark + * has no third ink, and the export tier's -low now follows --fs-faint (02-tokens.css), so + * the #969696 this used to carry landed at 4.46:1 on a tinted --fs-panel2 — under AA for a + * name apps print text in. The step above it is bootstrap's own, so the fainter one gives + * way rather than the ramp. */ --fs-dim: #9a9a9a; - --fs-faint: #969696; + --fs-faint: #9a9a9a; --fs-select-chevron: url('data:image/svg+xml;utf8,'); --fs-accent-base: #53a4c2; --fs-accent-soft: color-mix(in srgb, var(--fs-accent) 15%, transparent);