config: take the chrome's accent from the environment
PRO_TACTS_ACCENT names a Gloss accent, and the header and footer fill with that shade;
without it the page keeps the default accent and neither bar gets repainted.
rake dev picks ink blue, so it looks the same as before,
but a missing VERSION does not repaint the page anymore.
Assisted-by: Claude Opus 5 via Claude Code
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
diff --git a/Rakefile b/Rakefile
index 328c104..46effa7 100644
--- a/Rakefile
+++ b/Rakefile
@@ -30,6 +30,7 @@ task :dev do
ENV["PRO_TACTS_EXCHANGE_LOG"] ||= "log/dev.log"
ENV["PRO_TACTS_INSTANCE_NAME"] ||= "pro-tacts (dev)"
ENV["PRO_TACTS_FAVICON"] ||= "/favicon-dev.svg"
+ ENV["PRO_TACTS_ACCENT"] ||= "ink-blue"
File.truncate("log/dev.log", 0) if File.exist?("log/dev.log")
Dir.mktmpdir("pro-tacts-dev") do |dir|
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index 955784b..7321167 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -52,8 +52,8 @@ What that inheritance actually means:
`_components/<name>.md` rather than guessing from a sibling.
- **One accent, held constant.** Pro-tacts sets `--gl-color-accent` once and
never shows two accents in a view. `-ink` and `-soft` re-derive themselves.
- The one swap is by run, not by view: a dev server repoints it to ink blue on
- every screen (see the footer, below).
+ The one swap is by run, not by view: `PRO_TACTS_ACCENT` repoints it on every
+ screen, and `rake dev` uses it to wear ink blue (see the footer, below).
- `--gl-color-success` and `--gl-color-danger` are **fixed** — they mean
confirmed and destructive, never decoration, never emphasis.
- Surface contrast does the elevation work: a bounded surface reads by
@@ -105,9 +105,9 @@ Under every screen sits the footer, the header's counterpart, holding what no
search can turn up because none of it is a record: the version answering,
whether debug logging is recording, and a link to device setup. A run with no
release behind it says "dev server" in the version's place, since a working
-copy dressed as the deployment is the confusion the line exists to end. The
-same run fills the header and footer with its accent, so the difference shows
-before a word of either is read. Debug
+copy dressed as the deployment is the confusion the line exists to end. A run
+with its own accent fills the header and footer with it, so the difference
+shows before a word of either is read. Debug
logging appears only while it is on, written `+debug` — a flag on the build,
not a sentence beside it — and a quiet log has nothing to say.
diff --git a/lib/pro_tacts/admin/layout.rb b/lib/pro_tacts/admin/layout.rb
index 33e334d..ee1f009 100644
--- a/lib/pro_tacts/admin/layout.rb
+++ b/lib/pro_tacts/admin/layout.rb
@@ -41,11 +41,11 @@ module ProTacts
def view_template
doctype
- # A run with no release behind it, the footer's "dev server",
- # marks the root, and admin.css repaints the accent and the
- # chrome for it: a working copy should not pass for the
- # deployment at a glance (docs/DESIGN.md).
- html(lang: "en", **(ProTacts.config.version ? {} : {data: {server: "dev"}})) do
+ # A configured accent marks the root, and admin.css repaints the
+ # accent and the chrome for it: `rake dev` names one so a working
+ # copy cannot pass for the deployment at a glance (docs/DESIGN.md).
+ accent = ProTacts.config.accent
+ html(lang: "en", **(accent ? {data: {accent:}} : {})) do
head do
meta(charset: "utf-8")
meta(name: "viewport", content: "width=device-width, initial-scale=1")
diff --git a/lib/pro_tacts/config.rb b/lib/pro_tacts/config.rb
index b58b3cd..4b46cc5 100644
--- a/lib/pro_tacts/config.rb
+++ b/lib/pro_tacts/config.rb
@@ -71,6 +71,14 @@ module ProTacts
@env.fetch("PRO_TACTS_INSTANCE_NAME", "pro-tacts")
end
+ # A Gloss accent to wear in place of the default, PRO_TACTS_ACCENT —
+ # teal, clay, ink-blue, ochre, or plum (public/admin.css); nil keeps
+ # the default and leaves the chrome unfilled.
+ #: () -> String?
+ def accent
+ @env.fetch("PRO_TACTS_ACCENT", nil)
+ end
+
# The tab's icon, PRO_TACTS_FAVICON, as an href; nil leaves the page
# without one, which is the deployment's "no brand mark"
# (docs/DESIGN.md).
diff --git a/public/admin.css b/public/admin.css
index 9230adc..7674a20 100644
--- a/public/admin.css
+++ b/public/admin.css
@@ -400,28 +400,30 @@ textarea:focus {
margin-left: auto;
}
-/* A run with no release behind it (Layout marks the root; the footer
- names it "dev server") wears Gloss's ink blue instead of the default
- accent, and the chrome bars fill with it, so a working copy cannot
- pass for the deployment at a glance. The whole root repoints rather
- than the bars alone, so a view still shows one accent. Clay was
- passed over for sitting too near danger. The fill is the accent
- under accent-fg, the pairing Gloss's primary button uses;
+/* A run with an accent configured (PRO_TACTS_ACCENT; Layout marks the
+ root) wears that Gloss accent instead of the default, and the chrome
+ bars fill with it, so a working copy cannot pass for the deployment
+ at a glance — `rake dev` picks ink blue. The whole root repoints
+ rather than the bars alone, so a view still shows one accent. Clay
+ was passed over for dev for sitting too near danger. The fill is the
+ accent under accent-fg, the pairing Gloss's primary button uses;
accent-soft would be a pale band in dark mode, where tokens.css
never derives it again. */
-:root[data-server="dev"] {
- --gl-color-accent: var(--gl-accent-ink-blue);
-}
-:root[data-server="dev"] :is(.admin-header, .admin-footer) {
+:root[data-accent="teal"] { --gl-color-accent: var(--gl-accent-teal); }
+:root[data-accent="clay"] { --gl-color-accent: var(--gl-accent-clay); }
+:root[data-accent="ink-blue"] { --gl-color-accent: var(--gl-accent-ink-blue); }
+:root[data-accent="ochre"] { --gl-color-accent: var(--gl-accent-ochre); }
+:root[data-accent="plum"] { --gl-color-accent: var(--gl-accent-plum); }
+:root[data-accent] :is(.admin-header, .admin-footer) {
background: var(--gl-color-accent);
border-color: var(--gl-color-accent);
}
-:root[data-server="dev"] :is(.admin-header a, .admin-footer .type-label) {
+:root[data-accent] :is(.admin-header a, .admin-footer .type-label) {
color: var(--gl-color-accent-fg);
}
/* The one focus ring is drawn in the accent, which vanishes against a
bar filled with it. */
-:root[data-server="dev"] :is(.admin-header, .admin-footer) :focus-visible {
+:root[data-accent] :is(.admin-header, .admin-footer) :focus-visible {
outline-color: var(--gl-color-accent-fg);
}
diff --git a/test/pro_tacts/admin/test_footer.rb b/test/pro_tacts/admin/test_footer.rb
index b04b4ef..5440458 100644
--- a/test/pro_tacts/admin/test_footer.rb
+++ b/test/pro_tacts/admin/test_footer.rb
@@ -71,16 +71,16 @@ class AdminFooterTest < Minitest::Test
refute_includes last_response.body, "+debug"
end
- def test_an_unversioned_run_marks_the_page_for_the_dev_theme
- with_env({})
+ def test_a_configured_accent_marks_the_page
+ with_env("PRO_TACTS_ACCENT" => "ink-blue")
- assert_includes last_response.body, %(<html lang="en" data-server="dev">)
+ assert_includes last_response.body, %(<html lang="en" data-accent="ink-blue">)
end
- def test_a_release_leaves_the_page_unmarked
- with_env("VERSION" => "20260904-1822-a1b2c3d")
+ def test_no_accent_leaves_the_page_unmarked
+ with_env({})
- refute_includes last_response.body, "data-server"
+ assert_includes last_response.body, %(<html lang="en">)
end
def test_the_tab_carries_the_configured_favicon
diff --git a/test/pro_tacts/test_config.rb b/test/pro_tacts/test_config.rb
index 3067529..9102bcc 100644
--- a/test/pro_tacts/test_config.rb
+++ b/test/pro_tacts/test_config.rb
@@ -66,6 +66,14 @@ class ConfigTest < Minitest::Test
assert_equal "pro-tacts (dev)", ProTacts::Config.new("PRO_TACTS_INSTANCE_NAME" => "pro-tacts (dev)").instance_name
end
+ def test_accent_is_nil_when_unset
+ assert_nil ProTacts::Config.new({}).accent
+ end
+
+ def test_accent_is_passed_through
+ assert_equal "ink-blue", ProTacts::Config.new("PRO_TACTS_ACCENT" => "ink-blue").accent
+ end
+
def test_favicon_is_nil_when_unset
assert_nil ProTacts::Config.new({}).favicon
end