Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ lint-ci: check-planning

test:
uv run pytest

# Regenerate the brand kit and copy the subset the site serves into docs/assets.
sync-assets:
uv run python -m brand.build.render
cp brand/org/favicon.svg brand/org/mark.svg brand/org/wordmark.svg brand/org/wordmark-dark.svg docs/assets/
cp brand/org/social-card-green.png docs/assets/
49 changes: 49 additions & 0 deletions architecture/site-branding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Site branding assets

How the org site (`modern-python.org`) gets its logo, favicon, hero wordmark, and
social card. The canonical brand kit is generated by `brand/build/`; the site
serves a copied subset under `docs/assets/`.

## Pipeline

1. `brand/build/` is the source of truth. `uv run python -m brand.build.render`
draws every asset from `brand/build/geometry.py` (vector geometry; text is
outlined to paths via the bundled Jost TTF, so nothing depends on a font at
serve time) into `brand/org/`.
2. `just sync-assets` runs the render, then copies the site subset into
`docs/assets/`: `favicon.svg`, `mark.svg`, `wordmark.svg`, `wordmark-dark.svg`,
and `social-card-green.png`. (The site can only serve files under `docs/`, so
the copies are intentional duplication, not drift — regenerate via the recipe.)
3. MkDocs Material consumes them (see wiring below).

## Geometry building blocks (`brand/build/geometry.py`)

- `icon()` — full-bleed square mark (favicon/avatar/apple-touch), with background.
- `mark()` — the same chevron mark with **no background**, for the site header.
- `lockup_body()` — the `MODERN`/`PYTHON` crop-mark wordmark, drawn in a 540x250
space, returned as bare markup for embedding.
- `wordmark()` — `lockup_body` wrapped in a tight transparent viewBox
(`118 32 304 184`) for standalone use in the hero.
- `social_card()` / `social_square()` — the wordmark composited onto a background.

Two color roles flow through every function: `struct` (the cream/green-ink
structure) and `gold` (the accent). The site uses two wordmark variants:
green-ink + gold-light (`wordmark.svg`, light pages) and cream + gold-dark
(`wordmark-dark.svg`, dark pages and the green header).

## Site wiring

- **Header** (`mkdocs.yml` `theme.logo: assets/mark.svg`) — the chevron mark, with
Material's default "Modern Python" site title (Roboto) and search beside it.
- **Favicon** (`mkdocs.yml` `theme.favicon: assets/favicon.svg`).
- **Hero** (`docs/index.md`) — both wordmark variants sit inside the page `<h1>`
(`.mp-wordmark`); `extra.css` shows the light variant by default and swaps to
the dark variant under `[data-md-color-scheme="slate"]`. The `<h1>` is load-
bearing: Material injects a fallback `<h1>{{ page.title }}</h1>` (which read
"Home" on the index) only when the rendered content has no `<h1>`, so wrapping
the wordmark in one suppresses it.
- **Social card** (`overrides/main.html`) — the `og:image` / `twitter:image`
meta points at `assets/social-card-green.png` (the green card).

The brand palette and token roles live in `brand/build/tokens.py` and
`brand/README.md`.
18 changes: 18 additions & 0 deletions brand/build/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ def lockup_body(*, struct: str, gold: str) -> str:
return crops + modern + python


def wordmark(*, struct: str, gold: str) -> str:
"""Standalone two-color MODERN/PYTHON wordmark for the site hero. Wraps
`lockup_body` (drawn in a 540x250 space) in a tight viewBox centered on the
content, with no background — transparent so it sits on any page surface."""
return (
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="118 32 304 184" '
'role="img" aria-label="Modern Python">'
+ lockup_body(struct=struct, gold=gold)
+ "</svg>"
)


def mark(*, struct: str, gold: str) -> str:
"""The chevron mark on its own (no background) for the site header logo —
same glyph as `icon`, minus the full-bleed background rect."""
return _SVG_OPEN.format(w=100, h=100) + _icon_mark(struct, gold) + "</svg>"


def social_card(*, bg: str, struct: str, gold: str, url_color: str) -> str:
body = lockup_body(struct=struct, gold=gold)
url, _ = outline_text("modern-python.org", 34, x=640, baseline_y=575,
Expand Down
7 changes: 7 additions & 0 deletions brand/build/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ def render() -> None:
g.icon_circle(bg=t.GREEN_SURFACE, struct=t.CREAM, gold=t.GOLD_DARK))
export_png(ORG / "avatar-circle.svg", ORG / "avatar-circle-1024.png", width=1024, height=1024)

# Site logos — transparent, no background.
# wordmark (hero): two-color lockup, light + dark variants
# mark (header): chevron mark in cream/gold-dark for the green header bar
_write(ORG / "wordmark.svg", g.wordmark(struct=t.GREEN_INK, gold=t.GOLD_LIGHT))
_write(ORG / "wordmark-dark.svg", g.wordmark(struct=t.CREAM, gold=t.GOLD_DARK))
_write(ORG / "mark.svg", g.mark(struct=t.CREAM, gold=t.GOLD_DARK))

# Social cards — cream (primary) + green (alternate).
_write(ORG / "social-card.svg",
g.social_card(bg=t.CREAM, struct=t.GREEN_INK, gold=t.GOLD_LIGHT, url_color=t.GOLD_LIGHT))
Expand Down
1 change: 1 addition & 0 deletions brand/org/mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions brand/org/wordmark-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions brand/org/wordmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
1 change: 1 addition & 0 deletions docs/assets/mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion docs/assets/modern-python-mark.svg

This file was deleted.

1 change: 0 additions & 1 deletion docs/assets/modern-python-white.svg

This file was deleted.

Binary file removed docs/assets/modern-python.png
Binary file not shown.
1 change: 0 additions & 1 deletion docs/assets/modern-python.svg

This file was deleted.

Binary file added docs/assets/social-card-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/assets/social-card.png
Binary file not shown.
Loading