diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile
index 990200f9..905d63da 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:=30
+PKG_RELEASE:=31
endif
LUCI_TITLE:=Footstrap Theme
@@ -93,8 +93,7 @@ define Package/luci-theme-footstrap/postinst
# uci-defaults registers the theme; refresh rpcd + drop caches so LuCI sees it without a
# reboot. Calling it ourselves is belt-and-braces: OpenWrt's default_postinst also runs
# (then deletes) every /etc/uci-defaults/* we ship, so it executes twice per install. It is
- # idempotent, and its fresh-vs-upgrade marker is written at the end of the first pass, so
- # that pass still sees a fresh install.
+ # idempotent: the second pass finds the theme already registered and changes nothing.
[ -f /etc/uci-defaults/30_luci-theme-footstrap ] && \
sh /etc/uci-defaults/30_luci-theme-footstrap >/dev/null 2>&1 || true
rm -f /tmp/luci-indexcache* /tmp/luci-modulecache/* >/dev/null 2>&1 || true
@@ -107,33 +106,15 @@ define Package/luci-theme-footstrap/postrm
#!/bin/sh
# opkg runs the OLD package's postrm with arg "upgrade" during a version upgrade (proven on 24.10:
# `prerm upgrade`, then `postrm upgrade`, then the new `postinst configure`) and "remove" on a real
-# removal. On upgrade this script MUST change nothing: reverting mediaurlbase to bootstrap, wiping
-# the theme registration and deleting the .installed marker here is what flipped every updating
-# 24.10 user back to bootstrap — and it defeated uci-defaults' own upgrade guard, because with the
-# marker gone uci-defaults treated the run as "fresh", yet postrm had already set mediaurlbase to
-# bootstrap, which no "fresh" branch re-activates. apk never runs this on upgrade (it uses the new
-# package's pre/post-upgrade), so guarding on the arg is correct for both managers; real removal
-# passes "remove" (opkg) or no arg and proceeds below.
+# removal. On upgrade this script MUST change nothing: reverting mediaurlbase and wiping the theme
+# registration here is what flipped every updating 24.10 user back to bootstrap. apk never runs this
+# on upgrade (it uses the new package's pre/post-upgrade), so guarding on the arg is correct for both
+# managers; real removal passes "remove" (opkg) or no arg and proceeds below.
case "$$1" in *upgrade*) exit 0 ;; esac
[ -n "$${IPKG_INSTROOT}" ] || {
- # Every theme name this package has ever registered. postrm runs when uci-defaults is
- # already deleted, so it cannot source the list: it is duplicated by necessity and
- # mirror-pinned so it cannot drift. Only the assignment is byte-identical — the `$$` is
- # make's escaping.
-# @mirror theme/legacy-names
-LEGACY_THEMES="Footstrap FootstrapDark FootstrapLight FootstrapTop FootstrapTopDark FootstrapTopLight FootstrapSidebar FootstrapOnTop"
-# @endmirror
- for t in $$LEGACY_THEMES; do
- uci -q delete "luci.themes.$$t"
- done
- # The router's DEFAULT layout, set by uci-defaults when migrating an old top-nav install.
- # It means nothing to any other theme, and left behind it would silently re-apply the top
- # bar on a later reinstall.
- uci -q delete luci.main.footstrap_layout
- # Don't leave the active theme pointing at the media dir we just removed. The same TWO-part
- # check uci-defaults uses: a theme needs its media dir AND its ucode template to render, so
- # a one-part check could hand the UI to a half-removed bootstrap — the blank page this
- # branch exists to avoid.
+ uci -q delete luci.themes.Footstrap
+ # Don't leave the active theme pointing at the media dir we just removed: a theme needs its
+ # media dir AND its ucode template to render, so both are checked before handing the UI over.
case "$$(uci -q get luci.main.mediaurlbase)" in
/luci-static/footstrap*)
[ -d /www/luci-static/bootstrap ] && \
@@ -141,9 +122,6 @@ LEGACY_THEMES="Footstrap FootstrapDark FootstrapLight FootstrapTop FootstrapTopD
uci set luci.main.mediaurlbase=/luci-static/bootstrap ;;
esac
uci commit luci
- # The "installed before" marker uci-defaults writes to tell fresh from upgrade. Removing it
- # makes uninstall-then-reinstall count as fresh again.
- rm -rf /usr/share/luci-theme-footstrap >/dev/null 2>&1 || true
# The admin-uploaded login background (kept out of the package on purpose, so it survives an
# upgrade). A real removal is the one time it should go — this branch never runs on upgrade.
rm -rf /etc/footstrap >/dev/null 2>&1 || true
diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js
index 4571fde3..bcaa03ef 100644
--- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js
+++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js
@@ -179,7 +179,9 @@ function build() {
hicontrast: 'Hi-Contrast',
/* names the OTHER package, luci-theme-bootstrap, whose colours this palette is —
* so it is a proper noun and stays untranslated, like the two above it */
- bootstrap: 'Bootstrap'
+ bootstrap: 'Bootstrap',
+ /* names the OTHER package again, luci-theme-openwrt-2020, whose colourway this is */
+ '2020': 'OpenWrt 2020'
}, bump(repaint(prefs.applyPalette)), label)),
/* Density: how much air the UI uses. Pure token axis — 02-tokens.css multiplies the type and
diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-prefs.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-prefs.js
index 439c66d5..42b32e3f 100644
--- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-prefs.js
+++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-prefs.js
@@ -350,7 +350,7 @@ function propAxis(key, sdKey, prop, min, max, dfl, fmt) {
*
* Legacy 'rvht'/'roman'/'github' are migrated to explicit values by head.ut before paint, so they
* never reach currentPalette() on a loaded page; the stray fallthrough covers them anyway. */
-const PALETTES = [ 'hicontrast', 'bootstrap' ]; /* the non-default values; 'footstrap' = bare :root */
+const PALETTES = [ 'hicontrast', 'bootstrap', '2020' ]; /* the non-default values; 'footstrap' = bare :root */
function paletteDefault() {
const d = sd('palette');
return (PALETTES.indexOf(d) >= 0) ? d : 'footstrap';
diff --git a/luci-theme-footstrap/root/etc/uci-defaults/30_luci-theme-footstrap b/luci-theme-footstrap/root/etc/uci-defaults/30_luci-theme-footstrap
index 8fd77a12..0ba30758 100755
--- a/luci-theme-footstrap/root/etc/uci-defaults/30_luci-theme-footstrap
+++ b/luci-theme-footstrap/root/etc/uci-defaults/30_luci-theme-footstrap
@@ -1,162 +1,36 @@
#!/bin/sh
-# FRESH INSTALL OR UPGRADE? A fresh install may activate this theme; an upgrade must NEVER change
-# the theme the user is on. The guard used to be `[ "$PKG_UPGRADE" = 1 ]` alone — apk never sets
-# that and neither does postinst, so only dev-sync.sh (which exports it by hand) ever took the
-# upgrade branch: on a real router the guard was DEAD, and an upgrade that found an empty
-# mediaurlbase would activate this theme behind the user's back. The marker file is the state that
-# really tells the two apart — it exists iff this script has completed at least once.
-# `${PKG_UPGRADE:-}`, not `$PKG_UPGRADE`: the variable is unset on every real path (that is the
-# point of the paragraph above), and this file is `sh`-run by the Makefile today but sourced by
-# whatever runs uci-defaults tomorrow — under `set -u` a bare expansion aborts the script here,
-# the theme is never registered, and the Makefile's `>/dev/null 2>&1` swallows the reason.
-FS_MARKER=/usr/share/luci-theme-footstrap/.installed
-if [ "${PKG_UPGRADE:-}" = 1 ] || [ -f "$FS_MARKER" ]; then
- PKG_UPGRADE=1
-else
- PKG_UPGRADE=0
+# Registration, and the three symlinks that expose what the admin uploads. Nothing here migrates a
+# router off an older footstrap: OpenWrt expects a sysupgrade rather than a package upgrade, so
+# leftovers go with the image, and the cleanup that is worth doing is config rather than files.
+
+changed=0
+
+set_opt() {
+ uci -q get "luci.$1" >/dev/null 2>&1 && return
+ uci set "luci.$1=$2"
+ changed=1
+}
+
+set_opt themes.Footstrap /luci-static/footstrap
+
+# A fresh install may pick the theme; an upgrade must never move a router off the theme it is on.
+# `changed` is what tells the two apart — on an upgrade the entry is already there — and it carries
+# the decision on its own, because apk exports no PKG_UPGRADE.
+if [ "${PKG_UPGRADE:-}" != 1 ] && [ "$changed" = 1 ]; then
+ set_opt main.mediaurlbase /luci-static/footstrap
fi
-# Every theme name this package has EVER registered — delete them all; the ONE surviving entry
-# is re-added below (layout, mode and palette are client-side axes on the Footstrap tab of
-# System -> System, so they are not theme entries). The Makefile's postrm must delete the same set (it runs when
-# this file is already gone, so it cannot source it) and the two lists had already drifted into a
-# different ORDER — which is how one of them would quietly lose a name. Only the assignment is
-# pinned: the Makefile writes `$$t` where this writes `$t`.
-# @mirror theme/legacy-names
-LEGACY_THEMES="Footstrap FootstrapDark FootstrapLight FootstrapTop FootstrapTopDark FootstrapTopLight FootstrapSidebar FootstrapOnTop"
-# @endmirror
-for t in $LEGACY_THEMES; do
- uci -q delete "luci.themes.$t"
-done
+[ "$changed" = 1 ] && uci commit luci
-# WHETHER THE REGISTRATION ACTUALLY LANDED. Every uci WRITE below is checked into this flag, and a
-# run that ends with it set refuses to write the marker and exits non-zero — which is what makes the
-# failure survivable: OpenWrt's default_postinst deletes a uci-defaults script only when it exits 0
-# (`( . "$i" ) && rm -f "$i"`), so a non-zero run is retried on the next boot. Without this the
-# script reported success on a full or read-only overlay, on a locked /etc/config/luci and mid-
-# upgrade: the theme was never registered, the script was deleted so nothing retried it, AND the
-# marker was written anyway — so the next install took the upgrade branch, which never activates a
-# theme. One unwritable file turned into a permanently unthemed router, silently. Deletes are NOT
-# counted: `uci -q delete` on an absent entry exits 1 and that is the normal case here.
-fail=0
-
-# The ONE theme entry: sidebar and top bar are one template morphed by :root[data-layout].
-uci set luci.themes.Footstrap=/luci-static/footstrap || fail=1
-
-# Normalise the active theme. Every legacy media path below is now neither registered in
-# luci.themes nor on disk, so an install left pointing at one is stranded: the Design page does
-# not list it (it cannot be switched away from) and its media dir 404s. They all map onto the one
-# surviving path. Non-footstrap themes are left alone; a fresh install defaults to footstrap.
-#
-# MIGRATION — a router that was on the top-nav theme must STAY on the top bar. The layout lives
-# in the browser (localStorage 'fs-layout') and a shell script cannot write that, so carry it
-# server-side: footstrap_layout is the router's DEFAULT layout, which head.ut stamps onto
-# and the client's own choice then overrides. Set only when the old path says
-# top-nav, and only if unset, so a re-run never clobbers a deliberate change.
-cur=$(uci -q get luci.main.mediaurlbase)
-new=$cur
-case "$cur" in
- # was on the top-nav theme (incl. its legacy dark/light variants) -> keep the bar
- /luci-static/footstrap-top*)
- [ -n "$(uci -q get luci.main.footstrap_layout)" ] || \
- uci set luci.main.footstrap_layout=top || fail=1
- new=/luci-static/footstrap ;;
- # any other footstrap path (incl. the legacy -dark/-light) -> the built-in default (the bar):
- # this branch deliberately sets no footstrap_layout, and absent means top (head.ut)
- /luci-static/footstrap*) new=/luci-static/footstrap ;;
- "") [ "$PKG_UPGRADE" = 1 ] || new=/luci-static/footstrap ;;
-esac
-
-# Last-resort guard against a blank UI: whatever stays active must be on disk — media dir AND
-# ucode template. If a partial upgrade or a hand-edited mediaurlbase points at neither, fall back
-# to stock bootstrap (a plain UI beats no UI); skipped if bootstrap is not installed either.
-#
-# SCOPED TO OUR OWN THEME, and the scoping is the point: `new` defaults to whatever the router
-# already had, so without the case this guard judged OTHER PEOPLE'S themes — installing footstrap
-# would inspect an active luci-theme-argon and, if its template dir was not named after its media
-# basename (or it was mid-upgrade), quietly rewrite mediaurlbase to bootstrap.
-case "$new" in
- /luci-static/footstrap*)
- theme=${new##*/}
- if [ ! -d "/www/luci-static/$theme" ] ||
- [ ! -f "/usr/share/ucode/luci/template/themes/$theme/header.ut" ]; then
- [ -d /www/luci-static/bootstrap ] &&
- [ -f /usr/share/ucode/luci/template/themes/bootstrap/header.ut ] &&
- new=/luci-static/bootstrap
- fi
- ;;
-esac
-
-[ "$new" = "$cur" ] || uci set luci.main.mediaurlbase="$new" || fail=1
-
-uci commit luci || fail=1
-
-# Appearance "Save as default" writes the router-wide axis defaults into /etc/config/footstrap
-# through the scoped uci ACL (config 'footstrap' only). The rpcd uci `set` backend the browser
-# calls needs the section to ALREADY EXIST, so guarantee it here — the shipped conffile carries it,
-# but a router that installed a footstrap predating this feature has no such file. `uci set` refuses
-# to CREATE the config file (only the section within one), so touch it first. Idempotent.
-[ -f /etc/config/footstrap ] || touch /etc/config/footstrap
-if ! uci -q get footstrap.settings >/dev/null 2>&1; then
- uci set footstrap.settings=footstrap || fail=1
- uci commit footstrap || fail=1
-fi
-
-# Login/page background upload (Appearance -> Wallpaper -> File). cgi-upload writes the image to
-# /etc/footstrap/login-bg (a fixed, ACL-scoped path — kept in /etc so it survives a package upgrade
-# and, via lib/upgrade/keep.d, a sysupgrade). uhttpd serves only /www, so a symlink there exposes it
-# at /luci-static/footstrap/bg (uhttpd follows symlinks). Both are recreated on every install/upgrade
-# (postinst re-runs this script) and are idempotent; /www is repopulated from firmware on sysupgrade,
-# so recreating the symlink here rather than shipping it is what makes it reliable on both filesystems.
-mkdir -p /etc/footstrap 2>/dev/null
-[ -d /www/luci-static/footstrap ] && ln -sf /etc/footstrap/login-bg /www/luci-static/footstrap/bg 2>/dev/null
-# The uploaded PATTERN (Appearance -> Wallpaper -> Pattern) is the same arrangement, with ONE
-# difference that is not cosmetic: the exposed name KEEPS THE .svg EXTENSION. uhttpd types a
-# response by extension, and an SVG served as application/octet-stream is one no browser will
-# paint — as a CSS mask, as a background image or at all.
-[ -d /www/luci-static/footstrap ] && ln -sf /etc/footstrap/pattern.svg /www/luci-static/footstrap/pattern.svg 2>/dev/null
-# The admin-installed webfonts (/etc/footstrap/fonts, see partials/head.ut) are a whole DIRECTORY
-# rather than one file: the faces plus an @font-face sheet beside them. Same arrangement as the two
-# images above, and linking a directory has TWO traps, both of which end with `ln` exiting 0 and
-# the link sitting one level too deep, at .../fonts/fonts — where no browser looks, so the fonts
-# silently do not serve and nothing reports a thing:
-# - the link already exists and points at a directory: `ln -sf` follows it. `-n` is the fix.
-# - the path is a REAL directory, which every footstrap before 0.12.1 shipped here full of
-# woff2 files. `-n` does not help — measured on a 25.12 router: exit 0, link created inside.
-# So clear the path first unless it is already the symlink we want. What is being deleted can only
-# be ours, and its contents are package files of a version that no longer exists.
-#
-# DO NOT DELETE THIS AS "an old migration". It is needed for every router whose last footstrap was
-# v0.12.0 or older, because that is where the package still SHIPPED files in that directory — and
-# there is no floor on how old an installed footstrap can be. Without it `ln -sfn` exits 0 with the
-# link planted one level down, the admin's webfonts stop serving, and footstrap.settings.fonts
-# points at a 404 with nothing reporting anything. The doodle cleanup below is the opposite case:
-# dropping it costs 200 kB of flash and nothing else.
+# uhttpd serves /www only, so the admin's own uploads live in /etc — where a sysupgrade keeps them
+# (lib/upgrade/keep.d) — and are reached through symlinks. The pattern keeps its .svg name: uhttpd
+# types a response by extension, and an SVG sent as octet-stream paints nowhere.
+mkdir -p /etc/footstrap
if [ -d /www/luci-static/footstrap ]; then
- [ ! -e /www/luci-static/footstrap/fonts ] || [ -L /www/luci-static/footstrap/fonts ] ||
- rm -rf /www/luci-static/footstrap/fonts
- ln -sfn /etc/footstrap/fonts /www/luci-static/footstrap/fonts 2>/dev/null
+ ln -sf /etc/footstrap/login-bg /www/luci-static/footstrap/bg
+ ln -sf /etc/footstrap/pattern.svg /www/luci-static/footstrap/pattern.svg
+ ln -sfn /etc/footstrap/fonts /www/luci-static/footstrap/fonts
fi
-# The two doodle wallpapers earlier versions downloaded into /www on demand. They were never
-# package files, so no package manager will ever remove them, and nothing serves them any more —
-# the axis they belonged to now paints an admin-uploaded SVG. Left behind they are 200 kB of a
-# router's flash held by a feature that no longer exists.
-rm -f /www/luci-static/footstrap/cats.svg /www/luci-static/footstrap/dinos.svg 2>/dev/null
-
-# these caches embed theme paths — drop them so the switch applies on the next request
-rm -f /tmp/luci-indexcache* 2>/dev/null
-rm -rf /tmp/luci-modulecache 2>/dev/null
-
-# Write the marker LAST, so a run that dies half-way is still a fresh install next
-# time rather than an upgrade.
-if [ "$fail" = 1 ]; then
- echo "luci-theme-footstrap: a uci write failed — not marking this install as complete;" >&2
- echo " this script stays in /etc/uci-defaults and runs again on the next boot." >&2
- exit 1
-fi
-
-mkdir -p "$(dirname "$FS_MARKER")" 2>/dev/null
-: > "$FS_MARKER" 2>/dev/null
exit 0
diff --git a/luci-theme-footstrap/styles/03-palettes.css b/luci-theme-footstrap/styles/03-palettes.css
index e3a8702c..d97fb9c9 100644
--- a/luci-theme-footstrap/styles/03-palettes.css
+++ b/luci-theme-footstrap/styles/03-palettes.css
@@ -478,4 +478,73 @@
--fs-on-warn: #222222;
--fs-on-danger: #222222;
}
+ /* ============================================================
+ * 2020 — the OpenWrt 2020 theme's colourway
+ *
+ * `luci-theme-openwrt-2020` is one colour scheme and no dark mode: the OpenWrt CI cyan
+ * (#00B5E2) on the deep navy (#002B49), white content, and three semantic colours
+ * (#5CB85C / #CC8800 / #CC1111). This palette is that identity carried onto footstrap's
+ * surfaces — proposed in review as the way its look survives the theme itself.
+ *
+ * WHAT MOVED, AND WHY EACH ONE HAD TO. The cyan is the whole identity and it is unreadable as
+ * text on white: #00B5E2 measures 2.09:1, where the export tier every app prints text in wants
+ * 4.5:1. So the LIGHT mode keeps the hue and darkens it, while the DARK mode — whose surfaces
+ * are the navy the cyan was designed against — carries it near enough its own value. That
+ * split is the point of the pair: 2020's own scheme IS the dark one, and the light mode is
+ * what footstrap needs to show the same identity on a white page.
+ *
+ * The three semantic colours get the same treatment for the same reason: on white, 2020's
+ * green is 2.28:1, its amber 3.42:1 and its red 5.25:1 — only the red clears AA, and it is the
+ * only one kept close to its original.
+ *
+ * The navy is not the chrome here. In 2020 it is the header and the sidebar; a palette cannot
+ * give the chrome its own colour (that reads --fs-panel), so in dark mode the navy is the
+ * CANVAS and the chrome sits on it, which is the nearest thing this theme's structure has. */
+ :root[data-palette="2020"] {
+ /* LIGHT — white content, the CI cyan darkened until it can be read on it */
+ --fs-bg-base: #ffffff;
+ --fs-panel-base: #ffffff;
+ --fs-panel2-base: #eef4f8;
+ --fs-border-base: #c3d2dd;
+ /* navy-tinted neutrals rather than grey: the ink belongs to the same scheme as the accent */
+ --fs-text: #10202b;
+ --fs-dim: #2c4150;
+ --fs-faint: #3d5566;
+ --fs-select-chevron: url('data:image/svg+xml;utf8,');
+ /* #00B5E2 held at hue, taken down until it reads: 5.89:1 on white against its own 2.09:1 */
+ --fs-accent-base: #036c92;
+ --fs-accent-soft: color-mix(in srgb, var(--fs-accent) 10%, transparent);
+ --fs-track: #e3ebf1;
+ --fs-good-base: #276e2a; /* #5CB85C darkened; 2.28:1 as text is not a rounding error */
+ --fs-warn-base: #8a5a00; /* #CC8800, same move */
+ --fs-danger-base: #c1121f; /* #CC1111 is 5.25:1 already — the one that stays where it was */
+ /* ink ON the fills above — white clears AA on all four */
+ --fs-on-accent: #fff;
+ --fs-on-good: #fff;
+ --fs-on-warn: #fff;
+ --fs-on-danger: #fff;
+ }
+ :root[data-palette="2020"][data-darkmode="true"] {
+ /* DARK — 2020's own scheme: the navy is the canvas and the cyan is nearly untouched */
+ --fs-bg-base: #002b49;
+ --fs-panel-base: #0a3a58;
+ --fs-panel2-base: #0e4362;
+ --fs-border-base: #1d5a7d;
+ --fs-text: #e6f1f7;
+ --fs-dim: #b8cfdd;
+ --fs-faint: #9dbccd;
+ --fs-select-chevron: url('data:image/svg+xml;utf8,');
+ /* the CI cyan, one step up so it clears AA on the lightest of the three surfaces too */
+ --fs-accent-base: #35c6ea;
+ --fs-accent-soft: color-mix(in srgb, var(--fs-accent) 15%, transparent);
+ --fs-track: #123c56;
+ --fs-good-base: #74cc74; /* #5CB85C lifted for the same reason */
+ --fs-warn-base: #eab346;
+ --fs-danger-base: #ff9d9d;
+ /* these fills are LIGHT, so the ink is the canvas navy itself */
+ --fs-on-accent: #002b49;
+ --fs-on-good: #002b49;
+ --fs-on-warn: #002b49;
+ --fs-on-danger: #002b49;
+ }
}
diff --git a/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut b/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut
index 1b34b90c..1fd7e398 100644
--- a/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut
+++ b/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut
@@ -31,7 +31,7 @@
/* The ROUTER's saved Appearance defaults (Appearance -> Save as default, in /etc/config/footstrap):
head.ut stamps them before paint and the client's own localStorage overrides every one — the
browser choice always wins. Layout falls back to the legacy migration seed
- luci.main.footstrap_layout=top, which uci-defaults sets for a router upgraded from the old
+ luci.main.footstrap_layout=top, which an admin may set to give the router a default
top-nav theme (a shell script cannot write localStorage). Absent = the built-in defaults.
`uci` and `config` come from the render scope, which dispatcher.uc builds per request: `uci` is
@@ -77,7 +77,7 @@
for (let k in FS_AXES)
fs_defaults[k] = fsd[k] || '';
/* the one value with a fallback of its own: the legacy migration seed
- luci.main.footstrap_layout=top, which uci-defaults sets for a router upgraded from the old
+ luci.main.footstrap_layout=top, which an admin may set to give the router a default
top-nav theme (a shell script cannot write localStorage).
`config.main?.` and not `config.main.`: ucode raises a hard reference error on a property read
diff --git a/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut b/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut
index b0b03602..be58db91 100644
--- a/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut
+++ b/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut
@@ -20,7 +20,7 @@
it. */
let _sd_lay = index([ 'sidebar', 'top' ], fs_defaults.layout) >= 0 ? fs_defaults.layout : '';
let _sd_dark = index([ 'auto', 'light', 'dark' ], fs_defaults.darkmode) >= 0 ? fs_defaults.darkmode : '';
- let _sd_pal = index([ 'footstrap', 'hicontrast', 'bootstrap' ], fs_defaults.palette) >= 0 ? fs_defaults.palette : '';
+ let _sd_pal = index([ 'footstrap', 'hicontrast', 'bootstrap', '2020' ], fs_defaults.palette) >= 0 ? fs_defaults.palette : '';
let _sd_wall = index([ 'off', 'pattern', 'file' ], fs_defaults.wallpaper) >= 0 ? fs_defaults.wallpaper : '';
let _sd_pink = index([ 'theme', 'original' ], fs_defaults.pattern_ink) >= 0 ? fs_defaults.pattern_ink : '';
let _sd_ac = index([ 'on', 'off' ], fs_defaults.autocollapse) >= 0 ? fs_defaults.autocollapse : '';
@@ -265,7 +265,7 @@
against — a name in one and not the other paints here and is taken away by
the first live change. */
if (p === null) p = sd.palette || null;
- if (p === 'hicontrast' || p === 'bootstrap') root.setAttribute('data-palette', p);
+ if (p === 'hicontrast' || p === 'bootstrap' || p === '2020') root.setAttribute('data-palette', p);
else root.removeAttribute('data-palette');
/* Wallpaper: off | pattern (the uploaded SVG, tiled and recoloured) | file (the