Skip to content

Commit fcbe28b

Browse files
committed
Figures scale up to fill available space across all breakpoints
Per impeccable's adapt + layout rubrics: figures should fill the column with fluid clamp() sizing, not anchor at intrinsic CSS pixels on desktops where 800+px of horizontal space goes unused. Before this change, a 176px-wide figure stayed at 176px even on a 992px content column (18% utilisation). Two layers of change. Render-time scale (src/marginalia_grammar.py) Canvas.INTRINSIC_SCALE = 1.6: to_svg() now emits width="round(viewBox * 1.6)" and height likewise. The viewBox stays intact so paint coordinates, geometry contracts, and collision math don't change — only the rendered CSS-pixel size grows. Text inside scales with the viewBox transform so a 10px font renders at 16 CSS px on desktop, dropping back to ~9-10px when the container shrinks to mobile widths. Viewport-aware ceilings (public/site.css) Three banner containers gain fluid widths via clamp(): .cell-banner figure clamp(240px, 45vw, 480px) .cell-banner--1 figure clamp(280px, 65vw, 640px) .journey-section-figure clamp(280px, 70vw, 640px) The middle term (vw-based) means figures grow with the viewport until the ceiling. The min term guarantees a readable floor on ultra-narrow phones. The ceiling matches the 640px contract bound. What each viewport now gets Mobile portrait (320px): figure caps at 280px, ~96% of column Mobile landscape (~720): figure ~455px (65vw) Tablet (768px): figure ~499px Laptop (1024px): figure 640px (clamp ceiling) Wide desktop (1440px+): figure 640px Multi-figure banners (cell-banner without --1) cap at 480px so two figures fit side-by-side in a 960px gap without dominating either side. Contract 8 updated: rendered width = 1.6 × (Canvas.w + 16) must fit the 640px ceiling. Largest figure today (dataclass-fields, intrinsic canvas 312) renders at 1.6 × 328 = 525px — comfortably under. All 57 tests pass. The previous "emit explicit width/height; never use width: 100%" lesson still holds — explicit width/height stops browsers from ballooning small viewBoxes uncontrollably. The width attribute now just sits at 1.6× intrinsic instead of 1.0×, so the figure has room to grow before CSS max-width clamps it.
1 parent 64360a4 commit fcbe28b

19 files changed

Lines changed: 171 additions & 159 deletions

public/prototyping/journey-control-flow.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/journey-figures-gestalt.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/journey-interfaces.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/journey-iteration.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/journey-reliability.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/journey-runtime.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/journey-shapes.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/journey-types.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
<h1>Types</h1>
3838
<p class="meta">This journey maps Python&#x27;s runtime object model to optional static annotations so learners know what types can and cannot promise.</p>
3939
</section>
40-
<section class="journey-section"><div><h2>Keep runtime and static analysis separate.</h2><p class="meta">The first lesson is that annotations describe expectations for tools while ordinary Python objects still run the program.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/type-hints">Type Hints</a><p class="meta">document expected types and feed type checkers</p></li><li><a class="text-link journey-item-title" href="/examples/protocols">Protocols</a><p class="meta">describe required behavior by structural shape</p></li><li><a class="text-link journey-item-title" href="/examples/enums">Enums</a><p class="meta">name a fixed set of symbolic values</p></li><li><a class="text-link journey-item-title" href="/examples/runtime-type-checks">Runtime Type Checks</a><p class="meta">show `type()`, `isinstance()`, and `issubclass()` without turning Python into Java</p></li></ul></div><figure class="journey-figure"><svg viewBox="-8 -14 236 80" width="236" height="80" xmlns="http://www.w3.org/2000/svg"><text x="0" y="36" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="start">def f(x: int, y: str) -> bool: …</text><line x1="54" y1="28" x2="76" y2="28" stroke="#521000" stroke-width="0.6" stroke-dasharray="2 2"/><line x1="102" y1="28" x2="124" y2="28" stroke="#521000" stroke-width="0.6" stroke-dasharray="2 2"/><line x1="150" y1="28" x2="192" y2="28" stroke="#521000" stroke-width="0.6" stroke-dasharray="2 2"/></svg><figcaption>Annotations describe expected types for tools; the runtime accepts any object regardless.</figcaption></figure></section><section class="journey-section"><div><h2>Describe realistic data shapes.</h2><p class="meta">Typed Python becomes useful when annotations explain optional values, unions, callables, and JSON-like records.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/union-and-optional-types">Union and Optional Types</a><p class="meta">show `X | Y` and `None`-aware APIs</p></li><li><a class="text-link journey-item-title" href="/examples/type-aliases">Type Aliases</a><p class="meta">name complex types with `type` statements or aliases</p></li><li><a class="text-link journey-item-title" href="/examples/typed-dicts">TypedDict</a><p class="meta">type dictionary records that come from JSON</p></li><li><a class="text-link journey-item-title" href="/examples/literal-and-final">Literal and Final</a><p class="meta">express constrained values and names that should not be rebound</p></li><li><a class="text-link journey-item-title" href="/examples/callable-types">Callable Types</a><p class="meta">type functions that are passed as arguments</p></li></ul></div><figure class="journey-figure"><svg viewBox="-8 -14 172 108" width="172" height="108" xmlns="http://www.w3.org/2000/svg"><text x="0" y="14" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="rgba(82, 16, 0, 0.7)" text-anchor="start" letter-spacing="0.5">X: INT|STR|NONE</text><rect x="0" y="22" width="44" height="28" fill="none" stroke="#521000" stroke-width="1.0"/><text x="22.0" y="40.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">x</text><line x1="44" y1="36" x2="74.02702716443629" y2="17.650150066177822" stroke="#521000" stroke-width="1.0"/><polygon points="80,14 75.48708719090742,20.039339200403308 72.56696713796516,15.260960931952336" fill="#521000"/><line x1="44" y1="36" x2="73.0" y2="36.0" stroke="#521000" stroke-width="1.0"/><polygon points="80,36 73.0,38.8 73.0,33.2" fill="#521000"/><line x1="44" y1="36" x2="74.02702716443629" y2="54.34984993382218" stroke="#521000" stroke-width="1.0"/><polygon points="80,58 72.56696713796516,56.73903906804766 75.48708719090742,51.960660799596695" fill="#521000"/><rect x="82" y="4" width="70" height="22" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="117.0" y="19.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">int</text><rect x="82" y="26" width="70" height="22" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="117.0" y="41.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">str</text><rect x="82" y="48" width="70" height="22" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="117.0" y="63.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">None</text></svg><figcaption>A typed slot can accept one of several shapes — `int | str | None` covers expected absence and alternatives.</figcaption></figure></section><section class="journey-section"><div><h2>Scale annotations for reusable libraries.</h2><p class="meta">Advanced typing exists to preserve information across reusable functions, containers, and decorators.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/generics-and-typevar">Generics and TypeVar</a><p class="meta">write reusable typed containers and functions</p></li><li><a class="text-link journey-item-title" href="/examples/paramspec">ParamSpec</a><p class="meta">preserve callable signatures through decorators</p></li><li><a class="text-link journey-item-title" href="/examples/overloads">Overloads</a><p class="meta">describe APIs whose return type depends on the input shape</p></li><li><a class="text-link journey-item-title" href="/examples/casts-and-any">Casts and Any</a><p class="meta">show escape hatches and their tradeoffs</p></li><li><a class="text-link journey-item-title" href="/examples/newtype">NewType</a><p class="meta">create distinct static identities for runtime-compatible values</p></li></ul></div><figure class="journey-figure"><svg viewBox="-8 -14 266 98" width="266" height="98" xmlns="http://www.w3.org/2000/svg"><rect x="0" y="30" width="36" height="28" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="18.0" y="48.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">T</text><line x1="36" y1="44" x2="65.0" y2="44.0" stroke="#521000" stroke-width="1.0"/><polygon points="72,44 65.0,46.8 65.0,41.2" fill="#521000"/><rect x="74" y="26" width="100" height="36" fill="none" stroke="#521000" stroke-width="1.0"/><text x="80" y="23" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="rgba(82, 16, 0, 0.7)" text-anchor="start" letter-spacing="0.5">FN[T]</text><line x1="174" y1="44" x2="203.0" y2="44.0" stroke="#FF4801" stroke-width="1.4"/><polygon points="210,44 203.0,46.8 203.0,41.2" fill="#FF4801"/><rect x="212" y="30" width="36" height="28" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="230.0" y="48.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">T</text></svg><figcaption>A generic type variable preserves shape across a call: the same T flows in and out.</figcaption></figure></section>
40+
<section class="journey-section"><div><h2>Keep runtime and static analysis separate.</h2><p class="meta">The first lesson is that annotations describe expectations for tools while ordinary Python objects still run the program.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/type-hints">Type Hints</a><p class="meta">document expected types and feed type checkers</p></li><li><a class="text-link journey-item-title" href="/examples/protocols">Protocols</a><p class="meta">describe required behavior by structural shape</p></li><li><a class="text-link journey-item-title" href="/examples/enums">Enums</a><p class="meta">name a fixed set of symbolic values</p></li><li><a class="text-link journey-item-title" href="/examples/runtime-type-checks">Runtime Type Checks</a><p class="meta">show `type()`, `isinstance()`, and `issubclass()` without turning Python into Java</p></li></ul></div><figure class="journey-figure"><svg viewBox="-8 -14 236 80" width="378" height="128" xmlns="http://www.w3.org/2000/svg"><text x="0" y="36" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="start">def f(x: int, y: str) -> bool: …</text><line x1="54" y1="28" x2="76" y2="28" stroke="#521000" stroke-width="0.6" stroke-dasharray="2 2"/><line x1="102" y1="28" x2="124" y2="28" stroke="#521000" stroke-width="0.6" stroke-dasharray="2 2"/><line x1="150" y1="28" x2="192" y2="28" stroke="#521000" stroke-width="0.6" stroke-dasharray="2 2"/></svg><figcaption>Annotations describe expected types for tools; the runtime accepts any object regardless.</figcaption></figure></section><section class="journey-section"><div><h2>Describe realistic data shapes.</h2><p class="meta">Typed Python becomes useful when annotations explain optional values, unions, callables, and JSON-like records.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/union-and-optional-types">Union and Optional Types</a><p class="meta">show `X | Y` and `None`-aware APIs</p></li><li><a class="text-link journey-item-title" href="/examples/type-aliases">Type Aliases</a><p class="meta">name complex types with `type` statements or aliases</p></li><li><a class="text-link journey-item-title" href="/examples/typed-dicts">TypedDict</a><p class="meta">type dictionary records that come from JSON</p></li><li><a class="text-link journey-item-title" href="/examples/literal-and-final">Literal and Final</a><p class="meta">express constrained values and names that should not be rebound</p></li><li><a class="text-link journey-item-title" href="/examples/callable-types">Callable Types</a><p class="meta">type functions that are passed as arguments</p></li></ul></div><figure class="journey-figure"><svg viewBox="-8 -14 172 108" width="275" height="173" xmlns="http://www.w3.org/2000/svg"><text x="0" y="14" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="rgba(82, 16, 0, 0.7)" text-anchor="start" letter-spacing="0.5">X: INT|STR|NONE</text><rect x="0" y="22" width="44" height="28" fill="none" stroke="#521000" stroke-width="1.0"/><text x="22.0" y="40.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">x</text><line x1="44" y1="36" x2="74.02702716443629" y2="17.650150066177822" stroke="#521000" stroke-width="1.0"/><polygon points="80,14 75.48708719090742,20.039339200403308 72.56696713796516,15.260960931952336" fill="#521000"/><line x1="44" y1="36" x2="73.0" y2="36.0" stroke="#521000" stroke-width="1.0"/><polygon points="80,36 73.0,38.8 73.0,33.2" fill="#521000"/><line x1="44" y1="36" x2="74.02702716443629" y2="54.34984993382218" stroke="#521000" stroke-width="1.0"/><polygon points="80,58 72.56696713796516,56.73903906804766 75.48708719090742,51.960660799596695" fill="#521000"/><rect x="82" y="4" width="70" height="22" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="117.0" y="19.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">int</text><rect x="82" y="26" width="70" height="22" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="117.0" y="41.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">str</text><rect x="82" y="48" width="70" height="22" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="117.0" y="63.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">None</text></svg><figcaption>A typed slot can accept one of several shapes — `int | str | None` covers expected absence and alternatives.</figcaption></figure></section><section class="journey-section"><div><h2>Scale annotations for reusable libraries.</h2><p class="meta">Advanced typing exists to preserve information across reusable functions, containers, and decorators.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/generics-and-typevar">Generics and TypeVar</a><p class="meta">write reusable typed containers and functions</p></li><li><a class="text-link journey-item-title" href="/examples/paramspec">ParamSpec</a><p class="meta">preserve callable signatures through decorators</p></li><li><a class="text-link journey-item-title" href="/examples/overloads">Overloads</a><p class="meta">describe APIs whose return type depends on the input shape</p></li><li><a class="text-link journey-item-title" href="/examples/casts-and-any">Casts and Any</a><p class="meta">show escape hatches and their tradeoffs</p></li><li><a class="text-link journey-item-title" href="/examples/newtype">NewType</a><p class="meta">create distinct static identities for runtime-compatible values</p></li></ul></div><figure class="journey-figure"><svg viewBox="-8 -14 266 98" width="426" height="157" xmlns="http://www.w3.org/2000/svg"><rect x="0" y="30" width="36" height="28" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="18.0" y="48.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">T</text><line x1="36" y1="44" x2="65.0" y2="44.0" stroke="#521000" stroke-width="1.0"/><polygon points="72,44 65.0,46.8 65.0,41.2" fill="#521000"/><rect x="74" y="26" width="100" height="36" fill="none" stroke="#521000" stroke-width="1.0"/><text x="80" y="23" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="rgba(82, 16, 0, 0.7)" text-anchor="start" letter-spacing="0.5">FN[T]</text><line x1="174" y1="44" x2="203.0" y2="44.0" stroke="#FF4801" stroke-width="1.4"/><polygon points="210,44 203.0,46.8 203.0,41.2" fill="#FF4801"/><rect x="212" y="30" width="36" height="28" fill="rgba(82, 16, 0, 0.05)" stroke="#521000" stroke-width="1.0"/><text x="230.0" y="48.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#521000" text-anchor="middle">T</text></svg><figcaption>A generic type variable preserves shape across a call: the same T flows in and out.</figcaption></figure></section>
4141
</article>
4242

4343
</body>

0 commit comments

Comments
 (0)