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>
change wstrqrpzxvztpwtlyktyoxwzxnmsqxnx
commit bde38e48a086a0f0e271e55d359268baa201e4af
author Alpha Chen <alpha@kejadlen.dev>
date
parent mxlukqkr
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