ui: figures join the document — no card chrome, caption row below

Owner round: the figure card loses its container entirely (no border,
no padding box) - the figure is document material. Image first, then
the CAPTION ROW below it: 'Figure' label aligned to the image's left
edge, the controls (invert toggle, download, fullscreen) on the same
row aligned to its right edge. The wrapper carries the natural-sizing
law, so for a declared-width svg the row hugs the centered figure's
own edges, not the column's.

The invert affordance: the house Toggle gains an optional icon disc -
the figure's toggle renders lucide's Contrast (the half-filled circle)
AS the sliding disc, plus a native 'Invert colors' tooltip; every
plain toggle elsewhere is unchanged. Interactive demo cards keep their
boxed stage (owner ruling: an interactive surface reads as its own
stage).
This commit is contained in:
amosblomqvist
2026-10-08 19:01:04 +03:00
parent 0f2519a349
commit 1beb54d3d2
3 changed files with 88 additions and 30 deletions
+17
View File
@@ -505,6 +505,23 @@ describe("artifact chrome — scrollbar, invert toggles, downloads (release poli
assert.ok(!html.includes("hue-rotate"), "inversion OFF by default");
});
it("figure card: NO card chrome, image first, caption row BELOW (owner round, 2026-10-07)", () => {
const svg = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100"><circle cx="50" cy="50" r="20"/></svg>';
const html = renderToStaticMarkup(<VisualOutputCard output={{ svg }} />);
// Document material: the outer box carries no card styling.
const rootAt = html.indexOf('data-testid="visual-card"');
const root = html.slice(rootAt - 200, rootAt);
assert.ok(!root.includes("rounded-xl border"), "no card container");
// Image BEFORE the caption row; the caption label and the controls follow it.
const svgAt = html.indexOf('data-testid="visual-svg"');
const captionAt = html.indexOf(">Figure<");
assert.ok(svgAt !== -1 && captionAt !== -1 && svgAt < captionAt, "the caption row sits below the image");
assert.ok(captionAt < html.indexOf("visual-download"), "the controls ride the caption row");
// The invert affordance: the contrast-circle disc + the tooltip.
assert.ok(html.includes("size-3"), "the icon disc");
assert.ok(html.includes('title="Invert colors"'), "the tooltip names the toggle");
});
it("svgNaturalWidth: declared px sizes parse; %, auto, and absent fall back to null", () => {
assert.equal(svgNaturalWidth('<svg width="200" height="100"></svg>'), 200);
assert.equal(svgNaturalWidth("<svg width='320'></svg>"), 320);