What
Every published page carries the whole stylesheet inline (render.py:1009,
f"<style>\n{STYLESHEET}</style>\n"). Measured on the committed tree today, not
estimated: 23,305 HTML files carry 118,166,787 bytes of <style> between
them — 13.62% of the 867,639,523-byte tree — in four distinct blocks:
| Block |
Bytes |
Pages |
Total |
| school page |
5,060 |
21,068 |
106,604,080 |
| county / district page |
5,168 |
2,234 |
11,545,312 |
| landing page |
5,249 |
1 |
5,249 |
| ask page |
6,073 |
2 |
12,146 |
Lifting the first three into shared files under site/ and linking them, at
roughly 45 bytes of <link> per page, recovers about 117 MB.
Why it matters
The byte budget is now the thing deciding what Homeroom may publish. The tree is
at 86.8% of the 1 GB GitHub Pages allows; D5 on the school pages is a measured
+184 MB and does not fit (#82);
D4, D6 and one copied column per prior year
(#83) each land on all 21,068
school pages the same way.
Stated honestly, because the arithmetic matters more than the enthusiasm: this
does not on its own put a D5 republish under the repository's own 90% budget.
867.6 − 117.1 + 184 ≈ 934.5 MB, which is 93.5% of the ceiling and over the
900 MB budget tests/test_published_limits.py holds. What it does is move the
next publish from undeployable at any price (105% of the ceiling) to inside
the host's ceiling, and it is the largest saving available that takes nothing
off the page. #82's option 3 says "184 MB is more than county/ and district/ put
together (19.5 MB), so what is on the table is a whole existing section". This is
a fourth option that option list does not consider, and it is 6× the size of the
one it does.
The README rules the equivalent saving out for the ask pages, and rightly:
"those bytes being inline is exactly what makes tools/ask-optin.mjs's
zero-requests-on-load assertion true." That argument is about the ask page. It
does not reach the school, county, district and landing pages, which carry no
script, make no request today, and would be linking one same-origin static file
this build wrote — not a third party, not a tracker, and nothing a reader's
browser reports anywhere.
Scope
src/homeroom/render.py — STYLESHEET stops being emitted inline and becomes
a named asset; the school/browse/landing variants become two or three files
rather than one, since they are already three distinct blocks.
src/homeroom/site.py — it already copies package assets to the output tree
(ASSETS_DIR, shutil.copyfile, _publish_social_cards), so the emit path
exists. The <link> uses the relative form the pages already use across
directories (../district/…, ../index.html), so a county page references
../homeroom.css.
- The palette stays in Python.
render.py's LIGHT/DARK dicts exist
precisely so tests/test_pages.py can do contrast arithmetic without a
browser; that test reads the dicts, not the CSS string, and is unaffected.
Keep it that way.
src/homeroom/publish_limits.py and tests/test_published_limits.py — the
measured tree figure and the ROADMAP metrics-ledger row are restated from a
re-measurement, never edited to the expected number.
tests/test_published_site.py and tools/verify_live_site.py — the stylesheet
is a published file: the walk has to know about it and the daily sentinel has
to be able to fetch it, or a 404 stylesheet becomes an invisible failure.
- The ask pages keep their inline block, untouched, and a test asserts it.
- README "Status" and the "no script, no external asset" line, plus
docs/adr/0001-render-pages-in-python-and-gate-them-outside-it.md, need
restating rather than quietly contradicting.
The property that makes the trade safe, and it should be a test
A page whose stylesheet fails to load must still be honest. It already is, by
design: the four cell states are kept apart by words as well as colour — a
withheld figure renders "withheld to protect privacy" and never a digit. So an
unstyled page loses the visual separation and keeps the factual one. That is the
argument for the change and it should be asserted (render a page, strip the
stylesheet, assert no withheld or unpublished cell renders a digit) rather than
believed.
What has to be decided first
- Whether a linked same-origin stylesheet is compatible with the property the
README states as "no script, no external asset". It is a change to something
the site says about itself, so it is yours.
- Cache behaviour across a republish. A fixed filename can serve a stale
stylesheet against new markup for a reader with it cached; a filename carrying
its own content hash avoids that and changes on every publish, which the
publish diff (#84) would
then see every time.
Roughly how big
Multi-session. The emit is small; the gates, the re-measurement, the document
restatements and a republish are not, and the republish itself is yours.
Proposed with AI assistance.
What
Every published page carries the whole stylesheet inline (
render.py:1009,f"<style>\n{STYLESHEET}</style>\n"). Measured on the committed tree today, notestimated: 23,305 HTML files carry 118,166,787 bytes of
<style>betweenthem — 13.62% of the 867,639,523-byte tree — in four distinct blocks:
Lifting the first three into shared files under
site/and linking them, atroughly 45 bytes of
<link>per page, recovers about 117 MB.Why it matters
The byte budget is now the thing deciding what Homeroom may publish. The tree is
at 86.8% of the 1 GB GitHub Pages allows; D5 on the school pages is a measured
+184 MB and does not fit (#82);
D4, D6 and one copied column per prior year
(#83) each land on all 21,068
school pages the same way.
Stated honestly, because the arithmetic matters more than the enthusiasm: this
does not on its own put a D5 republish under the repository's own 90% budget.
867.6 − 117.1 + 184 ≈ 934.5 MB, which is 93.5% of the ceiling and over the
900 MB budget
tests/test_published_limits.pyholds. What it does is move thenext publish from undeployable at any price (105% of the ceiling) to inside
the host's ceiling, and it is the largest saving available that takes nothing
off the page. #82's option 3 says "184 MB is more than county/ and district/ put
together (19.5 MB), so what is on the table is a whole existing section". This is
a fourth option that option list does not consider, and it is 6× the size of the
one it does.
The README rules the equivalent saving out for the ask pages, and rightly:
"those bytes being inline is exactly what makes
tools/ask-optin.mjs'szero-requests-on-load assertion true." That argument is about the ask page. It
does not reach the school, county, district and landing pages, which carry no
script, make no request today, and would be linking one same-origin static file
this build wrote — not a third party, not a tracker, and nothing a reader's
browser reports anywhere.
Scope
src/homeroom/render.py—STYLESHEETstops being emitted inline and becomesa named asset; the school/browse/landing variants become two or three files
rather than one, since they are already three distinct blocks.
src/homeroom/site.py— it already copies package assets to the output tree(
ASSETS_DIR,shutil.copyfile,_publish_social_cards), so the emit pathexists. The
<link>uses the relative form the pages already use acrossdirectories (
../district/…,../index.html), so a county page references../homeroom.css.render.py'sLIGHT/DARKdicts existprecisely so
tests/test_pages.pycan do contrast arithmetic without abrowser; that test reads the dicts, not the CSS string, and is unaffected.
Keep it that way.
src/homeroom/publish_limits.pyandtests/test_published_limits.py— themeasured tree figure and the ROADMAP metrics-ledger row are restated from a
re-measurement, never edited to the expected number.
tests/test_published_site.pyandtools/verify_live_site.py— the stylesheetis a published file: the walk has to know about it and the daily sentinel has
to be able to fetch it, or a 404 stylesheet becomes an invisible failure.
docs/adr/0001-render-pages-in-python-and-gate-them-outside-it.md, needrestating rather than quietly contradicting.
The property that makes the trade safe, and it should be a test
A page whose stylesheet fails to load must still be honest. It already is, by
design: the four cell states are kept apart by words as well as colour — a
withheld figure renders "withheld to protect privacy" and never a digit. So an
unstyled page loses the visual separation and keeps the factual one. That is the
argument for the change and it should be asserted (render a page, strip the
stylesheet, assert no withheld or unpublished cell renders a digit) rather than
believed.
What has to be decided first
README states as "no script, no external asset". It is a change to something
the site says about itself, so it is yours.
stylesheet against new markup for a reader with it cached; a filename carrying
its own content hash avoids that and changes on every publish, which the
publish diff (#84) would
then see every time.
Roughly how big
Multi-session. The emit is small; the gates, the re-measurement, the document
restatements and a republish are not, and the republish itself is yours.
Proposed with AI assistance.