v0.3.0
What's Changed
Added
-
Canvas navigation mode. A toolbar button next to "Open in new tab"
drops the iframe entirely and navigates the parent window in-place to
/styleguide/render/<kind>/<slug>?canvas=1. In that view viewport
units (vh/svh/dvh/ Tailwindh-screen) resolve against the
real browser viewport, not the iframe's — fixes the truncated-hero
problem whereh-svhcollapsed to inner-document height inside an
iframe. The?canvas=1flag tellsrender-cell.twigto suppress its
standalone back-bar so the hero exactly fills the viewport; the
browser back button is the return path. -
Per-component render modes. New YAML metadata key on components:
render: inset | bleed | chrome | overlay
Drives the iframe wrapper in
render-cell.twig:Mode Inset wrap --header-heightBody min-heightUse for inset(default)24 px on all sides unchanged unchanged Atomic UI (button, alert, breadcrumb, picture). bleednone 0pxinjected at:rootunchanged Hero, slider, page-header — fills the iframe edge-to-edge. chromenone 0pxinjected at:root200vhon<body>Sticky / fixed page chrome ( header,footer,cookieconsent) that needs scrollable host content to demo sticky behaviour.overlaynone 0pxinjected at:rootunchanged Modals / dialogs. Functionally identical to bleedat the iframe level today; the separate label exists for future UI surfacing.Default is
inset, so legacy components without the new key keep
their pre-feature padding wrapper — zero breaking change. The
--header-height: 0pxreset forbleed/chrome/overlaycollapses
consumer "tuck under sticky header" hacks like
margin-top: var(--header-height, 75px) * -1to a no-op in
styleguide isolation (no sticky chrome above to hide behind).Validated against the canonical set:
ComponentParser::normaliseRender()
coerces typos and non-string values silently toinset, never throws. -
Device chassis frames for mobile / tablet presets. An abstract
rounded bezel with speaker slot + home indicator (mobile) or camera
dot (tablet) wraps the preview iframe so the device reads as a
device, not a white box. Pure Tailwind ring + absolute-positioned
decoration; the iframe itself still carries the preset's exact
logical CSS pixel dimensions, so@media,vw,vh, and breakpoints
inside resolve identically to the real device. Desktop keeps a clean
card; Full + Custom stay frame-free. Adapts to light + dark chrome. -
Desktop 2K preset (
2560 × 1440) — the QHD/2K resolution commonly
emulated for 27" monitor design work, alongside the existing Mobile S/M/L,
Tablet/L, Desktop/L/XL, and Full presets. -
Fit-to-bounds preview zoom. The zoom getter now picks the smaller
of the width-ratio and the height-ratio (Math.min(widthRatio, heightRatio, 1)), so a 2560 × 1440 preset on a 1280 × 800 chrome
pane shrinks proportionally in both axes — preserving the device's
aspect ratio — instead of overflowing the vertical edge. Custom
widths (no logical height) fall through viaInfinityso the
width-only path is unchanged. -
Vertically centred chassis + dimension badge. Non-Full presets
now sit centred in the canvas (items-centerinstead ofitems-start)
with a small font-monoW × Hbadge underneath the chassis. Helpful
when a wide preset is scaled down to 15 % and the visual size no
longer matches the logical CSS pixel size — the badge always reports
the emulated viewport, not the visible scale. -
Responsive toolbar dropdown. Below
xl(1280 px) the 10-button
segmented viewport pill collapses into a single trigger button that
opens a popover listing the same presets. The Custom-width input
stays next to the trigger on both layouts so typing-then-Enter never
costs an extra click. Removes the toolbar's historical
overflow-x-autoescape hatch — the dropdown keeps the row narrow
enough that overflow no longer happens, and the missing overflow
lets the popover break out of the toolbar's flow without getting
clipped. -
Logical-pixel readouts everywhere. Toolbar readout, dropdown
trigger label, and drag-to-resize all read the emulated CSS pixel
width from the store, not the scaled wrapper DOM box. Drag math now
compensates for the active zoom factor so a 50-screen-px cursor move
on a 0.5×-scaled 2K preset applies a 100-logical-px width change
(was 50).currentWidthis now a getter sourced frompreviewWidth
with a fallback to the ResizeObserver-tracked DOM measurement only
for Full mode (where the wrapper truly carries the real viewport). -
render-cell.twigwrapper height now falls back to
iframeContentHeight×zoomwhen the active mode has no logical
height (Custom widths), eliminating a blank gap below the visible
iframe equal tounscaledH * (1 - zoom)that previously appeared
when a Custom width was wider than the chrome pane.
Accessibility
- Icon-only toolbar controls (rotate, sidebar toggle, open-in-new-tab)
now carryaria-labelalongsidetitleso screen readers get an
accessible name independent of hover tooltips.
Changed
-
|resizeris now polymorphic: in addition to the historical
variadic-tuples shape, it also accepts a single orientation-keyed
map as its argument. When the input has alandscape,portrait,
orsquarekey, the filter classifies the source image's aspect
(±10 % tolerance band around 1:1) and dispatches to the matched
bucket, falling through tolandscapewhen the matched bucket is
empty / absent. Tolerance is hardcoded at0.1— the styleguide
has no WP-filter-equivalent override mechanism, and YAGNI applies
until a real demand for stricter classification surfaces.Two shapes, one filter:
{# Tuples mode — historical, unchanged #} {{ image|resizer(['960', '720', '1280', 'crop'], ['480', '360', '', 'crop']) }} {# Orientation-aware mode — new #} {{ component_picture({ image: item.image|resizer({ landscape: [['960', '720', '1280', 'crop'], ['480', '360', '', 'crop']], portrait: [['720', '960', '1280', 'crop'], ['360', '480', '', 'crop']], square: [['800', '800', '1280', 'crop'], ['400', '400', '', 'crop']], }) }) }}
Detection: a single arg that's an associative array carrying at
least one of the orientation keys flips dispatch into orientation
mode. Tuples have integer keys (width / height / min-width / op),
so the two shapes can't collide on a realistic call — fully
backward compatible with existing variadic calls.Square-band check uses cross-multiplication
(abs(w - h) <= tolerance * h) instead ofabs(w/h - 1) <= tolerance
to keep the boundary inclusive under IEEE 754 (1100 ÷ 1000 in float
is1.1 + 8.88e-17, which would trip the naïve<=to false at
the exact 10 % edge for integer-dimensioned sources).Mirrors the upstream
parisek/timber-kitunification — a single
Twig template renders identically against the WordPress runtime
and the styleguide preview.
Pull Requests
- #15 — feat: canvas mode, device chassis, render modes, 2K preset + dropdown
- #17 — feat: make |resizer polymorphic — orientation-keyed map dispatch (#16)
- #18 — feat(cli): component catalog CLI (vendor/bin/styleguide)
Full Changelog: v0.2.1...v0.3.0