diff --git a/board/_theme.py b/board/_theme.py
index 18ad7ed..364c51f 100644
--- a/board/_theme.py
+++ b/board/_theme.py
@@ -11,12 +11,18 @@
import it and ask for the stylesheet, the hero and the small components; they
keep owning what goes in the rows.
-The design in one line: *the page wears its organ's logo*. Each organ has an
-accent taken from its logo's glyph colour, and every board opens with a dark
-hero reproducing that logo's wordmark — white `PyAuto`, accent-coloured organ
-name, letterspaced tagline underneath, inside the logo's ring. Below the hero
-the page returns to a plain readable document, because these are lists people
-scan on a phone before breakfast, not dashboards to admire.
+The design in one line: *the page wears its organ's logo*. Every board opens
+with a dark hero that redraws the logo it sits under: the organ's own mark in
+line art, the wordmark (white `PyAuto`, the organ name in its accent), the
+hairline-and-dot rule, and the letterspaced tagline. Below the hero the page
+returns to a plain readable document, because these are lists people scan on
+a phone before breakfast, not dashboards to admire.
+
+The mark is **drawn, not borrowed**. An emoji stood in for it at first, which
+read as a cartoon beside the real logo — 🧠 is not the Brain's circuit brain.
+`MARKS` holds each logo's glyph as inline SVG line art traced off the file:
+it inherits the accent, stays sharp at any size, and costs ~1KB in the one
+page that uses it, where the 80-150KB `logo.png` would be most of the page.
Colour carries meaning, never decoration:
@@ -30,8 +36,9 @@
`ORGANS` is the palette table, keyed by the same board names as
`config/policy.yaml` `board: boards:` — that mapping is the declared config
surface an adopting fork replaces, so no repo is named here. Five of the six
-palettes are sampled from the actual logo files: the glyph colour and the
-wordmark's own tagline, straight off `logo.png` in each organ's repo. The
+palettes are sampled from the actual logo files: the mark, the glyph colour
+and the wordmark's own tagline, straight off `logo.png` in each organ's repo.
+The
umbrella board has no logo, so its accent and tagline are designed to sit in
the family instead. Every accent is checked for >= 4.5:1 contrast against
its own background (light on `#fff`, dark on `#0d1117`).
@@ -41,50 +48,215 @@
# ink_light / ink_dark: the accent as *text*, per colour scheme.
# glow: the logo's own glyph colour — used only on the dark hero, where it
# needs to sing rather than pass a contrast check against white.
+# glow2: the far end of the wordmark's gradient, for the two logos that set
+# their organ name in one (Brain runs blue → violet, Memory violet →
+# purple). Omitted where the logo's wordmark is a flat colour.
# hero: (lift, base) — the hero's radial gradient, echoing each logo's
# vignette: a tinted lift behind the wordmark falling to near-black.
ORGANS = {
"mind": {
"organ": "Mind",
- "glyph": "\U0001f4cb", "tagline": "Intent. Priority. Flow.",
+ "tagline": "Intent. Priority. Flow.",
"ink_light": "#0a7d72", "ink_dark": "#2ee6cf", "glow": "#00d1ba",
- "hero": ("#0a3f39", "#000000"),
+ "hero": ("#07302b", "#000000"),
},
"brain": {
"organ": "Brain",
- "glyph": "\U0001f9e0", "tagline": "Reason. Plan. Decide.",
+ "tagline": "Reason. Plan. Decide.",
"ink_light": "#2159c9", "ink_dark": "#6f9dff", "glow": "#4d8bff",
- "hero": ("#1b2a6b", "#000312"),
+ "glow2": "#a855f7",
+ "hero": ("#16234f", "#000312"),
},
"heart": {
"organ": "Heart",
- "glyph": "❤️", "tagline": "Check. Validate. Protect.",
+ "tagline": "Check. Validate. Protect.",
"ink_light": "#b50f1a", "ink_dark": "#ff6b73", "glow": "#ff1c28",
- "hero": ("#4a0a10", "#000000"),
+ "hero": ("#37080d", "#000000"),
},
"hands": {
"organ": "Hands",
- "glyph": "\U0001f6e0️", "tagline": "Build. Execute. Deliver.",
+ "tagline": "Build. Execute. Deliver.",
"ink_light": "#9a5400", "ink_dark": "#ffa733", "glow": "#ff9201",
- "hero": ("#4a2c00", "#000000"),
+ "hero": ("#372100", "#000000"),
},
"memory": {
"organ": "Memory",
- "glyph": "\U0001f4da", "tagline": "Remember. Learn. Evolve.",
+ "tagline": "Remember. Learn. Evolve.",
"ink_light": "#6b34d6", "ink_dark": "#b37eff", "glow": "#b37eff",
- "hero": ("#2e1a5c", "#000000"),
+ "glow2": "#8b5cf6",
+ "hero": ("#221345", "#000000"),
},
# The umbrella is the one board with no logo to sample: the accent and
# the tagline are designed to sit in the family rather than read off a
# file. Replace both from the logo if that repo ever grows one.
"organism": {
"organ": "Scientist",
- "glyph": "\U0001f9ec", "tagline": "Describe. Build. Release.",
+ "tagline": "Describe. Build. Release.",
"ink_light": "#0b6fa4", "ink_dark": "#5ec7f5", "glow": "#38bdf8",
- "hero": ("#0c3f5c", "#00070d"),
+ "hero": ("#092e43", "#00070d"),
},
}
+
+# ----------------------------------------------------------------- marks ---
+# Each organ's logo glyph, traced off `logo.png` as line art on a 48x48 grid:
+# a ring (closed, or left open where the logo opens it), the glyph inside it,
+# and the small filled nodes the family's drawing style uses. Stroke colour
+# is inherited, so one mark serves the hero, light mode and dark mode alike.
+#
+# Why drawn rather than embedded: the boards are self-contained single files
+# read on phones over mobile data. The logos are 80-150KB of PNG each; these
+# are ~1KB of path data, stay sharp on any screen, and take the accent.
+# Keep them that way — a mark is a logo's glyph in line art, not a rendering.
+MARKS = {
+ # Mind — a left-facing head profile, a check inside the skull, three
+ # node-tipped signal lines leaving the back of the head, inside a ring.
+ "mind": (
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ),
+ # Brain — a circuit brain: two lobed hemispheres either side of a spine,
+ # each branching into elbowed, node-tipped traces, inside a ring.
+ "brain": (
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ),
+ # Heart — a heart outline crossed by an ECG trace, with the check badge
+ # sitting in the ring's lower-right gap.
+ "heart": (
+ ''
+ ''
+ ''
+ ''
+ ''
+ ),
+ # Hands — an open palm offering a gear, speed lines behind the cuff,
+ # inside a ring left open at the lower-left where the lines enter.
+ "hands": (
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ),
+ # Memory — an open book with a node-tree growing out of its spine and a
+ # scatter of stars, under an arc open at the bottom.
+ "memory": (
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ),
+ # Umbrella — no logo to sample, so the family language (ring, line-art,
+ # node dots) applied to the double helix the board already carried.
+ "organism": (
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ''
+ ),
+}
+
import html as _html
WORD = "PyAuto" # the half of every wordmark that stays white
@@ -100,6 +272,18 @@ def organ(key):
return ORGANS.get(key, ORGANS["organism"])
+def mark(key):
+ """The organ's logo glyph as an inline SVG, drawn in the current colour.
+
+ Unknown keys fall back with `organ()`, so an adopting fork's board wears
+ the umbrella's mark rather than rendering an empty ring.
+ """
+ art = MARKS.get(key) or MARKS["organism"]
+ return ('')
+
+
# -------------------------------------------------------------------- css ---
# One stylesheet, two colour schemes, one substituted accent. Kept in the
# same dense hand-wrapped style as the renderers that inline it: this ships
@@ -108,7 +292,8 @@ def organ(key):
:root{color-scheme:light dark;--bg:#fff;--fg:#1f2328;--muted:#59636e;
--line:#d8dee4;--btn:#f6f8fa;--ok:#1a7f37;--warn:#9a6700;--bad:#d1242f;
--accent:%(ink_light)s;--tint:%(ink_light)s14;--edge:%(ink_light)s3d;
- --hero-lift:%(hero_lift)s;--hero-base:%(hero_base)s;--glow:%(glow)s}
+ --hero-lift:%(hero_lift)s;--hero-base:%(hero_base)s;--glow:%(glow)s;
+ --glow2:%(glow2)s}
@media(prefers-color-scheme:dark){:root{--bg:#0d1117;--fg:#f0f6fc;
--muted:#9198a1;--line:#2c333c;--btn:#151b23;--ok:#3fb950;--warn:#d29922;
--bad:#f85149;--accent:%(ink_dark)s;--tint:%(ink_dark)s1f;
@@ -127,23 +312,37 @@ def organ(key):
/* --- hero: the logo, rendered as type ---------------------------------- */
.hero{margin:0 -1rem 1.4rem;padding:2.1rem 1.4rem 1.7rem;position:relative;
overflow:hidden;text-align:center;color:#fff;background:var(--hero-base);
- background-image:radial-gradient(105%% 130%% at 50%% -25%%,
- var(--hero-lift) 0%%,var(--hero-base) 68%%)}
+ background-image:radial-gradient(78%% 104%% at 50%% 14%%,
+ var(--hero-lift) 0%%,var(--hero-base) 72%%)}
@media(min-width:46rem){.hero{margin:1rem 0 1.6rem;border-radius:16px}}
.hero::after{content:"";position:absolute;left:12%%;right:12%%;bottom:0;
height:2px;background:linear-gradient(90deg,transparent,var(--glow),
transparent);opacity:.75}
-.orb{display:inline-flex;align-items:center;justify-content:center;
- width:3.1rem;height:3.1rem;margin-bottom:.85rem;border-radius:50%%;
- font-size:1.45rem;line-height:1;border:1.5px solid var(--glow);
- background:radial-gradient(circle,#ffffff14 0%%,transparent 70%%);
- box-shadow:0 0 22px -4px var(--glow)}
+/* The mark carries its own ring, so the frame is light and glow only —
+ the logos set their glyph on black with a halo, not in a chip. */
+.orb{display:block;width:5.6rem;height:5.6rem;margin:0 auto .75rem;
+ color:var(--glow);filter:drop-shadow(0 0 9px %(glow)s7a)}
+.orb svg{display:block;width:100%%;height:100%%}
.hero h1{margin:0;font-size:1.85rem;line-height:1.1;font-weight:700;
letter-spacing:-.022em;color:#fff}
-.hero h1 b{color:var(--glow);font-weight:700}
+/* Two wordmarks run their organ name as a gradient; for the rest --glow2
+ repeats --glow, so the same rule paints a flat colour. */
+.hero h1 b{font-weight:700;color:var(--glow);
+ background:linear-gradient(96deg,var(--glow),var(--glow2));
+ -webkit-background-clip:text;background-clip:text}
+@supports(-webkit-background-clip:text){
+ .hero h1 b{-webkit-text-fill-color:transparent}}
.hero .kind{display:block;margin-top:.5rem;font-size:.66rem;font-weight:600;
letter-spacing:.26em;text-transform:uppercase;color:#ffffffa6}
-.hero .tag{margin:.85rem 0 0;font-size:.63rem;font-weight:600;
+/* The hairline with a lit dot at its centre: every logo separates wordmark
+ from tagline with one, and it is the detail that reads as "same mark". */
+.hero .rule{position:relative;width:11.5rem;height:1px;margin:1rem auto .75rem;
+ background:linear-gradient(90deg,transparent,var(--glow),transparent);
+ opacity:.7}
+.hero .rule::after{content:"";position:absolute;left:50%%;top:50%%;
+ width:.4rem;height:.4rem;margin:-.2rem 0 0 -.2rem;border-radius:50%%;
+ background:var(--glow);box-shadow:0 0 7px 1px var(--glow)}
+.hero .tag{margin:0;font-size:.63rem;font-weight:600;
letter-spacing:.3em;text-transform:uppercase;color:var(--glow);opacity:.9}
.lede{margin:0 0 .9rem}
/* --- sections ---------------------------------------------------------- */
@@ -231,7 +430,8 @@ def css(key):
o = organ(key)
lift, base = o["hero"]
sheet = _CSS % {"ink_light": o["ink_light"], "ink_dark": o["ink_dark"],
- "glow": o["glow"], "hero_lift": lift, "hero_base": base}
+ "glow": o["glow"], "glow2": o.get("glow2", o["glow"]),
+ "hero_lift": lift, "hero_base": base}
chips = "".join(_CHIP % {"sel": f'.boards a[data-organ="{k}"]',
"light": v["ink_light"], "dark": v["ink_dark"]}
for k, v in ORGANS.items())
@@ -240,16 +440,17 @@ def css(key):
# ------------------------------------------------------------- components ---
def hero(key, kind, lede_html=""):
- """The masthead: the organ's logo re-drawn as type.
+ """The masthead: the organ's logo re-drawn — mark, wordmark, rule, tagline.
`kind` is what this page *is* under the wordmark ("Dashboard", "Board"),
so the mark stays the organ's and the page keeps its own name.
"""
o = organ(key)
lede = f'
{lede_html}
' if lede_html else ""
- return (f'{o["glyph"]}'
+ return (f'{mark(key)}'
f'
{WORD}{o["organ"]}'
f'{kind}
'
+ f''
f'
{o["tagline"]}
{lede}')
diff --git a/tests/test_board_theme.py b/tests/test_board_theme.py
index 760b250..d4b4b94 100644
--- a/tests/test_board_theme.py
+++ b/tests/test_board_theme.py
@@ -30,10 +30,13 @@ def test_every_declared_board_has_a_complete_palette_entry():
for key in POLICY_BOARDS:
assert key in _theme.ORGANS, key
o = _theme.ORGANS[key]
- for field in ("organ", "glyph", "tagline",
+ for field in ("organ", "tagline",
"ink_light", "ink_dark", "glow", "hero"):
assert o.get(field), f"{key}.{field}"
assert len(o["hero"]) == 2
+ # The mark is the logo's own glyph; a board without one would render
+ # an empty ring where its logo should be.
+ assert key in _theme.MARKS, key
def _luminance(hex_colour):
@@ -71,11 +74,30 @@ def test_each_board_wears_its_own_accent():
assert _theme.ORGANS["mind"]["glow"] not in brain
+def test_every_mark_is_line_art_that_takes_the_accent():
+ # The marks replaced emoji precisely so they inherit the organ colour and
+ # stay sharp; a hard-coded fill or a stray emoji would undo both.
+ for key, art in _theme.MARKS.items():
+ assert "currentColor" in art or "