diff --git a/notes_to_self/howtodocs.md b/notes_to_self/howtodocs.md index 489d37b6..6c2f47a0 100644 --- a/notes_to_self/howtodocs.md +++ b/notes_to_self/howtodocs.md @@ -84,6 +84,67 @@ hostname -I - a `.d2` that *fails to compile* is a hard build error under `sphinx-build -W` (the last-good committed SVG is left intact). +## Tweaking the logo + +The logo is plain SVG — colours are just editable text, so change +them, save, and autobuild repaints the live page. Three contexts +use it, coloured three different ways (an ``-embedded svg +can't read the page's CSS, so only the *inlined* hero can follow +the live light/dark toggle): + +| context | file(s) | how it's coloured | +|---|---|---| +| **landing hero** | `docs/_static/tractor_logo_hero.html` (inlined into `index.rst`) + the `svg.hero-logo` rule in `docs/_static/css/custom.css` | linework is `fill: currentColor`, so it takes whatever `color:` the CSS sets — currently `var(--pst-color-text-base)` (the theme text colour). **← recolour on a whim by editing that one `color:` line.** | +| **navbar** | `docs/_static/tractor_logo_nav_light.svg` + `…_nav_dark.svg`, wired via `html_theme_options["logo"]` in `docs/conf.py` | baked fills — near-black lines on light, near-white on dark; pydata swaps them by theme | +| **README** | `docs/_static/tractor_logo_wire.svg` | one baked neutral-grey (GitHub/PyPI can't read the theme), with inline fills so it survives GitHub's svg sanitiser | + +The shape's "faces" are `fill: none` everywhere, so the page +background shows through — that's the wireframe look. The original +filled `tractor_logo_side.svg` is kept as the source to recolour +from (and as the favicon). + +## svgtool: recolor + preview svgs + +`notes_to_self/svgtool.py` is a tiny helper for iterating on the +logo (or any svg). It renders through headless firefox, so masks, +`currentColor` and theme CSS look exactly like the built site. + +``` +# list the distinct colours in an svg +python notes_to_self/svgtool.py colors docs/_static/tractor_logo_side.svg + +# write a recoloured copy (literal token swaps) +python notes_to_self/svgtool.py recolor in.svg out.svg \ + '#0A0A0A=currentColor' '#FCFCFB=none' + +# render an svg alone on a bg colour (reveals transparency) +python notes_to_self/svgtool.py preview out.svg /tmp/p.png --bg '#ff00ff' + +# screenshot a BUILT page, forcing light/dark (see below) +python notes_to_self/svgtool.py page docs/_build/html/index.html \ + /tmp/dark.png --theme dark +``` + +### Why `page --theme dark`? (verifying dark mode headlessly) + +The non-obvious bit, for the sphinx-rusty: + +- pydata-sphinx-theme decides light-vs-dark **in the browser** at + page load, from a value saved in `localStorage` (or your OS + setting if you've never clicked the toggle). +- a fresh headless screenshot starts with an *empty* `localStorage`, + so it always renders the default (light) — you could never grab + the dark variant. +- `--theme dark` sidesteps that: it writes a throwaway copy of the + built HTML with a one-line injected `' +) + + +def cmd_page(a: argparse.Namespace) -> None: + src = Path(a.url) + if a.theme and src.exists(): + # inject the force-theme script into a sibling temp copy + # (same dir → relative _static/ asset paths still resolve). + html: str = src.read_text() + inj: str = _force_theme.format(t=a.theme) + html = re.sub( + r'(]*>)', + r'\1' + inj, + html, + count=1, + ) + tmp = src.with_name(f'._svgtool_{a.theme}.html') + tmp.write_text(html) + try: + _shot(tmp.resolve().as_uri(), a.out, a.w, a.h, None) + finally: + tmp.unlink(missing_ok=True) + return + url: str = ( + a.url + if '://' in a.url + else src.resolve().as_uri() + ) + _shot(url, a.out, a.w, a.h, a.scheme) + + +def main() -> None: + p = argparse.ArgumentParser(prog='svgtool.py') + sub = p.add_subparsers(required=True) + + c = sub.add_parser('colors') + c.add_argument('svg') + c.set_defaults(fn=cmd_colors) + + r = sub.add_parser('recolor') + r.add_argument('svg') + r.add_argument('out') + r.add_argument('pairs', nargs='+') + r.set_defaults(fn=cmd_recolor) + + v = sub.add_parser('preview') + v.add_argument('svg') + v.add_argument('out') + v.add_argument('--bg', default=None) + v.add_argument('--w', type=int, default=600) + v.add_argument( + '--scheme', + choices=['light', 'dark'], + default=None, + ) + v.set_defaults(fn=cmd_preview) + + g = sub.add_parser('page') + g.add_argument('url') + g.add_argument('out') + g.add_argument('--w', type=int, default=1300) + g.add_argument('--h', type=int, default=900) + g.add_argument( + '--scheme', + choices=['light', 'dark'], + default=None, + ) + g.add_argument( + '--theme', + choices=['light', 'dark'], + default=None, + help='force a pydata data-theme (toggle-accurate)', + ) + g.set_defaults(fn=cmd_page) + + a = p.parse_args() + a.fn(a) + + +if __name__ == '__main__': + main()