Commit c55e090 h
Pat Ambrosio · 26-08-28 10:07
Stop a small diagram stretching to fill the modal
Changes: 3 files
+60
-0
Changes (Side-by-Side)
share/jenny/layout/page.sh +21 -0
diagram_script() { |
| 206 |
// a read-only SVGAnimatedString, so assigning to it silently |
206 |
// a read-only SVGAnimatedString, so assigning to it silently |
| 207 |
// does nothing and the sizing rule never matches. |
207 |
// does nothing and the sizing rule never matches. |
| 208 |
clone.setAttribute('class', 'diagram-modal-svg'); |
208 |
clone.setAttribute('class', 'diagram-modal-svg'); |
| |
209 |
// Filling the viewport is right for a dense chart and wrong for |
| |
210 |
// a small one: a three-node diagram blown up to 1400px is all |
| |
211 |
// gaps and oversized text. Cap the enlargement at twice natural |
| |
212 |
// size, which is enough that anything around 700px wide still |
| |
213 |
// fills a typical screen while a small diagram stays legible. |
| |
214 |
// Natural width comes from the diagram itself -- mermaid writes |
| |
215 |
// it as an inline max-width, with the viewBox as a fallback -- |
| |
216 |
// so the ceiling is per-diagram and nothing here is tuned to |
| |
217 |
// any particular chart. |
| |
218 |
var natural = parseFloat(svg.style.maxWidth); |
| |
219 |
if (!natural) { |
| |
220 |
var vb = (svg.getAttribute('viewBox') || '').split(/[ ,]+/); |
| |
221 |
natural = parseFloat(vb[2]); |
| |
222 |
} |
| |
223 |
// setProperty with 'important' because the stylesheet rule below |
| |
224 |
// is itself !important (it has to be, to beat mermaid's own |
| |
225 |
// inline max-width). A plain inline value would lose to it and |
| |
226 |
// this cap would silently do nothing. |
| |
227 |
if (natural > 0) { |
| |
228 |
clone.style.setProperty('max-width', (natural * 2) + 'px', 'important'); |
| |
229 |
} |
| 209 |
body.appendChild(clone); |
230 |
body.appendChild(clone); |
| 210 |
} |
231 |
} |
| 211 |
|
232 |
|
share/jenny/layout/post.sh +21 -0
diagram_script() { |
| 122 |
// a read-only SVGAnimatedString, so assigning to it silently |
122 |
// a read-only SVGAnimatedString, so assigning to it silently |
| 123 |
// does nothing and the sizing rule never matches. |
123 |
// does nothing and the sizing rule never matches. |
| 124 |
clone.setAttribute('class', 'diagram-modal-svg'); |
124 |
clone.setAttribute('class', 'diagram-modal-svg'); |
| |
125 |
// Filling the viewport is right for a dense chart and wrong for |
| |
126 |
// a small one: a three-node diagram blown up to 1400px is all |
| |
127 |
// gaps and oversized text. Cap the enlargement at twice natural |
| |
128 |
// size, which is enough that anything around 700px wide still |
| |
129 |
// fills a typical screen while a small diagram stays legible. |
| |
130 |
// Natural width comes from the diagram itself -- mermaid writes |
| |
131 |
// it as an inline max-width, with the viewBox as a fallback -- |
| |
132 |
// so the ceiling is per-diagram and nothing here is tuned to |
| |
133 |
// any particular chart. |
| |
134 |
var natural = parseFloat(svg.style.maxWidth); |
| |
135 |
if (!natural) { |
| |
136 |
var vb = (svg.getAttribute('viewBox') || '').split(/[ ,]+/); |
| |
137 |
natural = parseFloat(vb[2]); |
| |
138 |
} |
| |
139 |
// setProperty with 'important' because the stylesheet rule below |
| |
140 |
// is itself !important (it has to be, to beat mermaid's own |
| |
141 |
// inline max-width). A plain inline value would lose to it and |
| |
142 |
// this cap would silently do nothing. |
| |
143 |
if (natural > 0) { |
| |
144 |
clone.style.setProperty('max-width', (natural * 2) + 'px', 'important'); |
| |
145 |
} |
| 125 |
body.appendChild(clone); |
146 |
body.appendChild(clone); |
| 126 |
} |
147 |
} |
| 127 |
|
148 |
|
test/regressions.sh +18 -0
it "the modal content box fills the viewport" |
| 2673 |
assert_file_contains "$dm_post" "width: 100%; height: 100%; overflow: auto; display: flex; }" |
2673 |
assert_file_contains "$dm_post" "width: 100%; height: 100%; overflow: auto; display: flex; }" |
| 2674 |
assert_file_not_contains "$dm_post" "max-width: min(95vw, 95vh)" |
2674 |
assert_file_not_contains "$dm_post" "max-width: min(95vw, 95vh)" |
| 2675 |
|
2675 |
|
| |
2676 |
it "enlargement is capped at twice the diagram's natural size" |
| |
2677 |
# Filling the viewport suits a dense chart and ruins a small one -- a |
| |
2678 |
# three-node diagram at 1400px is all gaps and oversized text. The ceiling is |
| |
2679 |
# read from the diagram itself, so it holds for any chart rather than being |
| |
2680 |
# tuned to the ones in this repo. |
| |
2681 |
assert_file_contains "$dm_post" "(natural * 2) + 'px'" |
| |
2682 |
|
| |
2683 |
it "the natural width falls back to the viewBox" |
| |
2684 |
# mermaid writes an inline max-width, but a diagram without one still carries |
| |
2685 |
# its dimensions in the viewBox. |
| |
2686 |
assert_file_contains "$dm_post" "svg.getAttribute('viewBox')" |
| |
2687 |
|
| |
2688 |
it "the cap is set with important priority" |
| |
2689 |
# The stylesheet rule is !important (it must be, to beat mermaid's own inline |
| |
2690 |
# max-width). A plain inline value would lose to it and the cap would silently |
| |
2691 |
# do nothing -- measured: 100% wins over a non-important inline 600px. |
| |
2692 |
assert_file_contains "$dm_post" "setProperty('max-width', (natural * 2) + 'px', 'important')" |
| |
2693 |
|
| 2676 |
it "the diagram overrides mermaid's inline max-width" |
2694 |
it "the diagram overrides mermaid's inline max-width" |
| 2677 |
# mermaid writes style="max-width: {natural}px" directly on the <svg>. An |
2695 |
# mermaid writes style="max-width: {natural}px" directly on the <svg>. An |
| 2678 |
# inline style beats any selector, so without !important the diagram keeps its |
2696 |
# inline style beats any selector, so without !important the diagram keeps its |
Raw
commit c55e0909941894f3b0b4f0badaa7dc24cda0dedb
Author: Pat Ambrosio <cp.ambrosio@gmail.com>
Date: Fri Aug 28 10:07:28 2026 +0800
Stop a small diagram stretching to fill the modal
Filling the viewport is right for a dense chart and wrong for a small
one. A three-node legend blown up to 1400px is all gaps and oversized
text -- the modal made it worse than the page did.
The ceiling is twice the diagram's own natural size, read at open time
from mermaid's inline max-width with the viewBox as a fallback. Nothing
here knows what a chart looks like, so it holds for a gantt or a sequence
diagram as much as for the flowcharts in this repo:
natural cap on a 1440 screen
120px 240px 240px, not stretched
300px 600px 600px, not stretched
900px 1800px fills the screen
2400px 4800px fills the screen
Twice is where the two behaviours meet: anything from about 700px up
still reaches the edge of a typical screen, so nothing dense loses the
full-viewport view it just gained, while everything smaller stops before
it looks silly. Half again is too tight -- a 900px chart would stop short
of the screen. Two and a half lets a small one sprawl again.
The cap has to be set with important priority. The stylesheet rule is
itself !important, which it must be to beat the inline max-width mermaid
writes on the svg, and a plain inline value loses to it: measured, 100%
wins over an inline 600px and the cap does nothing at all. Two competing
!important declarations is not pretty, but an inline style is only
beatable by one, and mermaid gets there first.
A diagram carrying neither an inline width nor a viewBox sets no cap and
falls through to the stylesheet, rather than writing NaNpx.
diff --git a/share/jenny/layout/page.sh b/share/jenny/layout/page.sh
index 040778b..e420807 100755
--- a/share/jenny/layout/page.sh
+++ b/share/jenny/layout/page.sh
@@ -206,6 +206,27 @@ diagram_script() {
// a read-only SVGAnimatedString, so assigning to it silently
// does nothing and the sizing rule never matches.
clone.setAttribute('class', 'diagram-modal-svg');
+ // Filling the viewport is right for a dense chart and wrong for
+ // a small one: a three-node diagram blown up to 1400px is all
+ // gaps and oversized text. Cap the enlargement at twice natural
+ // size, which is enough that anything around 700px wide still
+ // fills a typical screen while a small diagram stays legible.
+ // Natural width comes from the diagram itself -- mermaid writes
+ // it as an inline max-width, with the viewBox as a fallback --
+ // so the ceiling is per-diagram and nothing here is tuned to
+ // any particular chart.
+ var natural = parseFloat(svg.style.maxWidth);
+ if (!natural) {
+ var vb = (svg.getAttribute('viewBox') || '').split(/[ ,]+/);
+ natural = parseFloat(vb[2]);
+ }
+ // setProperty with 'important' because the stylesheet rule below
+ // is itself !important (it has to be, to beat mermaid's own
+ // inline max-width). A plain inline value would lose to it and
+ // this cap would silently do nothing.
+ if (natural > 0) {
+ clone.style.setProperty('max-width', (natural * 2) + 'px', 'important');
+ }
body.appendChild(clone);
}
diff --git a/share/jenny/layout/post.sh b/share/jenny/layout/post.sh
index ba0be6a..3fd51b1 100755
--- a/share/jenny/layout/post.sh
+++ b/share/jenny/layout/post.sh
@@ -122,6 +122,27 @@ diagram_script() {
// a read-only SVGAnimatedString, so assigning to it silently
// does nothing and the sizing rule never matches.
clone.setAttribute('class', 'diagram-modal-svg');
+ // Filling the viewport is right for a dense chart and wrong for
+ // a small one: a three-node diagram blown up to 1400px is all
+ // gaps and oversized text. Cap the enlargement at twice natural
+ // size, which is enough that anything around 700px wide still
+ // fills a typical screen while a small diagram stays legible.
+ // Natural width comes from the diagram itself -- mermaid writes
+ // it as an inline max-width, with the viewBox as a fallback --
+ // so the ceiling is per-diagram and nothing here is tuned to
+ // any particular chart.
+ var natural = parseFloat(svg.style.maxWidth);
+ if (!natural) {
+ var vb = (svg.getAttribute('viewBox') || '').split(/[ ,]+/);
+ natural = parseFloat(vb[2]);
+ }
+ // setProperty with 'important' because the stylesheet rule below
+ // is itself !important (it has to be, to beat mermaid's own
+ // inline max-width). A plain inline value would lose to it and
+ // this cap would silently do nothing.
+ if (natural > 0) {
+ clone.style.setProperty('max-width', (natural * 2) + 'px', 'important');
+ }
body.appendChild(clone);
}
diff --git a/test/regressions.sh b/test/regressions.sh
index 714fc4f..dd1de6b 100755
--- a/test/regressions.sh
+++ b/test/regressions.sh
@@ -2673,6 +2673,24 @@ it "the modal content box fills the viewport"
assert_file_contains "$dm_post" "width: 100%; height: 100%; overflow: auto; display: flex; }"
assert_file_not_contains "$dm_post" "max-width: min(95vw, 95vh)"
+it "enlargement is capped at twice the diagram's natural size"
+# Filling the viewport suits a dense chart and ruins a small one -- a
+# three-node diagram at 1400px is all gaps and oversized text. The ceiling is
+# read from the diagram itself, so it holds for any chart rather than being
+# tuned to the ones in this repo.
+assert_file_contains "$dm_post" "(natural * 2) + 'px'"
+
+it "the natural width falls back to the viewBox"
+# mermaid writes an inline max-width, but a diagram without one still carries
+# its dimensions in the viewBox.
+assert_file_contains "$dm_post" "svg.getAttribute('viewBox')"
+
+it "the cap is set with important priority"
+# The stylesheet rule is !important (it must be, to beat mermaid's own inline
+# max-width). A plain inline value would lose to it and the cap would silently
+# do nothing -- measured: 100% wins over a non-important inline 600px.
+assert_file_contains "$dm_post" "setProperty('max-width', (natural * 2) + 'px', 'important')"
+
it "the diagram overrides mermaid's inline max-width"
# mermaid writes style="max-width: {natural}px" directly on the <svg>. An
# inline style beats any selector, so without !important the diagram keeps its