The mark: serve and derive the logo from the SVG, not the PNG #1238

Closed
opened 2026-11-12 08:43:26 +00:00 by tiagoagueda · 2 comments
Owner

The mark Postulo serves is still made from the raster, assets/brand/postulo.png, although a vector version has been beside it since #1218 (assets/brand/postulo.svg). The maintainer asked (2026-11-12) for the PNG logo to be replaced by the SVG version.

What exists today

  • assets/brand/postulo.png, 1024 pixels square, is the one source. scripts/brand.py trims and scales it into src/postulo/static/brand/: favicon-16/32/48.png, favicon.ico, apple-touch-icon.png (on the page's off-white), icon-192.png, icon-512.png, icon-maskable-512.png, logo-64.png and logo-256.png. brand.json records what was built from which source, and scripts/brand.py --check (CI's Checks job, ci.yml around line 571) refuses a mismatch.
  • The header shows brand/logo-64.png at 28 by 28 pixels (templates/base.html, around line 132), decorative, with an empty alt. The tab uses favicon-32.png and favicon-16.png (base.html 42-44); the web app manifest (core/views.py around line 191) and the browser-extension plugin (plugins/browser/__init__.py) use icon-192.png and icon-512.png.
  • assets/brand/postulo.svg (#1218): seven paper sheets, each a Bézier path with its own gradient and shadow filter, sampled from the raster. Pride and animated variants sit beside it. CONTRIBUTING.md's The mark says the raster stays the source of the served icons.

What would change

  1. The SVG becomes the source. scripts/brand.py renders the derived PNGs from postulo.svg instead of the raster (a renderer the project already depends on, if one can do gradients and filters faithfully: WeasyPrint's, or a library to be chosen under Look for a library before you write one; no new system dependency in the image without a reason), and brand.json records the SVG as the source. The raster stays in the repository only if something still needs it; otherwise it goes.
  2. The SVG is served where a vector helps: the header logo as brand/logo.svg (sharp at any zoom and on any screen density), and <link rel="icon" type="image/svg+xml"> before the PNG favicons, which stay for browsers that do not take an SVG icon. The manifest and Apple icons stay PNG, as those platforms require; they are rendered from the SVG.
  3. A served SVG is safe to serve: no script, no external reference, no foreignObject; minified; its size measured against the PNG it replaces. The shadow filters may cost more to draw than a 64-pixel PNG: measure the header's paint at 28 pixels, and simplify the served copy (fewer filters, or none at that size) if they do.
  4. Dark mode: check the mark on the dark theme as the PNG is today, and say whether the SVG needs a variant.
  5. CONTRIBUTING.md's The mark, scripts/brand.py's docstring and tests/test_brand.py say the SVG is the source.

Decisions

  • Favicon as SVG as well as PNG? Recommended: yes, SVG first with the PNGs as fallbacks; every current browser but Safari takes it.
  • Keep the raster? Recommended: no, once every derived file is rendered from the SVG; the pride raster stays as the record of what the pride SVG was coloured from, as CONTRIBUTING.md says.
  • Animated variants in the interface? Recommended: not in this issue; the static mark only.

Accessibility

The mark stays decorative with an empty alt. Check the favicon and header at 200 % and 400 % zoom and in forced colours.

Filed at the maintainer's request (2026-11-12).

The mark Postulo serves is still made from the raster, `assets/brand/postulo.png`, although a vector version has been beside it since #1218 (`assets/brand/postulo.svg`). The maintainer asked (2026-11-12) for the PNG logo to be replaced by the SVG version. ## What exists today - `assets/brand/postulo.png`, 1024 pixels square, is the one source. `scripts/brand.py` trims and scales it into `src/postulo/static/brand/`: `favicon-16/32/48.png`, `favicon.ico`, `apple-touch-icon.png` (on the page's off-white), `icon-192.png`, `icon-512.png`, `icon-maskable-512.png`, `logo-64.png` and `logo-256.png`. `brand.json` records what was built from which source, and `scripts/brand.py --check` (CI's Checks job, `ci.yml` around line 571) refuses a mismatch. - The header shows `brand/logo-64.png` at 28 by 28 pixels (`templates/base.html`, around line 132), decorative, with an empty `alt`. The tab uses `favicon-32.png` and `favicon-16.png` (`base.html` 42-44); the web app manifest (`core/views.py` around line 191) and the browser-extension plugin (`plugins/browser/__init__.py`) use `icon-192.png` and `icon-512.png`. - `assets/brand/postulo.svg` (#1218): seven paper sheets, each a Bézier path with its own gradient and shadow filter, sampled from the raster. Pride and animated variants sit beside it. CONTRIBUTING.md's *The mark* says the raster stays the source of the served icons. ## What would change 1. **The SVG becomes the source.** `scripts/brand.py` renders the derived PNGs from `postulo.svg` instead of the raster (a renderer the project already depends on, if one can do gradients and filters faithfully: WeasyPrint's, or a library to be chosen under *Look for a library before you write one*; no new system dependency in the image without a reason), and `brand.json` records the SVG as the source. The raster stays in the repository only if something still needs it; otherwise it goes. 2. **The SVG is served where a vector helps:** the header logo as `brand/logo.svg` (sharp at any zoom and on any screen density), and `<link rel="icon" type="image/svg+xml">` before the PNG favicons, which stay for browsers that do not take an SVG icon. The manifest and Apple icons stay PNG, as those platforms require; they are rendered from the SVG. 3. **A served SVG is safe to serve:** no script, no external reference, no `foreignObject`; minified; its size measured against the PNG it replaces. The shadow filters may cost more to draw than a 64-pixel PNG: measure the header's paint at 28 pixels, and simplify the served copy (fewer filters, or none at that size) if they do. 4. **Dark mode:** check the mark on the dark theme as the PNG is today, and say whether the SVG needs a variant. 5. CONTRIBUTING.md's *The mark*, `scripts/brand.py`'s docstring and `tests/test_brand.py` say the SVG is the source. ## Decisions - **Favicon as SVG as well as PNG?** Recommended: yes, SVG first with the PNGs as fallbacks; every current browser but Safari takes it. - **Keep the raster?** Recommended: no, once every derived file is rendered from the SVG; the pride raster stays as the record of what the pride SVG was coloured from, as CONTRIBUTING.md says. - **Animated variants in the interface?** Recommended: not in this issue; the static mark only. ## Accessibility The mark stays decorative with an empty `alt`. Check the favicon and header at 200 % and 400 % zoom and in forced colours. Filed at the maintainer's request (2026-11-12).
tiagoagueda added this to the 0.6.0 milestone 2026-11-12 08:43:26 +00:00
Author
Owner

Decided by the maintainer (2026-11-12)

  • The SVG is served first; every PNG is rendered from the SVG, not kept from the existing raster. The PNGs remain only as the fallbacks a platform needs (PNG favicons after the SVG one, the Apple and manifest icons), all generated from postulo.svg.
  • The animated mark is offered in the interface as an option. Read as: a preference a person can switch on (off by default), using the existing postulo_swell.svg / postulo_drift.svg; it never moves when the person or the system asks for reduced motion (prefers-reduced-motion), as CONTRIBUTING's The mark already requires of any animated use.
  • An easter egg: the pride variant during pride month. In June the interface shows postulo_pride.svg (and PNG fallbacks rendered from it) instead of the usual mark, with no setting needed. Read as: June, in the person's own time zone; the animated option uses the pride animations then.

This replaces the recommendations above on the raster and the animated variants.

🤖 Generated with Claude Code

## Decided by the maintainer (2026-11-12) - **The SVG is served first; every PNG is rendered from the SVG**, not kept from the existing raster. The PNGs remain only as the fallbacks a platform needs (PNG favicons after the SVG one, the Apple and manifest icons), all generated from `postulo.svg`. - **The animated mark is offered in the interface as an option.** Read as: a preference a person can switch on (off by default), using the existing `postulo_swell.svg` / `postulo_drift.svg`; it never moves when the person or the system asks for reduced motion (`prefers-reduced-motion`), as CONTRIBUTING's *The mark* already requires of any animated use. - **An easter egg: the pride variant during pride month.** In June the interface shows `postulo_pride.svg` (and PNG fallbacks rendered from it) instead of the usual mark, with no setting needed. Read as: June, in the person's own time zone; the animated option uses the pride animations then. This replaces the recommendations above on the raster and the animated variants. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Widened by the maintainer (2026-11-12)

The plugins and the wiki follow the same guideline: the vector mark is the source and is served first; a raster is used only where a platform requires one, and is rendered from the SVG, never kept from the old raster.

What that touches, found by a survey of every repository:

  • postulo-chromium: scripts/icons.py derives the toolbar and store icons from the core's raster through the core's brand.py: it follows the core to the SVG. The manifests keep PNG where a browser requires one (Chromium's manifest icons), and any logo drawn inside the extension's own pages is the SVG. postulo-thunderbird copies the icons through shared.json; postulo-firefox builds from postulo-chromium's tagged source.
  • The wiki: Home.md shows images/postulo.png; it becomes the SVG. The documentation site (Zensical, docker-recipes/wiki) takes the SVG as its logo and favicon, with a PNG favicon rendered from it as fallback.
  • The guideline is written down where each audience reads it: CONTRIBUTING's The mark (the core), the wiki's Writing a plugin (a plugin's own images), and postulo-helloworld's README (the plugin people copy).
  • The other plugins ship no logo image.

🤖 Generated with Claude Code

## Widened by the maintainer (2026-11-12) The plugins and the wiki follow the same guideline: the vector mark is the source and is served first; a raster is used only where a platform requires one, and is rendered from the SVG, never kept from the old raster. What that touches, found by a survey of every repository: - **postulo-chromium:** `scripts/icons.py` derives the toolbar and store icons from the core's raster through the core's `brand.py`: it follows the core to the SVG. The manifests keep PNG where a browser requires one (Chromium's manifest icons), and any logo drawn inside the extension's own pages is the SVG. postulo-thunderbird copies the icons through `shared.json`; postulo-firefox builds from postulo-chromium's tagged source. - **The wiki:** `Home.md` shows `images/postulo.png`; it becomes the SVG. The documentation site (Zensical, `docker-recipes/wiki`) takes the SVG as its logo and favicon, with a PNG favicon rendered from it as fallback. - **The guideline** is written down where each audience reads it: CONTRIBUTING's *The mark* (the core), the wiki's *Writing a plugin* (a plugin's own images), and postulo-helloworld's README (the plugin people copy). - The other plugins ship no logo image. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#1238
No description provided.