fix(ui): make the advice pop-out fail-safe against a stale stylesheet

The screenshot from Firefox showed the advice expanded inline in the
table cell, running down the column and off the screen, with the long
and short labels both showing ("BREWING ADVICEADVICE").  The page had
the new markup and Firefox was serving a stale stylesheet.

Two fixes.

The static files now send Cache-Control: no-store.  They sent
no-cache, which means "revalidate" - and Firefox was reusing them
without revalidating.  no-store is the only one that means "do not
keep this".  The HTML pages already sent no-store.

The pop-out carries the hidden attribute, and the script removes it
when the panel opens and puts it back when it closes.  That makes the
MARKUP fail-safe rather than the stylesheet: a stylesheet that does
not know about .advice-pop at all still leaves the panel hidden, so
the advice can never appear expanded inside the cell.

Verified by removing all eight .advice-pop rules from the live
stylesheet - the panel stays hidden, 0x0, and the advice is not
rendered.  Before this it would have rendered inline.

The button still opens it on tap and on hover, and the close button
still closes it.  The desktop is unchanged: the same full headings and
the same hover pop-out.

195 tests pass.
This commit is contained in:
2026-11-09 22:41:20 +10:00
parent cd45cc2f17
commit fb1088ceec
5 changed files with 56 additions and 1 deletions
+13
View File
@@ -97,6 +97,19 @@ def create_app(config: dict | None = None) -> Flask:
if response.mimetype == "text/html":
response.headers["Cache-Control"] = "no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
elif response.mimetype in ("text/css", "application/javascript",
"text/javascript"):
# The static files must NEVER be reused from a cache.
# Firefox kept serving a stale stylesheet even with
# `no-cache`, which meant the page had the new markup and
# the old CSS: the advice was expanded inline in the table
# instead of in a pop-out, and the long and short labels
# were both showing ("BREWING ADVICEADVICE").
# `no-store` is the only one that means "do not keep this".
response.headers["Cache-Control"] = "no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
return response
csrf = CSRFProtect(app)
+11
View File
@@ -999,10 +999,21 @@ table.data .comment { color: var(--text-muted); font-size: .84rem; }
/* The script opens and closes the panel, and places it. The CSS only
decides whether it is shown. */
/* The panel is opened by the script, which removes the `hidden`
attribute and adds `is-open`.
The `hidden` attribute is what makes this fail-safe: if the
stylesheet is stale and does not know about `.advice-pop` at all,
the panel is STILL hidden, so the advice cannot appear expanded
inside the table cell. */
.advice-pop.is-open {
display: block;
}
.advice-pop[hidden] {
display: none;
}
/* On a phone there is no hover, so the trigger is a button the script
toggles, and the panel is pinned to the middle of the screen so it is
readable whatever the table's width. */
+8
View File
@@ -131,6 +131,11 @@
clearTimeout(closeTimer);
closeTimer = null;
}
/* The `hidden` attribute is removed as well as the
class added, so the panel is shown even if the
stylesheet is an older one that only knows about
the class. */
panel.removeAttribute("hidden");
panel.classList.add("is-open");
trigger.setAttribute("aria-expanded", "true");
place();
@@ -138,6 +143,9 @@
function close() {
panel.classList.remove("is-open");
/* Put the attribute back, so a stale stylesheet
cannot leave the advice expanded in the cell. */
panel.setAttribute("hidden", "");
trigger.setAttribute("aria-expanded", "false");
}
+1 -1
View File
@@ -126,7 +126,7 @@
{% endif %}
</span>
</button>
<div class="advice-pop" role="tooltip">
<div class="advice-pop" role="tooltip" hidden>
<div class="advice-pop__head">
<span class="advice__label">Brewing advice</span>
<button type="button" class="advice-pop__close"