独家

Agent Vision <mv-agent-vision>

以 AI 智能体(或屏幕阅读器)的视角查看你的页面:包裹任意区域,并用一条可拖动的扫描线将其一分为二。

工作原理

以 AI 智能体(或屏幕阅读器)的视角查看你的页面:包裹任意区域,并用一条可拖动的扫描线将其一分为二。左侧是人类视图;右侧是 "agent view",无障碍树的每个节点都变成像素对齐的线框盒子,并像树中的一行那样标注(button "Add to cart"、heading 2 "Shipping"、text field "Promo code"),其余一切淡化为平静的网格。导致网站对智能体不可读的缺陷(没有名称的纯图标按钮、可点击的 div、没有 alt 的图片、只有占位文字的输入框、标题层级跳跃、无法区分的 "Learn more" 链接、无声的颜色圆点)会被标记出来,以 100 分制评分,并连同修复方法一起列出;点击某个问题会把它扫入 agent view 并使其闪烁。在 DOM 变更、尺寸调整和滚动时自动重新扫描。

分类实用工具
类型Web Component(<mv-agent-vision>)
状态稳定版
套件可核查的 AI
Keywordsexclusive, accessibility, a11y, ai-agent, agent, screen-reader, accessibility-tree, audit, lint, geo, seo, wipe, splitter, before-after, devtools, semantic-html

When to use

  • A team wants to show how AI agents and screen readers perceive a page during review or a demo
  • Developers audit a region for unnamed buttons, missing alt text and heading skips with a score
  • A before and after view helps explain accessibility or agent-readiness fixes to stakeholders

Avoid when

  • It would ship on a public production page for end users; keep it in dev, docs or review builds
  • A full compliance audit is required; it flags common issues but does not replace a real audit

安装

node scripts/add.mjs agent-vision --out ./src/marvelous

通过 Marvelous UI MCP 服务器使用 AI 智能体:install_components({ slugs: ["agent-vision"], target_dir: "<absolute path>/src/marvelous", framework: "react" })。

复制的文件(含依赖):tokens/tokens.css、core/base.css、core/dom.js、core/element.js、core/motion.js、core/observe.js、core/pointer.js、components/agent-vision/agent-vision.js、components/agent-vision/agent-vision.css。

用法

快速开始,最小可运行标记:

<mv-agent-vision>
  <form>
    <h2>Newsletter</h2>
    <input type="email" placeholder="Email"> <button>Subscribe</button>
    <button type="button"><svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 6l12 12M18 6 6 18" stroke="currentColor" stroke-width="2"/></svg></button>
  </form>
</mv-agent-vision>

标准标记,可在此基础上通过属性、data-* 和 CSS 变量进行定制:

<div id="av-demo" style="width:min(100%,76rem);margin-inline:auto">
  <style>
    #av-demo .av-bar { display:flex; align-items:center; justify-content:space-between; gap:.75rem 1rem; flex-wrap:wrap; margin:0 0 1rem }
    #av-demo .av-bar p { margin:0; color:var(--mv-fg-muted); font-size:.8125rem; max-width:46rem }
    #av-demo .av-bar strong { color:var(--mv-fg) }
    #av-demo .av-page { container-type:inline-size; padding:1rem 1.25rem 1.25rem; border:1px solid var(--mv-border); border-radius:var(--mv-radius-xl); background:var(--mv-surface) }
    #av-demo .av-grid { display:grid; gap:1.25rem; margin-top:.875rem }
    @container (min-width:36rem) { #av-demo .av-grid { grid-template-columns:minmax(0,1fr) minmax(0,1.05fr); gap:1.75rem } }
    #av-demo .av-media { display:grid; gap:.625rem; align-content:start }
    #av-demo .av-hero-wrap { position:relative }
    #av-demo .av-hero { width:100%; aspect-ratio:4/3; object-fit:cover; border-radius:var(--mv-radius-lg) }
    #av-demo .av-tools { position:absolute; top:.625rem; right:.625rem; display:flex; gap:.375rem }
    #av-demo .av-tools .mv-button { background:var(--mv-surface-raised) }
    #av-demo .av-tools svg { width:1rem; height:1rem }
    #av-demo .av-thumbs { display:grid; grid-template-columns:repeat(3,1fr); gap:.5rem }
    #av-demo .av-thumbs img { width:100%; aspect-ratio:1; object-fit:cover; border-radius:var(--mv-radius-md) }
    #av-demo .av-thumbs img:nth-child(2) { object-position:20% 80%; transform:scaleX(-1) }
    #av-demo .av-thumbs img:nth-child(3) { object-position:85% 30% }
    #av-demo .av-info { display:grid; gap:.75rem; align-content:start }
    #av-demo .av-brand { margin:0; color:var(--mv-fg-muted); font-size:.75rem; font-weight:600; letter-spacing:.06em; text-transform:uppercase }
    #av-demo .av-info h2 { margin:-.375rem 0 0; font-size:1.5rem; line-height:1.15; letter-spacing:-.02em }
    #av-demo .av-reviews { color:var(--mv-fg-muted); font-size:.8125rem; text-decoration:none; justify-self:start }
    #av-demo .av-reviews:hover { color:var(--mv-fg); text-decoration:underline }
    #av-demo .av-reviews span { color:var(--mv-warning); letter-spacing:.08em; margin-right:.25rem }
    #av-demo .av-price { display:flex; align-items:center; gap:.625rem; margin:0; font-size:1.375rem; font-weight:700; font-variant-numeric:tabular-nums }
    #av-demo .av-dot { width:.625rem; height:.625rem; border-radius:50%; background:var(--mv-success); box-shadow:0 0 0 3px color-mix(in oklab,var(--mv-success) 22%,transparent) }
    #av-demo .av-stock { color:var(--mv-fg-muted); font-size:.8125rem; font-weight:500 }
    #av-demo .av-buy { display:grid; grid-template-columns:7rem 1fr; gap:.625rem; align-items:end }
    #av-demo .av-buy > .mv-button { width:100% }
    #av-demo .av-h { margin:.25rem 0 -.25rem; font-size:.9375rem }
    #av-demo .av-perks { display:grid; gap:.375rem; margin:0; padding:0; list-style:none; font-size:.8125rem; color:var(--mv-fg-muted) }
    #av-demo .av-perks li { display:flex; justify-content:space-between; gap:.75rem; padding:.5rem .75rem; border-radius:var(--mv-radius-md); background:var(--mv-bg-muted) }
    #av-demo .av-perks a { color:var(--mv-fg); font-weight:500; white-space:nowrap }
    #av-demo .av-promo { display:grid; gap:.375rem }
    #av-demo .av-promo-row { display:flex; gap:.5rem }
    #av-demo .av-hint { margin:.75rem 0 0; color:var(--mv-fg-muted); font-size:.75rem; text-align:center }
  </style>

  <div class="av-bar">
    <p><strong>Agent view</strong>. Drag the scan line: on the right, the page as an AI agent or a screen reader perceives it. Click an issue to jump to it. The sample product page has deliberate flaws until you fix it.</p>
    <button class="mv-button" data-variant="outline" type="button" id="av-fix">Fix the page</button>
  </div>

  <mv-agent-vision id="av-vision" split="48">
    <article class="av-page" aria-labelledby="av-title">
      <nav class="mv-breadcrumb" aria-label="Breadcrumb">
        <ol>
          <li><a href="#home">Home</a></li>
          <li><a href="#trail">Trail</a></li>
          <li><span aria-current="page">Aster 3</span></li>
        </ol>
      </nav>

      <div class="av-grid">
        <div class="av-media">
          <div class="av-hero-wrap">
            <img class="av-hero" src="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 480 360%27%3E%3Cdefs%3E%3ClinearGradient id=%27bg%27 x1=%270%27 y1=%270%27 x2=%270%27 y2=%271%27%3E%3Cstop offset=%270%27 stop-color=%27oklch(0.95 0.015 150)%27/%3E%3Cstop offset=%271%27 stop-color=%27oklch(0.88 0.03 150)%27/%3E%3C/linearGradient%3E%3CradialGradient id=%27fl%27%3E%3Cstop offset=%270%27 stop-color=%27black%27 stop-opacity=%27.25%27/%3E%3Cstop offset=%271%27 stop-color=%27black%27 stop-opacity=%270%27/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect width=%27480%27 height=%27360%27 fill=%27url(%23bg)%27/%3E%3Cellipse cx=%27246%27 cy=%27286%27 rx=%27186%27 ry=%2716%27 fill=%27url(%23fl)%27/%3E%3Cpath d=%27M70 246 Q70 276 104 280 L386 280 Q424 278 428 246 Z%27 fill=%27oklch(0.3 0.03 160)%27/%3E%3Cpath d=%27M104 280v9M140 280v9M176 280v9M212 280v9M248 280v9M284 280v9M320 280v9M356 280v9M392 280v8%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-width=%2712%27/%3E%3Cpath d=%27M74 236 L426 236 Q430 254 416 262 L98 262 Q76 258 74 236Z%27 fill=%27oklch(0.96 0.01 90)%27/%3E%3Cpath d=%27M82 238 Q76 196 118 174 L192 136 Q212 98 252 94 L290 96 Q302 138 340 158 L392 182 Q428 200 426 238 Z%27 fill=%27oklch(0.66 0.16 40)%27/%3E%3Cpath d=%27M290 96 Q302 138 340 158 L392 182 Q428 200 426 238 L360 238 Q350 180 300 150 Z%27 fill=%27black%27 fill-opacity=%27.1%27/%3E%3Cpath d=%27M212 126 L262 148 M204 142 L254 164 M196 158 L246 180%27 stroke=%27oklch(0.96 0.01 90)%27 stroke-width=%277%27 stroke-linecap=%27round%27/%3E%3Cpath d=%27M104 218 Q230 200 416 220%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-opacity=%27.45%27 stroke-width=%276%27 fill=%27none%27/%3E%3Cpath d=%27M120 200 Q160 186 176 150%27 stroke=%27white%27 stroke-opacity=%27.35%27 stroke-width=%275%27 fill=%27none%27 stroke-linecap=%27round%27/%3E%3Ccircle cx=%27378%27 cy=%27206%27 r=%279%27 fill=%27oklch(0.96 0.01 90)%27 fill-opacity=%27.8%27/%3E%3C/svg%3E" alt="Aster 3 trail running shoe, side view" width="480" height="360">
            <div class="av-tools">
              <button class="mv-button" data-variant="outline" data-size="icon" data-shape="round" type="button" id="av-fav" data-audit-skip="Deliberate flaw: the sample page is broken on purpose so mv-agent-vision can flag it (Fix the page repairs it)">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.5-1.5 3-3.2 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.8 0-3 .5-4.5 2-1.5-1.5-2.7-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4 3 5.5l7 7z"/></svg>
              </button>
              <button class="mv-button" data-variant="outline" data-size="icon" data-shape="round" type="button" id="av-share" data-audit-skip="Deliberate flaw: the sample page is broken on purpose so mv-agent-vision can flag it (Fix the page repairs it)">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 13.5 6.8 4M15.4 6.5l-6.8 4"/></svg>
              </button>
            </div>
          </div>
          <div class="av-thumbs">
            <img src="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 480 360%27%3E%3Cdefs%3E%3ClinearGradient id=%27bg%27 x1=%270%27 y1=%270%27 x2=%270%27 y2=%271%27%3E%3Cstop offset=%270%27 stop-color=%27oklch(0.95 0.015 150)%27/%3E%3Cstop offset=%271%27 stop-color=%27oklch(0.88 0.03 150)%27/%3E%3C/linearGradient%3E%3CradialGradient id=%27fl%27%3E%3Cstop offset=%270%27 stop-color=%27black%27 stop-opacity=%27.25%27/%3E%3Cstop offset=%271%27 stop-color=%27black%27 stop-opacity=%270%27/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect width=%27480%27 height=%27360%27 fill=%27url(%23bg)%27/%3E%3Cellipse cx=%27246%27 cy=%27286%27 rx=%27186%27 ry=%2716%27 fill=%27url(%23fl)%27/%3E%3Cpath d=%27M70 246 Q70 276 104 280 L386 280 Q424 278 428 246 Z%27 fill=%27oklch(0.3 0.03 160)%27/%3E%3Cpath d=%27M104 280v9M140 280v9M176 280v9M212 280v9M248 280v9M284 280v9M320 280v9M356 280v9M392 280v8%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-width=%2712%27/%3E%3Cpath d=%27M74 236 L426 236 Q430 254 416 262 L98 262 Q76 258 74 236Z%27 fill=%27oklch(0.96 0.01 90)%27/%3E%3Cpath d=%27M82 238 Q76 196 118 174 L192 136 Q212 98 252 94 L290 96 Q302 138 340 158 L392 182 Q428 200 426 238 Z%27 fill=%27oklch(0.66 0.16 40)%27/%3E%3Cpath d=%27M290 96 Q302 138 340 158 L392 182 Q428 200 426 238 L360 238 Q350 180 300 150 Z%27 fill=%27black%27 fill-opacity=%27.1%27/%3E%3Cpath d=%27M212 126 L262 148 M204 142 L254 164 M196 158 L246 180%27 stroke=%27oklch(0.96 0.01 90)%27 stroke-width=%277%27 stroke-linecap=%27round%27/%3E%3Cpath d=%27M104 218 Q230 200 416 220%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-opacity=%27.45%27 stroke-width=%276%27 fill=%27none%27/%3E%3Cpath d=%27M120 200 Q160 186 176 150%27 stroke=%27white%27 stroke-opacity=%27.35%27 stroke-width=%275%27 fill=%27none%27 stroke-linecap=%27round%27/%3E%3Ccircle cx=%27378%27 cy=%27206%27 r=%279%27 fill=%27oklch(0.96 0.01 90)%27 fill-opacity=%27.8%27/%3E%3C/svg%3E" alt="Aster 3, full profile" width="160" height="160">
            <img src="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 480 360%27%3E%3Cdefs%3E%3ClinearGradient id=%27bg%27 x1=%270%27 y1=%270%27 x2=%270%27 y2=%271%27%3E%3Cstop offset=%270%27 stop-color=%27oklch(0.95 0.015 150)%27/%3E%3Cstop offset=%271%27 stop-color=%27oklch(0.88 0.03 150)%27/%3E%3C/linearGradient%3E%3CradialGradient id=%27fl%27%3E%3Cstop offset=%270%27 stop-color=%27black%27 stop-opacity=%27.25%27/%3E%3Cstop offset=%271%27 stop-color=%27black%27 stop-opacity=%270%27/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect width=%27480%27 height=%27360%27 fill=%27url(%23bg)%27/%3E%3Cellipse cx=%27246%27 cy=%27286%27 rx=%27186%27 ry=%2716%27 fill=%27url(%23fl)%27/%3E%3Cpath d=%27M70 246 Q70 276 104 280 L386 280 Q424 278 428 246 Z%27 fill=%27oklch(0.3 0.03 160)%27/%3E%3Cpath d=%27M104 280v9M140 280v9M176 280v9M212 280v9M248 280v9M284 280v9M320 280v9M356 280v9M392 280v8%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-width=%2712%27/%3E%3Cpath d=%27M74 236 L426 236 Q430 254 416 262 L98 262 Q76 258 74 236Z%27 fill=%27oklch(0.96 0.01 90)%27/%3E%3Cpath d=%27M82 238 Q76 196 118 174 L192 136 Q212 98 252 94 L290 96 Q302 138 340 158 L392 182 Q428 200 426 238 Z%27 fill=%27oklch(0.66 0.16 40)%27/%3E%3Cpath d=%27M290 96 Q302 138 340 158 L392 182 Q428 200 426 238 L360 238 Q350 180 300 150 Z%27 fill=%27black%27 fill-opacity=%27.1%27/%3E%3Cpath d=%27M212 126 L262 148 M204 142 L254 164 M196 158 L246 180%27 stroke=%27oklch(0.96 0.01 90)%27 stroke-width=%277%27 stroke-linecap=%27round%27/%3E%3Cpath d=%27M104 218 Q230 200 416 220%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-opacity=%27.45%27 stroke-width=%276%27 fill=%27none%27/%3E%3Cpath d=%27M120 200 Q160 186 176 150%27 stroke=%27white%27 stroke-opacity=%27.35%27 stroke-width=%275%27 fill=%27none%27 stroke-linecap=%27round%27/%3E%3Ccircle cx=%27378%27 cy=%27206%27 r=%279%27 fill=%27oklch(0.96 0.01 90)%27 fill-opacity=%27.8%27/%3E%3C/svg%3E" width="160" height="160" id="av-thumb" data-audit-skip="Deliberate flaw: the sample page is broken on purpose so mv-agent-vision can flag it (Fix the page repairs it)">
            <img src="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 480 360%27%3E%3Cdefs%3E%3ClinearGradient id=%27bg%27 x1=%270%27 y1=%270%27 x2=%270%27 y2=%271%27%3E%3Cstop offset=%270%27 stop-color=%27oklch(0.95 0.015 150)%27/%3E%3Cstop offset=%271%27 stop-color=%27oklch(0.88 0.03 150)%27/%3E%3C/linearGradient%3E%3CradialGradient id=%27fl%27%3E%3Cstop offset=%270%27 stop-color=%27black%27 stop-opacity=%27.25%27/%3E%3Cstop offset=%271%27 stop-color=%27black%27 stop-opacity=%270%27/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect width=%27480%27 height=%27360%27 fill=%27url(%23bg)%27/%3E%3Cellipse cx=%27246%27 cy=%27286%27 rx=%27186%27 ry=%2716%27 fill=%27url(%23fl)%27/%3E%3Cpath d=%27M70 246 Q70 276 104 280 L386 280 Q424 278 428 246 Z%27 fill=%27oklch(0.3 0.03 160)%27/%3E%3Cpath d=%27M104 280v9M140 280v9M176 280v9M212 280v9M248 280v9M284 280v9M320 280v9M356 280v9M392 280v8%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-width=%2712%27/%3E%3Cpath d=%27M74 236 L426 236 Q430 254 416 262 L98 262 Q76 258 74 236Z%27 fill=%27oklch(0.96 0.01 90)%27/%3E%3Cpath d=%27M82 238 Q76 196 118 174 L192 136 Q212 98 252 94 L290 96 Q302 138 340 158 L392 182 Q428 200 426 238 Z%27 fill=%27oklch(0.66 0.16 40)%27/%3E%3Cpath d=%27M290 96 Q302 138 340 158 L392 182 Q428 200 426 238 L360 238 Q350 180 300 150 Z%27 fill=%27black%27 fill-opacity=%27.1%27/%3E%3Cpath d=%27M212 126 L262 148 M204 142 L254 164 M196 158 L246 180%27 stroke=%27oklch(0.96 0.01 90)%27 stroke-width=%277%27 stroke-linecap=%27round%27/%3E%3Cpath d=%27M104 218 Q230 200 416 220%27 stroke=%27oklch(0.3 0.03 160)%27 stroke-opacity=%27.45%27 stroke-width=%276%27 fill=%27none%27/%3E%3Cpath d=%27M120 200 Q160 186 176 150%27 stroke=%27white%27 stroke-opacity=%27.35%27 stroke-width=%275%27 fill=%27none%27 stroke-linecap=%27round%27/%3E%3Ccircle cx=%27378%27 cy=%27206%27 r=%279%27 fill=%27oklch(0.96 0.01 90)%27 fill-opacity=%27.8%27/%3E%3C/svg%3E" alt="Quick-lace detail" width="160" height="160">
          </div>
        </div>

        <div class="av-info">
          <p class="av-brand">Fernpeak Supply Co. · Trail</p>
          <h2 id="av-title">Aster 3 Trail Running Shoe</h2>
          <a class="av-reviews" href="#avis"><span aria-hidden="true">★★★★☆</span>Read reviews (128)</a>
          <p class="av-price">$139.00 <span class="av-dot" title="In stock" id="av-dot"></span></p>

          <div class="av-buy">
            <div class="mv-field">
              <label class="mv-label" for="av-size">Size (US)</label>
              <select class="mv-select" id="av-size">
                <option>8</option><option>9</option><option selected>10</option><option>10.5</option><option>11</option>
              </select>
            </div>
            <div class="mv-button" data-size="lg" id="av-cart" onclick="this.textContent = 'Added ✓'">Add to cart</div>
          </div>

          <h4 class="av-h" id="av-heading">Shipping &amp; returns</h4>
          <ul class="av-perks">
            <li><span>Free shipping on orders over $75</span><a href="#shipping">Learn more</a></li>
            <li><span>Free returns within 30 days</span><a href="#returns">Learn more</a></li>
            <li><span>2-year workshop warranty</span><a href="#warranty">Learn more</a></li>
          </ul>

          <form class="av-promo" aria-label="Promo code" onsubmit="event.preventDefault()">
            <div class="av-promo-row">
              <input class="mv-input" id="av-code" placeholder="Promo code" autocomplete="off">
              <button class="mv-button" data-variant="secondary" type="submit">Apply</button>
            </div>
          </form>
        </div>
      </div>
    </article>
  </mv-agent-vision>
  <p class="av-hint">Arrow keys or <kbd class="mv-kbd">Home</kbd> / <kbd class="mv-kbd">End</kbd> on the handle · “Fix the page” applies the fixes live and re-runs the scan</p>

  <script type="module">
    const vision = document.getElementById("av-vision");
    const fix = document.getElementById("av-fix");
    const $ = (id) => document.getElementById(id);
    await customElements.whenDefined("mv-agent-vision");

    // Semantic twins of the broken elements, swapped in and out.
    const fakeCart = $("av-cart");
    const realCart = Object.assign(document.createElement("button"), { type: "button", className: "mv-button", textContent: "Add to cart" });
    realCart.dataset.size = "lg";
    const badHeading = $("av-heading");
    const goodHeading = Object.assign(document.createElement("h3"), { className: "av-h", textContent: badHeading.textContent });
    const label = Object.assign(document.createElement("label"), { className: "mv-label", htmlFor: "av-code", textContent: "Promo code" });
    const stock = Object.assign(document.createElement("span"), { className: "av-stock", textContent: "In stock" });
    const links = [...document.querySelectorAll("#av-demo .av-perks a")];
    const precise = ["Learn more about shipping", "Learn more about returns", "Learn more about the warranty"];

    const apply = (fixed) => {
      const toggle = (el, name, value) => (fixed ? el.setAttribute(name, value) : el.removeAttribute(name));
      toggle($("av-fav"), "aria-label", "Add to wishlist");
      toggle($("av-share"), "aria-label", "Share");
      toggle($("av-thumb"), "alt", "Lugged outsole, bottom view");
      (fixed ? fakeCart : realCart).replaceWith(fixed ? realCart : fakeCart);
      (fixed ? badHeading : goodHeading).replaceWith(fixed ? goodHeading : badHeading);
      const dot = $("av-dot");
      if (fixed) { dot.removeAttribute("title"); dot.setAttribute("aria-hidden", "true"); dot.after(stock); }
      else { dot.title = "In stock"; dot.removeAttribute("aria-hidden"); stock.remove(); }
      links.forEach((a, i) => toggle(a, "aria-label", precise[i]));
      if (fixed) document.querySelector("#av-demo .av-promo").prepend(label); else label.remove();
      fix.dataset.fixed = String(fixed);
      fix.textContent = fixed ? "Bring the flaws back" : "Fix the page";
    };
    fix.addEventListener("click", () => apply(fix.dataset.fixed !== "true"));

    vision.addEventListener("mv-agent-scan", (e) => console.info("mv-agent-scan", e.detail.score, e.detail.issues.length));
  </script>
</div>

API

Attributes

Name类型DefaultDescription
splitnumber (0-100)55Scan line position, as the % of the region shown in human view (the rest is agent view).
scopeCSS selectorRestricts the analysis to matching elements inside the region (default: all wrapped content).
showall | issuesallFull agent view, or only the boxes with an issue. Also driven by the toggle in the panel.

Properties

Name类型Description
splitnumberCurrent line position (read/write; dragging does not rewrite the attribute).
scorenumberAgent readability, 0-100: 100 − 7 per blocking issue − 3 per minor issue (a group of duplicate names counts once).
issues{ type, severity, severityLabel, title, what, note, fix, role, name, count }[]Detected issues, blockers first. severity: "blocker" | "minor". A non-enumerable elements property holds the affected nodes.
tree{ role, label, name, level, states, rect, issue }[]Perceived tree, JSON-serialisable: ARIA role, readable label, accessible name, states, rect relative to the region, issue if any.

Methods

NameDescription
scan()Re-runs the analysis now, announces the result and returns { score, issues } (the analysis also runs on its own, batched per frame).
focusIssue(index)Moves the line so the element is in agent view, scrolls it on screen if needed, selects and pulses its box, and announces the issue and its fix.

Events

NameDescription
mv-agent-scanAnalysis finished with a new result (or an explicit scan()). detail: { score, issues }.

Content structure

NameDescription
(content)One child element: the region to analyse (card, section, form, whole page…). It stays fully interactive on both sides of the line.

CSS classes

NameDescription
mv-agent-vision-layer / -box / -tagAgent view layer (aria-hidden), wireframe boxes (data-kind control | heading | media | container, data-issue blocker | minor, data-selected) and their tree line.
mv-agent-vision-rail / -handle / -scanSplitter: role=slider handle and glowing scan line.
mv-agent-vision-summary / -list / -issuePanel: readability gauge, counts, display toggle, issue list (buttons).

CSS variables

NameDefaultDescription
--mv-agent-vision-accentvar(--mv-accent)Scan line and interactive box color.
--mv-agent-vision-blockervar(--mv-danger)Blocking issue color (always paired with an icon and a label).
--mv-agent-vision-minorvar(--mv-warning)Minor issue color.
--mv-agent-vision-radiusvar(--mv-radius-xl)Agent view layer radius (match it to the region's).
--mv-agent-vision-grid16pxPitch of the grid that replaces the visual rendering.

Accessibility

The tool is accessible itself. The handle is a role="slider" (arrows ±2, Shift ±10, Page Up/Down ±10, Home = all agent view, End = all human view) with aria-valuetext "48% human view, 52% agent view". The agent view layer is aria-hidden and pointer-events: none: the region keeps its keyboard, clicks and accessibility tree intact on both sides, and since the layer covers the page, it draws its own focus ring over whatever region element has keyboard focus in agent view (a system Highlight outline in forced colors). The issue list is a real list of buttons (Tab, or Up/Down arrows, Home/End); the active issue carries aria-current. A polite live region announces the score when the analysis changes and, when an issue is selected, its description and fix. Severity is always an icon + label ("Blocker", "Minor") on top of color. Reduced motion: no typed-in labels or pulse, instant scrolling. No loop: everything is driven by MutationObserver, ResizeObserver and events, batched into one read-then-write pass per frame. Known limits: it is a design and QA aid, not a WCAG audit; the accessible name computation is simplified (no ::before / ::after content, shadow DOM or aria-owns); clickable non-semantic elements are only detected through onclick or cursor: pointer; the "color only" check only covers small dots and badges without text.

本页面由 AI 翻译。报告翻译问题