Gate set. Protocol floor: check integrity, check copy --manifest copy.txt, check design, check composition, check color, check a11y contrast, check a11y focus, check a11y touch, scan scroll, scan handlers, check interactions. The brief defines no theme and no theme URL parameter, so contrast runs on the one light theme. Added for the brief's behaviour: check scroll (the progress bar is position:fixed and must hold the top edge while the page scrolls), check motion + check animation (the bar's easing is the only motion and must stop under prefers-reduced-motion; also ?animate=0), stress i18n (listed by the brief; Japanese text in nav, buttons and meta rows), check breakpoints --sweep (the layout changes at 700px and 1000px and has to be free of horizontal overflow at every width in between). Left out: verify flow (no multi-step flow), check grounding (not asked), scan handlers --probe-drag (nothing is draggable).
vlmkit demo · judgment log
ほとり — 海辺の町の古本屋が、夜だけ店を開ける理由
How this editorial long-form article was judged, in the order it happened: every gate run with its whole output, every screenshot that was looked at with what was seen in it, and where each defect came from. Each finished round keeps the pictures of one full-page walk per width; the other shots keep what was seen in them. Open the site.
- 6rounds
- 71gate runs, 8 failing
- 150screens in 43 shots, 118 kept
- 43looks recorded
- 26defects found by eye
- 11defects found by a gate
- 5gate false positives
The brief the builder was given
# Brief: long-form magazine article — 「ほとり」 web magazine - **Directory**: `examples/sites/magazine/` - **Pattern**: editorial long-form article - **Language**: Japanese (`lang="ja"`) ## What it is One feature article in a fictional web magazine, **ほとり** (hotori — "the water's edge"): **「海辺の町の古本屋が、夜だけ店を開ける理由」**, about a second-hand bookshop in a small seaside town that opens only from dusk until midnight. One page, `index.html`. Write the article yourself: about 3,000 Japanese characters of real, readable prose — not placeholder text. ## Layout and behaviour - **Masthead**: the magazine's wordmark, section navigation (特集 / 暮らし / 旅 / 本 / インタビュー) and a search button. A thin reading-progress bar at the very top grows as the article is read. - **Article header**: a large drawn illustration (inline SVG — the shop at night), the category label 本, the title, a lede, the byline 文・写真 三浦 灯, the date 2026年9月12日 and the reading time 約8分. - **Body**: three or four `h2` sections; a pull quote; a figure with a caption; an interview passage with the speakers marked; two or three footnote references that jump to notes at the end, each note with a link back. Links in the prose must be distinguishable by more than colour. - **After the article**: an author card, a "copy link" button that confirms 「リンクをコピーしました」 in a live region, a 保存 toggle (`aria-pressed`), and three related articles with drawn images. - **Footer**. - **Motion**: the progress bar is the only motion; it respects `prefers-reduced-motion`. ## Visual direction Editorial and quiet: an off-white page, a deep ink colour, one restrained accent, generous margins and line spacing (around 1.9 for body text), a measure of roughly 35–40 full-width characters on desktop, and a clear typographic hierarchy between headings, body, captions and notes. Body text prefers a Mincho serif on real devices (this machine has none — see the protocol); headings may be Gothic. ## Required copy ``` ほとり 海辺の町の古本屋が、夜だけ店を開ける理由 文・写真 三浦 灯 2026年9月12日 約8分 リンクをコピーしました 関連する記事 注 ``` ## States to show (final round) Desktop and mobile full page; mid-article with the progress bar partly filled; the page scrolled to a footnote after following its reference; the copy-link confirmation; the 保存 toggle pressed; reduced motion. ## Gates this brief leans on `check composition` (type hierarchy and grouping over a long page), `check color` (prose links), `check copy` on Japanese, `check integrity` (Japanese line breaking, clipping), `check scroll` (the fixed progress bar), `check motion` / `check animation` (reduced motion honoured), `stress i18n`.
Defects
| Defect | Found by | What | Fix | Checked by |
|---|---|---|---|---|
| D1 | eye L1 on S1 |
Desktop S1-1: the lede's last line splits the owner's name across the break, 汐田律 / 子さんをたずねた。 A display paragraph should not break inside a personal name. | The lede is no longer justified: word-break: auto-phrase (phrase-level Japanese line breaking) and text-wrap: pretty, so lines break between phrases and a name stays whole. | L7 |
| D2 | eye L1 on S1 |
Every digit and Latin letter renders fixed-pitch (S1-1 meta 2026 年 9 月 12 日, S1-9 card dates, S2-12 footer 'vlmkit'): the sans stack names IPAGothic, whose Latin glyphs are monospaced (fc-list spacing=90); the proportional IPAPGothic exists on this machine. | The sans stack now names IPAPGothic (proportional Latin and digits) instead of IPAGothic; there is a comment in style.css saying why. | L11 |
| D3 | eye L1 on S1 |
S1-4: the sunbeam over the shelf ends in a hard vertical edge at x≈720 that cuts through all three shelves; it reads as a rendering glitch, not as light. | The shelf sunbeam is now a gradient polygon that fades from .26 to 0 across the shelf (f-shelfbeam), with no flat-opacity shape and no hard vertical edge. | L7 |
| D4 | eye L1 on S1 |
S1-6: the pull quote breaks inside a word (…誰かが起 / きている…); on mobile (S2-7) it ends on a lone い。 line. Display type needs phrase-level breaks. | The pull quote uses word-break: auto-phrase + text-wrap: balance, so it breaks between phrases into lines of even length. | L8 |
| D5 | eye L1 on S1 |
S1-10 / S2-13: the © 2026 ほとり編集部 line renders at about 10px (<small> inside a 12px paragraph) and is hard to read. | Removed the <small> from the © line; it now renders at the footer's 12px like the note above it. | L7 |
| D6 | eye L2 on S2 |
Several paragraphs end on a one- or two-character line (S1-3 た。, S2-2 いく。, S2-4 いる。, S2-8 る。) and the figure caption ends on a lone る。 (S2-5). Short last lines break the texture of justified Japanese text. | text-wrap: pretty on body paragraphs, interview utterances and the figure caption, so the browser rebalances the last lines instead of leaving one or two characters alone. Second attempt (F6 did not work): the last four visible characters of each body paragraph, interview utterance and the figure caption are wrapped in <span class=tail> with white-space: nowrap (26 tails, made by a one-off script; a tail that would end inside an element takes the whole element, such as the 注1 <sup>). No paragraph can end on a line shorter than four characters; the line above absorbs at most three characters' worth of space as justification. | L13 |
| D7 | eye L2 on S2 |
S2-1: at 375px the 検索 button wraps onto its own row under the tagline, left-aligned, so the masthead is three rows (~165px) before the nav and the button looks misplaced. | Below 700px the tagline is hidden, so the masthead is two rows: wordmark with 検索 on the right, then the section nav. | L8 |
| D8 | eye L2 on S2 |
S2-2: the meta row wraps and its second line starts with a stray separator bar: '| 読了時間 約8分'. | Below 700px the byline takes its own line and the date loses its leading separator, so the second line reads '2026年9月12日 | 読了時間 約8分' with the bar only between the two items. | L8 |
| D9 | eye L2 on S2 |
S2-2: the first h2 wraps with a single character on its second line: 日が沈むと、シャッターが上が / る. Headings need balanced, phrase-level wrapping. | Chapter h2s use word-break: auto-phrase + text-wrap: balance. | L13 |
| D10 | eye L2 on S2 |
S2-10: when 本文へ戻る wraps to a new line it starts with a stray indent (its margin-left) that does not line up with the note text. | Replaced the back link's margin-left with a plain space in the markup ('。 <a class=backref>'), which collapses at the start of a line, so a wrapped 本文へ戻る now starts flush with the note text. | L8 |
| D11 | eye L2 on S2 |
S2-10: the share row breaks badly at 375px: この記事を + リンクをコピー on the first line and 保存 alone on a second line. | Below 700px the この記事を label takes its own line, both buttons share the next, and the confirmation wraps below them. | L8 |
| D12 | eye L2 on S2 |
S2-11 to S2-13: on mobile the three related cards are stacked full-width 335x223 images, so the related section runs over 1,100px and reads like a second article instead of a short list of next reads. | Below 700px each related card is a two-column grid (112px thumbnail, then category, title and date), and the band's padding and margin are reduced to the 64/40/56 steps. | L8 |
| D13 | eye L3 on S3 |
S3: the white sliver at the quay's left end is the page's paper showing through: nothing is painted between the quay's slanted left edge and the water, and the sky rect stops at y=480. | The hero SVG now starts with a full 640x640 night rect under everything, and the quay's left edge is vertical at x=124, where the water begins, so no gap is left unpainted. | L9 |
| D14 | eye L3 on S3 |
S3: the three bare bulbs are drawn exactly under the door mullions (x 256/321/386), so the bulbs themselves are hidden and only their glows show. | The bulbs now hang in the middle of the four glass panels (x 224/288/354/418), clear of the mullions, each with a small halo; the customer moved to the third panel. | L9 |
| D15 | gate G1 |
check integrity near-misalignment: the middle bulb glow sits 3.5-4px above its two siblings. It was meant to hang on a shorter cord, but in S3 a 4px offset reads as an accident, as the rule says. Either align the bulbs or make the difference unmistakable. | All four bulbs hang at one height (cy 389, cords 374-385): I chose alignment over a 4px variation that only reads as a mistake. | G17 |
| D16 | eye L3 on S3 |
S3: a small dark bollard dome sits in the middle of the lit spill in front of the door and reads as a stray blob. | Removed both bollards; the light spill in front of the door is clean. | L9 |
| D17 | eye L3 on S3 |
S3: the quay's front face shows as an outlined box: a light hairline runs along its bottom edge where it meets the water. | The quay face now overlaps the water by 1px on the full-frame background, and the edge highlight is darker and 5px tall, so no light seam outlines the face. | L9 |
| D18 | eye L5 on S5 |
S5 vs S6: the body text is in a different Gothic from the headings. No font named in the serif stack exists here, so the generic serif resolves (fc-match serif:lang=ja) to WenQuanYi Zen Hei, a Chinese font: the page mixes two Gothic designs, and the body gets Chinese glyph forms. | The serif stack adds IPAPMincho, then IPAGothic as the last named face before the generic, so a device with no Japanese serif renders Japanese glyph forms in the same design as the headings instead of whatever the generic resolves to (a Chinese font here). There is a comment in style.css saying why. | L10 |
| D19 | gate G2 |
check copy: the required title line is reported copy-invisible (found only in text a user cannot see). The visible h1 is split into two inline-block spans to force the break after 、, so the rendered text has no contiguous 海辺の町の古本屋が、夜だけ店を開ける理由; the only contiguous copy is the document <title>. | The h1 is a single text node again; the two-line break comes from word-break: auto-phrase + text-wrap: balance instead of two inline-block spans. | G18 |
| D20 | gate G3 |
check design component-drift: 7 h2 in 4 styles. The four chapter heads share 25px, but the three secondary heads are 17px (注), 18px (author name) and 20px (関連する記事): three sizes for one role, with no reason for any of them. | 注, the author's name and 関連する記事 share one secondary-head rule (20px/700, .08em); the four chapter heads keep 25px, so h2 has two styles, each used at least 3x. | G19 |
| D21 | gate G3 |
check design component-drift: 4 h3 in 2 styles. The shop-info name is 18px against the three card titles at 17px, a 1px difference that is an accident, not a decision. | The shop-info name is 17px like the card titles. | G19 |
| D22 | gate G3 |
check design scale-outlier (info): masthead padding-top 22px sits next to the page's 20px step, and the related band's 88px margin-top next to the 96px used four times. Both are strays from my own scale. | Masthead padding is 20px top and bottom, and the related band's margin-top is 96px (64px below 700px). | G19 |
| D23 | gate G4 |
check composition proximity-inversion: the shop name sits 4px under its 店舗情報 kicker but 16px above the list it labels, so it groups upward. | The shop-info name margin is now 6px above and 10px below, so it sits closer to the list it labels than the rule's threshold allows it to drift upward. | G20 |
| D24 | gate G4 |
check composition proximity-inversion x3: each card title sits 6px under its category label but 14px above its date, so title and date read as two groups. | Card title margin is 6px 0 2px and the date is a block at 1.5 line-height, so the title is 2px from its date and about 12px from its label. | G20 |
| D25 | gate G15 |
stress i18n: the interview speaker labels (汐田X) and the note numbers (注1X) wrap to two lines under inflation, because both sit in fixed 3.2em / 3em grid columns. A three- or four-character speaker name, or a 注10, would break the same way. The prose rows are ordinary wrapping. | The interview and the notes are each one grid (minmax(3em, max-content) then 1fr) with subgrid rows, so the label column grows to the longest speaker name or note number and every row stays aligned; labels are white-space: nowrap. The notes' :target highlight now uses side box-shadows instead of padding and a negative margin. | L7 |
| D26 | eye L1 on S1 |
S1-5: the ruby いさりび over 漁火 and the 注3 superscript right after it run together into one clump of small text at the same height; the reference needs a little air to read as a separate mark. | A footnote reference that follows ruby gets .4em of left margin (ruby + .fnref); other references keep .12em. | L7 |
| D27 | eye L7 on S7 |
Regression from F2: IPAPGothic sets 、。() half-width, so every sans run looks cramped at its punctuation: 日が沈むと、シャッター, 西日から、本の, 零時に、明かり (S7-2/3/6), 宵待書房(よいまち and 水曜日、海が (S7-7/8), and the notes and bio read dense. S10 shows the heading's comma next to the full-width commas of the body. | The sans stack goes back to IPAGothic for Japanese (full-width 、。()), with DejaVu Sans named just before it so Latin and digits resolve to a proportional face first; real devices still use Hiragino / Yu Gothic / Noto, which come earlier in the list. | L11 |
| D28 | eye L8 on S8 |
S8-11: in the compact related list, two card titles wrap with widows: 岬の貸本屋に、手紙で本を頼 / む and 潮風と暮らす家の、窓のしつ / らえ. The h3s never got the phrase-level wrapping the h1/h2 got. | A base rule gives h1, h2 and h3 word-break: auto-phrase + text-wrap: balance, which covers the card titles, the shop name and the secondary heads. | L13 |
| D29 | gate G19 |
check design scale-outlier (info): the brand block's 18px gap (wordmark to tagline) sits next to the 16px step used 13 times. | The brand gap is 16px. | G24 |
| D30 | eye L12 on S12 |
S12-1 / S15: at tablet width the one-row desktop masthead does not fit in 688px, and the flex items shrink until their text wraps a character at a time: ほと / り, 特 / 集, 暮 / ら / し, インタ / ビュー, and a tall 検 / 索 pill. No gate reported it: check integrity at 768 was CLEAN (G17) and the breakpoint sweep was clean (G16), because nothing overflows or overlaps. | The tagline shows only at 1100px and up, where the one-row masthead has the ~910px it needs; from 700 to 999px the nav gap is 20px and the row gap 28px (about 590px needed, 620px available at 700). The wordmark, nav links and buttons are white-space: nowrap, so if a width is ever too narrow the row overflows, which the breakpoint sweep can catch, instead of silently wrapping a character at a time. | L17 |
| D31 | eye L13 on S13 |
S13-5: the figure caption's last line begins with a small kana (っている。), which strict Japanese line breaking forbids; the caption never got the line-break: strict the body paragraphs have. | line-break: strict is set on body, so the caption, notes, bio and UI text get the same kinsoku as the body paragraphs. | L20 |
| D32 | eye L21 on S21 |
S21: the search field's native cancel × renders in system blue, the only blue on the page. | ::-webkit-search-cancel-button is restyled: no native appearance, and a 14px × drawn with two ink-soft gradients. | L28 |
| D33 | eye L21 on S21 |
S21/S22: the 検索 toggle has no open state. The only fill in the shots is the pointer's hover, so once the pointer leaves, nothing ties the button to the open panel. | While aria-expanded is true, 検索 takes the panel's paper-deep fill and its magnifier is swapped for a close mark (two icons in the button, toggled in CSS); the visible and accessible name stays 検索. | L28 |
| D34 | eye L23 on S23 |
S23/S24: both footnote jumps land the target 32px under the top edge, with a sliced line of the item above it (the end of 注1 over 注2; the first line of the quoted paragraph over the 注2 reference). The reader lands without context and has to scroll up to re-read the sentence. | Footnote notes and references get scroll-margin-top: 25vh, so a jump lands with the preceding lines in view; the returned-to reference's tint is backed by a 3px tint ring, so it is easier to find. | L29 |
| D35 | gate G27 |
check animation on index.html#note-2: '#progress-bar transform 120ms x1: visible, motion region (319,0) 961x3'. A page opened at a fragment animates the bar on load, sweeping it from about 25% to full width, because the easing is switched on one frame after the script runs and the browser's fragment scroll comes later. The README and the code comment both claim a page opened mid-article does not animate on load. | main.js: the easing class (is-ready) is added by the first wheel, touchstart, keydown or pointerdown (capture, passive, removed after firing), not one frame after load. A fragment or restored scroll made by the browser therefore jumps the bar straight to its place. The README now says this; the reduced-motion and ?animate=0 rules are unchanged. | G28 |
| D36 | gate G45 |
stress i18n: the shop-info labels wrap under inflation: 'dt そのほかXX 77×50 +25px' and 'dt 営業時間XX 77×50 +25px' (one line to two; the dd beside each only grows because the row does). The dt column is a fixed 5.5em, the same fixed-label pattern D25 removed from the interview and the notes; a five-character label such as 最寄りの駅 would wrap it without any inflation. | The shop-info dl is one grid (minmax(5.5em, max-content) then 1fr) with each row a subgrid, and dt is white-space: nowrap, so the label column grows to the longest label and every row stays aligned, as in the interview and notes. | G48 |
| D37 | eye L39 on S39 |
On desktop the pull quote's first line ends on 誰かが, the subject of the SECOND sentence, so the quote reads 「…思うの。誰かが/起きている明かりが…」. word-break: auto-phrase makes 誰かが a legal break and text-wrap: balance prefers 23/21 characters to the sentence boundary's 20/24, although both sentences fit the 694px column on one line each. | Each sentence of the pull quote is a span, inline-block from 700px, so on wide screens the quote breaks at the sentence boundary; below 700px the spans stay inline and auto-phrase / balance break it as before (the phone version already broke by phrase well). | L41 |
Rounds
R1 · first draft
builderindex.html · 1280×800 · full page, 10 screen(s)
Desktop 1280, full page, 10 screens. S1-1: first read is right: the masthead (wave mark + ほとり, grey tagline, nav right-aligned with 本 in the accent and underlined, 検索 pill) and the title. The progress track is a faint grey hairline at the very top with no fill. Header: text column on the left (boxed 本 label, series line, two-line bold title split exactly after 、, grey lede with なぎうら ruby over 凪浦, meta row under a hairline), 620px night illustration on the right; the text block is centred against it, so about 140px of empty paper sits above the 本 label. Wrong: (a) the lede's last line splits the owner's name: 汐田律 / 子さんをたずねた。 (b) every digit and Latin letter is fixed-pitch, so the meta reads 2026 年 9 月 12 日 like a typewriter; (c) in the illustration a bright white sliver stands at the left end of the quay (x≈720, y≈600-650), and the quay/water join there looks unfinished. S1-2/S1-3: body column 293-986px, about 37 characters a line at 1.9 leading, justified; h2s carry a 36px accent rule; the link 前回訪ねた岬の貸本屋 is accent + underline; 注1 is a small underlined superscript. S1-3: section 2's first paragraph ends on a lone line 「た。」. S1-4: the shelf figure breaks out to 880px and the faded-vs-saturated spines tell the story, but the sunbeam overlay stops at a hard vertical edge (x≈720) through all three shelves, which reads as a rendering glitch rather than light; the 文庫 百円 card covers spines. S1-5: interview between hairlines, speaker labels 三浦 (grey) / 汐田 (accent) aligned to each first line; the ruby いさりび and the 注3 superscript crowd each other. S1-6: pull quote under a 2px ink rule, 26px bold, but the line breaks inside a word: …誰かが起 / きている… S1-7/S1-8: shop-info box with ruled rows; notes 注1-3 with underlined 本文へ戻る; share row (この記事を, リンクをコピー, 保存 pills) between hairlines; author card with the 灯 monogram. S1-9/S1-10: related band, three cards with drawn images (letter + book, cape lighthouse, window with curtain), accent labels, bold titles, grey dates; dark footer with wordmark, underlined links, the fictional-magazine note; the © line is about 10px and hard to read.
.ledeDesktop S1-1: the lede's last line splits the owner's name across the break, 汐田律 / 子さんをたずねた。 A display paragraph should not break inside a personal name.
font stack (--sans)Every digit and Latin letter renders fixed-pitch (S1-1 meta 2026 年 9 月 12 日, S1-9 card dates, S2-12 footer 'vlmkit'): the sans stack names IPAGothic, whose Latin glyphs are monospaced (fc-list spacing=90); the proportional IPAPGothic exists on this machine.
.figure__svg (sunbeam overlay)S1-4: the sunbeam over the shelf ends in a hard vertical edge at x≈720 that cuts through all three shelves; it reads as a rendering glitch, not as light.
.pullquote blockquote pS1-6: the pull quote breaks inside a word (…誰かが起 / きている…); on mobile (S2-7) it ends on a lone い。 line. Display type needs phrase-level breaks.
.site-footer__copyS1-10 / S2-13: the © 2026 ほとり編集部 line renders at about 10px (<small> inside a 12px paragraph) and is hard to read.
sup.fnref after ruby (漁火)S1-5: the ruby いさりび over 漁火 and the 注3 superscript right after it run together into one clump of small text at the same height; the reference needs a little air to read as a separate mark.
index.html · 375×812 · full page, 13 screen(s)
Mobile 375, full page, 13 screens. S2-1: the masthead takes three rows (about 165px): wordmark, tagline, then the 検索 button wrapped onto its own line under the tagline, left-aligned, before the nav row (特集 暮らし 旅 本 インタビュー spread edge to edge, 本 underlined). The illustration is 335px square with rounded corners, then 本 + series, a two-line 28px title (clean split), and the lede. S2-2: the meta row wraps and the second line starts with a stray separator bar: "| 読了時間 約8分". The first h2 wraps with a single character on its second line: 日が沈むと、シャッターが上が / る. S2-2 to S2-9: body at 16px, about 19-20 characters a line, justified, strict line breaking; several paragraphs end on a two-character line (いく。 in S2-2, いる。 in S2-4, る。 in S2-8), and the figure caption ends on a lone る。 (S2-5). S2-6: the interview grid holds at this width; interviewer lines grey, speaker column narrow but aligned. S2-7: the pull quote ends on a single い。 line. S2-9: shop info fits, rows wrap cleanly. S2-10: notes: where 本文へ戻る wraps to a new line it starts with a small indent that does not line up with the note text; the share row breaks badly: この記事を + リンクをコピー on one line and 保存 alone on a second line. Author card fine. S2-11 to S2-13: about 85px of blank paper before the related band; the three related cards are stacked as full-width 335x223 images, so the section is over 1,100px tall and reads like a second article; digits in the dates are fixed-pitch; footer links wrap to two rows (プライバシー alone), © line tiny.
.chapter > p, .figure figcaptionSeveral paragraphs end on a one- or two-character line (S1-3 た。, S2-2 いく。, S2-4 いる。, S2-8 る。) and the figure caption ends on a lone る。 (S2-5). Short last lines break the texture of justified Japanese text.
.masthead__inner (mobile)S2-1: at 375px the 検索 button wraps onto its own row under the tagline, left-aligned, so the masthead is three rows (~165px) before the nav and the button looks misplaced.
.meta (mobile)S2-2: the meta row wraps and its second line starts with a stray separator bar: '| 読了時間 約8分'.
.chapter > h2S2-2: the first h2 wraps with a single character on its second line: 日が沈むと、シャッターが上が / る. Headings need balanced, phrase-level wrapping.
.backref (mobile)S2-10: when 本文へ戻る wraps to a new line it starts with a stray indent (its margin-left) that does not line up with the note text.
.share (mobile)S2-10: the share row breaks badly at 375px: この記事を + リンクをコピー on the first line and 保存 alone on a second line.
.related__list (mobile)S2-11 to S2-13: on mobile the three related cards are stacked full-width 335x223 images, so the related section runs over 1,100px and reads like a second article instead of a short list of next reads.
vlmkit check integrity index.htmlverdict: NO DEFECTS, 1 WARN (0 fail, 1 warn, 0 exempted)
verdict clean · findings 1 · fails 0 · warns 1 · exempted 0 · viewports 3
output, 11 line(s), 7.1s
vlmkit check integrity source: index.html verdict: NO DEFECTS, 1 WARN (0 fail, 1 warn, 0 exempted) exits 0 — 1 warn(s) did not fail this command. To gate on one: --rule near-misalignment=suspect 1280x800: 1 component(s), ink 93.6%, 107 text block(s) 768x900: 1 component(s), ink 92.5%, 107 text block(s) 375x700: 1 component(s), ink 91.0%, 107 text block(s) Findings: ! [near-misalignment] svg.art__svg > g:nth-of-type(11) > circle:nth-of-type(2) @1280,768,375: svg.art__svg > g:nth-of-type(11) > circle:nth-of-type(2) is off its siblings' shared top edge by 3.5px inside svg.art__svg > g:nth-of-type(11) — siblings align exactly; a 2-8px deviation is almost always an accident (stray margin/padding), not a design choice.
svg.art__svg > g:nth-of-type(11) > circle:nth-of-type(2)check integrity near-misalignment: the middle bulb glow sits 3.5-4px above its two siblings. It was meant to hang on a shorter cord, but in S3 a 4px offset reads as an accident, as the rule says. Either align the bulbs or make the difference unmistakable.
vlmkit check copy index.html --manifest copy.txtmissing 1 · placeholders 0 · manifestLines 8 · invisibleOnly 1 · statesExplored 1 · revealedOnly 0
output, 11 line(s), 1.5s
vlmkit check copy source: index.html status: suspect rendered text: 3458 chars disclosure states: 1 explored (details / tabs / aria-expanded) manifest: 8 line(s), missing 1, 1 invisible-only Issues: x copy-invisible: Manifest line found ONLY in text a user cannot see (reason: unknown): "海辺の町の古本屋が、夜だけ店を開ける理由". Invisible text does not satisfy the copy gate — render it visibly or remove the hidden copy. If this invisibility is deliberate (e.g. assistive-tech-only copy), re-run with --allow-invisible unknown. x copy-missing: Manifest line not found in rendered text or any of 1 revealed disclosure state(s): "リンクをコピーしました" (comparison is whitespace-normalized, case-sensitive).
h1#story-titlecheck copy: the required title line is reported copy-invisible (found only in text a user cannot see). The visible h1 is split into two inline-block spans to force the break after 、, so the rendered text has no contiguous 海辺の町の古本屋が、夜だけ店を開ける理由; the only contiguous copy is the document <title>.
vlmkit check design index.htmlverdict: DRIFT (3 finding(s))
verdict drift · drifting 2 · roles 5
output, 28 line(s), 1.6s
vlmkit check design
source: index.html
verdict: DRIFT (3 finding(s))
exits 0 — 2 warn(s) did not fail this command. To gate on one: --rule component-drift=suspect
roles judged: 3 of 5 seen, spacing values: 24
coverage: 17 of 700 visible element(s) carried an inferable role
no role: rect x396, p x41, path x40, circle x34, g x28, a x23, +27 more tag(s)
A role comes from role="..." or from button/input/select/textarea/h1-h6. Layout elements
(div, span, p, a) have none, so a large skip count is normal — this gate judges components,
not every box. Add role="..." where an element IS a component to widen the coverage.
Role reuse (instances / distinct styles; drift below 3x from 3 instances)
h2 7 inst 4 styles reuse 1.75x 3 one-off drift
h3 4 inst 2 styles reuse 2x 1 one-off drift
button 3 inst 1 styles reuse 3x 0 one-off ok
img 2 inst 2 styles reuse 1x 2 one-off not judged
h1 1 inst 1 styles reuse 1x 1 one-off not judged
not judged: img (2), h1 (1) — under --min-instances 3, so no finding can come from them.
To keep judging them: --min-instances 2 --min-reuse 2 (both — a 2-instance role cannot reach 3x however many instances the floor allows).
Findings
! [component-drift] h2: 7 "h2" elements render 4 distinct styles (used 4x, 1x, 1x, 1x; a system reuses each style 3x or more, and this role averages 1.75x). Dominant style, used 4x: padding 0/0/0/0, radius 0, 25px/700, border 0, bg rgba(0, 0, 0, 0). Deviating: h2#notes-title (padding 0/0/0/0, radius 0, 17px/700, border 0, bg rgba(0, 0, 0, 0)) — differs in font-size 25 → 17; h2#author-name (padding 0/0/0/0, radius 0, 18px/700, border 0, bg rgba(0, 0, 0, 0)) — differs in font-size 25 → 18; h2#related-title (padding 0/0/0/0, radius 0, 20px/700, border 0, bg rgba(0, 0, 0, 0)) — differs in font-size 25 → 20. This reports inconsistency, not which style is correct.
! [component-drift] h3: 4 "h3" elements render 2 distinct styles (used 3x, 1x; a system reuses each style 3x or more, and this role averages 2x). Dominant style, used 3x: padding 0/0/0/0, radius 0, 17px/700, border 0, bg rgba(0, 0, 0, 0). Deviating: h3#shop-info-title (padding 0/0/0/0, radius 0, 18px/700, border 0, bg rgba(0, 0, 0, 0)) — differs in font-size 17 → 18. This reports inconsistency, not which style is correct.
Informational (true, but does not carry the verdict)
i [scale-outlier]: 2 spacing value(s) sit just off the page's own scale: 22px (1x) next to 20px (14x) — header#top>div.wrap paddingTop; 88px (1x) next to 96px (4x) — main#main>section.related marginTop. Snap them to the established value or add them deliberately.h2#notes-title, h2#author-name, h2#related-titlecheck design component-drift: 7 h2 in 4 styles. The four chapter heads share 25px, but the three secondary heads are 17px (注), 18px (author name) and 20px (関連する記事): three sizes for one role, with no reason for any of them.
h3#shop-info-titlecheck design component-drift: 4 h3 in 2 styles. The shop-info name is 18px against the three card titles at 17px, a 1px difference that is an accident, not a decision.
header#top>div.wrap paddingTop; section.related marginTopcheck design scale-outlier (info): masthead padding-top 22px sits next to the page's 20px step, and the related band's 88px margin-top next to the 96px used four times. Both are strays from my own scale.
vlmkit check composition index.htmlverdict: UNBALANCED (4 finding(s))
verdict unbalanced · proximity 4 · flatSteps 0 · railNearMisses 0
output, 22 line(s), 1.6s
vlmkit check composition source: index.html verdict: UNBALANCED (4 finding(s)) exits 0 — 4 warn(s) did not fail this command. To gate on one: --rule proximity-inversion=suspect measured: 8 label(s), 3 heading level(s), 99 wide block(s) on 10 left / 7 right rail(s) — from 186 visible box(es) 4 label(s) not judged: they open a box that paints its own group edge, so nothing above them could be mis-grouped with Type hierarchy (largest instance of each declared level) h1 42px / 700 h1#story-title h2 25px / 700 h2#ch1 h3 18px / 700 h3#shop-info-title body 18px, largest 42px (2.33x), heaviest +300 weight Group gaps: 12.95px between heading-led groups vs 21.5px inside them (0.6x, 12 sample(s)). Context only — intact pages measure 0.86-3.00x, so this cannot carry a verdict. Findings ! [proximity-inversion]: h3#shop-info-title (h3) sits 4px below aside#shop-info>p.shop-info__kicker but 16px above the content it labels, inside aside#shop-info — a label reads as belonging to whatever it is CLOSEST to, so this one groups upward. Reduce the gap under it below 11px, or widen the gap above it. This reports the ambiguity, not which gap is correct. ! [proximity-inversion]: li#related-1>a.card__link>h3.card__title (h3) sits 6px below li#related-1>a.card__link>span.card__cat but 14px above the content it labels, inside li#related-1>a.card__link — a label reads as belonging to whatever it is CLOSEST to, so this one groups upward. Reduce the gap under it below 13px, or widen the gap above it. This reports the ambiguity, not which gap is correct. ! [proximity-inversion]: li#related-2>a.card__link>h3.card__title (h3) sits 6px below li#related-2>a.card__link>span.card__cat but 14px above the content it labels, inside li#related-2>a.card__link — a label reads as belonging to whatever it is CLOSEST to, so this one groups upward. Reduce the gap under it below 13px, or widen the gap above it. This reports the ambiguity, not which gap is correct. ! [proximity-inversion]: li#related-3>a.card__link>h3.card__title (h3) sits 6px below li#related-3>a.card__link>span.card__cat but 14px above the content it labels, inside li#related-3>a.card__link — a label reads as belonging to whatever it is CLOSEST to, so this one groups upward. Reduce the gap under it below 13px, or widen the gap above it. This reports the ambiguity, not which gap is correct.
h3#shop-info-titlecheck composition proximity-inversion: the shop name sits 4px under its 店舗情報 kicker but 16px above the list it labels, so it groups upward.
li#related-1..3 > a.card__link > h3.card__titlecheck composition proximity-inversion x3: each card title sits 6px under its category label but 14px above its date, so title and date read as two groups.
vlmkit check color index.htmlverdict: CONSISTENT (0 finding(s))
verdict consistent · base #f6f2ea · bodyInk #1d2733 · linkInk #a4462b · invisibleControls 0 · colorOnlyLinks 0 · unreadableColors 0
output, 24 line(s), 1.8s
vlmkit check color
source: index.html
verdict: CONSISTENT (0 finding(s))
measured: 0 control(s), 23 link(s) in a text flow — from 197 visible box(es)
Palette (by painted area; reported, never judged — see the rejected candidates in the module docs)
surfaces 3 distinct
#f6f2ea 87.9% 1 el
#ede6d8 8.2% 2 el aside#shop-info
#1d2733 3.9% 4 el a.skip
ink 6 distinct
#1d2733 80.5% 53 el div.brand>a.wordmark>span.wordmark__text
#4a5360 11.9% 25 el div.story-head__text>p.kicker>span.series
#e8e3d9 2.1% 5 el div.wrap>div.site-footer__brand>p.site-footer__mark
#5b636e 2.0% 7 el div.wrap>div.brand>p.tagline
#a4462b 1.8% 16 el ul>li>a
…and 1 more
marks 3 distinct
#d8d0c0 71.0% 11 el header#top
#1d2733 27.5% 14 el button#search-toggle
#a4462b 1.6% 5 el ul>li>a
base #f6f2ea, body ink #1d2733, link ink #a4462b on 9 element(s)vlmkit check a11y contrast index.htmlinspected 122 · failures 0
output, 5 line(s), 1.7s
vlmkit check a11y contrast html: examples/sites/magazine/index.html inspected 122 text-bearing element(s) ✓ 0 contrast failure(s) report: examples/sites/magazine/test-results/a11y-contrast/index-861ad078/report.md
vlmkit check a11y focus index.htmlsteps 26 · findings 4
output, 9 line(s), 1.9s
vlmkit check a11y focus
source: index.html
captured 26 focus step(s)
✗ 4 finding(s)
[skip-row] Focus jumped down by 617px (from `header#top>div.wrap.masthead__inner>button#search-toggle` to `header.story-head.wrap>div.story-head__text>p.meta>span.meta__item>a`). Confirm no focusable element was unintentionally skipped.
[reverse] Focus moved left within the same row (from `div#story-body>section.chapter>p>sup.fnref>a#ref-2` at x=927 to `div.interview>p.turn>span.utterance>sup.fnref>a#ref-3` at x=395). Visual order is L-to-R; check `tabindex` or DOM order.
[reverse] Focus moved up by 424px (from `main#main>section.after>div.share>button#save` at y=612 to `section.related>div.wrap>ul.related__list>li#related-1>a.card__link` at y=188). Visual order is top-to-bottom; check `tabindex` or DOM order.
[skip-row] Focus jumped down by 351px (from `section.related>div.wrap>ul.related__list>li#related-3>a.card__link` to `div.wrap.site-footer__inner>nav.site-footer__nav>ul>li>a`). Confirm no focusable element was unintentionally skipped.
report: examples/sites/magazine/test-results/a11y-focus-order/index-861ad078/report.mdvlmkit check a11y touch index.htmllevel AA · inspected 26 · failures 0 · wcagExempt 11
output, 6 line(s), 1.6s
vlmkit check a11y touch source: index.html level: WCAG AA (24×24 min) inspected 26 interactive element(s) ✓ 0 undersized target(s) 11 undersized target(s) excused by WCAG 2.5.8 itself (9 in a sentence, 2 with clear spacing) report: examples/sites/magazine/test-results/a11y-touch/index-861ad078/report.md
vlmkit scan scroll index.htmlcontainers 0 · overflowX 0 · issues 0
output, 8 line(s), 1.6s
vlmkit scan scroll source: index.html (1280x720) status: ok page: 1280x7370 — horizontal overflow 0px, vertical scroll 6650px scroll containers: 0 (dead scrollports 0, clipped 0) No scroll issues detected.
vlmkit scan handlers index.htmlregistrations 7 · elements 5 · suspects 0
output, 16 line(s), 1.7s
vlmkit scan handlers source: index.html status: 1 warn(s) exits 0 — 1 warn(s) did not fail this command. To gate on one: --rule unprobed-handler-types=suspect registrations: 7 across 5 element(s) + globals, on a page presenting 26 control(s) - header#top>div.wrap>button#search-toggle "検索": click - header#top>div#search-panel "サイト内検索 検索する 記事のタイトルとセクション名から探します。": keydown (no role) - header#top>div#search-panel>div.wrap>form#search-form "サイト内検索 検索する 記事のタイトルとセクション名から探します。": submit (no role) - main#main>section.after>div.share>button#copy-link "リンクをコピー": click - main#main>section.after>div.share>button#save "保存": click - globals: window:scroll, window:resize Issues: warn [unprobed-handler-types] 3 handler type(s) registered and NONE exercised: click, keydown, submit — this gate is an inventory and presses nothing. A clean result here says the wiring exists, not that it works. Run 'check interactions --handlers' to focus and activate the controls, add '--probe-drag' for the drag surfaces, or drive the rest with a 'verify flow' script.
vlmkit check interactions index.html --handlerselements 26 · suspects 0 · warns 0
output, 49 line(s), 5.9s
vlmkit check interactions source: index.html status: ok exits 0 — 1 warn(s) did not fail this command. To gate on one: --rule unprobed-handler-types=suspect interactive elements: 26 - [link] "本文へスキップ" focus✓ - [link] "ほとり" focus✓ - [link] "特集" focus✓ - [link] "暮らし" focus✓ - [link] "旅" focus✓ - [link] "本" focus✓ - [link] "インタビュー" focus✓ - [button] "検索" focus✓ | Enter: expanded false -> true - [link] "三浦 灯" focus✓ - [link] "前回訪ねた岬の貸本屋" focus✓ - [link] "注1" focus✓ - [link] "注2" focus✓ - [link] "注3" focus✓ - [link] "記事の終わりの店舗情報" focus✓ - [link] "本文へ戻る" focus✓ - [link] "本文へ戻る" focus✓ - [link] "本文へ戻る" focus✓ - [button] "リンクをコピー" focus✓ | Enter: layout change | announces - [button] "保存" focus✓ | Enter: pressed false -> true - [link] "本 連載 第2回 岬の貸本屋に、手紙で本を頼む 2026年8月8日" focus✓ - [link] "旅 終点の駅から、灯台まで歩く 2026年7月25日" focus✓ - [link] "暮らし 潮風と暮らす家の、窓のしつらえ 2026年6月30日" focus✓ - [link] "ほとりについて" focus✓ - [link] "執筆者" focus✓ - [link] "お問い合わせ" focus✓ - [link] "プライバシー" focus✓ vlmkit scan handlers source: index.html status: 1 warn(s) registrations: 7 across 5 element(s) + globals, on a page presenting 26 control(s) - header#top>div.wrap>button#search-toggle "検索": click - header#top>div#search-panel "サイト内検索 検索する 記事のタイトルとセクション名から探します。": keydown (no role) - header#top>div#search-panel>div.wrap>form#search-form "サイト内検索 検索する 記事のタイトルとセクション名から探します。": submit (no role) - main#main>section.after>div.share>button#copy-link "リンクをコピー": click - main#main>section.after>div.share>button#save "保存": click - globals: window:scroll, window:resize Issues: warn [unprobed-handler-types] Handler types registered but NOT exercised by this run's probes: keydown, submit — the probe focuses a control and presses the key its role activates with, so a `click` handler on anything but a native control, and any type outside focus/blur/keyboard, is untested here. Verify those paths with a 'verify flow' script.
vlmkit check scroll index.htmlpageScrolled 1080 · stickyFixed 1 · engagedSticky 0 · snaps 0 · issues 0
output, 12 line(s), 1.4s
vlmkit check scroll source: index.html status: ok page scrolled: 1080px sticky/fixed elements: 1 (sticky engaged by the scroll: 0) snap containers driven: 0 Sticky / fixed: - div.progress: fixed top=0px viewport y 0 -> 0 No scroll-behavior issues detected.
vlmkit check motion index.htmlactiveAnimations 0 · running 0 · issues 0
output, 15 line(s), 1.3s
vlmkit check motion source: index.html status: ok samples: 1 active animations: 0 active transitions: 1 running animations: 0 paused animations: 0 reduced-motion rule: yes Motion samples: - #progress-bar: animation=none 0s running; transition=transform 0.12s No motion issues detected.
vlmkit check animation index.htmlanimations 0 · settleMs 0 · issues 0
output, 8 line(s), 1.5s
vlmkit check animation source: index.html (1280x720) status: ok animations: 0 (evaluated 0, infinite 0) settle: 0ms No animation issues detected.
vlmkit stress i18n index.htmlinflate 1.4 · inspected 152 · overflowing 37
output, 12 line(s), 1.6s
vlmkit stress i18n
html: examples/sites/magazine/index.html inflate: 1.4x
! 37 overflow / wrap issue(s) across 152 inspected element(s)
[vertical-wrap] main#main>article#story>header.story-head.wrap>div.story-head__text>h1#story-title — h 122 → 244
[vertical-wrap] article#story>header.story-head.wrap>div.story-head__text>h1#story-title>span — h 61 → 122
[vertical-wrap] article#story>header.story-head.wrap>div.story-head__text>h1#story-title>span — h 61 → 122
[vertical-wrap] main#main>article#story>header.story-head.wrap>div.story-head__text>p.lede — h 167 → 199
[vertical-wrap] article#story>div#story-body>section.chapter>figure.pullquote>blockquote — h 91 → 137
[vertical-wrap] main#main>article#story>div#story-body>section.chapter>figure.pullquote — h 192 → 238
… 31 more (see the report, or --json for all)
report: examples/sites/magazine/test-results/i18n-stress/report.md
exits 0 — 37 warn(s) did not fail this command. To gate on one: --rule vertical-wrap=suspect.turn (speaker column), .note (number column)stress i18n: the interview speaker labels (汐田X) and the note numbers (注1X) wrap to two lines under inflation, because both sit in fixed 3.2em / 3em grid columns. A three- or four-character speaker name, or a 注10, would break the same way. The prose rows are ordinary wrapping.
vlmkit check breakpoints index.html --sweepbreakpoints 4 · issues 0 · sweepRanges 0
output, 13 line(s), 3.6s
vlmkit check breakpoints source: index.html status: ok width sweep: 320-1280px step 25 (39 widths) — clean breakpoints checked: 699, 700, 999, 1000px (each at B-1 / B / B+1) 699px: clean (max-width: 699px) 700px: clean (min-width: 700px) 999px: clean (max-width: 999px) 1000px: clean (min-width: 1000px) All boundaries consistent.
hero art close-up
index.html · 1280×800 · element .art · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Hero illustration at 2x (1240px). Reads well overall: star field, two hill layers with tiny house lights, the lit two-storey shop with the 宵待書房 fascia and a lamp glow above it, three shelf rows of books, a customer silhouette in the second glass panel, the 古本 hanging board, the 日の入 17:58 chalkboard, the 100-yen crate, a bicycle, a street lamp, the lighthouse with its beam on the breakwater, and warm reflections on the harbour. Defects: (1) the bright white sliver at the left end of the quay (x≈240-248, y≈980-1030 in this 2x frame) is the page's paper showing through: nothing is painted between the quay's slanted left edge and the water, and the sky rect ends at y=480. (2) The three bare bulbs are drawn exactly under the door mullions (x 256, 321, 386), so the bulbs themselves are hidden and only their glows show; the middle glow sits about 4px higher than the other two, which is the circle check integrity G1 flagged as near-misaligned. The middle bulb was meant to hang on a shorter cord, but at 4px it looks accidental, not designed. (3) A small dark dome (a bollard) sits in the middle of the light spill in front of the door and reads as a stray blob. (4) The quay's front face shows as an outlined box: a light hairline runs along its bottom edge against the water.
.art__svg (quay, left end)S3: the white sliver at the quay's left end is the page's paper showing through: nothing is painted between the quay's slanted left edge and the water, and the sky rect stops at y=480.
.art__svg (storefront bulbs)S3: the three bare bulbs are drawn exactly under the door mullions (x 256/321/386), so the bulbs themselves are hidden and only their glows show.
.art__svg (light spill)S3: a small dark bollard dome sits in the middle of the lit spill in front of the door and reads as a stray blob.
.art__svg (quay face)S3: the quay's front face shows as an outlined box: a light hairline runs along its bottom edge where it meets the water.
copy link pressed
index.html · 1280×800 · click #copy-link · wait 300
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
After clicking リンクをコピー (desktop viewport, scrolled to the share row). The confirmation is there and legible: an accent check mark followed by bold ink text 「リンクをコピーしました」, sitting on the same row to the right of the 保存 pill, so the row does not grow or shift at this width. The clicked button shows its light hover fill (the pointer is still over it) and no focus ring, which is right for a mouse click. The progress bar across the top is full, since the article ends above this point. Notes 注1-3 are above with their 本文へ戻る links; the author card and the start of the related band are below. Nothing looks broken in this state. So the copy the gate called missing (G2) is rendered and visible once the action happens.
body paragraph close-up
index.html · 1280×800 · element #story-body > section:first-child > p:nth-of-type(2) · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
2x close-up of section 1, paragraph 2 (body text, set in the serif stack). Justified at 1.9 leading; the accent link 前回訪ねた岬の貸本屋 has an underline that clears the glyphs. But the body face is visibly a different Gothic from the headings in S6: the kana here are wider and rounder (compare が, と, る, and the heavier ず/ぎ dakuten). None of the fonts named in the serif stack exist on this machine, so the text falls through to the generic serif, and fc-match serif:lang=ja resolves that to WenQuanYi Zen Hei, a Chinese font. The page therefore mixes two Gothic designs (IPAGothic headings over WenQuanYi body), and the body kanji come from a Chinese font rather than a Japanese one. No clipping and no collisions in the paragraph.
font stack (--serif)S5 vs S6: the body text is in a different Gothic from the headings. No font named in the serif stack exists here, so the generic serif resolves (fc-match serif:lang=ja) to WenQuanYi Zen Hei, a Chinese font: the page mixes two Gothic designs, and the body gets Chinese glyph forms.
h2 close-up
index.html · 1280×800 · element #ch1 · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
2x close-up of the first h2, 日が沈むと、シャッターが上がる. The 36px accent rule sits 20px above the heading; the heading is IPAGothic (the sans stack names it) with synthetic bold, tracked at .06em. Glyphs are crisp and the ideographic comma sits correctly in the lower left of its cell. At desktop width the heading is one line. The only fault in this frame is by comparison with S5: the body text below it is not in this typeface.
copy-missing 'リンクをコピーしました' is state copy: the brief makes it the confirmation that appears in a live region after リンクをコピー is pressed, so at rest the region is empty by design. A pre-filled region would announce nothing and would show a confirmation for an action nobody took. The gate's sweep reveals details, tabs and aria-expanded controls, and this is none of those. S4/L4 shows the line rendered, visible and verbatim after the click, and check interactions G11 records the button as 'announces'. I will also prove it deterministically with a verify flow post-condition.
All four focus-order findings are artifacts of comparing viewport coordinates across scrolls. The report's own table records each stop's position after the browser scrolled it into view: steps 9-16 run from section 1 to the notes, about 4,000px of page, yet all sit between y=350 and 505. reverse 11->12 (注2 x=927 -> 注3 x=395, 'same row') are 1,700px apart in the document; reverse 18->19 (#save y=612 -> #related-1 y=188, 'moved up') goes DOWN the page: S1-8/S1-9 show the share row above the related band. skip-row 7->8 (検索 -> byline) and 21->22 (last card -> footer nav) skip nothing: S1-1 shows only the non-interactive title, lede and illustration between the first pair, and S1-10 only the band's padding between the second. The tab order is DOM order, and DOM order is the visual order.
check a11y focus compares focus stops in viewport coordinates captured after each auto-scroll, so on any page taller than the viewport it reports 'reverse' (a suspect, exit 1) for forward moves. Verbatim: '[reverse] Focus moved up by 424px (from main#main>section.after>div.share>button#save at y=612 to section.related>div.wrap>ul.related__list>li#related-1>a.card__link at y=188)' and '[reverse] Focus moved left within the same row (from ...a#ref-2 at x=927 to ...a#ref-3 at x=395)', where the two footnote links are 1,700px apart in the document. Adding scrollY (document coordinates) to each captured position would remove both.
The lede is no longer justified: word-break: auto-phrase (phrase-level Japanese line breaking) and text-wrap: pretty, so lines break between phrases and a name stays whole.
The sans stack now names IPAPGothic (proportional Latin and digits) instead of IPAGothic; there is a comment in style.css saying why.
The shelf sunbeam is now a gradient polygon that fades from .26 to 0 across the shelf (f-shelfbeam), with no flat-opacity shape and no hard vertical edge.
The pull quote uses word-break: auto-phrase + text-wrap: balance, so it breaks between phrases into lines of even length.
Removed the <small> from the © line; it now renders at the footer's 12px like the note above it.
text-wrap: pretty on body paragraphs, interview utterances and the figure caption, so the browser rebalances the last lines instead of leaving one or two characters alone.
Below 700px the tagline is hidden, so the masthead is two rows: wordmark with 検索 on the right, then the section nav.
Below 700px the byline takes its own line and the date loses its leading separator, so the second line reads '2026年9月12日 | 読了時間 約8分' with the bar only between the two items.
Chapter h2s use word-break: auto-phrase + text-wrap: balance.
Replaced the back link's margin-left with a plain space in the markup ('。 <a class=backref>'), which collapses at the start of a line, so a wrapped 本文へ戻る now starts flush with the note text.
Below 700px the この記事を label takes its own line, both buttons share the next, and the confirmation wraps below them.
Below 700px each related card is a two-column grid (112px thumbnail, then category, title and date), and the band's padding and margin are reduced to the 64/40/56 steps.
The hero SVG now starts with a full 640x640 night rect under everything, and the quay's left edge is vertical at x=124, where the water begins, so no gap is left unpainted.
The bulbs now hang in the middle of the four glass panels (x 224/288/354/418), clear of the mullions, each with a small halo; the customer moved to the third panel.
All four bulbs hang at one height (cy 389, cords 374-385): I chose alignment over a 4px variation that only reads as a mistake.
Removed both bollards; the light spill in front of the door is clean.
The quay face now overlaps the water by 1px on the full-frame background, and the edge highlight is darker and 5px tall, so no light seam outlines the face.
The serif stack adds IPAPMincho, then IPAGothic as the last named face before the generic, so a device with no Japanese serif renders Japanese glyph forms in the same design as the headings instead of whatever the generic resolves to (a Chinese font here). There is a comment in style.css saying why.
The h1 is a single text node again; the two-line break comes from word-break: auto-phrase + text-wrap: balance instead of two inline-block spans.
注, the author's name and 関連する記事 share one secondary-head rule (20px/700, .08em); the four chapter heads keep 25px, so h2 has two styles, each used at least 3x.
The shop-info name is 17px like the card titles.
Masthead padding is 20px top and bottom, and the related band's margin-top is 96px (64px below 700px).
The shop-info name margin is now 6px above and 10px below, so it sits closer to the list it labels than the rule's threshold allows it to drift upward.
Card title margin is 6px 0 2px and the date is a block at 1.5 line-height, so the title is 2px from its date and about 12px from its label.
The interview and the notes are each one grid (minmax(3em, max-content) then 1fr) with subgrid rows, so the label column grows to the longest speaker name or note number and every row stays aligned; labels are white-space: nowrap. The notes' :target highlight now uses side box-shadows instead of padding and a negative margin.
A footnote reference that follows ruby gets .4em of left margin (ruby + .fnref); other references keep .12em.
R2 · fix pass
buildervlmkit check integrity index.htmlverdict: CLEAN (0 fail, 0 warn, 0 exempted)
verdict clean · findings 0 · fails 0 · warns 0 · exempted 0 · viewports 3
output, 9 line(s), 7.5s
vlmkit check integrity source: index.html verdict: CLEAN (0 fail, 0 warn, 0 exempted) 1280x800: 1 component(s), ink 93.8%, 106 text block(s) 768x900: 1 component(s), ink 92.7%, 106 text block(s) 375x700: 1 component(s), ink 92.9%, 105 text block(s) No integrity defects detected.
vlmkit check copy index.html --manifest copy.txtmissing 1 · placeholders 0 · manifestLines 8 · statesExplored 1 · revealedOnly 0
output, 10 line(s), 1.5s
vlmkit check copy source: index.html status: suspect rendered text: 3461 chars disclosure states: 1 explored (details / tabs / aria-expanded) manifest: 8 line(s), missing 1 Issues: x copy-missing: Manifest line not found in rendered text or any of 1 revealed disclosure state(s): "リンクをコピーしました" (comparison is whitespace-normalized, case-sensitive).
vlmkit check design index.htmlverdict: COHERENT (1 finding(s))
verdict coherent · drifting 0 · roles 5
output, 23 line(s), 2.2s
vlmkit check design
source: index.html
verdict: COHERENT (1 finding(s))
roles judged: 3 of 5 seen, spacing values: 23
coverage: 17 of 697 visible element(s) carried an inferable role
no role: rect x397, p x41, path x38, circle x36, g x27, a x23, +26 more tag(s)
A role comes from role="..." or from button/input/select/textarea/h1-h6. Layout elements
(div, span, p, a) have none, so a large skip count is normal — this gate judges components,
not every box. Add role="..." where an element IS a component to widen the coverage.
Role reuse (instances / distinct styles; drift below 3x from 3 instances)
h2 7 inst 2 styles reuse 3.5x 0 one-off ok
h3 4 inst 1 styles reuse 4x 0 one-off ok
button 3 inst 1 styles reuse 3x 0 one-off ok
img 2 inst 2 styles reuse 1x 2 one-off not judged
h1 1 inst 1 styles reuse 1x 1 one-off not judged
not judged: img (2), h1 (1) — under --min-instances 3, so no finding can come from them.
To keep judging them: --min-instances 2 --min-reuse 2 (both — a 2-instance role cannot reach 3x however many instances the floor allows).
Informational (true, but does not carry the verdict)
i [scale-outlier]: 1 spacing value(s) sit just off the page's own scale: 18px (2x) next to 16px (13x) — header#top>div.wrap>div.brand rowGap. Snap them to the established value or add them deliberately.div.brand gapcheck design scale-outlier (info): the brand block's 18px gap (wordmark to tagline) sits next to the 16px step used 13 times.
vlmkit check composition index.htmlverdict: COMPOSED (0 finding(s))
verdict composed · proximity 0 · flatSteps 0 · railNearMisses 0
output, 17 line(s), 1.7s
vlmkit check composition source: index.html verdict: COMPOSED (0 finding(s)) measured: 8 label(s), 3 heading level(s), 99 wide block(s) on 9 left / 6 right rail(s) — from 183 visible box(es) 4 label(s) not judged: they open a box that paints its own group edge, so nothing above them could be mis-grouped with Type hierarchy (largest instance of each declared level) h1 42px / 700 h1#story-title h2 25px / 700 h2#ch1 h3 17px / 700 h3#shop-info-title body 14px, largest 42px (3x), heaviest +0 weight Group gaps: 12.95px between heading-led groups vs 21.5px inside them (0.6x, 12 sample(s)). Context only — intact pages measure 0.86-3.00x, so this cannot carry a verdict. Proximity, alignment and type hierarchy all read consistently.
vlmkit stress i18n index.html --jsoninflate 1.4 · inspected 148 · overflowing 34
output, 1350 line(s), 1.6s
{
"gate": "stress.i18n",
"command": "stress i18n",
"verdict": "pass",
"counts": {
"suspect": 0,
"warn": 34,
"info": 0
},
"findings": [
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 476px -> 476px (scroll 476px) — \"海辺の町の古本屋が、夜だけ店を開ける理由XXXXXXXX\"",
"evidence": {
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>h1#story-title",
"tag": "h1",
"before": {
"width": 476,
"height": 121.78125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 182.671875,
"clientWidth": 476,
"scrollWidth": 476
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 476px -> 476px (scroll 476px) — \"文・写真XX 三浦X 灯X\n 2026年9月12日XXXX\n 読了時間XX 約8分X\"",
"evidence": {
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.meta",
"tag": "p",
"before": {
"width": 476,
"height": 39.09375,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 67.1875,
"clientWidth": 476,
"scrollWidth": 476
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 476px -> 476px (scroll 476px) — \"港の灯りがひとつ、またひとつとともりはじめるころ、XXXXXXXXXX凪浦XなぎうらXXの商店街のはずれで、一軒の古本屋\"",
"evidence": {
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.lede",
"tag": "p",
"before": {
"width": 476,
"height": 165.703125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 198.84375,
"clientWidth": 476,
"scrollWidth": 476
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>figure.pullquote>blockquote",
"tag": "blockquote",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>figure.pullquote",
"tag": "figure",
"before": {
"width": 694,
"height": 192.09375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 237.59375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"三浦X 夜には、どんなお客さんが来るんですか。XXXXXXXX\n 汐田X いろいろですよ。夜勤に向かう\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>div.interview",
"tag": "div",
"before": {
"width": 694,
"height": 500.875,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 603.4375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 48px -> 48px (scroll 48px) — \"注1X\"",
"evidence": {
"path": "article#story>section#notes>ol.notes__list>li#note-1>span.note__num",
"tag": "span",
"before": {
"width": 48,
"height": 51.78125,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 77.671875,
"clientWidth": 48,
"scrollWidth": 48
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 632px -> 632px (scroll 632px) — \"まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に開いてい\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 632px -> 632px (scroll 632px) — \"いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖にいるで\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"汐田X まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"汐田X いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"注1X黒板の日の入り時刻は、汐田さんが毎朝、新聞の暦の欄で確かめて書き直している。九月なかばの凪浦では、日の入りは一日に\"",
"evidence": {
"path": "main#main>article#story>section#notes>ol.notes__list>li#note-1",
"tag": "li",
"before": {
"width": 694,
"height": 71.78125,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 97.671875,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 48px -> 48px (scroll 48px) — \"汐田X\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 48px -> 48px (scroll 48px) — \"汐田X\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX\"",
"evidence": {
"path": "div#story-body>section.chapter>figure.pullquote>blockquote>p",
"tag": "p",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"零時ちょうど、汐田さんは電球をひとつずつ消していき、最後に入口の明かりを落とした。私は背の焼けていない詩集を一冊抱えて、\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"帰りぎわ、閉店をなぜ零時にしているのかを尋ねた。汐田さんは少し考えてから、「日付が変わるまでは、今日のうちでしょう」と言\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"店の外には、百円均一の文庫を並べた木箱がひとつ出ている。この箱だけは昼も夜も出しっぱなしで、代金は横に置いた缶に入れても\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"「夜の本屋は、灯台みたいなものだと思うの」と汐田さんは言う。「誰かが起きている明かりが、町にひとつあればいい。本を買わな\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 205.125,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"十一時を過ぎると、客はさすがにまばらになる。汐田さんは帳場で値札を書きながら、ときどき顔を上げて通りのほうを見る。視線の\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜に売れる本には傾向がある、と汐田さんは言う。昼に比べて、詩集や随筆、古い旅行記がよく出る。反対に、実用書は昼の店ほどに\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"話しているあいだにも、客足は途切れなかった。作業着のまま文庫の棚の前にしゃがみこむ青年がいて、参考書の棚を行ったり来たり\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"ところが夜に店を開けてみると、昼には一度も見かけなかった客がやってくるようになった。最初の晩に来たのは、これから夜勤に向\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"そこで汐田さんは、昼のあいだはシャッターを下ろし、除湿機を回して本を休ませることにした。日の光が届かなくなってから店を開\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"見せてもらった文庫本は、表紙には深い緑が残っているのに、背だけが白茶けた砂の色になっていた。本を傷めるのは光だけではない\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"「うちは店が西を向いているでしょう。午後になると、海に照り返した光まで店の奥に入ってくるの。父の棚を片づけていたら、窓ぎ\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"汐田さんが店を夜だけ開けるようになったのは、十二年前に父親から店を継いでからだ。先代は朝十時に店を開けて夕方六時に閉める\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"店内は奥行きが深く、左右の壁と中央の平台に、ざっと一万冊の本が並ぶ。文庫と新書が半分、残りは詩集や郷土史、古い旅行記。港\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"十七時五十八分。岸壁に腰を下ろして待っていると、水平線に残っていた橙色が消え、背中のほうで、がらがらとシャッターの巻き上\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 205.125,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"シャッターの脇には小さな黒板が掛かっていて、白いチョークで「本日の日の入りXXXXXXXXXXXXXXX 十七時五十八分\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"商店街がとぎれ、岸壁が見えてくる手前の角に、その店はある。宵待書房。間口二間ほどの木造二階建てで、昼間に通りかかった人に\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"九月のなかば、夕方の凪浦駅に降りた。駅前から海へまっすぐ延びる商店街は、五時を過ぎると半分ほどの店が戸を閉める。魚屋が店\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 638px -> 638px (scroll 638px) — \"黒板の日の入り時刻は、汐田さんが毎朝、新聞の暦の欄で確かめて書き直している。九月なかばの凪浦では、日の入りは一日に一分ほ\"",
"evidence": {
"path": "article#story>section#notes>ol.notes__list>li#note-1>p",
"tag": "p",
"before": {
"width": 638,
"height": 51.78125,
"clientWidth": 638,
"scrollWidth": 638
},
"after": {
"width": 638,
"height": 77.671875,
"clientWidth": 638,
"scrollWidth": 638
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 206px -> 694px (scroll 0px) — \"前回訪ねた岬の貸本屋XXXX\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>p>a",
"tag": "a",
"before": {
"width": 205.921875,
"height": 18,
"clientWidth": 0,
"scrollWidth": 0
},
"after": {
"width": 694,
"height": 52.1875,
"clientWidth": 0,
"scrollWidth": 0
}
}
}
],
"suppressed": [],
"retuned": [],
"report": {
"html": "examples/sites/magazine/index.html",
"inflateFactor": 1.4,
"beforeScreenshot": "examples/sites/magazine/test-results/i18n-stress/before.png",
"afterScreenshot": "examples/sites/magazine/test-results/i18n-stress/after.png",
"overflowing": [
{
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>h1#story-title",
"tag": "h1",
"text": "海辺の町の古本屋が、夜だけ店を開ける理由XXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 476,
"height": 121.78125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 182.671875,
"clientWidth": 476,
"scrollWidth": 476
}
},
{
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.meta",
"tag": "p",
"text": "文・写真XX 三浦X 灯X\n 2026年9月12日XXXX\n 読了時間XX 約8分X",
"kind": "vertical-wrap",
"before": {
"width": 476,
"height": 39.09375,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 67.1875,
"clientWidth": 476,
"scrollWidth": 476
}
},
{
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.lede",
"tag": "p",
"text": "港の灯りがひとつ、またひとつとともりはじめるころ、XXXXXXXXXX凪浦XなぎうらXXの商店街のはずれで、一軒の古本屋",
"kind": "vertical-wrap",
"before": {
"width": 476,
"height": 165.703125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 198.84375,
"clientWidth": 476,
"scrollWidth": 476
}
},
{
"path": "article#story>div#story-body>section.chapter>figure.pullquote>blockquote",
"tag": "blockquote",
"text": "夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>figure.pullquote",
"tag": "figure",
"text": "夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 192.09375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 237.59375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>div.interview",
"tag": "div",
"text": "三浦X 夜には、どんなお客さんが来るんですか。XXXXXXXX\n 汐田X いろいろですよ。夜勤に向かう",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 500.875,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 603.4375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "article#story>section#notes>ol.notes__list>li#note-1>span.note__num",
"tag": "span",
"text": "注1X",
"kind": "vertical-wrap",
"before": {
"width": 48,
"height": 51.78125,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 77.671875,
"clientWidth": 48,
"scrollWidth": 48
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"text": "まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に開いてい",
"kind": "vertical-wrap",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"text": "いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖にいるで",
"kind": "vertical-wrap",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
},
{
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"text": "汐田X まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"text": "汐田X いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>section#notes>ol.notes__list>li#note-1",
"tag": "li",
"text": "注1X黒板の日の入り時刻は、汐田さんが毎朝、新聞の暦の欄で確かめて書き直している。九月なかばの凪浦では、日の入りは一日に",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 71.78125,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 97.671875,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"text": "汐田X",
"kind": "vertical-wrap",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"text": "汐田X",
"kind": "vertical-wrap",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
},
{
"path": "div#story-body>section.chapter>figure.pullquote>blockquote>p",
"tag": "p",
"text": "夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "零時ちょうど、汐田さんは電球をひとつずつ消していき、最後に入口の明かりを落とした。私は背の焼けていない詩集を一冊抱えて、",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "帰りぎわ、閉店をなぜ零時にしているのかを尋ねた。汐田さんは少し考えてから、「日付が変わるまでは、今日のうちでしょう」と言",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "店の外には、百円均一の文庫を並べた木箱がひとつ出ている。この箱だけは昼も夜も出しっぱなしで、代金は横に置いた缶に入れても",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "「夜の本屋は、灯台みたいなものだと思うの」と汐田さんは言う。「誰かが起きている明かりが、町にひとつあればいい。本を買わな",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 205.125,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "十一時を過ぎると、客はさすがにまばらになる。汐田さんは帳場で値札を書きながら、ときどき顔を上げて通りのほうを見る。視線の",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "夜に売れる本には傾向がある、と汐田さんは言う。昼に比べて、詩集や随筆、古い旅行記がよく出る。反対に、実用書は昼の店ほどに",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "話しているあいだにも、客足は途切れなかった。作業着のまま文庫の棚の前にしゃがみこむ青年がいて、参考書の棚を行ったり来たり",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "ところが夜に店を開けてみると、昼には一度も見かけなかった客がやってくるようになった。最初の晩に来たのは、これから夜勤に向",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "そこで汐田さんは、昼のあいだはシャッターを下ろし、除湿機を回して本を休ませることにした。日の光が届かなくなってから店を開",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "見せてもらった文庫本は、表紙には深い緑が残っているのに、背だけが白茶けた砂の色になっていた。本を傷めるのは光だけではない",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "「うちは店が西を向いているでしょう。午後になると、海に照り返した光まで店の奥に入ってくるの。父の棚を片づけていたら、窓ぎ",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "汐田さんが店を夜だけ開けるようになったのは、十二年前に父親から店を継いでからだ。先代は朝十時に店を開けて夕方六時に閉める",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "店内は奥行きが深く、左右の壁と中央の平台に、ざっと一万冊の本が並ぶ。文庫と新書が半分、残りは詩集や郷土史、古い旅行記。港",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "十七時五十八分。岸壁に腰を下ろして待っていると、水平線に残っていた橙色が消え、背中のほうで、がらがらとシャッターの巻き上",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 205.125,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "シャッターの脇には小さな黒板が掛かっていて、白いチョークで「本日の日の入りXXXXXXXXXXXXXXX 十七時五十八分",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "商店街がとぎれ、岸壁が見えてくる手前の角に、その店はある。宵待書房。間口二間ほどの木造二階建てで、昼間に通りかかった人に",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "九月のなかば、夕方の凪浦駅に降りた。駅前から海へまっすぐ延びる商店街は、五時を過ぎると半分ほどの店が戸を閉める。魚屋が店",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "article#story>section#notes>ol.notes__list>li#note-1>p",
"tag": "p",
"text": "黒板の日の入り時刻は、汐田さんが毎朝、新聞の暦の欄で確かめて書き直している。九月なかばの凪浦では、日の入りは一日に一分ほ",
"kind": "vertical-wrap",
"before": {
"width": 638,
"height": 51.78125,
"clientWidth": 638,
"scrollWidth": 638
},
"after": {
"width": 638,
"height": 77.671875,
"clientWidth": 638,
"scrollWidth": 638
}
},
{
"path": "article#story>div#story-body>section.chapter>p>a",
"tag": "a",
"text": "前回訪ねた岬の貸本屋XXXX",
"kind": "vertical-wrap",
"before": {
"width": 205.921875,
"height": 18,
"clientWidth": 0,
"scrollWidth": 0
},
"after": {
"width": 694,
"height": 52.1875,
"clientWidth": 0,
"scrollWidth": 0
}
}
],
"totalInspected": 148,
"reportPath": "examples/sites/magazine/test-results/i18n-stress/report.md"
}
}vlmkit verify flow index.html --flow flow.jsonverdict: FAILED (4/7 steps)
done false · passed 4 · total 7
output, 22 line(s), 1.8s
vlmkit verify flow
source: index.html
verdict: FAILED (4/7 steps)
✓ step 1: 検索 opens the search panel and moves focus into the field
✓ #search-toggle aria-expanded=true
✓ #search-panel visible
✓ #search-input focused
✓ step 2: type a section name
✓ #search-input visible
✓ step 3: Enter submits the form and lists the two 本 articles
✓ #search-status text~"「本」に一致する記事が2件あります。"
✓ #search-results li count=2
✓ step 4: Escape closes the panel and returns focus to the 検索 button
✓ #search-panel hidden
✓ #search-toggle aria-expanded=false
✓ #search-toggle focused
✗ step 5: リンクをコピー shows the confirmation in the live region
✗ #share-status text~"リンクをコピーしました" (got: )
✗ #share-status visible (got: hidden/absent)
✓ #share-status role=statusvlmkit verify flow index.html --flow flow.jsonverdict: DONE (8/8 steps)
done true · passed 8 · total 8
output, 27 line(s), 2.0s
vlmkit verify flow
source: index.html
verdict: DONE (8/8 steps)
✓ step 1: 検索 opens the search panel and moves focus into the field
✓ #search-toggle aria-expanded=true
✓ #search-panel visible
✓ #search-input focused
✓ step 2: type a section name
✓ #search-input visible
✓ step 3: Enter submits the form and lists the two 本 articles
✓ #search-status text~"「本」に一致する記事が2件あります。"
✓ #search-results li count=2
✓ step 4: Escape closes the panel and returns focus to the 検索 button
✓ #search-panel hidden
✓ #search-toggle aria-expanded=false
✓ #search-toggle focused
✓ step 5: リンクをコピー is pressed; the live region exists before anything is written to it
✓ #share-status role=status
✓ step 6: within 100ms (the clipboard promise has settled) the confirmation is shown
✓ #share-status text~"リンクをコピーしました"
✓ #share-status visible
✓ step 7: 保存 toggles on
✓ #save aria-pressed=true
✓ step 8: 保存 toggles off again
✓ #save aria-pressed=falseG22's step 5 asserted the confirmation in the same instant as the click, but the page writes it only after navigator.clipboard.writeText() settles (resolved, or rejected and then copied through the execCommand fallback); claiming コピーしました before the copy happened would be wrong. G23 splits the step: click (the live region already exists, role=status), then wait 100ms, then assert. It passes, so the confirmation lands within 100ms. The page did not change between G22 and G23; only the flow did.
G23 (verify flow, steps 5-6) is the deterministic proof for the one manifest line check copy cannot reach: after リンクをコピー, #share-status contains リンクをコピーしました and is visible. With S4/L4 as the visual evidence, the line is covered both ways.
scan handlers is an inventory, and its unprobed-handler-types warn says so. The click handlers are exercised by check interactions --handlers (G11: 検索 expanded false->true, リンクをコピー announces, 保存 pressed false->true); the search panel's keydown (Escape) and the form's submit, the two types G11 could not reach, are exercised by verify flow G23 steps 3-4.
unprobed-handler-types (keydown, submit) is closed by verify flow G23: step 3 submits the search form with Enter and gets the result status plus 2 results; step 4 presses Escape inside the panel and gets the panel hidden, aria-expanded=false and focus back on 検索.
index.html · 1280×800 · full page, 10 screen(s)
Desktop 1280, full page after the fix pass, 10 screens. S7-1: title breaks after 、 as before, now from auto-phrase + balance on a single text node. The lede is ragged-right and breaks between phrases: …ともりはじめるころ、凪浦の / 商店街のはずれで、… / 店主の汐田律子さんをたずねた。, so the name is whole. Meta digits are proportional now (2026 年 9 月 12 日 no longer typewritten). New problem: every sans run is now IPAPGothic, whose 、。() are half-width, so headings and UI text look cramped at the punctuation: 日が沈むと、シャッター (S7-2), 西日から、本の (S7-3), 零時に、明かり (S7-6), 宵待書房(よいまち… and 水曜日、海が (S7-7/S7-8), the notes and the author bio read dense, and the card titles 貸本屋に、手紙 (S7-9). S7-3: section 2's first paragraph still ends on a lone 「た。」: text-wrap: pretty changed nothing there. S7-4: the shelf sunbeam now fades out across the shelf with no hard edge; the caption reads cleanly. S7-5: interview rows are aligned on one speaker column (labels one line each), and 漁火 注3 now has clear air between the ruby and the reference. S7-6: pull quote breaks between phrases (…思うの。誰かが / 起きている明かりが、…). S7-8: the share row, notes and author card are intact; the author name is now the same 20px secondary head as 注 and 関連する記事. S7-9/S7-10: card title sits 2px over its date with a larger gap to its label; the © line is 12px and readable.
font stack (--sans)Regression from F2: IPAPGothic sets 、。() half-width, so every sans run looks cramped at its punctuation: 日が沈むと、シャッター, 西日から、本の, 零時に、明かり (S7-2/3/6), 宵待書房(よいまち and 水曜日、海が (S7-7/8), and the notes and bio read dense. S10 shows the heading's comma next to the full-width commas of the body.
index.html · 375×812 · full page, 11 screen(s)
Mobile 375, full page after the fix pass, 11 screens (was 13). S8-1: the masthead is two rows: wave + ほとり with the 検索 pill top-right, then the nav; the tagline is gone at this width. Title is two clean lines, and the lede breaks between phrases (one imperfect split: ともりは / じめるころ). S8-2: meta is 文・写真 三浦 灯 on line 1 and 2026年9月12日 | 読了時間 約8分 on line 2, with no leading bar. The first h2 now fits one line, but only because IPAPGothic is narrower; it has not been tested wrapping. Body paragraphs still end on short lines: いく。 (S8-2), いる。 (S8-4), いた。 (S8-5, the interview intro), so D6 is not fixed. S8-6: interview column aligned; 漁火 注3 separated. S8-7: the pull quote sets as four phrase lines (夜の本屋は、灯台みたいな / ものだと思うの。誰かが / 起きている明かりが、 / 町にひとつあればいい。) with no lone い。. S8-10: every 本文へ戻る follows its note text on the same line with an ordinary space and no indent; the share row is この記事を on its own line, then リンクをコピー and 保存 side by side. S8-11: related is now a compact list (112px thumbnails, label, title, date), about 330px instead of 1,100px, but two titles wrap with widows: 岬の貸本屋に、手紙で本を頼 / む and 潮風と暮らす家の、窓のしつ / らえ. Footer © readable.
h3.card__title (mobile)S8-11: in the compact related list, two card titles wrap with widows: 岬の貸本屋に、手紙で本を頼 / む and 潮風と暮らす家の、窓のしつ / らえ. The h3s never got the phrase-level wrapping the h1/h2 got.
hero art close-up, after fixes
index.html · 1280×800 · element .art · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Hero illustration at 2x after the art fixes. The quay's left end is now a clean vertical edge against the dark water, and nowhere does the page's paper show through (the full-frame night rect sits under everything). Four bare bulbs hang in the centres of the four glass panels, all at one height on equal cords, each with a small halo, and none is behind a mullion; the customer silhouette now stands in the third panel under a bulb. The light spill in front of the door is clean (no bollard blob). The quay face reads as a dark band under a soft edge highlight, and there is no light seam along its bottom edge. The lighthouse, fascia, chalkboard, crate, bicycle and street lamp are unchanged and read well. I see nothing wrong in this frame.
section 1 close-up, heading + body
index.html · 1280×800 · element #story-body > section:first-child · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Section 1 at 2x: the first h2 and four paragraphs. The body text is now in the same Japanese design as the heading (IPAGothic via the serif stack's last named face): the kana match the heading's shapes, and the punctuation is full-width, so なかば、夕方 and 手前の角に、その have a comfortable cell after each 、. The accent link underline and the 注1 superscript are crisp. The heading directly above is IPAPGothic and shows the problem next to that body text: 日が沈むと、シャッター has a half-width comma that nearly touches シ, noticeably tighter than every comma in the paragraphs below. Paragraph 1 ends on a 13-character line here, which is fine.
S7-1: the lede breaks between phrases and ends 店主の汐田律子さんをたずねた。 with the name whole.
S7-4: the sunbeam fades across the shelf; no hard vertical edge.
S8-7 (mobile): four phrase lines with no lone い。; S7-6 (desktop): …誰かが / 起きている… with no break inside a word.
S7-10: the © line renders at 12px and reads cleanly.
S8-1: two-row masthead, 検索 top-right beside the wordmark.
S8-2: byline alone on line 1; '2026年9月12日 | 読了時間 約8分' on line 2 with no leading bar.
S8-10: every 本文へ戻る follows its note text after an ordinary space, with no indent anywhere; a space cannot indent a wrapped line, because it collapses at line start.
S8-10: この記事を on its own line, both buttons side by side on the next.
S8-11: related is a compact thumbnail list of about 330px instead of 1,100px of stacked images (the title widows it exposed are a separate defect).
S9: the quay's left end is a clean vertical edge; no paper shows through.
S9: four bulbs visible in the panel centres, clear of the mullions.
check integrity is CLEAN at 1280/768/375: no near-misalignment; S9 shows the four bulbs on one line.
S9: the light spill in front of the door is clean.
S9: the quay face shows no light seam along its bottom edge.
S10: the body kana and punctuation now match the heading's Japanese design; no Chinese fallback.
check copy no longer reports the title: 7 of 8 manifest lines are found visibly, and only the state-only confirmation line remains (see N2, N6).
check design: h2 7 instances, 2 styles, 3.5x reuse, ok.
check design: h3 4 instances, 1 style, 4x reuse, ok.
22px and 88px are gone from the scale-outlier list; the one remaining info (the brand gap, 18px) is new and raised separately.
check composition: COMPOSED, 0 findings; the shop-info heading is no longer reported.
check composition: COMPOSED, 0 findings; none of the three card titles is reported.
S7-5: 漁火 注3 now has clear air between the ruby and the reference (also S8-6 on mobile).
Correction to D25's premise: the labels never wrapped. G15 already listed b.speaker at 51x103 and span.note__num at 42x52 before any inflation. Those are grid items stretched to their row (the utterance or note beside them runs 2-4 lines), and the rule counts a taller box as a gained line. G21, after the fix, still lists 汐田X and 注1X at 48px for the same reason. I keep the fix because the old fixed 3.2em/3em columns really would wrap a four-character name or 注10, and the subgrid makes one column for all rows, but it did not change, and could not change, what G21 reports.
Two kinds of vertical-wrap row. (1) b.speaker (汐田X) and span.note__num (注1X), 48px wide: not wrapping. They are stretched grid cells whose height follows the text beside them (see the D25 correction), and they are white-space: nowrap. (2) The other 32 rows (title, lede, meta, pull quote, utterances, paragraphs, notes, the inline link) are prose gaining lines under 1.4x inflation, which is what prose should do. None of them overflows or leaves its parent: 0 horizontal-overflow and 0 extends-beyond-parent.
stress i18n's vertical-wrap infers 'gained a line' from box height, so a grid or flex item stretched by a taller sibling reports as wrapping although its own text is one line. Verbatim: '[vertical-wrap] div#story-body>section.chapter>div.interview>p.turn>b.speaker — 汐田X 51x103 → 51x137 +34px' (G15), where the label is one 13px line in a 103px cell. Comparing line count (height / line-height of the element's own text), or the text's client rects, instead of the box height would separate the two.
S7-5 / S8-6: the interview is one grid, every utterance starts on the same column, and the labels are one line each (the column is minmax(3em, max-content)); the notes list keeps 注1-3 on one column too (S7-8).
F6 did not work: text-wrap: pretty changed none of the short last lines in Chromium. S7-3 still ends 営んでい / た。 at desktop, and S8-2/S8-4/S8-5 still end on いく。 / いる。 / いた。 at mobile, probably because Chromium's pretty looks only for a one-word orphan and a two-kana tail is not one. The second attempt is F-tail below: a fixed 4-character tail per paragraph in white-space: nowrap.
Second attempt (F6 did not work): the last four visible characters of each body paragraph, interview utterance and the figure caption are wrapped in <span class=tail> with white-space: nowrap (26 tails, made by a one-off script; a tail that would end inside an element takes the whole element, such as the 注1 <sup>). No paragraph can end on a line shorter than four characters; the line above absorbs at most three characters' worth of space as justification.
The sans stack goes back to IPAGothic for Japanese (full-width 、。()), with DejaVu Sans named just before it so Latin and digits resolve to a proportional face first; real devices still use Hiragino / Yu Gothic / Noto, which come earlier in the list.
A base rule gives h1, h2 and h3 word-break: auto-phrase + text-wrap: balance, which covers the card titles, the shop name and the secondary heads.
The brand gap is 16px.
R3 · type and wrapping
builderindex.html · 1280×800 · full page, 10 screen(s)
Desktop 1280, full page after the type pass, 10 screens. Punctuation is full-width again everywhere (IPAGothic for Japanese): the tagline's 届く、暮らし (S11-1), 日が沈むと、シャッター (S11-2), 西日から、本の (S11-3), 零時に、明かり (S11-6), 宵待書房(よいまちしょぼう) and 水曜日、海が (S11-8) all have a comfortable cell after each mark, and the notes and bio no longer read dense. Digits and Latin are proportional (DejaVu Sans): 2026 年 9 月 12 日 in the meta, the card dates, and 'vlmkit' in the footer (S11-10). Paragraph ends: section 2 paragraph 1 now ends on 「んでいた。」 instead of a lone 「た。」 (S11-3), and 「だと思う。」 (S11-7). I found no line in the ten screens that ends on fewer than four characters, and the lines above those tails do not look stretched at this measure. Pull quote, interview, notes, share row, author card, related cards and footer are as in S7, with no regressions.
index.html · 768×1024 · full page, 8 screen(s)
Tablet 768, full page, 8 screens (first tablet look). S12-1 is broken: the one-row desktop masthead does not fit in 688px, so the flex items shrink until their text wraps a character at a time: the wordmark reads ほと / り, the tagline takes two lines, the nav items stack vertically (特 / 集, 暮 / ら / し, インタ / ビュー), and the 検索 pill becomes a tall capsule with 検 / 索 stacked. The masthead is about 130px of wreckage above an otherwise sound page. Nothing overflows, which may be why no gate reported it (check integrity at 768 was CLEAN, and so was the breakpoint sweep). The rest of the tablet layout works: a centred 640px illustration, the title in two lines, a three-line lede, and the meta on one line (S12-2); body at 18px on the 688px measure (S12-2 to S12-6); the figure at full column width (S12-3); pull quote and notes (S12-5/S12-7); three related columns whose titles break as balanced phrases, 岬の貸本屋に、 / 手紙で本を頼む (S12-8). In S12-2 the lede's first line seemed to end 一軒 without its の; S14 checks that.
.masthead__inner (700-999px)S12-1 / S15: at tablet width the one-row desktop masthead does not fit in 688px, and the flex items shrink until their text wraps a character at a time: ほと / り, 特 / 集, 暮 / ら / し, インタ / ビュー, and a tall 検 / 索 pill. No gate reported it: check integrity at 768 was CLEAN (G17) and the breakpoint sweep was clean (G16), because nothing overflows or overlaps.
index.html · 375×812 · full page, 12 screen(s)
Mobile 375, full page after the type pass, 12 screens. Masthead two rows as fixed. The first h2 now wraps as a balanced phrase pair, 日が沈むと、 / シャッターが上がる (S13-2), where before it had a lone る. Paragraph ends are all four characters or more: いていく。 (S13-2), 時を刻んでいる。 (S13-3), している。 (S13-4), を聞いた。 (S13-5). The cost is visible in a few lines above a tail, which are justified a little loosely: the interview intro 客の途切れた合間に、店の奥の帳場で話 (S13-5) and the first utterance line 夜には、どんなお客さんが来る (S13-6). It is mild, and better than an orphan. One new flaw: the figure caption's last line begins with a small kana, っている。 (S13-5); the caption has no strict line breaking, unlike the body. Notes: 注2's 本文へ戻る wraps onto its own line and starts flush with the note text (S13-10). Related list: all three titles break as phrases on two lines, 岬の貸本屋に、 / 手紙で本を頼む, 終点の駅から、 / 灯台まで歩く, 潮風と暮らす家の、 / 窓のしつらえ (S13-11). Digits proportional, © readable.
.figure figcaptionS13-5: the figure caption's last line begins with a small kana (っている。), which strict Japanese line breaking forbids; the caption never got the line-break: strict the body paragraphs have.
tablet lede close-up
index.html · 768×1024 · element .lede · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Tablet lede at 2x: three lines, ragged right at phrase boundaries. Line 1 ends 「…商店街のはずれで、一軒の」, so the の is present and my reading of S12-2 was a misread of the small screenshot, not a clipped glyph. Line 2 「古本屋がシャッターを上げる。開店は日の入り、閉店は午前零時。昼のあいだは一度も」, line 3 「開かない店に、夜ごと人が集まってくるのはなぜなのか。店主の汐田律子さんをたずねた。」. The ruby なぎうら sits centred over 凪浦 without touching the line above. Nothing is clipped; no defect here.
tablet masthead close-up
index.html · 768×1024 · element .masthead · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Tablet masthead at 2x confirms S12-1: in one flex row at 768px the wordmark wraps as ほ と / り in two lines, the tagline breaks into two grey lines, and every nav item is squeezed to one or two characters wide, so 特集, 暮らし and インタビュー stack vertically (本 keeps its accent underline under a single glyph). The 検索 button is a tall pill with its two characters stacked beside the magnifier. There is no overflow and no overlap, only per-character wrapping, and it looks broken to anyone who opens the page at tablet width.
S11-1/S11-9/S11-10: digits and Latin proportional (DejaVu Sans) in the meta, the card dates and 'vlmkit'.
S11: full-width 、。() in every heading, label, note and caption; nothing cramped.
S13 (mobile, 12 screens) and S11-3/S11-7 (desktop): every paragraph ends on four characters or more; the lone た。 / いく。 / いる。 / いた。 lines are gone.
S13-2: at 375 the first h2, now back in the wider IPAGothic, wraps as 日が沈むと、 / シャッターが上がる, a balanced phrase break with no lone character.
S13-11: all three card titles wrap as balanced phrase pairs; S12-8 shows the same at tablet.
The tagline shows only at 1100px and up, where the one-row masthead has the ~910px it needs; from 700 to 999px the nav gap is 20px and the row gap 28px (about 590px needed, 620px available at 700). The wordmark, nav links and buttons are white-space: nowrap, so if a width is ever too narrow the row overflows, which the breakpoint sweep can catch, instead of silently wrapping a character at a time.
line-break: strict is set on body, so the caption, notes, bio and UI text get the same kinsoku as the body paragraphs.
masthead at 700 / 768 / 1000 / 1100
index.html · 700×900 · element .masthead
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Masthead at 700px, the narrowest width of the one-row layout: wave mark + ほとり on the left, the five nav items on one line with 20px gaps (本 in the accent with its underline), and the 検索 pill on the right, all on a single row with about 30px to spare. No tagline at this width, and nothing wraps or overlaps.
masthead at 700 / 768 / 1000 / 1100
index.html · 768×1024 · element .masthead
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Masthead at 768px (the width that was broken in S12-1/S15): one clean row. ほとり at full size in one line, 特集 暮らし 旅 本 インタビュー each on one line, and 検索 as a normal single-line pill with its magnifier. The per-character wrapping is gone, with comfortable space between the wordmark and the nav.
masthead at 700 / 768 / 1000 / 1100
index.html · 1000×900 · element .masthead
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Masthead at 1000px, the first desktop width: still no tagline (it needs 1100px), the nav back to 28px gaps, the pill on the right, and one row with plenty of room. The wordmark, nav baseline and pill are vertically centred on each other, and the 本 underline sits just under its glyph.
masthead at 700 / 768 / 1000 / 1100
index.html · 1100×900 · element .masthead
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Masthead at 1100px: the tagline 水辺から届く、暮らしと本のウェブマガジン returns in grey after the wordmark, on the same baseline row, with the full-width comma intact. The nav and the 検索 pill still fit on one row with about 100px free, so the 1100px threshold leaves a margin rather than landing exactly at the limit.
S17: at 768 the masthead is one clean row; S16 (700), S18 (1000) and S19 (1100) show the edges of both new ranges holding.
mobile caption close-up
index.html · 375×812 · element .figure figcaption · @2x
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Mobile figure caption at 2x: three ragged lines, 窓ぎわの棚。先代のころに西日を浴びた文庫は、い / まも背の色が抜けたままだ。奥の棚の本は、色を / 保っている。. The last line now starts with 保, not the small っ: strict line breaking moved the break before 保っ, and the four-character tail keeps the end together. Grey caption ink on paper, 13px at 1.75 leading, with nothing clipped.
S20: the caption's last line is 保っている。; no line starts with a small kana.
vlmkit check design index.htmlverdict: COHERENT (0 finding(s))
verdict coherent · drifting 0 · roles 5
output, 22 line(s), 1.8s
vlmkit check design
source: index.html
verdict: COHERENT (0 finding(s))
roles judged: 3 of 5 seen, spacing values: 22
coverage: 17 of 723 visible element(s) carried an inferable role
no role: rect x397, span x47, p x41, path x38, circle x36, g x27, +26 more tag(s)
A role comes from role="..." or from button/input/select/textarea/h1-h6. Layout elements
(div, span, p, a) have none, so a large skip count is normal — this gate judges components,
not every box. Add role="..." where an element IS a component to widen the coverage.
Role reuse (instances / distinct styles; drift below 3x from 3 instances)
h2 7 inst 2 styles reuse 3.5x 0 one-off ok
h3 4 inst 1 styles reuse 4x 0 one-off ok
button 3 inst 1 styles reuse 3x 0 one-off ok
img 2 inst 2 styles reuse 1x 2 one-off not judged
h1 1 inst 1 styles reuse 1x 1 one-off not judged
not judged: img (2), h1 (1) — under --min-instances 3, so no finding can come from them.
To keep judging them: --min-instances 2 --min-reuse 2 (both — a 2-instance role cannot reach 3x however many instances the floor allows).
No design drift detected.vlmkit check breakpoints index.html --sweepbreakpoints 5 · issues 0 · sweepRanges 0
output, 14 line(s), 4.8s
vlmkit check breakpoints source: index.html status: ok width sweep: 320-1280px step 25 (39 widths) — clean breakpoints checked: 699, 700, 999, 1000, 1099px (each at B-1 / B / B+1) 699px: clean (max-width: 699px) 700px: clean (min-width: 700px) 999px: clean (max-width: 999px) 1000px: clean (min-width: 1000px) 1099px: clean (max-width: 1099px) All boundaries consistent.
vlmkit check integrity index.htmlverdict: CLEAN (0 fail, 0 warn, 0 exempted)
verdict clean · findings 0 · fails 0 · warns 0 · exempted 0 · viewports 3
output, 9 line(s), 7.7s
vlmkit check integrity source: index.html verdict: CLEAN (0 fail, 0 warn, 0 exempted) 1280x800: 1 component(s), ink 93.8%, 106 text block(s) 768x900: 1 component(s), ink 92.7%, 105 text block(s) 375x700: 1 component(s), ink 92.8%, 105 text block(s) No integrity defects detected.
check design: 'No design drift detected', with no scale-outlier info left.
check integrity reported the tablet width clean while its masthead was broken. With the one-row masthead squeezed into 688px, every flex item shrank to its min-content and the text wrapped one or two glyphs per line (ほと / り, 暮 / ら / し, 検 / 索: S12-1, S15). G17 printed 'verdict: CLEAN (0 fail, 0 warn, 0 exempted)' and '768x900: 1 component(s), ink 92.7%, 106 text block(s)', and check breakpoints --sweep G16 printed 'width sweep: 320-1280px step 25 (39 widths) — clean'. Nothing overflowed, collided or clipped, so no rule applies; the defect is the text's shape. A rule comparing a text box's rendered width with its single-line (max-content) width, flagging short labels squeezed to one or two glyphs per line, would have caught it. Only my own tablet look (L12) found it.
R4 · interactions
buildersearch panel with results
index.html · 1280×800 · click #search-toggle · fill #search-input "本" · press Enter · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop, search open after typing 本 and pressing Enter. A darker paper band opens under the masthead and pushes the page down: bold label サイト内検索, the field with the query and a clear 2px ink focus ring, a 検索する pill, the grey hint, the status 「本」に一致する記事が2件あります。 and two underlined results with section and date. It reads as part of the page. Two flaws: the field's native search-cancel × is system blue, the only blue on the page, and it looks foreign in this palette; and the 検索 toggle in the masthead has no open state of its own. Its fill here is only the pointer's hover, so with the pointer elsewhere nothing would connect the button to the open panel.
input#search-input::-webkit-search-cancel-buttonS21: the search field's native cancel × renders in system blue, the only blue on the page.
button#search-toggle[aria-expanded=true]S21/S22: the 検索 toggle has no open state. The only fill in the shots is the pointer's hover, so once the pointer leaves, nothing ties the button to the open panel.
search panel open, mobile
index.html · 375×812 · click #search-toggle · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Mobile, search panel open. The band spans the width under the two-row masthead: label, an empty field with a visible focus ring (focus moved into it), the 検索する pill beside it, and the hint on one line below. Spacing is even and nothing wraps badly. The 検索 toggle again shows only the pointer's hover fill as its open state (the same issue as S21).
after following 注2
index.html · 1280×800 · click #ref-2 · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop, after clicking 注2 in section 2. The page jumped to the notes, and 注2 is highlighted with the warm tint running 12px past the column on both sides, so the target is obvious. The landing is tight: the note sits 32px under the top edge, and the last line of 注1 (…早くなっていく。 本文へ戻る) is sliced by the viewport edge above it, with the 注 heading out of view, so the reader lands mid-list with a cut line overhead. The share row, author card and related band follow below; the progress bar is full.
[id^=note-], [id^=ref-] scroll-marginS23/S24: both footnote jumps land the target 32px under the top edge, with a sliced line of the item above it (the end of 注1 over 注2; the first line of the quoted paragraph over the 注2 reference). The reader lands without context and has to scroll up to re-read the sentence.
back from note 2
index.html · 1280×800 · click #ref-2 · click #note-2 .backref · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop, after 注2 and then that note's 本文へ戻る. The page is back in section 2 on the sentence with …背焼け注2していてね, and 注2 carries a faint tint as the returned-to target. The same tightness as S23: the reference's line is the second line on screen, and the paragraph's first line (「うちは店が西を向いているでしょう…) is sliced by the top edge. The reader has to scroll up to re-read the sentence they left. The figure and caption below are intact.
保存 pressed, mobile
index.html · 375×812 · click #save · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Mobile, after pressing 保存. The toggle is unmistakably on: the pill fills with the ink colour, the bookmark icon turns solid, and 保存 is paper-coloured, against the outlined リンクをコピー beside it. The label stays 保存, so the accessible name is constant while aria-pressed carries the state. The share row stays one line of buttons under この記事を; the notes above and the author card below are unchanged.
first Tab: skip link
index.html · 1280×800 · press Tab · wait 150
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop, first Tab press. The skip link 本文へスキップ slides in at the top left as an ink pill with paper text and a 2px ink ring outside it. It sits over the wordmark (only り shows beside it), which is the usual skip-link placement and is fine because it disappears on the next Tab. Nothing else moves; the header and illustration are as in S11-1.
focus ring on 注1
index.html · 1280×800 · element #story-body > section:first-child · focus #ref-1 · wait 150
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Section 1 with keyboard focus on the 注1 reference. A clear rounded 2px ink ring surrounds the small accent superscript at the end of 「…ただひとつの決まりだ注1。」 and stays legible at that size without colliding with the line above. The rest of the section is unchanged, and the progress bar shows as a partial accent line at the top of the frame.
::-webkit-search-cancel-button is restyled: no native appearance, and a 14px × drawn with two ink-soft gradients.
While aria-expanded is true, 検索 takes the panel's paper-deep fill and its magnifier is swapped for a close mark (two icons in the button, toggled in CSS); the visible and accessible name stays 検索.
Footnote notes and references get scroll-margin-top: 25vh, so a jump lands with the preceding lines in view; the returned-to reference's tint is backed by a 3px tint ring, so it is easier to find.
search open, pointer moved away
index.html · 1280×800 · click #search-toggle · fill #search-input "本" · press Enter · hover #story-title · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop, search open with the 本 results and the pointer moved away onto the title. The masthead toggle now shows its open state on its own: the pill takes the panel's paper-deep fill and the magnifier is replaced by a thin ink ×, reading "× 検索" and visibly tied to the band below. The field's cancel mark is a thin ink-grey × drawn in CSS; the system blue is gone. The results and status are unchanged from S21.
after following 注2 (25vh landing)
index.html · 1280×800 · click #ref-2 · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop, after clicking 注2 with the 25vh landing. The whole notes block is in view: the ink rule, the 注 heading, 注1 in full, then the highlighted 注2 about a quarter of the way down with its tint band, then 注3. No line is sliced at the top edge, and the reader sees where they are in the list. The share row and author card sit below.
back from note 2 (25vh landing)
index.html · 1280×800 · click #ref-2 · click #note-2 .backref · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop, after 注2 and then 本文へ戻る. The paragraph containing the reference sits about a quarter of the way down, with the previous paragraph (汐田さんが店を夜だけ開けるように…) fully visible above it, so the sentence can be re-read without scrolling. The returned-to 注2 superscript has a tint with a 3px ring and is easy to spot at the end of …背焼け. The figure follows below.
S28: the cancel mark is an ink-grey ×, not system blue.
S28: with the pointer elsewhere, the open toggle shows its own state: paper-deep fill and a × icon.
S29: 注2 lands at a quarter height with the 注 heading and 注1 above it; S30 does the same for the returned-to reference, whose paragraph and the one before it are fully in view.
vlmkit check animation 'file://$PWD/examples/sites/magazine/index.html#note-2'animations 1 · settleMs 120 · issues 0
output, 12 line(s), 3.0s
vlmkit check animation source: file://examples/sites/magazine/index.html#note-2 (1280x720) status: ok animations: 1 (evaluated 1, infinite 0) settle: 120ms reduced-motion: honored Evaluated animations: - #progress-bar `transform` 120ms x1: visible, motion region (319,0) 961x3, peak frame delta 0.31% No animation issues detected.
#progress-bar (main.js is-ready)check animation on index.html#note-2: '#progress-bar transform 120ms x1: visible, motion region (319,0) 961x3'. A page opened at a fragment animates the bar on load, sweeping it from about 25% to full width, because the easing is switched on one frame after the script runs and the browser's fragment scroll comes later. The README and the code comment both claim a page opened mid-article does not animate on load.
main.js: the easing class (is-ready) is added by the first wheel, touchstart, keydown or pointerdown (capture, passive, removed after firing), not one frame after load. A fragment or restored scroll made by the browser therefore jumps the bar straight to its place. The README now says this; the reduced-motion and ?animate=0 rules are unchanged.
vlmkit check animation 'file://$PWD/examples/sites/magazine/index.html#note-2'animations 0 · settleMs 0 · issues 0
output, 8 line(s), 1.7s
vlmkit check animation source: file://examples/sites/magazine/index.html#note-2 (1280x720) status: ok animations: 0 (evaluated 0, infinite 0) settle: 0ms No animation issues detected.
check animation on index.html#note-2: 0 animations, settle 0ms. Opening mid-article no longer moves anything.
G27 is the only frame-sampled evidence for the brief's reduced-motion clause, and it stays valid after D35. When the bar's transform transition ran (120ms, 'visible'), the gate's reduced-motion pass reported 'reduced-motion: honored'. D35 changed when the easing is switched on (the first user input instead of the first frame), not the CSS that disables it under prefers-reduced-motion: reduce or ?animate=0. After D35 no gate can make the transition run, because none performs user input before sampling, so check motion's declaration check (reduced-motion rule: yes) is the standing evidence from here on.
R5 · final check
buildervlmkit check integrity index.htmlverdict: CLEAN (0 fail, 0 warn, 0 exempted)
verdict clean · findings 0 · fails 0 · warns 0 · exempted 0 · viewports 3
output, 9 line(s), 7.2s
vlmkit check integrity source: index.html verdict: CLEAN (0 fail, 0 warn, 0 exempted) 1280x800: 1 component(s), ink 93.8%, 106 text block(s) 768x900: 1 component(s), ink 92.7%, 105 text block(s) 375x700: 1 component(s), ink 92.8%, 105 text block(s) No integrity defects detected.
vlmkit check copy index.html --manifest copy.txtmissing 1 · placeholders 0 · manifestLines 8 · statesExplored 1 · revealedOnly 0
output, 10 line(s), 1.4s
vlmkit check copy source: index.html status: suspect rendered text: 3461 chars disclosure states: 1 explored (details / tabs / aria-expanded) manifest: 8 line(s), missing 1 Issues: x copy-missing: Manifest line not found in rendered text or any of 1 revealed disclosure state(s): "リンクをコピーしました" (comparison is whitespace-normalized, case-sensitive).
vlmkit check design index.htmlverdict: COHERENT (0 finding(s))
verdict coherent · drifting 0 · roles 5
output, 22 line(s), 1.6s
vlmkit check design
source: index.html
verdict: COHERENT (0 finding(s))
roles judged: 3 of 5 seen, spacing values: 22
coverage: 17 of 723 visible element(s) carried an inferable role
no role: rect x397, span x47, p x41, path x38, circle x36, g x27, +26 more tag(s)
A role comes from role="..." or from button/input/select/textarea/h1-h6. Layout elements
(div, span, p, a) have none, so a large skip count is normal — this gate judges components,
not every box. Add role="..." where an element IS a component to widen the coverage.
Role reuse (instances / distinct styles; drift below 3x from 3 instances)
h2 7 inst 2 styles reuse 3.5x 0 one-off ok
h3 4 inst 1 styles reuse 4x 0 one-off ok
button 3 inst 1 styles reuse 3x 0 one-off ok
img 2 inst 2 styles reuse 1x 2 one-off not judged
h1 1 inst 1 styles reuse 1x 1 one-off not judged
not judged: img (2), h1 (1) — under --min-instances 3, so no finding can come from them.
To keep judging them: --min-instances 2 --min-reuse 2 (both — a 2-instance role cannot reach 3x however many instances the floor allows).
No design drift detected.vlmkit check composition index.htmlverdict: COMPOSED (0 finding(s))
verdict composed · proximity 0 · flatSteps 0 · railNearMisses 0
output, 17 line(s), 1.7s
vlmkit check composition source: index.html verdict: COMPOSED (0 finding(s)) measured: 8 label(s), 3 heading level(s), 99 wide block(s) on 9 left / 6 right rail(s) — from 209 visible box(es) 4 label(s) not judged: they open a box that paints its own group edge, so nothing above them could be mis-grouped with Type hierarchy (largest instance of each declared level) h1 42px / 700 h1#story-title h2 25px / 700 h2#ch1 h3 17px / 700 h3#shop-info-title body 18px, largest 42px (2.33x), heaviest +300 weight Group gaps: 12.95px between heading-led groups vs 21.5px inside them (0.6x, 12 sample(s)). Context only — intact pages measure 0.86-3.00x, so this cannot carry a verdict. Proximity, alignment and type hierarchy all read consistently.
vlmkit check color index.htmlverdict: CONSISTENT (0 finding(s))
verdict consistent · base #f6f2ea · bodyInk #1d2733 · linkInk #a4462b · invisibleControls 0 · colorOnlyLinks 0 · unreadableColors 0
output, 24 line(s), 1.7s
vlmkit check color
source: index.html
verdict: CONSISTENT (0 finding(s))
measured: 0 control(s), 23 link(s) in a text flow — from 220 visible box(es)
Palette (by painted area; reported, never judged — see the rejected candidates in the module docs)
surfaces 3 distinct
#f6f2ea 88.1% 1 el
#ede6d8 8.0% 2 el aside#shop-info
#1d2733 3.9% 4 el a.skip
ink 6 distinct
#1d2733 79.5% 74 el div.brand>a.wordmark>span.wordmark__text
#4a5360 11.6% 28 el div.story-head__text>p.kicker>span.series
#5b636e 2.6% 8 el div.wrap>div.brand>p.tagline
#b9bfc8 2.5% 3 el div.wrap>div.site-footer__brand>p.site-footer__tagline
#e8e3d9 2.1% 5 el div.wrap>div.site-footer__brand>p.site-footer__mark
…and 1 more
marks 3 distinct
#d8d0c0 71.0% 11 el header#top
#1d2733 27.5% 14 el button#search-toggle
#a4462b 1.6% 5 el ul>li>a
base #f6f2ea, body ink #1d2733, link ink #a4462b on 9 element(s)vlmkit check a11y contrast index.htmlinspected 147 · failures 0
output, 5 line(s), 1.5s
vlmkit check a11y contrast html: examples/sites/magazine/index.html inspected 147 text-bearing element(s) ✓ 0 contrast failure(s) report: examples/sites/magazine/test-results/a11y-contrast/index-861ad078/report.md
vlmkit check a11y focus index.htmlsteps 26 · findings 5
output, 10 line(s), 1.6s
vlmkit check a11y focus
source: index.html
captured 26 focus step(s)
✗ 5 finding(s)
[skip-row] Focus jumped down by 617px (from `header#top>div.wrap.masthead__inner>button#search-toggle` to `header.story-head.wrap>div.story-head__text>p.meta>span.meta__item>a`). Confirm no focusable element was unintentionally skipped.
[reverse] Focus moved left within the same row (from `div#story-body>section.chapter>p>sup.fnref>a#ref-2` at x=925 to `div.interview>p.turn>span.utterance>sup.fnref>a#ref-3` at x=397). Visual order is L-to-R; check `tabindex` or DOM order.
[reverse] Focus moved up by 93px (from `div.interview>p.turn>span.utterance>sup.fnref>a#ref-3` at y=443 to `article#story>div#story-body>section.chapter>p>a` at y=351). Visual order is top-to-bottom; check `tabindex` or DOM order.
[reverse] Focus moved up by 419px (from `main#main>section.after>div.share>button#save` at y=612 to `section.related>div.wrap>ul.related__list>li#related-1>a.card__link` at y=194). Visual order is top-to-bottom; check `tabindex` or DOM order.
[skip-row] Focus jumped down by 345px (from `section.related>div.wrap>ul.related__list>li#related-3>a.card__link` to `div.wrap.site-footer__inner>nav.site-footer__nav>ul>li>a`). Confirm no focusable element was unintentionally skipped.
report: examples/sites/magazine/test-results/a11y-focus-order/index-861ad078/report.mdvlmkit check a11y touch index.htmllevel AA · inspected 26 · failures 0 · wcagExempt 11
output, 6 line(s), 1.6s
vlmkit check a11y touch source: index.html level: WCAG AA (24×24 min) inspected 26 interactive element(s) ✓ 0 undersized target(s) 11 undersized target(s) excused by WCAG 2.5.8 itself (9 in a sentence, 2 with clear spacing) report: examples/sites/magazine/test-results/a11y-touch/index-861ad078/report.md
vlmkit scan scroll index.htmlcontainers 0 · overflowX 0 · issues 0
output, 8 line(s), 1.4s
vlmkit scan scroll source: index.html (1280x720) status: ok page: 1280x7364 — horizontal overflow 0px, vertical scroll 6644px scroll containers: 0 (dead scrollports 0, clipped 0) No scroll issues detected.
vlmkit scan scroll index.html --viewport 375x812containers 0 · overflowX 0 · issues 0
output, 8 line(s), 1.4s
vlmkit scan scroll source: index.html (375x812) status: ok page: 375x8980 — horizontal overflow 0px, vertical scroll 8168px scroll containers: 0 (dead scrollports 0, clipped 0) No scroll issues detected.
vlmkit scan handlers index.htmlregistrations 11 · elements 5 · suspects 0
output, 16 line(s), 1.8s
vlmkit scan handlers source: index.html status: 1 warn(s) exits 0 — 1 warn(s) did not fail this command. To gate on one: --rule unprobed-handler-types=suspect registrations: 11 across 5 element(s) + globals, on a page presenting 26 control(s) - header#top>div.wrap>button#search-toggle "検索": click - header#top>div#search-panel "サイト内検索 検索する 記事のタイトルとセクション名から探します。": keydown (no role) - header#top>div#search-panel>div.wrap>form#search-form "サイト内検索 検索する 記事のタイトルとセクション名から探します。": submit (no role) - main#main>section.after>div.share>button#copy-link "リンクをコピー": click - main#main>section.after>div.share>button#save "保存": click - globals: window:wheel, window:touchstart, window:keydown, window:pointerdown, window:scroll, window:resize Issues: warn [unprobed-handler-types] 3 handler type(s) registered and NONE exercised: click, keydown, submit — this gate is an inventory and presses nothing. A clean result here says the wiring exists, not that it works. Run 'check interactions --handlers' to focus and activate the controls, add '--probe-drag' for the drag surfaces, or drive the rest with a 'verify flow' script.
vlmkit check interactions index.html --handlerselements 26 · suspects 0 · warns 0
output, 49 line(s), 5.7s
vlmkit check interactions source: index.html status: ok exits 0 — 1 warn(s) did not fail this command. To gate on one: --rule unprobed-handler-types=suspect interactive elements: 26 - [link] "本文へスキップ" focus✓ - [link] "ほとり" focus✓ - [link] "特集" focus✓ - [link] "暮らし" focus✓ - [link] "旅" focus✓ - [link] "本" focus✓ - [link] "インタビュー" focus✓ - [button] "検索" focus✓ | Enter: expanded false -> true - [link] "三浦 灯" focus✓ - [link] "前回訪ねた岬の貸本屋" focus✓ - [link] "注1" focus✓ - [link] "注2" focus✓ - [link] "注3" focus✓ - [link] "記事の終わりの店舗情報" focus✓ - [link] "本文へ戻る" focus✓ - [link] "本文へ戻る" focus✓ - [link] "本文へ戻る" focus✓ - [button] "リンクをコピー" focus✓ | Enter: layout change | announces - [button] "保存" focus✓ | Enter: pressed false -> true - [link] "本 連載 第2回 岬の貸本屋に、手紙で本を頼む 2026年8月8日" focus✓ - [link] "旅 終点の駅から、灯台まで歩く 2026年7月25日" focus✓ - [link] "暮らし 潮風と暮らす家の、窓のしつらえ 2026年6月30日" focus✓ - [link] "ほとりについて" focus✓ - [link] "執筆者" focus✓ - [link] "お問い合わせ" focus✓ - [link] "プライバシー" focus✓ vlmkit scan handlers source: index.html status: 1 warn(s) registrations: 11 across 5 element(s) + globals, on a page presenting 26 control(s) - header#top>div.wrap>button#search-toggle "検索": click - header#top>div#search-panel "サイト内検索 検索する 記事のタイトルとセクション名から探します。": keydown (no role) - header#top>div#search-panel>div.wrap>form#search-form "サイト内検索 検索する 記事のタイトルとセクション名から探します。": submit (no role) - main#main>section.after>div.share>button#copy-link "リンクをコピー": click - main#main>section.after>div.share>button#save "保存": click - globals: window:wheel, window:touchstart, window:keydown, window:pointerdown, window:scroll, window:resize Issues: warn [unprobed-handler-types] Handler types registered but NOT exercised by this run's probes: keydown, submit — the probe focuses a control and presses the key its role activates with, so a `click` handler on anything but a native control, and any type outside focus/blur/keyboard, is untested here. Verify those paths with a 'verify flow' script.
vlmkit check scroll index.htmlpageScrolled 1080 · stickyFixed 1 · engagedSticky 0 · snaps 0 · issues 0
output, 12 line(s), 1.4s
vlmkit check scroll source: index.html status: ok page scrolled: 1080px sticky/fixed elements: 1 (sticky engaged by the scroll: 0) snap containers driven: 0 Sticky / fixed: - div.progress: fixed top=0px viewport y 0 -> 0 No scroll-behavior issues detected.
vlmkit check motion index.htmlactiveAnimations 0 · running 0 · issues 0
output, 12 line(s), 1.5s
vlmkit check motion source: index.html status: ok samples: 0 active animations: 0 active transitions: 0 running animations: 0 paused animations: 0 reduced-motion rule: yes No motion issues detected.
vlmkit check animation index.htmlanimations 0 · settleMs 0 · issues 0
output, 8 line(s), 1.6s
vlmkit check animation source: index.html (1280x720) status: ok animations: 0 (evaluated 0, infinite 0) settle: 0ms No animation issues detected.
vlmkit check animation 'file://$PWD/examples/sites/magazine/index.html#note-2'animations 0 · settleMs 0 · issues 0
output, 8 line(s), 1.7s
vlmkit check animation source: file://examples/sites/magazine/index.html#note-2 (1280x720) status: ok animations: 0 (evaluated 0, infinite 0) settle: 0ms No animation issues detected.
vlmkit stress i18n index.htmlinflate 1.4 · inspected 151 · overflowing 38
output, 12 line(s), 1.6s
vlmkit stress i18n
html: examples/sites/magazine/index.html inflate: 1.4x
! 38 overflow / wrap issue(s) across 151 inspected element(s)
[vertical-wrap] main#main>article#story>header.story-head.wrap>div.story-head__text>h1#story-title — h 122 → 183
[vertical-wrap] article#story>div#story-body>section.chapter>aside#shop-info>dl.shop-info__list — h 153 → 203
[vertical-wrap] main#main>article#story>header.story-head.wrap>div.story-head__text>p.meta — h 39 → 67
[vertical-wrap] main#main>article#story>header.story-head.wrap>div.story-head__text>p.lede — h 166 → 199
[vertical-wrap] article#story>div#story-body>section.chapter>figure.pullquote>blockquote — h 91 → 137
[vertical-wrap] main#main>article#story>div#story-body>section.chapter>figure.pullquote — h 192 → 238
… 32 more (see the report, or --json for all)
report: examples/sites/magazine/test-results/i18n-stress/report.md
exits 0 — 38 warn(s) did not fail this command. To gate on one: --rule vertical-wrap=suspectaside#shop-info dl.shop-info__list dtstress i18n: the shop-info labels wrap under inflation: 'dt そのほかXX 77×50 +25px' and 'dt 営業時間XX 77×50 +25px' (one line to two; the dd beside each only grows because the row does). The dt column is a fixed 5.5em, the same fixed-label pattern D25 removed from the interview and the notes; a five-character label such as 最寄りの駅 would wrap it without any inflation.
vlmkit check breakpoints index.html --sweepbreakpoints 5 · issues 0 · sweepRanges 0
output, 14 line(s), 3.9s
vlmkit check breakpoints source: index.html status: ok width sweep: 320-1280px step 25 (39 widths) — clean breakpoints checked: 699, 700, 999, 1000, 1099px (each at B-1 / B / B+1) 699px: clean (max-width: 699px) 700px: clean (min-width: 700px) 999px: clean (max-width: 999px) 1000px: clean (min-width: 1000px) 1099px: clean (max-width: 1099px) All boundaries consistent.
vlmkit verify flow index.html --flow flow.jsonverdict: DONE (8/8 steps)
done true · passed 8 · total 8
output, 27 line(s), 1.9s
vlmkit verify flow
source: index.html
verdict: DONE (8/8 steps)
✓ step 1: 検索 opens the search panel and moves focus into the field
✓ #search-toggle aria-expanded=true
✓ #search-panel visible
✓ #search-input focused
✓ step 2: type a section name
✓ #search-input visible
✓ step 3: Enter submits the form and lists the two 本 articles
✓ #search-status text~"「本」に一致する記事が2件あります。"
✓ #search-results li count=2
✓ step 4: Escape closes the panel and returns focus to the 検索 button
✓ #search-panel hidden
✓ #search-toggle aria-expanded=false
✓ #search-toggle focused
✓ step 5: リンクをコピー is pressed; the live region exists before anything is written to it
✓ #share-status role=status
✓ step 6: within 100ms (the clipboard promise has settled) the confirmation is shown
✓ #share-status text~"リンクをコピーしました"
✓ #share-status visible
✓ step 7: 保存 toggles on
✓ #save aria-pressed=true
✓ step 8: 保存 toggles off again
✓ #save aria-pressed=falseThe shop-info dl is one grid (minmax(5.5em, max-content) then 1fr) with each row a subgrid, and dt is white-space: nowrap, so the label column grows to the longest label and every row stays aligned, as in the interview and notes.
vlmkit stress i18n index.html --jsoninflate 1.4 · inspected 151 · overflowing 31
output, 1233 line(s), 1.6s
{
"gate": "stress.i18n",
"command": "stress i18n",
"verdict": "pass",
"counts": {
"suspect": 0,
"warn": 31,
"info": 0
},
"findings": [
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 476px -> 476px (scroll 476px) — \"海辺の町の古本屋が、夜だけ店を開ける理由XXXXXXXX\"",
"evidence": {
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>h1#story-title",
"tag": "h1",
"before": {
"width": 476,
"height": 121.78125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 182.671875,
"clientWidth": 476,
"scrollWidth": 476
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 476px -> 476px (scroll 476px) — \"文・写真XX 三浦X 灯X\n 2026年9月12日XXXX\n 読了時間XX 約8分X\"",
"evidence": {
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.meta",
"tag": "p",
"before": {
"width": 476,
"height": 39.09375,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 67.1875,
"clientWidth": 476,
"scrollWidth": 476
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 476px -> 476px (scroll 476px) — \"港の灯りがひとつ、またひとつとともりはじめるころ、XXXXXXXXXX凪浦XなぎうらXXの商店街のはずれで、一軒の古本屋\"",
"evidence": {
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.lede",
"tag": "p",
"before": {
"width": 476,
"height": 165.703125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 198.84375,
"clientWidth": 476,
"scrollWidth": 476
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>figure.pullquote>blockquote",
"tag": "blockquote",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>figure.pullquote",
"tag": "figure",
"before": {
"width": 694,
"height": 192.09375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 237.59375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"三浦X 夜には、どんなお客さんが来るんXXXXXXですか。XX\n 汐田X いろいろですよ。夜勤に向かう\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>div.interview",
"tag": "div",
"before": {
"width": 694,
"height": 500.875,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 603.4375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 632px -> 632px (scroll 632px) — \"まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に開いてい\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 632px -> 632px (scroll 632px) — \"いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖にいるで\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"汐田X まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"汐田X いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 48px -> 48px (scroll 48px) — \"汐田X\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 48px -> 48px (scroll 48px) — \"汐田X\"",
"evidence": {
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX\"",
"evidence": {
"path": "div#story-body>section.chapter>figure.pullquote>blockquote>p",
"tag": "p",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"零時ちょうど、汐田さんは電球をひとつずつ消していき、最後に入口の明かりを落とした。私は背の焼けていない詩集を一冊抱えて、\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"帰りぎわ、閉店をなぜ零時にしているのかを尋ねた。汐田さんは少し考えてから、「日付が変わるまでは、今日のうちでしょう」と言\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"店の外には、百円均一の文庫を並べた木箱がひとつ出ている。この箱だけは昼も夜も出しっぱなしで、代金は横に置いた缶に入れても\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"「夜の本屋は、灯台みたいなものだと思うの」と汐田さんは言う。「誰かが起きている明かりが、町にひとつあればいい。本を買わな\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 239.3125,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"十一時を過ぎると、客はさすがにまばらになる。汐田さんは帳場で値札を書きながら、ときどき顔を上げて通りのほうを見る。視線の\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"夜に売れる本には傾向がある、と汐田さんは言う。昼に比べて、詩集や随筆、古い旅行記がよく出る。反対に、実用書は昼の店ほどに\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"話しているあいだにも、客足は途切れなかった。作業着のまま文庫の棚の前にしゃがみこむ青年がいて、参考書の棚を行ったり来たり\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"ところが夜に店を開けてみると、昼には一度も見かけなかった客がやってくるようになった。最初の晩に来たのは、これから夜勤に向\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"そこで汐田さんは、昼のあいだはシャッターを下ろし、除湿機を回して本を休ませることにした。日の光が届かなくなってから店を開\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"見せてもらった文庫本は、表紙には深い緑が残っているのに、背だけが白茶けた砂の色になっていた。本を傷めるのは光だけではない\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"「うちは店が西を向いているでしょう。午後になると、海に照り返した光まで店の奥に入ってくるの。父の棚を片づけていたら、窓ぎ\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"汐田さんが店を夜だけ開けるようになったのは、十二年前に父親から店を継いでからだ。先代は朝十時に店を開けて夕方六時に閉める\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"店内は奥行きが深く、左右の壁と中央の平台に、ざっと一万冊の本が並ぶ。文庫と新書が半分、残りは詩集や郷土史、古い旅行記。港\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"十七時五十八分。岸壁に腰を下ろして待っていると、水平線に残っていた橙色が消え、背中のほうで、がらがらとシャッターの巻き上\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 239.3125,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"シャッターの脇には小さな黒板が掛かっていて、白いチョークで「本日の日の入りXXXXXXXXXXXXXXX 十七時五十八分\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"商店街がとぎれ、岸壁が見えてくる手前の角に、その店はある。宵待書房。間口二間ほどの木造二階建てで、昼間に通りかかった人に\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 694px -> 694px (scroll 694px) — \"九月のなかば、夕方の凪浦駅に降りた。駅前から海へまっすぐ延びる商店街は、五時を過ぎると半分ほどの店が戸を閉める。魚屋が店\"",
"evidence": {
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
}
},
{
"rule": "vertical-wrap",
"severity": "warn",
"message": "vertical-wrap at 1.4x text — 206px -> 694px (scroll 0px) — \"前回訪ねた岬の貸本屋XXXX\"",
"evidence": {
"path": "article#story>div#story-body>section.chapter>p>a",
"tag": "a",
"before": {
"width": 205.921875,
"height": 18,
"clientWidth": 0,
"scrollWidth": 0
},
"after": {
"width": 694,
"height": 52.1875,
"clientWidth": 0,
"scrollWidth": 0
}
}
}
],
"suppressed": [],
"retuned": [],
"report": {
"html": "examples/sites/magazine/index.html",
"inflateFactor": 1.4,
"beforeScreenshot": "examples/sites/magazine/test-results/i18n-stress/before.png",
"afterScreenshot": "examples/sites/magazine/test-results/i18n-stress/after.png",
"overflowing": [
{
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>h1#story-title",
"tag": "h1",
"text": "海辺の町の古本屋が、夜だけ店を開ける理由XXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 476,
"height": 121.78125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 182.671875,
"clientWidth": 476,
"scrollWidth": 476
}
},
{
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.meta",
"tag": "p",
"text": "文・写真XX 三浦X 灯X\n 2026年9月12日XXXX\n 読了時間XX 約8分X",
"kind": "vertical-wrap",
"before": {
"width": 476,
"height": 39.09375,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 67.1875,
"clientWidth": 476,
"scrollWidth": 476
}
},
{
"path": "main#main>article#story>header.story-head.wrap>div.story-head__text>p.lede",
"tag": "p",
"text": "港の灯りがひとつ、またひとつとともりはじめるころ、XXXXXXXXXX凪浦XなぎうらXXの商店街のはずれで、一軒の古本屋",
"kind": "vertical-wrap",
"before": {
"width": 476,
"height": 165.703125,
"clientWidth": 476,
"scrollWidth": 476
},
"after": {
"width": 476,
"height": 198.84375,
"clientWidth": 476,
"scrollWidth": 476
}
},
{
"path": "article#story>div#story-body>section.chapter>figure.pullquote>blockquote",
"tag": "blockquote",
"text": "夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>figure.pullquote",
"tag": "figure",
"text": "夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 192.09375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 237.59375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>div.interview",
"tag": "div",
"text": "三浦X 夜には、どんなお客さんが来るんXXXXXXですか。XX\n 汐田X いろいろですよ。夜勤に向かう",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 500.875,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 603.4375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"text": "まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に開いてい",
"kind": "vertical-wrap",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>span.utterance",
"tag": "span",
"text": "いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖にいるで",
"kind": "vertical-wrap",
"before": {
"width": 632,
"height": 102.5625,
"clientWidth": 632,
"scrollWidth": 632
},
"after": {
"width": 632,
"height": 136.75,
"clientWidth": 632,
"scrollWidth": 632
}
},
{
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"text": "汐田X まるで違います。昼のお客さんは、用事のついでに寄る人。夜のお客さんは、ここに来ること自体が用事なの。この町で夜に",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "article#story>div#story-body>section.chapter>div.interview>p.turn",
"tag": "p",
"text": "汐田X いろいろですよ。夜勤に向かう看護師さん、塾帰りの高校生、それから漁師さん。いか釣りの船は夕方に港を出て、朝まで沖",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"text": "汐田X",
"kind": "vertical-wrap",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
},
{
"path": "div#story-body>section.chapter>div.interview>p.turn>b.speaker",
"tag": "b",
"text": "汐田X",
"kind": "vertical-wrap",
"before": {
"width": 48,
"height": 102.5625,
"clientWidth": 48,
"scrollWidth": 48
},
"after": {
"width": 48,
"height": 136.75,
"clientWidth": 48,
"scrollWidth": 48
}
},
{
"path": "div#story-body>section.chapter>figure.pullquote>blockquote>p",
"tag": "p",
"text": "夜の本屋は、灯台みたいなものだと思うの。誰かが起きている明かりが、町にひとつあればいい。XXXXXXXXXXXXXXXX",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 91,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.5,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "零時ちょうど、汐田さんは電球をひとつずつ消していき、最後に入口の明かりを落とした。私は背の焼けていない詩集を一冊抱えて、",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "帰りぎわ、閉店をなぜ零時にしているのかを尋ねた。汐田さんは少し考えてから、「日付が変わるまでは、今日のうちでしょう」と言",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "店の外には、百円均一の文庫を並べた木箱がひとつ出ている。この箱だけは昼も夜も出しっぱなしで、代金は横に置いた缶に入れても",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "「夜の本屋は、灯台みたいなものだと思うの」と汐田さんは言う。「誰かが起きている明かりが、町にひとつあればいい。本を買わな",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 239.3125,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "十一時を過ぎると、客はさすがにまばらになる。汐田さんは帳場で値札を書きながら、ときどき顔を上げて通りのほうを見る。視線の",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "夜に売れる本には傾向がある、と汐田さんは言う。昼に比べて、詩集や随筆、古い旅行記がよく出る。反対に、実用書は昼の店ほどに",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "話しているあいだにも、客足は途切れなかった。作業着のまま文庫の棚の前にしゃがみこむ青年がいて、参考書の棚を行ったり来たり",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "ところが夜に店を開けてみると、昼には一度も見かけなかった客がやってくるようになった。最初の晩に来たのは、これから夜勤に向",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "そこで汐田さんは、昼のあいだはシャッターを下ろし、除湿機を回して本を休ませることにした。日の光が届かなくなってから店を開",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "見せてもらった文庫本は、表紙には深い緑が残っているのに、背だけが白茶けた砂の色になっていた。本を傷めるのは光だけではない",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "「うちは店が西を向いているでしょう。午後になると、海に照り返した光まで店の奥に入ってくるの。父の棚を片づけていたら、窓ぎ",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "汐田さんが店を夜だけ開けるようになったのは、十二年前に父親から店を継いでからだ。先代は朝十時に店を開けて夕方六時に閉める",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 102.5625,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "店内は奥行きが深く、左右の壁と中央の平台に、ざっと一万冊の本が並ぶ。文庫と新書が半分、残りは詩集や郷土史、古い旅行記。港",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "十七時五十八分。岸壁に腰を下ろして待っていると、水平線に残っていた橙色が消え、背中のほうで、がらがらとシャッターの巻き上",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 239.3125,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "シャッターの脇には小さな黒板が掛かっていて、白いチョークで「本日の日の入りXXXXXXXXXXXXXXX 十七時五十八分",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 136.75,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "商店街がとぎれ、岸壁が見えてくる手前の角に、その店はある。宵待書房。間口二間ほどの木造二階建てで、昼間に通りかかった人に",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "main#main>article#story>div#story-body>section.chapter>p",
"tag": "p",
"text": "九月のなかば、夕方の凪浦駅に降りた。駅前から海へまっすぐ延びる商店街は、五時を過ぎると半分ほどの店が戸を閉める。魚屋が店",
"kind": "vertical-wrap",
"before": {
"width": 694,
"height": 68.375,
"clientWidth": 694,
"scrollWidth": 694
},
"after": {
"width": 694,
"height": 170.9375,
"clientWidth": 694,
"scrollWidth": 694
}
},
{
"path": "article#story>div#story-body>section.chapter>p>a",
"tag": "a",
"text": "前回訪ねた岬の貸本屋XXXX",
"kind": "vertical-wrap",
"before": {
"width": 205.921875,
"height": 18,
"clientWidth": 0,
"scrollWidth": 0
},
"after": {
"width": 694,
"height": 52.1875,
"clientWidth": 0,
"scrollWidth": 0
}
}
],
"totalInspected": 151,
"reportPath": "examples/sites/magazine/test-results/i18n-stress/report.md"
}
}stress i18n after the fix: no shop-info row at all (dt, dd and list are gone from the report); 31 warns, all prose or the stretched speaker cells described in N10.
D36 changed style.css during the final round, after G29-G47 had run, so the whole gate set is run again below (G49 onward), against the page as shipped. G29-G47 stand as the first pass of this round.
vlmkit check integrity index.htmlverdict: CLEAN (0 fail, 0 warn, 0 exempted)
verdict clean · findings 0 · fails 0 · warns 0 · exempted 0 · viewports 3
output, 9 line(s), 7.0s
vlmkit check integrity source: index.html verdict: CLEAN (0 fail, 0 warn, 0 exempted) 1280x800: 1 component(s), ink 93.8%, 106 text block(s) 768x900: 1 component(s), ink 92.7%, 105 text block(s) 375x700: 1 component(s), ink 92.8%, 105 text block(s) No integrity defects detected.
vlmkit check copy index.html --manifest copy.txtmissing 1 · placeholders 0 · manifestLines 8 · statesExplored 1 · revealedOnly 0
output, 10 line(s), 1.4s
vlmkit check copy source: index.html status: suspect rendered text: 3461 chars disclosure states: 1 explored (details / tabs / aria-expanded) manifest: 8 line(s), missing 1 Issues: x copy-missing: Manifest line not found in rendered text or any of 1 revealed disclosure state(s): "リンクをコピーしました" (comparison is whitespace-normalized, case-sensitive).
vlmkit check design index.htmlverdict: COHERENT (0 finding(s))
verdict coherent · drifting 0 · roles 5
output, 22 line(s), 1.8s
vlmkit check design
source: index.html
verdict: COHERENT (0 finding(s))
roles judged: 3 of 5 seen, spacing values: 22
coverage: 17 of 723 visible element(s) carried an inferable role
no role: rect x397, span x47, p x41, path x38, circle x36, g x27, +26 more tag(s)
A role comes from role="..." or from button/input/select/textarea/h1-h6. Layout elements
(div, span, p, a) have none, so a large skip count is normal — this gate judges components,
not every box. Add role="..." where an element IS a component to widen the coverage.
Role reuse (instances / distinct styles; drift below 3x from 3 instances)
h2 7 inst 2 styles reuse 3.5x 0 one-off ok
h3 4 inst 1 styles reuse 4x 0 one-off ok
button 3 inst 1 styles reuse 3x 0 one-off ok
img 2 inst 2 styles reuse 1x 2 one-off not judged
h1 1 inst 1 styles reuse 1x 1 one-off not judged
not judged: img (2), h1 (1) — under --min-instances 3, so no finding can come from them.
To keep judging them: --min-instances 2 --min-reuse 2 (both — a 2-instance role cannot reach 3x however many instances the floor allows).
No design drift detected.vlmkit check composition index.htmlverdict: COMPOSED (0 finding(s))
verdict composed · proximity 0 · flatSteps 0 · railNearMisses 0
output, 17 line(s), 1.7s
vlmkit check composition source: index.html verdict: COMPOSED (0 finding(s)) measured: 8 label(s), 3 heading level(s), 99 wide block(s) on 9 left / 6 right rail(s) — from 209 visible box(es) 4 label(s) not judged: they open a box that paints its own group edge, so nothing above them could be mis-grouped with Type hierarchy (largest instance of each declared level) h1 42px / 700 h1#story-title h2 25px / 700 h2#ch1 h3 17px / 700 h3#shop-info-title body 18px, largest 42px (2.33x), heaviest +300 weight Group gaps: 12.95px between heading-led groups vs 21.5px inside them (0.6x, 12 sample(s)). Context only — intact pages measure 0.86-3.00x, so this cannot carry a verdict. Proximity, alignment and type hierarchy all read consistently.
vlmkit check color index.htmlverdict: CONSISTENT (0 finding(s))
verdict consistent · base #f6f2ea · bodyInk #1d2733 · linkInk #a4462b · invisibleControls 0 · colorOnlyLinks 0 · unreadableColors 0
output, 24 line(s), 1.8s
vlmkit check color
source: index.html
verdict: CONSISTENT (0 finding(s))
measured: 0 control(s), 23 link(s) in a text flow — from 220 visible box(es)
Palette (by painted area; reported, never judged — see the rejected candidates in the module docs)
surfaces 3 distinct
#f6f2ea 88.1% 1 el
#ede6d8 8.0% 2 el aside#shop-info
#1d2733 3.9% 4 el a.skip
ink 6 distinct
#1d2733 79.5% 74 el div.brand>a.wordmark>span.wordmark__text
#4a5360 11.6% 28 el div.story-head__text>p.kicker>span.series
#5b636e 2.6% 8 el div.wrap>div.brand>p.tagline
#b9bfc8 2.5% 3 el div.wrap>div.site-footer__brand>p.site-footer__tagline
#e8e3d9 2.1% 5 el div.wrap>div.site-footer__brand>p.site-footer__mark
…and 1 more
marks 3 distinct
#d8d0c0 71.0% 11 el header#top
#1d2733 27.5% 14 el button#search-toggle
#a4462b 1.6% 5 el ul>li>a
base #f6f2ea, body ink #1d2733, link ink #a4462b on 9 element(s)vlmkit check a11y contrast index.htmlinspected 147 · failures 0
output, 5 line(s), 1.6s
vlmkit check a11y contrast html: examples/sites/magazine/index.html inspected 147 text-bearing element(s) ✓ 0 contrast failure(s) report: examples/sites/magazine/test-results/a11y-contrast/index-861ad078/report.md
vlmkit check a11y focus index.htmlsteps 26 · findings 5
output, 10 line(s), 1.8s
vlmkit check a11y focus
source: index.html
captured 26 focus step(s)
✗ 5 finding(s)
[skip-row] Focus jumped down by 617px (from `header#top>div.wrap.masthead__inner>button#search-toggle` to `header.story-head.wrap>div.story-head__text>p.meta>span.meta__item>a`). Confirm no focusable element was unintentionally skipped.
[reverse] Focus moved left within the same row (from `div#story-body>section.chapter>p>sup.fnref>a#ref-2` at x=925 to `div.interview>p.turn>span.utterance>sup.fnref>a#ref-3` at x=397). Visual order is L-to-R; check `tabindex` or DOM order.
[reverse] Focus moved up by 93px (from `div.interview>p.turn>span.utterance>sup.fnref>a#ref-3` at y=443 to `article#story>div#story-body>section.chapter>p>a` at y=351). Visual order is top-to-bottom; check `tabindex` or DOM order.
[reverse] Focus moved up by 419px (from `main#main>section.after>div.share>button#save` at y=612 to `section.related>div.wrap>ul.related__list>li#related-1>a.card__link` at y=194). Visual order is top-to-bottom; check `tabindex` or DOM order.
[skip-row] Focus jumped down by 345px (from `section.related>div.wrap>ul.related__list>li#related-3>a.card__link` to `div.wrap.site-footer__inner>nav.site-footer__nav>ul>li>a`). Confirm no focusable element was unintentionally skipped.
report: examples/sites/magazine/test-results/a11y-focus-order/index-861ad078/report.mdvlmkit check a11y touch index.htmllevel AA · inspected 26 · failures 0 · wcagExempt 11
output, 6 line(s), 1.5s
vlmkit check a11y touch source: index.html level: WCAG AA (24×24 min) inspected 26 interactive element(s) ✓ 0 undersized target(s) 11 undersized target(s) excused by WCAG 2.5.8 itself (9 in a sentence, 2 with clear spacing) report: examples/sites/magazine/test-results/a11y-touch/index-861ad078/report.md
vlmkit scan scroll index.htmlcontainers 0 · overflowX 0 · issues 0
output, 8 line(s), 1.5s
vlmkit scan scroll source: index.html (1280x720) status: ok page: 1280x7364 — horizontal overflow 0px, vertical scroll 6644px scroll containers: 0 (dead scrollports 0, clipped 0) No scroll issues detected.
vlmkit scan scroll index.html --viewport 375x812containers 0 · overflowX 0 · issues 0
output, 8 line(s), 1.4s
vlmkit scan scroll source: index.html (375x812) status: ok page: 375x8980 — horizontal overflow 0px, vertical scroll 8168px scroll containers: 0 (dead scrollports 0, clipped 0) No scroll issues detected.
vlmkit scan handlers index.htmlregistrations 11 · elements 5 · suspects 0
output, 16 line(s), 1.6s
vlmkit scan handlers source: index.html status: 1 warn(s) exits 0 — 1 warn(s) did not fail this command. To gate on one: --rule unprobed-handler-types=suspect registrations: 11 across 5 element(s) + globals, on a page presenting 26 control(s) - header#top>div.wrap>button#search-toggle "検索": click - header#top>div#search-panel "サイト内検索 検索する 記事のタイトルとセクション名から探します。": keydown (no role) - header#top>div#search-panel>div.wrap>form#search-form "サイト内検索 検索する 記事のタイトルとセクション名から探します。": submit (no role) - main#main>section.after>div.share>button#copy-link "リンクをコピー": click - main#main>section.after>div.share>button#save "保存": click - globals: window:wheel, window:touchstart, window:keydown, window:pointerdown, window:scroll, window:resize Issues: warn [unprobed-handler-types] 3 handler type(s) registered and NONE exercised: click, keydown, submit — this gate is an inventory and presses nothing. A clean result here says the wiring exists, not that it works. Run 'check interactions --handlers' to focus and activate the controls, add '--probe-drag' for the drag surfaces, or drive the rest with a 'verify flow' script.
vlmkit check interactions index.html --handlerselements 26 · suspects 0 · warns 0
output, 49 line(s), 5.6s
vlmkit check interactions source: index.html status: ok exits 0 — 1 warn(s) did not fail this command. To gate on one: --rule unprobed-handler-types=suspect interactive elements: 26 - [link] "本文へスキップ" focus✓ - [link] "ほとり" focus✓ - [link] "特集" focus✓ - [link] "暮らし" focus✓ - [link] "旅" focus✓ - [link] "本" focus✓ - [link] "インタビュー" focus✓ - [button] "検索" focus✓ | Enter: expanded false -> true - [link] "三浦 灯" focus✓ - [link] "前回訪ねた岬の貸本屋" focus✓ - [link] "注1" focus✓ - [link] "注2" focus✓ - [link] "注3" focus✓ - [link] "記事の終わりの店舗情報" focus✓ - [link] "本文へ戻る" focus✓ - [link] "本文へ戻る" focus✓ - [link] "本文へ戻る" focus✓ - [button] "リンクをコピー" focus✓ | Enter: layout change | announces - [button] "保存" focus✓ | Enter: pressed false -> true - [link] "本 連載 第2回 岬の貸本屋に、手紙で本を頼む 2026年8月8日" focus✓ - [link] "旅 終点の駅から、灯台まで歩く 2026年7月25日" focus✓ - [link] "暮らし 潮風と暮らす家の、窓のしつらえ 2026年6月30日" focus✓ - [link] "ほとりについて" focus✓ - [link] "執筆者" focus✓ - [link] "お問い合わせ" focus✓ - [link] "プライバシー" focus✓ vlmkit scan handlers source: index.html status: 1 warn(s) registrations: 11 across 5 element(s) + globals, on a page presenting 26 control(s) - header#top>div.wrap>button#search-toggle "検索": click - header#top>div#search-panel "サイト内検索 検索する 記事のタイトルとセクション名から探します。": keydown (no role) - header#top>div#search-panel>div.wrap>form#search-form "サイト内検索 検索する 記事のタイトルとセクション名から探します。": submit (no role) - main#main>section.after>div.share>button#copy-link "リンクをコピー": click - main#main>section.after>div.share>button#save "保存": click - globals: window:wheel, window:touchstart, window:keydown, window:pointerdown, window:scroll, window:resize Issues: warn [unprobed-handler-types] Handler types registered but NOT exercised by this run's probes: keydown, submit — the probe focuses a control and presses the key its role activates with, so a `click` handler on anything but a native control, and any type outside focus/blur/keyboard, is untested here. Verify those paths with a 'verify flow' script.
vlmkit check scroll index.htmlpageScrolled 1080 · stickyFixed 1 · engagedSticky 0 · snaps 0 · issues 0
output, 12 line(s), 1.4s
vlmkit check scroll source: index.html status: ok page scrolled: 1080px sticky/fixed elements: 1 (sticky engaged by the scroll: 0) snap containers driven: 0 Sticky / fixed: - div.progress: fixed top=0px viewport y 0 -> 0 No scroll-behavior issues detected.
vlmkit check motion index.htmlactiveAnimations 0 · running 0 · issues 0
output, 12 line(s), 1.5s
vlmkit check motion source: index.html status: ok samples: 0 active animations: 0 active transitions: 0 running animations: 0 paused animations: 0 reduced-motion rule: yes No motion issues detected.
vlmkit check animation index.htmlanimations 0 · settleMs 0 · issues 0
output, 8 line(s), 1.5s
vlmkit check animation source: index.html (1280x720) status: ok animations: 0 (evaluated 0, infinite 0) settle: 0ms No animation issues detected.
vlmkit check animation 'file://$PWD/examples/sites/magazine/index.html#note-2'animations 0 · settleMs 0 · issues 0
output, 8 line(s), 1.5s
vlmkit check animation source: file://examples/sites/magazine/index.html#note-2 (1280x720) status: ok animations: 0 (evaluated 0, infinite 0) settle: 0ms No animation issues detected.
vlmkit check breakpoints index.html --sweepbreakpoints 5 · issues 0 · sweepRanges 0
output, 14 line(s), 3.7s
vlmkit check breakpoints source: index.html status: ok width sweep: 320-1280px step 25 (39 widths) — clean breakpoints checked: 699, 700, 999, 1000, 1099px (each at B-1 / B / B+1) 699px: clean (max-width: 699px) 700px: clean (min-width: 700px) 999px: clean (max-width: 999px) 1000px: clean (min-width: 1000px) 1099px: clean (max-width: 1099px) All boundaries consistent.
vlmkit verify flow index.html --flow flow.jsonverdict: DONE (8/8 steps)
done true · passed 8 · total 8
output, 27 line(s), 2.0s
vlmkit verify flow
source: index.html
verdict: DONE (8/8 steps)
✓ step 1: 検索 opens the search panel and moves focus into the field
✓ #search-toggle aria-expanded=true
✓ #search-panel visible
✓ #search-input focused
✓ step 2: type a section name
✓ #search-input visible
✓ step 3: Enter submits the form and lists the two 本 articles
✓ #search-status text~"「本」に一致する記事が2件あります。"
✓ #search-results li count=2
✓ step 4: Escape closes the panel and returns focus to the 検索 button
✓ #search-panel hidden
✓ #search-toggle aria-expanded=false
✓ #search-toggle focused
✓ step 5: リンクをコピー is pressed; the live region exists before anything is written to it
✓ #share-status role=status
✓ step 6: within 100ms (the clipboard promise has settled) the confirmation is shown
✓ #share-status text~"リンクをコピーしました"
✓ #share-status visible
✓ step 7: 保存 toggles on
✓ #save aria-pressed=true
✓ step 8: 保存 toggles off again
✓ #save aria-pressed=falseindex.html · 1280×800 · full page, 10 screen(s)
Final desktop full page, 10 screens, as shipped. S31-1: the masthead reads in one row (wave + ほとり, grey tagline with full-width comma, five nav items with 本 underlined in the accent, 検索 pill); the header puts the two-line title, phrase-broken lede, and meta (文・写真 三浦 灯 | 2026年9月12日 | 読了時間 約8分, proportional digits) beside the 620px night illustration with four lit bulbs and a clean quay. S31-2 to S31-7: body column 293-986px, about 37 characters a line, 1.9 leading; every h2 has its accent rule and full-width punctuation; no paragraph ends on fewer than four characters; the shelf figure's light fades; the interview column is aligned; 漁火 注3 is spaced; the pull quote breaks as phrases. The progress bar at the top grows tile by tile (about 175, 370, 565, 760, 950px). S31-8: the shop box's labels sit on one aligned column, then notes, the share row and the author card. S31-9/S31-10: three related cards, the dark footer, © readable. I found nothing broken, clipped, cramped or misaligned in the ten screens.
index.html · 375×812 · full page, 12 screen(s)
Final mobile full page, 12 screens, as shipped. S32-1: a two-row masthead (wordmark with 検索 top-right, then the five nav items edge to edge), the 335px illustration, 本 + series, a two-line title, and the phrase-broken lede. S32-2: meta on two lines with no stray bar; the first h2 as a balanced pair, 日が沈むと、 / シャッターが上がる. S32-2 to S32-9: body at 16px, about 20 characters a line; every paragraph ends on four or more characters; the caption ends 保っている。 (S32-5); the interview aligned with its speaker column (S32-6); the pull quote in four phrase lines (S32-7); shop info with aligned labels (S32-9). S32-10: the notes' back links flush after text or on their own line; この記事を above both buttons; the author card. S32-11/S32-12: the compact related list with balanced two-line titles; the footer with readable ©. No horizontal overflow and nothing clipped. The only soft spot is the slightly loose justification on a few lines above a paragraph tail, which is the accepted cost of D6.
state: mid-article, progress partly filled
index.html · 1280×800 · scroll-to #ch3 · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
State: mid-article on desktop, scrolled to section 3. The 3px accent progress bar at the very top is partly filled: it runs from the left edge to about x=680 of 1280, roughly 53%, over the faint grey track, and it holds the top edge above the text (fixed). Below it are the end of section 2, the section 3 accent rule and h2, the intro line and the start of the interview. It reads exactly as a reading-progress bar should, thin and quiet, in the one accent colour.
state: mid-article on mobile
index.html · 375×812 · scroll-to #ch2 · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
State: mid-article on mobile, scrolled to section 2. The progress bar covers about 95px of the 375px width (about a quarter), which matches the position in a 9,000px page. It stays pinned above the text; the h2 西日から、本の背を守るために fits on one line, and the paragraphs below end on full lines.
state: after following 注1 (mobile)
index.html · 375×812 · click #ref-1 · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
State: the page after following the 注1 reference on mobile. The jump lands with context: the end of the shop box, the ink rule and the 注 heading are above, and 注1 is highlighted with the warm tint band (12px past the column on each side, inside the viewport), its 本文へ戻る link at the end of the text. 注2 and 注3 follow, then the share row. The progress bar is full, since the article's end is on screen.
state: copy-link confirmation (mobile)
index.html · 375×812 · click #copy-link · wait 300
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
State: the copy-link confirmation on mobile. The buttons stay on one row (リンクをコピー with the pointer's hover fill, 保存 outlined), and the live region's message wraps below them as its own line: an accent check followed by bold 「リンクをコピーしました」, verbatim. The author card follows below the rule. Nothing overlaps, and the message is fully visible.
state: 保存 pressed (desktop)
index.html · 1280×800 · click #save · hover #author · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
State: 保存 pressed on desktop, with the pointer moved off onto the author card. 保存 is a filled ink pill with a solid bookmark and paper text beside the outlined リンクをコピー, so the on state reads at a glance without relying on the pointer. The label stays 保存. The notes above and the author card and related heading below are unchanged.
state: reduced motion, mid-article
index.html · 1280×800 · reduced motion · scroll-to #ch4 · wait 200
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
State: reduced motion (prefers-reduced-motion: reduce emulated), scrolled to section 4. The progress bar is at about x=997 of 1280 (78%), exactly where it should be for this scroll position, with no easing. The pull quote with its ink rules, the 零時に、明かりを落とす h2 and the section's first paragraphs render as in the default mode; nothing else on the page moves in either mode. A still cannot show the absence of easing: G27's frame-sampled reduced-motion pass (honored) and check motion's reduced-motion rule carry that evidence.
Final copy run: 7 of 8 manifest lines are found visibly. The one reported missing, リンクをコピーしました, is the brief's state copy: it appears in the live region only after リンクをコピー is pressed (see N2 for why it is not pre-filled). The final verify flow G66 asserts it deterministically (step 5: the region exists with role=status; step 6: within 100ms it contains the line and is visible), and S36/L36 (mobile, final round) and S4/L4 (desktop) show it rendered verbatim.
Final focus run: the same viewport-coordinate artifacts as G7 (see N3 and tool note N4), plus one new reverse, 注3 (y=443) -> 記事の終わりの店舗情報 (y=351), which appeared because the footnote references now scroll into view with scroll-margin-top: 25vh (D34), so the browser parks them lower in the viewport than a plain link. In the document 注3 is in section 3's interview and the shop-info link is in section 4's last paragraph, far below (S31-5, then S31-7). The page has no tabindex anywhere; Tab order is DOM order and DOM order is the visual order. The two skip-row warns cross only non-interactive regions (S31-1, S31-9).
Inventory warn only (unprobed-handler-types): the click handlers are exercised by G60 and the keydown/submit paths by G66, as in N7. The four new window listeners (wheel, touchstart, keydown, pointerdown) are the passive, capture-phase, fire-once switch that turns on the progress-bar easing (D35); none of them calls preventDefault.
unprobed-handler-types (keydown, submit) is covered by verify flow G66 steps 3-4 (form submit with Enter gives the result status and 2 results; Escape closes the panel and returns focus to 検索). All three buttons show their transitions here: 検索 expanded false -> true, リンクをコピー announces, 保存 pressed false -> true.
31 vertical-wrap warns, 0 suspects: 29 rows are prose, the title, the lede, the meta line and an inline link gaining lines under 1.4x inflation, which is what text should do; 2 rows are the interview's stretched speaker cells (48px, nowrap, one line of text in a tall grid cell, see N10/N11). No horizontal overflow and nothing extends past its parent.
check copy --manifest has no way to reach copy that an action produces. Its sweep opens details, tabs and aria-expanded controls ('disclosure states: 1 explored (details / tabs / aria-expanded)'), so a brief whose required copy is a confirmation in a live region fails on every run: 'x copy-missing: Manifest line not found in rendered text or any of 1 revealed disclosure state(s): "リンクをコピーしました"'. I proved the line with verify flow instead (G66). A flag naming controls to activate before matching (e.g. --reveal '#copy-link'), or accepting a verify-flow step as the carrier, would let the copy gate itself cover state copy.
check animation and check motion cannot see a scroll-linked transition. Neither scrolls nor sends input before sampling, so G63 and G64 report 'animations: 0' and G62 'active transitions: 0' for a page whose one motion, the brief's progress bar, runs only while the reader scrolls. The only frame-sampled check of its reduced-motion behaviour was G27, possible only because the page then animated on a fragment load, which was itself a defect (D35). A --scroll option (sample while scrolling the window by N px) on check animation would make 'respects prefers-reduced-motion' provable for scroll-driven motion.
ほとり: one Japanese long-form feature (2,949 characters: four sections, a figure, an interview with the speakers marked, a pull quote, three footnotes with back links, a shop box, an author card and three related articles; all art is inline SVG), judged in 5 rounds, 66 gate runs and 38 shots with 38 looks. 36 defects, all fixed and verified: 25 found only by eye and 11 by a gate. The eye-only ones include the tablet masthead collapsing to one glyph per line while check integrity called 768px CLEAN, body text falling back to a Chinese font, bulbs hidden behind the door mullions, and one- and two-kana last lines in justified Japanese that text-wrap: pretty did not fix. Final set: integrity CLEAN at 1280/768/375, design COHERENT, composition COMPOSED, color CONSISTENT (23 underlined prose links), contrast 0 failures, touch 0 undersized, scan scroll clean at 1280 and 375, check scroll ok (the fixed bar holds), motion/animation ok (reduced-motion honoured, G27), breakpoints --sweep clean, verify flow DONE 8/8, stress i18n 0 suspects. Two gates still exit 1, both noted with evidence: check copy (the state-only confirmation, proven by G66 and S36) and check a11y focus (viewport-coordinate artifacts, tool note N4).
R6 · independent review
reviewervlmkit check integrity index.htmlverdict: CLEAN (0 fail, 0 warn, 0 exempted)
verdict clean · findings 0 · fails 0 · warns 0 · exempted 0 · viewports 3
output, 9 line(s), 7.1s
vlmkit check integrity source: index.html verdict: CLEAN (0 fail, 0 warn, 0 exempted) 1280x800: 1 component(s), ink 93.8%, 106 text block(s) 768x900: 1 component(s), ink 92.7%, 105 text block(s) 375x700: 1 component(s), ink 92.8%, 105 text block(s) No integrity defects detected.
vlmkit check composition index.htmlverdict: COMPOSED (0 finding(s))
verdict composed · proximity 0 · flatSteps 0 · railNearMisses 0
output, 17 line(s), 1.6s
vlmkit check composition source: index.html verdict: COMPOSED (0 finding(s)) measured: 8 label(s), 3 heading level(s), 99 wide block(s) on 9 left / 6 right rail(s) — from 209 visible box(es) 4 label(s) not judged: they open a box that paints its own group edge, so nothing above them could be mis-grouped with Type hierarchy (largest instance of each declared level) h1 42px / 700 h1#story-title h2 25px / 700 h2#ch1 h3 17px / 700 h3#shop-info-title body 18px, largest 42px (2.33x), heaviest +300 weight Group gaps: 12.95px between heading-led groups vs 21.5px inside them (0.6x, 12 sample(s)). Context only — intact pages measure 0.86-3.00x, so this cannot carry a verdict. Proximity, alignment and type hierarchy all read consistently.
vlmkit check color index.htmlverdict: CONSISTENT (0 finding(s))
verdict consistent · base #f6f2ea · bodyInk #1d2733 · linkInk #a4462b · invisibleControls 0 · colorOnlyLinks 0 · unreadableColors 0
output, 24 line(s), 1.6s
vlmkit check color
source: index.html
verdict: CONSISTENT (0 finding(s))
measured: 0 control(s), 23 link(s) in a text flow — from 220 visible box(es)
Palette (by painted area; reported, never judged — see the rejected candidates in the module docs)
surfaces 3 distinct
#f6f2ea 88.1% 1 el
#ede6d8 8.0% 2 el aside#shop-info
#1d2733 3.9% 4 el a.skip
ink 6 distinct
#1d2733 79.5% 74 el div.brand>a.wordmark>span.wordmark__text
#4a5360 11.6% 28 el div.story-head__text>p.kicker>span.series
#5b636e 2.6% 8 el div.wrap>div.brand>p.tagline
#b9bfc8 2.5% 3 el div.wrap>div.site-footer__brand>p.site-footer__tagline
#e8e3d9 2.1% 5 el div.wrap>div.site-footer__brand>p.site-footer__mark
…and 1 more
marks 3 distinct
#d8d0c0 71.0% 11 el header#top
#1d2733 27.5% 14 el button#search-toggle
#a4462b 1.6% 5 el ul>li>a
base #f6f2ea, body ink #1d2733, link ink #a4462b on 9 element(s)vlmkit check a11y contrast index.htmlinspected 147 · failures 0
output, 5 line(s), 1.5s
vlmkit check a11y contrast html: examples/sites/magazine/index.html inspected 147 text-bearing element(s) ✓ 0 contrast failure(s) report: examples/sites/magazine/test-results/a11y-contrast/index-861ad078/report.md
the article as a reader lands
index.html · 1280×800 · full page, 10 screen(s)
Desktop (10 screens). Screen 1: the masthead (wave mark, ほとり, tagline, nav with 本 underlined in the accent, a pill 検索) over a two-column hero — category chip, series line, the title broken at its comma, a lede with ruby over 凪浦, a byline row — and a large night illustration of 宵待書房 with a lighthouse, lit shelves and reflections. It reads as a magazine at once. Screens 2–7: a single ~694px column with a short accent rule over each h2, generous line spacing, underlined accent links and 注 superscripts; the bookshelf figure breaks out wider with its caption; the interview sets speaker names in a gutter, questions muted and answers in ink; the progress bar at the top grows screen by screen. Screen 6: the pull quote breaks its first line after 誰かが — 「…だと思うの。誰かが/起きている明かりが、町にひとつあればいい。」 — so the second sentence's subject hangs at the end of the first line. Screens 8–10: shop box, 注 with back-links, share buttons, author card, three illustrated related cards, dark footer. Only the pull-quote break is off.
.pullquote blockquote pOn desktop the pull quote's first line ends on 誰かが, the subject of the SECOND sentence, so the quote reads 「…思うの。誰かが/起きている明かりが…」. word-break: auto-phrase makes 誰かが a legal break and text-wrap: balance prefers 23/21 characters to the sentence boundary's 20/24, although both sentences fit the 694px column on one line each.
the article as a reader lands
index.html · 375×812 · full page, 12 screen(s)
Phone (12 screens). The masthead becomes two rows — wordmark with the 検索 pill, then the five sections evenly spaced — and the illustration leads, then the chip, series line, the title broken at its comma and the lede. Headings break by phrase (「日が沈むと、/シャッターが上がる」), body text keeps a comfortable 16–17px with generous leading, links stay underlined in the accent. The interview keeps its speaker gutter at 375px; the pull quote breaks by phrase into four balanced lines; the shop box keeps a two-column table; notes, share buttons, the author card and related cards (image left, phrase-broken titles right) follow, then the footer. The only rough edge: note 3's text ends 「並/ぶ。」 before its back-link, which the back-link on the same line softens. Nothing overflows or collides.
Each sentence of the pull quote is a span, inline-block from 700px, so on wide screens the quote breaks at the sentence boundary; below 700px the spans stay inline and auto-phrase / balance break it as before (the phone version already broke by phrase well).
pull quote after the fix
index.html · 1280×800 · element .pullquote
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Desktop close-up of the pull quote after the fix: two lines, 「夜の本屋は、灯台みたいなものだと思うの。」 and 「誰かが起きている明かりが、町にひとつあればいい。」 — each sentence on its own line, the second one longer but inside the column, the attribution below in small grey type, the heavy rule above and the thin one below unchanged.
pull quote after the fix
index.html · 768×1024 · element .pullquote
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Tablet 768 close-up of the pull quote: the same two-line sentence-per-line break as desktop at 26px; the longer second sentence still fits the narrower column on one line, so the inline-block spans never have to wrap inside themselves here.
pull quote after the fix
index.html · 375×812 · element .pullquote
1 screen(s), looked at when taken; not kept once the round was done — a round keeps one full-page walk per width.
Phone 375 close-up of the pull quote: unchanged from before the fix — four phrase-broken lines (夜の本屋は、灯台みたいな/ものだと思うの。誰かが/起きている明かりが、/町にひとつあればいい。) at 22px, because below 700px the sentence spans stay inline and auto-phrase with balance still decide.
vlmkit check integrity index.htmlverdict: CLEAN (0 fail, 0 warn, 0 exempted)
verdict clean · findings 0 · fails 0 · warns 0 · exempted 0 · viewports 3
output, 9 line(s), 7.3s
vlmkit check integrity source: index.html verdict: CLEAN (0 fail, 0 warn, 0 exempted) 1280x800: 1 component(s), ink 93.8%, 107 text block(s) 768x900: 1 component(s), ink 92.7%, 106 text block(s) 375x700: 1 component(s), ink 92.8%, 105 text block(s) No integrity defects detected.
The desktop and tablet breaks now fall at the sentence boundary; the phone version is unchanged, and integrity is still clean after the change.
Independent review: integrity, composition, color and contrast re-run green; the builder's two exit-1 gates (check copy's state-only line, check a11y focus's viewport-coordinate reverses) stay closed by its notes, and I read both false-positive claims against the shots — they hold. Of 22 fresh screens, one editorial defect (D37, the pull quote's break) was worth fixing.
Reviewer round: one eye-found defect fixed and verified; everything else the builder shipped holds up at desktop, tablet and phone.





















































































































