{
  "$comment": "@xxd/css 原生组件注册表：文档站、pnpm xxd ls / doc、xxd-ui Skill 都读它。改组件时同步改这里（registry.test.mjs 会检查类名真的存在于 CSS）。",
  "components": [
    {
      "name": "Text",
      "zh": "文字层级",
      "category": "基础",
      "classes": [
        "xxd-text",
        "xxd-text-pair"
      ],
      "attributes": {
        "data-variant": "display | heading1 | heading2 | heading3 | body | secondary | caption | mono | mono-secondary | success | danger | warning",
        "data-size": "xs | sm | base | lg",
        "data-truncate": "一行省略"
      },
      "markup": "<div class=\"xxd-text-pair\">\n  <h2 class=\"xxd-text\" data-variant=\"heading2\">外观</h2>\n  <p class=\"xxd-text\" data-variant=\"caption\">只影响这个插件</p>\n</div>\n<p class=\"xxd-text\">正文 13px <span class=\"xxd-text\" data-variant=\"mono\">--r-sm</span></p>",
      "react": "Text",
      "rules": [
        "字重只有 400 / 500 / 600",
        "相关的字靠得更近",
        "不改字距",
        "行内等宽字 0.92em"
      ],
      "summary": "标题、正文、说明、等宽字的固定几档：层级靠字号和字重，不靠颜色。",
      "a11y": [
        "标题用真正的 h1–h3（按页面层级，不跳级），不要用加粗的 p 冒充",
        "说明文字用 --muted（≥ 4.5:1），不要更浅"
      ]
    },
    {
      "name": "Interaction",
      "zh": "交互工具",
      "category": "基础",
      "classes": [
        "xxd-reveal",
        "xxd-concentric",
        "xxd-icon-line",
        "xxd-num",
        "xxd-bubble"
      ],
      "markup": "<div class=\"xxd-row\" style=\"border:1px solid var(--line)\"><div class=\"xxd-row__text\">悬停这一行</div><button class=\"xxd-btn xxd-reveal\" data-size=\"xs\">编辑</button></div>\n<div class=\"xxd-concentric\" style=\"--pad:4px;border:1px solid var(--line)\"><button class=\"xxd-btn\" data-size=\"sm\">同心圆角</button></div>",
      "js": "Xxd.showBubble(button, \"已复制\")",
      "rules": [
        "hover 进入立即变、离开 140ms 淡回",
        "悬停才出现的操作：触屏常显、键盘能到",
        "同心圆角：外 = 内 + 间距"
      ],
      "summary": "悬停才出现的操作（.xxd-reveal）、整行可点、按下下沉这类交互小工具。",
      "a11y": [
        "悬停才出现的按钮在键盘聚焦时也出现（:focus-within），触屏上一直显示",
        "整行可点时行里只放一个主链接，其余按钮单独可聚焦"
      ]
    },
    {
      "name": "Button",
      "zh": "按钮",
      "category": "操作",
      "classes": [
        "xxd-btn",
        "xxd-label-text"
      ],
      "attributes": {
        "data-variant": "primary | secondary（缺省）| ghost | outline | danger | danger-outline",
        "data-size": "xs | sm | md | lg",
        "data-icon-only": "纯图标（必须有 aria-label）",
        "aria-pressed": "切换态",
        "aria-busy": "进行中：宽度不跳，转圈居中；Xxd.runBusy(btn, task) 完成后图标变勾",
        "data-block": "撑满一行",
        "data-shape": "round：圆形图标按钮（跟随圆角开关）"
      },
      "markup": "<button class=\"xxd-btn\" data-variant=\"primary\">写入 X</button>\n<button class=\"xxd-btn\">保存草稿</button>\n<button class=\"xxd-btn\" data-variant=\"ghost\">稍后</button>\n<button class=\"xxd-btn\" data-variant=\"danger\">删除</button>\n<button class=\"xxd-btn\" data-variant=\"outline\">导出</button>\n<button class=\"xxd-btn\" data-size=\"xs\">xs</button>",
      "react": "Button",
      "rules": [
        "按钮文字描述下一次点击会做什么",
        "一处只放一个实心按钮",
        "破坏性操作写清楚，不只靠颜色",
        "§9",
        "长标签包在 <span> 里（或加 .xxd-label-text）才有省略号；按钮永远不会撑出容器，裸文字超长时只会被裁掉。"
      ],
      "js": "await Xxd.runBusy(button, () => save())",
      "sample": "<button class=\"xxd-btn\" data-variant=\"primary\">写入 X</button>",
      "states": [
        "default",
        "hover",
        "focus",
        "active",
        "disabled",
        "busy"
      ],
      "summary": "按钮文字描述下一次点击会做什么。一处只放一个实心按钮。",
      "examples": [
        {
          "title": "尺寸",
          "markup": "<button class=\"xxd-btn\" data-size=\"xs\">xs</button>\n<button class=\"xxd-btn\" data-size=\"sm\">sm</button>\n<button class=\"xxd-btn\">md</button>\n<button class=\"xxd-btn\" data-size=\"lg\">lg</button>"
        },
        {
          "title": "图标按钮",
          "markup": "<button class=\"xxd-btn\" data-icon-only aria-label=\"搜索\" data-tooltip=\"搜索 ⌘F\"><i data-xxd-icon=\"Search\"></i></button>\n<button class=\"xxd-btn\" data-icon-only data-shape=\"round\" aria-label=\"新建\" data-tooltip=\"新建\"><i data-xxd-icon=\"Plus\"></i></button>\n<button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only aria-label=\"更多\" data-tooltip=\"更多\"><i data-xxd-icon=\"Ellipsis\"></i></button>",
          "text": "纯图标按钮必须有 aria-label；悬停时 tooltip 连续出现。圆形图标按钮跟随圆角开关。"
        },
        {
          "title": "纯平 / 质感（D1 对比）",
          "markup": "<div class=\"xxd-cluster\" data-finish-preview=\"flat\"><button class=\"xxd-btn\" data-variant=\"primary\">写入 X</button><button class=\"xxd-btn\" data-variant=\"danger\">删除</button></div>",
          "text": "右上角「主色」里把按钮质感切到「质感」：实心按钮多一条顶部 1px 内高光、深一点的边，按下时下沉 0.5px。"
        },
        {
          "title": "进行中与完成",
          "markup": "<button class=\"xxd-btn\" data-variant=\"primary\" onclick=\"Xxd.runBusy(this, () => new Promise((r) => setTimeout(r, 1200)))\"><i data-xxd-icon=\"Save\" data-size=\"16\"></i>保存</button>",
          "text": "点一下：宽度不跳，转圈居中；完成后图标变勾 1 秒。"
        }
      ],
      "a11y": [
        "只有图标的按钮必须写 aria-label（tooltip 不算名字）",
        "进行中写 aria-busy=\"true\"：文字隐藏、出 spinner，读屏仍读按钮名",
        "切换按钮用 aria-pressed；按下 / 禁用 / 聚焦状态都不只靠颜色",
        "紧凑模式下按钮变矮，点击区仍不小于 --hit-min（40px，触屏 44px）"
      ]
    },
    {
      "name": "Link",
      "zh": "文字链接按钮",
      "category": "操作",
      "classes": [
        "xxd-link"
      ],
      "markup": "<button class=\"xxd-link\">改为新建一篇</button>",
      "react": "Link",
      "rules": [
        "40% 下划线，hover 变实"
      ],
      "attributes": {
        "data-variant": "inline（缺省）| plain | current",
        "data-external / target=_blank": "后面带外链图标"
      },
      "sample": "<a class=\"xxd-link\" href=\"#\">改为新建一篇</a>",
      "states": [
        "default",
        "hover",
        "focus"
      ],
      "summary": "看起来像链接的按钮：句子里的次要操作，比如「改为新建一篇」。",
      "a11y": [
        "跳转用 <a href>，执行操作用 <button class=\"xxd-link\">——读屏据此告诉用户会发生什么",
        "下划线常在，不只靠颜色区分"
      ]
    },
    {
      "name": "ButtonGroup",
      "zh": "按钮组",
      "category": "操作",
      "classes": [
        "xxd-btn-group"
      ],
      "markup": "<div class=\"xxd-btn-group\"><button class=\"xxd-btn\" data-size=\"sm\">左</button><button class=\"xxd-btn\" data-size=\"sm\">右</button></div>",
      "react": "ButtonGroup",
      "summary": "几个并列的按钮拼成一条：同一件事的几种做法。",
      "a11y": [
        "是一组互斥选项时改用分段控件（radiogroup），不要用按钮组",
        "需要时给外层 role=\"group\" 和 aria-label"
      ]
    },
    {
      "name": "Field",
      "zh": "字段",
      "category": "表单",
      "classes": [
        "xxd-field",
        "xxd-label",
        "xxd-help",
        "xxd-error",
        "xxd-input",
        "xxd-textarea"
      ],
      "attributes": {
        "aria-invalid": "出错时边框变危险色",
        "data-optional": "在 label 上：后面加「可选」",
        "data-autosize": "textarea 随内容长高"
      },
      "markup": "<div class=\"xxd-field\">\n  <label class=\"xxd-label\" for=\"title\">标题</label>\n  <input class=\"xxd-input\" id=\"title\" placeholder=\"给这篇文章起个名字\">\n  <p class=\"xxd-help\">最多 100 个字</p>\n</div>",
      "react": "Input",
      "rules": [
        "一个框只有一个框：聚焦时边框变深一次，不加外圈和内框",
        "§4"
      ],
      "sample": "<input class=\"xxd-input\" style=\"width:100%;max-width:120px\" placeholder=\"标题\">",
      "states": [
        "default",
        "hover",
        "focus",
        "disabled",
        "error",
        "readonly"
      ],
      "summary": "标签 + 输入框 + 说明 / 错误：表单的一行。",
      "a11y": [
        "label 的 for 指向输入框；说明和错误用 aria-describedby 连上",
        "出错时输入框写 aria-invalid=\"true\"，错误文字写清楚怎么改，不只变红"
      ]
    },
    {
      "name": "Select",
      "zh": "选择",
      "category": "表单",
      "classes": [
        "xxd-select"
      ],
      "attributes": {
        "data-variant": "bare：设置行里的「一个值 + 一个 chevron」"
      },
      "markup": "<label class=\"xxd-select\"><select aria-label=\"图片质量\"><option>高</option><option>标准</option></select></label>",
      "react": "Select",
      "rules": [
        "原生 <select>，不重写下拉",
        "设置行里用 bare：值 + chevron，不是盒子"
      ],
      "sample": "<label class=\"xxd-select\"><select aria-label=\"质量\"><option>高</option></select></label>",
      "states": [
        "default",
        "hover",
        "focus",
        "disabled"
      ],
      "summary": "原生 <select> 换了皮：选项少、不需要说明和分组时用它。",
      "a11y": [
        "没有可见标签时给 <select> 写 aria-label",
        "键盘和读屏行为都是浏览器原生的"
      ]
    },
    {
      "name": "Picker",
      "zh": "自绘选择",
      "category": "表单",
      "classes": [
        "xxd-picker",
        "xxd-picker__trigger",
        "xxd-picker__value",
        "xxd-option-group",
        "xxd-option-group__label"
      ],
      "summary": "原生 <select> 做不到的：分组、每项说明、图标、多选。简单场景继续用 Select（原生）。",
      "attributes": {
        "data-multiple": "多选（勾选，列表不关）",
        "data-placeholder": "没选时显示的字",
        "data-size": "sm",
        "aria-invalid": "出错",
        "data-disabled": "禁用"
      },
      "markup": "<div class=\"xxd-picker\" data-xxd-picker style=\"width:100%;max-width:220px\">\n  <button class=\"xxd-picker__trigger\"><span class=\"xxd-picker__value\"></span></button>\n  <div class=\"xxd-listbox\">\n    <div class=\"xxd-option-group\" role=\"group\" aria-label=\"画质\">\n      <div class=\"xxd-option-group__label\">画质</div>\n      <div class=\"xxd-option\" data-value=\"high\" aria-selected=\"true\"><span>高<small>2K，慢一点</small></span></div>\n      <div class=\"xxd-option\" data-value=\"standard\"><span>标准<small>1K，最快</small></span></div>\n    </div>\n    <div class=\"xxd-option-group\" role=\"group\" aria-label=\"其他\">\n      <div class=\"xxd-option\" data-value=\"auto\">跟随会员计划</div>\n      <div class=\"xxd-option\" data-value=\"x\" aria-disabled=\"true\">4K（即将推出）</div>\n    </div>\n  </div>\n</div>\n<div class=\"xxd-picker\" data-xxd-picker data-multiple data-placeholder=\"选择标签\" style=\"width:100%;max-width:220px\">\n  <button class=\"xxd-picker__trigger\"><span class=\"xxd-picker__value\"></span></button>\n  <div class=\"xxd-listbox\">\n    <div class=\"xxd-option\" data-value=\"poster\" aria-selected=\"true\">海报</div>\n    <div class=\"xxd-option\" data-value=\"cover\">封面</div>\n    <div class=\"xxd-option\" data-value=\"card\">卡片</div>\n  </div>\n</div>",
      "js": "picker.addEventListener(\"xxd:change\", (e) => e.detail.value)  // 多选时是数组；picker.dataset.value 逗号分隔",
      "react": "Select",
      "a11y": [
        "按钮 aria-haspopup=listbox，列表 aria-activedescendant 跟随高亮",
        "↑ ↓ Home End 移动，Enter / 空格选择，Esc 关，输入首字跳转"
      ]
    },
    {
      "name": "Search",
      "zh": "搜索框",
      "category": "表单",
      "classes": [
        "xxd-input-wrap",
        "xxd-input-clear"
      ],
      "markup": "<div class=\"xxd-input-wrap\">\n  <i data-xxd-icon=\"Search\" data-size=\"16\"></i>\n  <input class=\"xxd-input\" type=\"search\" placeholder=\"搜索设置（⌘F）\">\n  <button class=\"xxd-input-clear\" aria-label=\"清空\"><i data-xxd-icon=\"X\" data-size=\"14\"></i></button>\n</div>",
      "js": "Xxd.bindSearch(input, { onQuery: (q) => Xxd.markMatches(container, q) })",
      "rules": [
        "⌘F / Ctrl+F 跳到它，Esc 清空",
        "匹配的字用 mark.xxd-hit 标出来"
      ],
      "summary": "带放大镜和清空按钮的搜索框；Esc 先清空，再按一次才离开。",
      "a11y": [
        "type=\"search\"，没有可见标签时写 aria-label",
        "清空按钮有 aria-label；Esc 有内容时清空、没内容时交给外层"
      ]
    },
    {
      "name": "InputGroup",
      "zh": "输入组",
      "category": "表单",
      "classes": [
        "xxd-input-group",
        "xxd-input-group__addon",
        "xxd-input-group__suffix"
      ],
      "summary": "前后缀、内嵌按钮。整组是一个框，聚焦时整组边框变深一次。",
      "attributes": {
        "data-size": "sm",
        "aria-invalid": "出错"
      },
      "markup": "<div class=\"xxd-input-group\" style=\"width:100%;max-width:320px\">\n  <span class=\"xxd-input-group__addon\">https://</span>\n  <input aria-label=\"子域名\" value=\"today\">\n  <span class=\"xxd-input-group__suffix\">.xiaoxiaodong.ai</span>\n  <button class=\"xxd-btn\" data-variant=\"ghost\" data-size=\"sm\">检查</button>\n</div>",
      "react": "InputGroup",
      "rules": [
        "一个框只有一个框：前后缀在框里面，不是两个框拼起来"
      ],
      "a11y": [
        "前后缀是纯文字说明，输入框仍需要自己的 label 或 aria-label",
        "组里的按钮是独立的 Tab 停留点"
      ]
    },
    {
      "name": "SecretInput",
      "zh": "密钥输入",
      "category": "表单",
      "classes": [
        "xxd-secret"
      ],
      "summary": "API 密钥、令牌：默认遮住，可以显示 / 隐藏、一键复制。会员站的 API 设置和插件的 AI 服务密钥都用它。",
      "markup": "<div class=\"xxd-field\" style=\"width:100%;max-width:340px\">\n  <label class=\"xxd-label\" for=\"k1\">API 密钥</label>\n  <div class=\"xxd-input-group xxd-secret\" data-xxd-secret>\n    <input id=\"k1\" type=\"password\" value=\"sk_live_4f8a2c91d7e3b6a0\" readonly>\n    <button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"sm\" data-secret-toggle aria-label=\"显示\" aria-pressed=\"false\"><i data-xxd-icon=\"Eye\" data-size=\"16\"></i></button>\n    <button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"sm\" data-secret-copy aria-label=\"复制\"><i data-xxd-icon=\"Copy\" data-size=\"16\"></i></button>\n  </div>\n  <p class=\"xxd-help\">只保存在这台电脑上。</p>\n</div>",
      "react": "SensitiveInput",
      "a11y": [
        "显示 / 隐藏按钮是切换按钮（aria-pressed），文字跟着变",
        "复制成功的「已复制」通过 live region 读出"
      ],
      "rules": [
        "默认遮住；显示后不要自动再遮住（用户自己决定）"
      ]
    },
    {
      "name": "ClipboardText",
      "zh": "可复制的值",
      "category": "表单",
      "classes": [
        "xxd-clipboard"
      ],
      "summary": "只读的值加一个复制按钮；复制后按钮上方冒出「已复制」，图标变勾一下。",
      "attributes": {
        "data-size": "sm"
      },
      "markup": "<div class=\"xxd-clipboard\" style=\"width:100%;max-width:300px\">\n  <code>pnpm xxd new ~/js/插件-今日待办</code>\n  <button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"sm\" aria-label=\"复制\" data-copy=\"pnpm xxd new ~/js/插件-今日待办\"><i data-xxd-icon=\"Copy\" data-size=\"14\"></i></button>\n</div>",
      "react": "ClipboardText",
      "js": "[data-copy=\"文本\"] 或 [data-copy-target=\"#选择器\"]；data-copy-done 自定义气泡文字",
      "a11y": [
        "复制按钮有 aria-label；「已复制」通过 live region（role=status）读出",
        "被复制的值是可选中的文字，键盘用户也能自己复制"
      ]
    },
    {
      "name": "InlineCopy",
      "zh": "正文里可复制的字",
      "category": "表单",
      "classes": [
        "xxd-inline-copy"
      ],
      "markup": "<p class=\"xxd-text\">命令 <button class=\"xxd-inline-copy\" data-copy=\"pnpm xxd ls\" data-copy-done=\"已复制命令\">pnpm xxd ls<i data-xxd-icon=\"Copy\" data-size=\"12\"></i></button>，一点就复制。</p>",
      "react": "InlineCopyText",
      "rules": [
        "hover / 聚焦时才出复制图标；触屏常显"
      ],
      "summary": "句子里的一段可复制的字（命令、ID）：点一下复制，冒出「已复制」。",
      "a11y": [
        "本身是 <button>，读屏会读出里面的文字；需要时用 data-copy-done 换成更具体的提示",
        "复制结果通过 live region 读出"
      ]
    },
    {
      "name": "Switch",
      "zh": "开关",
      "category": "表单",
      "classes": [
        "xxd-switch"
      ],
      "markup": "<label class=\"xxd-choice\"><input type=\"checkbox\" role=\"switch\" class=\"xxd-switch\" checked>完成音效</label>",
      "react": "Switch",
      "rules": [
        "直角模式下开关也是方的（--r-pill）",
        "开 = --switch-on：墨色时是灰，有主色时是主色"
      ],
      "sample": "<input type=\"checkbox\" role=\"switch\" class=\"xxd-switch\" aria-label=\"开关\">",
      "states": [
        "default",
        "hover",
        "focus",
        "checked",
        "disabled"
      ],
      "attributes": {
        "data-size": "sm | （缺省）| lg",
        "role=\"switch\"": "必须"
      },
      "summary": "立即生效的开 / 关。需要「保存」才生效的选项用复选。",
      "a11y": [
        "<input type=\"checkbox\" role=\"switch\">，读屏读「开 / 关」",
        "标签写开着时是什么（「完成音效」），不要写「开启完成音效」"
      ]
    },
    {
      "name": "Checkbox",
      "zh": "复选",
      "category": "表单",
      "classes": [
        "xxd-check",
        "xxd-choice"
      ],
      "markup": "<label class=\"xxd-choice\"><input type=\"checkbox\" class=\"xxd-check\" checked>智能标点</label>",
      "react": "Checkbox",
      "sample": "<input type=\"checkbox\" class=\"xxd-check\" aria-label=\"复选\">",
      "states": [
        "default",
        "hover",
        "focus",
        "checked",
        "disabled"
      ],
      "attributes": {
        "aria-invalid": "出错",
        "indeterminate（JS）": "半选"
      },
      "summary": "多选、或需要确认后才生效的单个选项；支持半选。",
      "a11y": [
        "原生 checkbox，空格切换；半选用 indeterminate 属性，不是视觉样式",
        "整个 label 都可点，点击区不小于 --hit-min"
      ]
    },
    {
      "name": "Radio",
      "zh": "单选",
      "category": "表单",
      "classes": [
        "xxd-radio",
        "xxd-choice",
        "xxd-choices"
      ],
      "markup": "<fieldset class=\"xxd-choices\">\n  <legend>写入方式</legend>\n  <label class=\"xxd-choice\"><input type=\"radio\" class=\"xxd-radio\" name=\"w\" checked>只写这一篇</label>\n  <label class=\"xxd-choice\"><input type=\"radio\" class=\"xxd-radio\" name=\"w\">全部写入</label>\n</fieldset>",
      "react": "Radio",
      "rules": [
        "单选是语义上的圆，永远是圆",
        "一组选项是一个列表，不是一叠盒子"
      ],
      "sample": "<input type=\"radio\" class=\"xxd-radio\" aria-label=\"单选\">",
      "states": [
        "default",
        "hover",
        "focus",
        "checked",
        "disabled"
      ],
      "summary": "一组里只能选一个，选项 2–5 个、要同时看到时用它；更多用选择。",
      "a11y": [
        "放在 <fieldset> + <legend> 里，读屏先读组名",
        "同一组同一个 name：↑ ↓ 切换并选中，Tab 只停一次"
      ]
    },
    {
      "name": "Range",
      "zh": "滑块",
      "category": "表单",
      "classes": [
        "xxd-range",
        "xxd-range-pair",
        "xxd-range-value"
      ],
      "markup": "<div class=\"xxd-field\" style=\"width:100%;max-width:260px\"><label class=\"xxd-label\">同时生成</label><input type=\"range\" class=\"xxd-range\" data-fill min=\"1\" max=\"4\" value=\"2\" aria-label=\"同时生成\"></div>\n<div class=\"xxd-field\" style=\"width:100%;max-width:260px\"><span class=\"xxd-label\">价格区间 <span class=\"xxd-range-value\"></span></span><div class=\"xxd-range-pair\"><input type=\"range\" class=\"xxd-range\" min=\"0\" max=\"100\" value=\"20\" aria-label=\"最低\"><input type=\"range\" class=\"xxd-range\" min=\"0\" max=\"100\" value=\"70\" aria-label=\"最高\"></div></div>",
      "react": "Slider",
      "attributes": {
        "data-fill": "已填部分用主色",
        "data-ticks + --ticks": "刻度",
        ".xxd-range-pair": "两个把手的区间（最低不会超过最高）"
      },
      "summary": "拖一个数；data-fill 时左边填主色。区间用两个滑块（.xxd-range-pair）。",
      "a11y": [
        "原生 range：← → 微调，Page Up / Down 大步，Home / End 到头",
        "当前值要另外用文字显示（aria-valuetext 或旁边的数字），不能只靠滑块位置"
      ]
    },
    {
      "name": "TagInput",
      "zh": "标签输入",
      "category": "表单",
      "classes": [
        "xxd-taginput",
        "xxd-chip"
      ],
      "markup": "<div class=\"xxd-taginput\" data-xxd-taginput><input placeholder=\"回车添加\" aria-label=\"标签\"></div>",
      "js": "box.addEventListener(\"xxd:change\", (e) => e.detail.value)  // 字符串数组；Xxd.addTag(box, \"海报\")",
      "react": "TagInput",
      "summary": "回车或逗号把文字变成标签；退格删掉最后一个。",
      "a11y": [
        "每个标签的删除按钮有 aria-label（data-remove-label 可改）",
        "输入框没有可见标签时写 aria-label；变化派发 xxd:change"
      ]
    },
    {
      "name": "Combobox",
      "zh": "可输入的下拉",
      "category": "表单",
      "classes": [
        "xxd-listbox",
        "xxd-option",
        "xxd-option__hint"
      ],
      "markup": "<div data-xxd-combobox style=\"width:100%;max-width:240px\"><input class=\"xxd-input\" placeholder=\"输入筛选\" aria-label=\"主色\"><ul class=\"xxd-listbox\"><li class=\"xxd-option\" data-value=\"ink\">墨色</li><li class=\"xxd-option\" data-value=\"#1d9bf0\">X 蓝</li><li class=\"xxd-option\" data-value=\"#3b74b8\">湖蓝</li><li class=\"xxd-option\" data-value=\"#5a8a3c\">苔绿</li></ul></div>\n<div data-xxd-combobox data-multiple data-creatable style=\"width:100%;max-width:280px\"><div class=\"xxd-taginput\"><input placeholder=\"选或新建标签\" aria-label=\"标签\"></div><ul class=\"xxd-listbox\"><li class=\"xxd-option\">海报</li><li class=\"xxd-option\">封面</li><li class=\"xxd-option\">卡片</li></ul></div>",
      "js": "wrap.addEventListener(\"xxd:change\", (e) => e.detail.value)",
      "react": "Combobox",
      "attributes": {
        "data-multiple": "多选：选中的变成前面的 chip（.xxd-taginput 包住 input）",
        "data-creatable": "没有完全相同的项时出现「创建「xx」」",
        "data-create-label": "自定义「创建」的字",
        ".xxd-option-group": "分组（筛掉后整组隐藏）"
      },
      "summary": "可以打字筛选的下拉；data-multiple 多选成 chip，data-creatable 可以新建。",
      "a11y": [
        "输入框 role=combobox + aria-expanded，列表 role=listbox，高亮项用 aria-activedescendant",
        "↑ ↓ 移动，Enter 选择，Esc 收起"
      ]
    },
    {
      "name": "Tabs",
      "zh": "标签页",
      "category": "导航",
      "classes": [
        "xxd-tabs",
        "xxd-tabs__ink",
        "xxd-tabpanel",
        "xxd-count"
      ],
      "markup": "<div class=\"xxd-tabs\" data-xxd-tabs>\n  <button role=\"tab\" id=\"t1\" aria-controls=\"p1\" aria-selected=\"true\">写作</button>\n  <button role=\"tab\" id=\"t2\" aria-controls=\"p2\">记录</button>\n</div>\n<div id=\"p1\">…</div><div id=\"p2\">…</div>",
      "js": "list.addEventListener(\"xxd:change\", (e) => e.detail.value)",
      "react": "Tabs",
      "rules": [
        "基线上的文字 + 滑动的短墨线",
        "选中改颜色不加粗",
        "tab 下面不再画第二条线",
        "放不下时两端渐隐，选中项自动滚进视野"
      ],
      "summary": "切换同一处的几块内容；墨线滑到选中项。",
      "a11y": [
        "role=tablist / tab / tabpanel 由 JS 补齐，选中项 aria-selected，面板 aria-labelledby",
        "← → Home End 切换，Tab 只停在选中的那一个（roving tabindex）"
      ]
    },
    {
      "name": "Segmented",
      "zh": "分段控件",
      "category": "导航",
      "classes": [
        "xxd-segmented"
      ],
      "markup": "<div class=\"xxd-segmented\" role=\"radiogroup\" data-xxd-segmented aria-label=\"类型\">\n  <button role=\"radio\" aria-checked=\"true\">文本</button>\n  <button role=\"radio\" aria-checked=\"false\">图片</button>\n</div>",
      "js": "group.addEventListener(\"xxd:change\", (e) => e.detail.value)",
      "react": "Tabs variant=\"segmented\"",
      "rules": [
        "一个托盘，选中段像纸一样浮出来，不是框里套框"
      ],
      "summary": "2–4 个互斥选项挤在一条里：视图切换、类型选择。",
      "a11y": [
        "role=radiogroup + aria-label，每段 role=radio + aria-checked",
        "← → 切换，Tab 只停一次；选中态在高对比模式下有描边"
      ]
    },
    {
      "name": "Breadcrumbs",
      "zh": "面包屑",
      "category": "导航",
      "classes": [
        "xxd-breadcrumbs",
        "xxd-breadcrumbs__more"
      ],
      "markup": "<ol class=\"xxd-breadcrumbs\" data-collapse=\"4\"><li><a href=\"#\">首页</a></li><li><a href=\"#\">插件</a></li><li><a href=\"#\">长文发布</a></li><li><a href=\"#\">版本</a></li><li><a href=\"#\">1.8</a></li><li aria-current=\"page\">1.8.2</li></ol>",
      "react": "Breadcrumbs",
      "attributes": {
        "data-collapse": "超过 N 项（缺省 4）时中间折叠成「…」菜单",
        "data-size": "sm"
      },
      "summary": "当前页在哪：一行路径；data-collapse=\"4\" 时中间几级折进「…」菜单。",
      "a11y": [
        "外层 <nav aria-label=\"面包屑\">；当前页写 aria-current=\"page\"，不做成链接",
        "折叠按钮的 aria-label 写清还有几级，菜单项可用方向键"
      ]
    },
    {
      "name": "Pagination",
      "zh": "分页",
      "category": "导航",
      "classes": [
        "xxd-pagination",
        "xxd-pagination__gap",
        "xxd-pagination-bar",
        "xxd-pagination__info",
        "xxd-pagination__size"
      ],
      "markup": "<div class=\"xxd-pagination-bar\">\n  <span class=\"xxd-pagination__info\"></span>\n  <nav class=\"xxd-pagination\" data-xxd-pagination data-page=\"3\" data-total=\"230\" data-per-page=\"20\" aria-label=\"分页\"></nav>\n  <label class=\"xxd-pagination__size\">每页<span class=\"xxd-select\" data-size=\"sm\"><select><option>20</option><option>50</option></select></span></label>\n</div>\n<nav class=\"xxd-pagination\" data-simple aria-label=\"分页（简洁）\"><button aria-label=\"上一页\"><i data-xxd-icon=\"ChevronLeft\" data-size=\"14\"></i></button><button aria-current=\"page\">2</button><button aria-label=\"下一页\"><i data-xxd-icon=\"ChevronRight\" data-size=\"14\"></i></button></nav>",
      "react": "Pagination",
      "attributes": {
        "data-xxd-pagination": "按 data-page / data-total / data-per-page 生成按钮",
        "data-simple": "只留上一页 / 下一页"
      },
      "js": "nav.addEventListener(\"xxd:change\", (e) => e.detail.page)\nXxd.renderPagination(nav, { page: 1, total: 230, perPage: 20 })",
      "block": true,
      "summary": "页码、上一页 / 下一页、每页条数；JS 按 data-total 生成按钮，派发 xxd:change。",
      "a11y": [
        "<nav> 写 aria-label；当前页 aria-current=\"page\"；上一页 / 下一页按钮有文字标签（三语）",
        "首页和末页时对应按钮 disabled"
      ]
    },
    {
      "name": "Rail",
      "zh": "侧栏导航",
      "category": "导航",
      "classes": [
        "xxd-rail",
        "xxd-rail__spacer"
      ],
      "markup": "<nav class=\"xxd-rail\" aria-label=\"主导航\"><a href=\"#\" aria-current=\"page\"><i data-xxd-icon=\"House\"></i>首页</a><a href=\"#\"><i data-xxd-icon=\"Bookmark\"></i>收藏</a></nav>",
      "react": "Sidebar",
      "rules": [
        "图标 20px、标签 10px",
        "激活改颜色和底色，不改线宽"
      ],
      "summary": "窄的图标侧栏导航（插件侧边栏左边那一条）。",
      "a11y": [
        "<nav aria-label>；当前项 aria-current=\"page\"",
        "只有图标时每项都要有 aria-label 和 tooltip"
      ]
    },
    {
      "name": "Sidebar",
      "zh": "侧栏",
      "category": "导航",
      "classes": [
        "xxd-sidebar",
        "xxd-sidebar__head",
        "xxd-sidebar__body",
        "xxd-sidebar__foot",
        "xxd-sidebar__group",
        "xxd-sidebar__label",
        "xxd-sidebar__item",
        "xxd-sidebar__sub",
        "xxd-sidebar__resize"
      ],
      "summary": "完整的侧栏：分组、子菜单、徽标、折叠成 56px 图标栏（标签淡出、悬停出 tooltip）、拖动宽度；浮动 / 内嵌两种变体。",
      "block": true,
      "attributes": {
        "data-collapsed": "折叠成图标栏",
        "data-variant": "floating | inset",
        "--sidebar-w": "宽度（拖动时更新）",
        "data-min / data-max": "拖动范围（缺省 200–400）",
        "[data-xxd-sidebar-toggle=\"id\"]": "折叠 / 展开按钮",
        "data-sidebar-sub=\"id\"": "有子菜单的项"
      },
      "markup": "<div style=\"display:flex;height:420px;border:1px solid var(--line)\">\n<aside class=\"xxd-sidebar\" data-xxd-sidebar id=\"sb1\" style=\"--sidebar-w:232px\">\n  <div class=\"xxd-sidebar__head\"><span class=\"xxd-logo\"><span class=\"xxd-logo__mark\"></span><span data-sidebar-hide>小小东</span></span></div>\n  <nav class=\"xxd-sidebar__body\">\n    <div class=\"xxd-sidebar__group\">\n      <div class=\"xxd-sidebar__label\">内容</div>\n      <a class=\"xxd-sidebar__item\" href=\"#\" aria-current=\"page\"><i data-xxd-icon=\"House\" data-size=\"18\"></i><span>首页</span></a>\n      <a class=\"xxd-sidebar__item\" href=\"#\"><i data-xxd-icon=\"Bookmark\" data-size=\"18\"></i><span>收藏</span><span class=\"xxd-badge\">12</span></a>\n      <button class=\"xxd-sidebar__item\" aria-expanded=\"true\" data-sidebar-sub=\"sb1-sub\"><i data-xxd-icon=\"Folder\" data-size=\"18\"></i><span>风格库</span></button>\n      <div class=\"xxd-sidebar__sub\" id=\"sb1-sub\">\n        <a class=\"xxd-sidebar__item\" href=\"#\"><span>海报</span></a>\n        <a class=\"xxd-sidebar__item\" href=\"#\"><span>封面</span></a>\n      </div>\n    </div>\n    <div class=\"xxd-sidebar__group\">\n      <div class=\"xxd-sidebar__label\">账号</div>\n      <a class=\"xxd-sidebar__item\" href=\"#\"><i data-xxd-icon=\"Settings\" data-size=\"18\"></i><span>设置</span></a>\n    </div>\n  </nav>\n  <div class=\"xxd-sidebar__foot\"><button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"sm\" data-xxd-sidebar-toggle=\"sb1\" aria-label=\"折叠侧栏\"><i data-xxd-icon=\"PanelsTopLeft\" data-size=\"16\"></i></button></div>\n  <div class=\"xxd-sidebar__resize\" data-sidebar-resize></div>\n</aside>\n<main style=\"flex:1;min-width:0;padding:16px\" class=\"xxd-muted\">内容区</main>\n</div>",
      "react": "Sidebar",
      "a11y": [
        "折叠时每个项的名字自动变成 aria-label 和 tooltip",
        "子菜单按钮带 aria-expanded"
      ],
      "rules": [
        "折叠切换即时，标签淡出、图标不动（只动 opacity）",
        "激活改颜色和底色，不改线宽、不加粗"
      ]
    },
    {
      "name": "Topbar",
      "zh": "顶栏",
      "category": "导航",
      "classes": [
        "xxd-topbar",
        "xxd-topbar__title"
      ],
      "markup": "<header class=\"xxd-topbar\"><span class=\"xxd-topbar__title\">提示词</span><button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only aria-label=\"搜索\"><i data-xxd-icon=\"Search\"></i></button></header>",
      "rules": [
        "56px，分隔线属于这一行"
      ],
      "summary": "页面顶部一条：标题 + 少量图标按钮。",
      "a11y": [
        "只有图标的按钮都要有 aria-label",
        "整页只有一个顶栏时可以用 <header>（banner）"
      ]
    },
    {
      "name": "Toolbar",
      "zh": "工具条",
      "category": "导航",
      "classes": [
        "xxd-toolbar",
        "xxd-toolbar__item",
        "xxd-toolbar__sep",
        "xxd-toolbar__spacer"
      ],
      "summary": "编辑器的格式工具栏这类：一组小按钮，← → 在项之间移动（roving tabindex）。",
      "attributes": {
        "role=\"toolbar\"": "开启键盘移动",
        "data-framed": "带框",
        "data-size": "sm | lg",
        "aria-pressed": "切换态（加粗等）"
      },
      "markup": "<div class=\"xxd-toolbar\" role=\"toolbar\" aria-label=\"格式\" data-framed>\n  <button class=\"xxd-toolbar__item\" aria-label=\"加粗\" aria-pressed=\"true\" data-tooltip=\"加粗 ⌘B\"><i data-xxd-icon=\"Bold\" data-size=\"16\"></i></button>\n  <button class=\"xxd-toolbar__item\" aria-label=\"斜体\" data-tooltip=\"斜体 ⌘I\"><i data-xxd-icon=\"Italic\" data-size=\"16\"></i></button>\n  <button class=\"xxd-toolbar__item\" aria-label=\"标题\" data-tooltip=\"标题\"><i data-xxd-icon=\"Heading1\" data-size=\"16\"></i></button>\n  <span class=\"xxd-toolbar__sep\"></span>\n  <button class=\"xxd-toolbar__item\" aria-label=\"链接\" data-tooltip=\"链接 ⌘K\"><i data-xxd-icon=\"Link\" data-size=\"16\"></i></button>\n  <button class=\"xxd-toolbar__item\" aria-label=\"图片\" data-tooltip=\"图片\"><i data-xxd-icon=\"Image\" data-size=\"16\"></i></button>\n  <span class=\"xxd-toolbar__spacer\"></span>\n  <button class=\"xxd-toolbar__item\"><i data-xxd-icon=\"Eye\" data-size=\"14\"></i>预览</button>\n</div>",
      "react": "Toolbar",
      "a11y": [
        "← → Home End 在项之间移动；Tab 进出整个工具条只停一次"
      ]
    },
    {
      "name": "PageHeader",
      "zh": "页面头",
      "category": "导航",
      "classes": [
        "xxd-page-header",
        "xxd-page-header__row",
        "xxd-page-header__title",
        "xxd-page-header__desc",
        "xxd-page-header__actions"
      ],
      "summary": "面包屑、标题、说明、操作、tab —— 资源页、设置页的顶部。",
      "block": true,
      "markup": "<header class=\"xxd-page-header\">\n  <ol class=\"xxd-breadcrumbs\"><li><a href=\"#\">插件</a></li><li aria-current=\"page\">长文发布</li></ol>\n  <div class=\"xxd-page-header__row\">\n    <h1 class=\"xxd-page-header__title\">长文发布</h1>\n    <div class=\"xxd-page-header__actions\"><button class=\"xxd-btn\">打开商店页</button><button class=\"xxd-btn\" data-variant=\"primary\">发布新版本</button></div>\n  </div>\n  <p class=\"xxd-page-header__desc\">把 Markdown 写进 X 文章。v1.8.2 · 2,410 位用户</p>\n  <div class=\"xxd-tabs\" role=\"tablist\" aria-label=\"插件\" data-xxd-tabs><button role=\"tab\" aria-selected=\"true\">概览</button><button role=\"tab\">版本</button><button role=\"tab\">设置</button></div>\n</header>",
      "react": "PageHeader（区块）",
      "a11y": [
        "标题是页面的 h1；面包屑和 tab 各自带语义（nav / tablist）",
        "操作按钮按重要性从左到右，主操作在最后"
      ]
    },
    {
      "name": "SettingsGroup",
      "zh": "设置组",
      "category": "设置",
      "classes": [
        "xxd-settings",
        "xxd-group",
        "xxd-group__title",
        "xxd-group__body",
        "xxd-group__note",
        "xxd-row",
        "xxd-row__text",
        "xxd-row__title",
        "xxd-row__desc",
        "xxd-row__control"
      ],
      "markup": "<section class=\"xxd-group\">\n  <h2 class=\"xxd-group__title\">写入</h2>\n  <div class=\"xxd-group__body\">\n    <div class=\"xxd-row\">\n      <div class=\"xxd-row__text\"><div class=\"xxd-row__title\">写入标题</div><div class=\"xxd-row__desc\">第一行 # 标题作为文章标题</div></div>\n      <div class=\"xxd-row__control\"><input type=\"checkbox\" role=\"switch\" class=\"xxd-switch\" aria-label=\"写入标题\"></div>\n    </div>\n  </div>\n</section>",
      "rules": [
        "灰色小标题 + 一个带框的主体，行之间细线",
        "整个 tab 外面不画框",
        "a.xxd-row 整行可点，自动带 chevron",
        "§5"
      ],
      "summary": "设置页的一组：组名 + 若干行（标题、说明、控件）。",
      "a11y": [
        "组名用 h2（在页面 h1 下面），控件的 aria-label 和行标题一致",
        "说明文字可以用 aria-describedby 连到控件"
      ]
    },
    {
      "name": "Appearance",
      "zh": "外观设置组",
      "category": "设置",
      "classes": [
        "xxd-swatches",
        "xxd-swatch"
      ],
      "markup": "<div id=\"appearance\"></div>",
      "js": "Xxd.mountAppearance(el, { locale: \"zh-CN\", showDensity: false, lockCorners: false, languages, onLanguage })\n// 插件里：get/set 接 XxdExtension.createAppearanceStore({ key })",
      "rules": [
        "主题 · 圆角 · 主色 ·（密度）·（语言）—— 每个插件都要的那一组",
        "会员站 lockCorners: true"
      ],
      "summary": "主题、圆角、主色、密度、质感的现成设置组：Xxd.mountAppearance(container)。",
      "a11y": [
        "每个选项都是分段控件或开关，带 aria-label；色块是 radio，名字读得出来",
        "文字跟 <html lang> 或 locale 参数走三语"
      ]
    },
    {
      "name": "List",
      "zh": "列表",
      "category": "设置",
      "classes": [
        "xxd-list",
        "xxd-list__item",
        "xxd-list__main",
        "xxd-list__title",
        "xxd-list__meta"
      ],
      "attributes": {
        "data-interactive": "行可点（hover 底色）",
        "aria-selected": "选中行：一层淡色"
      },
      "markup": "<ul class=\"xxd-list\" data-interactive>\n  <li class=\"xxd-list__item\"><div class=\"xxd-list__main\"><div class=\"xxd-list__title\">一篇长文</div><div class=\"xxd-list__meta\">今天 14:02</div></div></li>\n  <li class=\"xxd-list__item\" aria-current=\"true\"><div class=\"xxd-list__main\"><div class=\"xxd-list__title\">另一篇</div></div></li>\n</ul>",
      "rules": [
        "只在行之间画线，第一行上、最后一行下不画",
        "选中是淡色，不加彩色边或竖条"
      ],
      "summary": "一列可点的行：标题、说明、右侧操作；当前项用 aria-current。",
      "a11y": [
        "选中的那一行写 aria-current=\"true\"（不是 aria-selected——那是给 listbox 选项用的）",
        "整行可点时用一个链接 / 按钮包住主区域，其余操作单独可聚焦"
      ]
    },
    {
      "name": "Glyphs",
      "zh": "符号格",
      "category": "设置",
      "classes": [
        "xxd-glyphs"
      ],
      "markup": "<div class=\"xxd-glyphs\"><button>①</button><button>②</button><button>③</button></div>",
      "rules": [
        "裸字符，hover 才填色"
      ],
      "summary": "一格一个符号的选择网格（编号、标点、表情）。",
      "a11y": [
        "每个按钮的 aria-label 写符号的名字（「带圈数字一」），不要只放字符",
        "当前选中的符号写 aria-pressed=\"true\""
      ]
    },
    {
      "name": "Dialog",
      "zh": "对话框",
      "category": "浮层",
      "classes": [
        "xxd-dialog",
        "xxd-dialog__head",
        "xxd-dialog__title",
        "xxd-dialog__body",
        "xxd-dialog__actions",
        "ui-dialog-cancel",
        "xxd-dialog__desc"
      ],
      "attributes": {
        "data-light-dismiss": "点遮罩关闭（确认类对话框不要加）",
        "data-size": "sm | （缺省 440）| lg | xl",
        "role=\"alertdialog\"": "必须回答的确认：不加 data-light-dismiss"
      },
      "markup": "<dialog class=\"xxd-dialog\" id=\"d1\" aria-labelledby=\"d1-title\">\n  <div class=\"xxd-dialog__head\"><h2 class=\"xxd-dialog__title\" id=\"d1-title\">写入哪篇文章？</h2></div>\n  <div class=\"xxd-dialog__body\"><p>默认不会替换已有内容。</p></div>\n  <div class=\"xxd-dialog__actions\">\n    <button class=\"xxd-btn ui-dialog-cancel\" data-xxd-dialog-close>取消</button>\n    <button class=\"xxd-btn\" data-variant=\"primary\" data-xxd-dialog-close=\"new\">写入为新文章</button>\n  </div>\n</dialog>\n<button class=\"xxd-btn\" data-xxd-dialog-open=\"d1\">打开</button>",
      "js": "await Xxd.confirmDialog({ title, message, confirmLabel: \"删除\", tone: \"danger\" })  // → true / false\nawait Xxd.confirmDelete({ name: \"长文发布\", what: \"这个插件的全部记录\" })  // 输入名字才能删",
      "react": "Dialog + DialogActions（@xxd/react）",
      "rules": [
        "取消在前（DOM 顺序），内容宽度、安静；主操作最后、最右",
        "破坏性确认默认聚焦取消",
        "§10"
      ],
      "summary": "必须先回答才能继续的问题：确认、选择、短表单。",
      "a11y": [
        "原生 <dialog> + showModal：背后的页面不可交互，Esc 关闭，焦点困在里面",
        "关闭后焦点回到打开它的按钮；破坏性操作默认聚焦在「取消」上"
      ]
    },
    {
      "name": "Dock",
      "zh": "底部任务栏",
      "category": "浮层",
      "classes": [
        "xxd-dock",
        "xxd-dock__status",
        "xxd-dock__tools",
        "xxd-dock__primary",
        "xxd-dock__danger"
      ],
      "markup": "<footer class=\"xxd-dock\">\n  <div class=\"xxd-dock__status\">30 字符 · 已保存</div>\n  <div class=\"xxd-dock__tools\"><button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"sm\" aria-label=\"封面\"><i data-xxd-icon=\"Image\"></i></button></div>\n  <div class=\"xxd-dock__primary\"><button class=\"xxd-btn\" data-variant=\"primary\" data-size=\"sm\">写入 X</button></div>\n</footer>",
      "rules": [
        "状态在开头 · 可撤销的工具一组 · 后果最大的下一步在末尾",
        "不是第二条导航"
      ],
      "summary": "插件侧边栏底部常驻的一条：状态 + 工具 + 主按钮。",
      "a11y": [
        "状态文字变化用 role=status 读出（如「已保存」）",
        "主按钮只有一个，写清楚做什么（「写入 X」）"
      ]
    },
    {
      "name": "Sheet",
      "zh": "从底部升起的面板",
      "category": "浮层",
      "classes": [
        "xxd-sheet-host",
        "xxd-scrim",
        "xxd-sheet",
        "xxd-sheet__head",
        "xxd-sheet__title",
        "xxd-sheet__body"
      ],
      "markup": "<div class=\"xxd-sheet-host\">\n  …\n  <div class=\"xxd-scrim\"></div>\n  <section class=\"xxd-sheet\" id=\"s1\" aria-labelledby=\"s1-title\"><div class=\"xxd-sheet__head\"><h3 class=\"xxd-sheet__title\" id=\"s1-title\">封面</h3><button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"sm\" data-xxd-sheet-close aria-label=\"关闭\"><i data-xxd-icon=\"X\"></i></button></div><div class=\"xxd-sheet__body\">…</div></section>\n  <footer class=\"xxd-dock\">… <button class=\"xxd-btn\" data-xxd-sheet-open=\"s1\">封面</button></footer>\n</div>",
      "js": "Xxd.openSheet(el) / Xxd.closeSheet(el)",
      "rules": [
        "升起 10px + 淡入 200ms，收回 120ms",
        "打开时 dock 主操作隐藏"
      ],
      "summary": "从 dock 升起的面板：设置、选择这类二级内容。",
      "a11y": [
        "打开时焦点进到面板里第一个可聚焦元素，Tab 不出面板，Esc 关闭",
        "关闭后焦点回到打开它的按钮；面板 aria-labelledby 指向标题"
      ]
    },
    {
      "name": "Panel",
      "zh": "右侧详情面板",
      "category": "浮层",
      "classes": [
        "xxd-panel",
        "xxd-panel__head",
        "xxd-panel__title",
        "xxd-panel__body",
        "xxd-panel-scrim"
      ],
      "markup": "<aside class=\"xxd-panel\" id=\"pd\" aria-labelledby=\"pd-title\"><div class=\"xxd-panel__head\"><h2 class=\"xxd-panel__title\" id=\"pd-title\">详情</h2><button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-xxd-panel-close aria-label=\"关闭\"><i data-xxd-icon=\"X\"></i></button></div><div class=\"xxd-panel__body\">…</div></aside>\n<button class=\"xxd-btn\" data-xxd-panel-open=\"pd\">详情</button>",
      "js": "Xxd.openPanel(el); Xxd.toast(\"已保存\", { within: el.querySelector(\".xxd-panel__body\") })",
      "react": "LayerDialog",
      "rules": [
        "面板里的操作，toast 出现在面板里"
      ],
      "summary": "从右侧滑出的详情面板（会员站资源详情）。",
      "a11y": [
        "aria-labelledby 指向标题；打开时焦点进面板，Esc 关闭并把焦点还回去",
        "关闭按钮有 aria-label"
      ]
    },
    {
      "name": "Menu",
      "zh": "菜单",
      "category": "浮层",
      "classes": [
        "xxd-menu",
        "xxd-menu__item",
        "xxd-menu__label",
        "xxd-menu__sep",
        "xxd-menu__kbd"
      ],
      "markup": "<button class=\"xxd-btn\" data-xxd-menu=\"m1\">更多<i data-xxd-icon=\"ChevronDown\" data-size=\"14\"></i></button>\n<div class=\"xxd-menu\" id=\"m1\" role=\"menu\" popover>\n  <button class=\"xxd-menu__item\" role=\"menuitem\"><i data-xxd-icon=\"Pen\" data-size=\"14\"></i>重命名<span class=\"xxd-menu__kbd\">F2</span></button>\n  <button class=\"xxd-menu__item\" role=\"menuitem\" data-submenu=\"m1-export\" aria-haspopup=\"menu\"><i data-xxd-icon=\"Download\" data-size=\"14\"></i>导出为</button>\n  <div class=\"xxd-menu\" id=\"m1-export\" role=\"menu\" popover=\"manual\">\n    <button class=\"xxd-menu__item\" role=\"menuitem\">Markdown</button>\n    <button class=\"xxd-menu__item\" role=\"menuitem\">图片（PNG）</button>\n  </div>\n  <div class=\"xxd-menu__sep\" role=\"separator\"></div>\n  <div class=\"xxd-menu__label\">排序</div>\n  <div role=\"group\" aria-label=\"排序\">\n    <button class=\"xxd-menu__item\" role=\"menuitemradio\" aria-checked=\"true\">最新在前</button>\n    <button class=\"xxd-menu__item\" role=\"menuitemradio\" aria-checked=\"false\">最早在前</button>\n  </div>\n  <button class=\"xxd-menu__item\" role=\"menuitemcheckbox\" aria-checked=\"true\">智能标点</button>\n  <div class=\"xxd-menu__sep\" role=\"separator\"></div>\n  <button class=\"xxd-menu__item\" role=\"menuitem\" data-tone=\"danger\"><i data-xxd-icon=\"Trash2\" data-size=\"14\"></i>删除</button>\n</div>",
      "react": "DropdownMenu",
      "rules": [
        "方向键 / Home / End / Esc；点外面关闭（Popover API）",
        "子菜单放在父菜单里面，popover=\"manual\"；→ 打开、← / Esc 回到父项",
        "鼠标移进哪一项焦点就在哪一项（只有一个高亮）"
      ],
      "js": "group.addEventListener(\"xxd:change\", (e) => e.detail.value)  // 单选组",
      "summary": "一列操作：更多、导出、排序；支持子菜单、单选组、勾选项。",
      "a11y": [
        "触发按钮 aria-haspopup + aria-expanded；↓ ↑ 打开并聚焦第一 / 最后一项",
        "↑ ↓ 移动，→ 进子菜单，← / Esc 退出，焦点回到触发按钮"
      ]
    },
    {
      "name": "Popover",
      "zh": "说明浮层",
      "category": "浮层",
      "classes": [
        "xxd-popover",
        "xxd-surface-float",
        "xxd-popover__title",
        "xxd-popover__text",
        "xxd-popover__close"
      ],
      "markup": "<button class=\"xxd-btn\" data-xxd-popover=\"p1\">这是什么？</button>\n<div class=\"xxd-popover\" id=\"p1\" popover>主色会被自动提亮或加深。</div>",
      "react": "Popover",
      "attributes": {
        "data-arrow": "带小箭头（跟随锚点）",
        "data-placement": "top | bottom | *-start | *-end"
      },
      "summary": "点开的说明或小表单：比 tooltip 能放更多，比对话框轻。",
      "a11y": [
        "原生 popover：点外面或 Esc 关闭，触发按钮 aria-expanded 同步",
        "里面有可交互的东西时，打开后焦点移进去；Esc 关闭后焦点回到触发按钮"
      ]
    },
    {
      "name": "Tooltip",
      "zh": "提示",
      "category": "浮层",
      "classes": [
        "xxd-tooltip"
      ],
      "markup": "<button class=\"xxd-btn\" data-icon-only aria-label=\"搜索\" data-tooltip=\"搜索 ⌘F\"><i data-xxd-icon=\"Search\"></i></button>",
      "react": "Tooltip",
      "rules": [
        "11px，小阴影；hover 400ms，聚焦立即",
        "连续出现：关掉后 300ms 内移到别的按钮立即出现（tooltip 组）"
      ],
      "attributes": {
        "data-tooltip": "提示文字",
        "data-tooltip-placement": "top（缺省）| bottom | top-start …",
        "data-tooltip-arrow": "带小箭头"
      },
      "summary": "悬停或键盘聚焦时出现的一句话说明；只放说明，不放操作。",
      "a11y": [
        "键盘聚焦也会出现，Esc 收起；通过 aria-describedby 连到按钮",
        "只有图标的按钮仍要有 aria-label——tooltip 是补充，不是名字"
      ]
    },
    {
      "name": "Toast",
      "zh": "轻提示",
      "category": "浮层",
      "classes": [
        "xxd-toast",
        "xxd-toast-region",
        "xxd-toast__action"
      ],
      "markup": "<button class=\"xxd-btn\" onclick=\"Xxd.toast('已保存草稿', { tone: 'success' })\">保存</button>\n<button class=\"xxd-btn\" onclick=\"Xxd.toast('已移到废纸篓', { action: { label: '撤销', onClick() { Xxd.toast('已恢复', { tone: 'success' }) } } })\">删除（可撤销）</button>\n<button class=\"xxd-btn\" onclick=\"Xxd.toast('第 ' + (window.__n = (window.__n || 0) + 1) + ' 条消息', { tone: 'info' })\">连发几条</button>",
      "js": "Xxd.toast(message, { tone: \"success\" | \"danger\" | \"warning\" | \"info\", within, duration, action: { label: \"撤销\", onClick }, stack: true })",
      "react": "Toast",
      "rules": [
        "就地出现（within）",
        "堆叠：多张叠成一摞，hover 展开；同一句话再来时不加新的，只轻轻动一下",
        "横向滑动关闭",
        "绿色只表示完成"
      ],
      "summary": "操作结果的一句话：已保存、已复制、失败可重试；同一句再来时只动一下。",
      "a11y": [
        "区域是 role=status + aria-live=polite，不打断正在读的内容",
        "带操作（撤销、重试）的缺省停 6 秒；鼠标放上去或键盘聚焦到里面时暂停计时"
      ]
    },
    {
      "name": "CommandPalette",
      "zh": "命令面板",
      "category": "浮层",
      "classes": [
        "xxd-palette",
        "xxd-palette__search",
        "xxd-palette__list",
        "xxd-palette__empty"
      ],
      "markup": "<button class=\"xxd-btn\" onclick=\"Xxd.openCommandPalette({ recentKey: 'xxd-site-demo', items: [{ label: '新建文章', hint: '⌘N', group: '写作', run(){} }, { label: '写入 X', group: '写作', run(){} }, { label: '切换主题', group: '外观', run(){ XxdTheme.set({ theme: XxdTheme.read().theme === 'dark' ? 'light' : 'dark' }) } }, { label: '切换圆角', group: '外观', run(){ XxdTheme.set({ corners: XxdTheme.read().corners === 'rounded' ? 'square' : 'rounded' }) } }] })\">命令面板 ⌘K</button>",
      "js": "Xxd.openCommandPalette({ items: [{ label, hint, group, run }], recentKey: \"my-ext-recent\", loading: false })\nXxd.bindCommandPalette(items)  // ⌘K / Ctrl+K",
      "react": "CommandPalette",
      "rules": [
        "按 group 分组，带组标题",
        "recentKey：没有输入时「最近使用」排最前",
        "loading：先显示「正在加载…」，再 setItems"
      ],
      "summary": "⌘K：搜命令、跳页面；支持分组、最近使用、异步加载。",
      "a11y": [
        "输入框 role=combobox，列表 role=listbox，高亮项 aria-activedescendant",
        "↑ ↓ 移动，Enter 运行，Esc 关闭并把焦点还回去"
      ]
    },
    {
      "name": "Badge",
      "zh": "徽标",
      "category": "反馈",
      "classes": [
        "xxd-badge"
      ],
      "attributes": {
        "data-tone": "action | success | danger | warning",
        "data-variant": "solid | outline | new",
        "data-appearance": "dot：点 + 字"
      },
      "markup": "<span class=\"xxd-badge\">草稿</span> <span class=\"xxd-badge\" data-tone=\"success\">已写入</span> <span class=\"xxd-badge\" data-tone=\"warning\">待更新</span> <span class=\"xxd-badge\" data-appearance=\"dot\" data-tone=\"success\">运行中</span> <span class=\"xxd-badge\" data-variant=\"new\">新</span>",
      "react": "Badge",
      "summary": "状态或数量的小标签：草稿、已写入、12。",
      "a11y": [
        "颜色之外一定有文字；只有数字时补一句说明（aria-label=\"12 条未读\"）",
        "淡底徽标的文字对比度 ≥ 4.5:1"
      ]
    },
    {
      "name": "Chip",
      "zh": "可选标签",
      "category": "反馈",
      "classes": [
        "xxd-chip"
      ],
      "markup": "<button class=\"xxd-chip\" aria-pressed=\"true\">海报</button> <button class=\"xxd-chip\">封面</button>",
      "sample": "<button class=\"xxd-chip\">海报</button>",
      "states": [
        "default",
        "hover",
        "focus",
        "pressed"
      ],
      "summary": "可以点选的标签：筛选、多选标记。",
      "a11y": [
        "是 <button aria-pressed>，读屏读出「已按下」",
        "选中态在高对比模式下有描边，不只靠底色"
      ]
    },
    {
      "name": "Dot",
      "zh": "状态点",
      "category": "反馈",
      "classes": [
        "xxd-dot"
      ],
      "markup": "<span class=\"xxd-dot\" data-tone=\"success\"></span>",
      "rules": [
        "语义圆，永远是圆"
      ],
      "summary": "一个小圆点表示状态（运行中、出错）。",
      "a11y": [
        "圆点只是装饰（aria-hidden 或旁边有文字）；状态必须同时写成文字",
        "不要只用颜色区分两种状态"
      ]
    },
    {
      "name": "Banner",
      "zh": "横幅",
      "category": "反馈",
      "classes": [
        "xxd-banner",
        "xxd-banner__title",
        "xxd-banner__text",
        "xxd-banner__actions",
        "xxd-banner__close"
      ],
      "markup": "<div class=\"xxd-banner\" data-tone=\"warning\" style=\"max-width:520px\"><i data-xxd-icon=\"TriangleAlert\"></i><div><p class=\"xxd-banner__title\">今天还能写入 1 篇</p><p class=\"xxd-banner__text\">明天 0 点恢复；会员不限。</p></div><div class=\"xxd-banner__actions\"><button class=\"xxd-btn\" data-size=\"sm\">开通会员</button></div><button class=\"xxd-btn xxd-banner__close\" data-variant=\"ghost\" data-icon-only data-size=\"xs\" data-banner-close aria-label=\"关闭\"><i data-xxd-icon=\"X\" data-size=\"14\"></i></button></div>",
      "react": "Banner",
      "attributes": {
        "data-tone": "warning | danger | success",
        "data-size": "sm",
        "[data-banner-close]": "关闭按钮（淡出后移除，派发 xxd:banner-close）"
      },
      "block": true,
      "summary": "页面或面板顶部的一条提醒：额度、更新、错误。",
      "a11y": [
        "重要提醒用 role=status（错误用 role=alert），图标 aria-hidden",
        "关闭按钮有 aria-label；关闭时焦点交给后面最近的可聚焦元素，不会掉到 body 上"
      ]
    },
    {
      "name": "Empty",
      "zh": "空状态",
      "category": "反馈",
      "classes": [
        "xxd-empty",
        "xxd-empty__title",
        "xxd-empty__text",
        "xxd-empty__actions"
      ],
      "markup": "<div class=\"xxd-empty\" data-size=\"sm\"><i data-xxd-icon=\"FileText\"></i><p class=\"xxd-empty__title\">还没有记录</p></div>\n<div class=\"xxd-empty\"><i data-xxd-icon=\"Package\"></i><p class=\"xxd-empty__title\">还没有插件</p><p class=\"xxd-empty__text\">用一行命令新建一个。</p><div class=\"xxd-clipboard\" data-size=\"sm\"><code>pnpm xxd new ~/js/插件-今日待办</code><button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"xs\" aria-label=\"复制\" data-copy=\"pnpm xxd new ~/js/插件-今日待办\"><i data-xxd-icon=\"Copy\" data-size=\"14\"></i></button></div></div>",
      "react": "Empty",
      "rules": [
        "说明 + 一个明确的下一步"
      ],
      "attributes": {
        "data-size": "sm | lg"
      },
      "summary": "还没有内容时：说明为什么空、下一步做什么。",
      "a11y": [
        "图标是装饰（aria-hidden）；标题写事实，正文写下一步",
        "下一步是按钮或链接，键盘可达"
      ]
    },
    {
      "name": "Progress",
      "zh": "进度",
      "category": "反馈",
      "classes": [
        "xxd-progress",
        "xxd-loadbar",
        "xxd-spinner",
        "xxd-skeleton",
        "xxd-meter",
        "xxd-meter__head",
        "xxd-meter__track",
        "xxd-meter__fill"
      ],
      "markup": "<progress class=\"xxd-progress\" value=\"60\" max=\"100\"></progress>\n<div class=\"xxd-loadbar\" role=\"progressbar\" aria-label=\"正在加载\"></div>\n<span class=\"xxd-spinner\"></span>",
      "react": "Loader / Meter",
      "rules": [
        "只动 transform"
      ],
      "summary": "确定进度用 <progress>，不确定用加载条 / spinner。",
      "a11y": [
        "<progress> 自带进度语义；加载条写 role=progressbar + aria-label",
        "spinner 是装饰时 aria-hidden，并在旁边用文字说正在做什么"
      ]
    },
    {
      "name": "Table",
      "zh": "表格",
      "category": "内容",
      "classes": [
        "xxd-table",
        "xxd-table-wrap",
        "xxd-table__check",
        "xxd-th-sort",
        "xxd-th-resize",
        "xxd-table__actions"
      ],
      "markup": "<div class=\"xxd-table-wrap\" style=\"max-width:560px\">\n<table class=\"xxd-table\" data-interactive>\n  <thead><tr>\n    <th class=\"xxd-table__check\"><input type=\"checkbox\" class=\"xxd-check\" data-select-all aria-label=\"全选\"></th>\n    <th aria-sort=\"ascending\" data-key=\"name\" data-sticky=\"start\"><button class=\"xxd-th-sort\">插件</button><span class=\"xxd-th-resize\"></span></th>\n    <th>版本</th>\n    <th aria-sort=\"none\" data-key=\"users\" data-align=\"end\"><button class=\"xxd-th-sort\">用户</button></th>\n    <th class=\"xxd-table__actions\"><span class=\"xxd-sr-only\">操作</span></th>\n  </tr></thead>\n  <tbody>\n    <tr><td class=\"xxd-table__check\"><input type=\"checkbox\" class=\"xxd-check\" data-select-row aria-label=\"选择 长文发布\"></td><td data-sticky=\"start\">长文发布</td><td>1.8.2</td><td data-align=\"end\">2,410</td><td class=\"xxd-table__actions\"><button class=\"xxd-btn xxd-reveal\" data-variant=\"ghost\" data-size=\"xs\">编辑</button></td></tr>\n    <tr><td class=\"xxd-table__check\"><input type=\"checkbox\" class=\"xxd-check\" data-select-row checked aria-label=\"选择 排序链接\"></td><td data-sticky=\"start\">排序链接</td><td>0.9.0</td><td data-align=\"end\">830</td><td class=\"xxd-table__actions\"><button class=\"xxd-btn xxd-reveal\" data-variant=\"ghost\" data-size=\"xs\">编辑</button></td></tr>\n    <tr><td class=\"xxd-table__check\"><input type=\"checkbox\" class=\"xxd-check\" data-select-row aria-label=\"选择 批量存图\"></td><td data-sticky=\"start\">批量存图</td><td>2.1.0</td><td data-align=\"end\">5,102</td><td class=\"xxd-table__actions\"><button class=\"xxd-btn xxd-reveal\" data-variant=\"ghost\" data-size=\"xs\">编辑</button></td></tr>\n  </tbody>\n  <tfoot><tr><td></td><td colspan=\"2\">3 个插件</td><td data-align=\"end\">8,342</td><td></td></tr></tfoot>\n</table></div>",
      "react": "Table",
      "attributes": {
        "data-interactive": "行可悬停",
        "data-density": "compact：紧凑",
        "data-sticky-head": "表头吸顶",
        "data-sticky（th/td）": "start | end：固定首列 / 末列",
        "aria-sort（th）": "none | ascending | descending；里面放 .xxd-th-sort",
        "[data-select-all] / [data-select-row]": "全选与行选择（半选自动）",
        ".xxd-th-resize": "拖动列宽"
      },
      "js": "table.addEventListener(\"xxd:sort\", (e) => e.detail /* { key, direction } */)\ntable.addEventListener(\"xxd:select\", (e) => e.detail.count)",
      "block": true,
      "summary": "对齐的数据：排序、全选、拖列宽、固定列、页脚合计。",
      "a11y": [
        "表头用 <th>，可排序列写 aria-sort 并把表头文字放在按钮里",
        "全选框 aria-label，半选是 indeterminate；横向滚动的容器键盘可聚焦"
      ]
    },
    {
      "name": "Collapsible",
      "zh": "折叠",
      "category": "内容",
      "classes": [
        "xxd-collapse"
      ],
      "markup": "<details class=\"xxd-collapse\" name=\"faq\" open><summary>为什么默认直角？</summary><p>会员站的硬规则：对话框、卡片、按钮、输入框都不许圆角。</p></details>\n<details class=\"xxd-collapse\" name=\"faq\"><summary>主色怎么挑？</summary><p>18 个预设，墨色是第一个也是默认。</p></details>\n<details class=\"xxd-collapse\" name=\"faq\"><summary>能用 React 吗？</summary><p>@xxd/react，同一套令牌。</p></details>",
      "react": "Collapsible",
      "attributes": {
        "name（details）": "同名的一组只开一个（手风琴，浏览器原生）"
      },
      "rules": [
        "展开只淡入 + 小位移，不做高度动画"
      ],
      "block": true,
      "summary": "原生 <details>：常见问题、可选的高级设置；同一个 name 时只开一个。",
      "a11y": [
        "<summary> 本身就是按钮：Enter / 空格展开，读屏读出展开状态",
        "不要把必须看到的信息藏在折叠里"
      ]
    },
    {
      "name": "Code",
      "zh": "代码",
      "category": "内容",
      "classes": [
        "xxd-code",
        "xxd-code-inline",
        "xxd-kbd",
        "xxd-copy",
        "xxd-code-block",
        "xxd-code__head"
      ],
      "markup": "<div class=\"xxd-code-block\" style=\"max-width:520px\">\n  <div class=\"xxd-code__head\">sidepanel.js<button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"xs\" aria-label=\"复制\" data-copy-target=\"#code-demo code\"><i data-xxd-icon=\"Copy\" data-size=\"14\"></i></button></div>\n  <pre class=\"xxd-code\" id=\"code-demo\" tabindex=\"0\" data-highlight data-lines><code data-lang=\"js\">// 外观设置存在 chrome.storage\nconst store = createAppearanceStore({ key: \"today-ui\" });\nawait store.load();\nXxd.toast(\"已保存\", { tone: \"success\" });</code></pre>\n</div>\n<p>行内代码 <code class=\"xxd-code-inline\">--r-sm</code>，按键 <kbd class=\"xxd-kbd\">⌘</kbd><kbd class=\"xxd-kbd\">K</kbd></p>",
      "js": "Xxd.highlight(codeElement, \"js\")",
      "react": "Code / ClipboardText",
      "rules": [
        "行内代码 12%，代码块 6%"
      ],
      "attributes": {
        "data-highlight": "轻量高亮（js / ts / json / css / html / bash，data-lang）",
        "data-lines": "行号",
        ".xxd-code__head": "文件名 + 复制"
      },
      "block": true,
      "summary": "代码块：轻量高亮、行号、复制按钮。",
      "a11y": [
        "能横向滚动的代码块可以用键盘聚焦（JS 自动补 tabindex=\"0\"），方向键滚动",
        "复制按钮有 aria-label；高亮颜色在亮暗两种外观下都 ≥ 4.5:1"
      ]
    },
    {
      "name": "Avatar",
      "zh": "头像",
      "category": "内容",
      "classes": [
        "xxd-avatar"
      ],
      "markup": "<span class=\"xxd-avatar\">东</span> <span class=\"xxd-avatar\" data-member>X</span>",
      "summary": "人的头像或首字；会员有一个小标记。",
      "a11y": [
        "头像图片写 alt（人名）；只有首字时整体给 aria-label 或旁边有名字",
        "会员标记不能只靠颜色——旁边的文字或 aria-label 里要写出来"
      ]
    },
    {
      "name": "Card",
      "zh": "卡片",
      "category": "内容",
      "classes": [
        "xxd-card",
        "xxd-card__media",
        "xxd-card__body",
        "xxd-card__title",
        "xxd-card__meta",
        "xxd-card__marks",
        "xxd-card__rating",
        "xxd-card__action",
        "xxd-grid",
        "xxd-masonry"
      ],
      "markup": "<div class=\"xxd-grid\" style=\"--grid-min:200px\">\n  <a class=\"xxd-card\" href=\"#\"><div class=\"xxd-card__media\" style=\"--ratio:3/4\"></div><div class=\"xxd-card__body\"><p class=\"xxd-card__title\">编辑式海报</p><span class=\"xxd-card__meta\">xxd-panel-001</span></div></a>\n</div>",
      "react": "LayerCard",
      "rules": [
        "卡片没有阴影，hover 只加深边线",
        "角标贴着角（读作界面）"
      ],
      "summary": "瀑布流 / 网格里的一张卡：图、标题、说明；整张可点。",
      "a11y": [
        "整张可点时卡片是一个 <a>，里面不要再放链接",
        "图片写 alt；纯装饰的图 alt=\"\""
      ]
    },
    {
      "name": "LayerCard",
      "zh": "分层卡片",
      "category": "内容",
      "classes": [
        "xxd-layer",
        "xxd-layer__secondary",
        "xxd-layer__primary"
      ],
      "summary": "主体（纸）+ 次要区（洗色）：卡片上方或下方的一条说明 / 操作。不要层层嵌套。",
      "markup": "<div class=\"xxd-layer\" style=\"width:100%;max-width:300px\">\n  <div class=\"xxd-layer__secondary\">下一步<button class=\"xxd-btn\" data-variant=\"ghost\" data-icon-only data-size=\"xs\" aria-label=\"前往\" style=\"margin-left:auto\"><i data-xxd-icon=\"ArrowRight\" data-size=\"14\"></i></button></div>\n  <div class=\"xxd-layer__primary\">用小小东 UI 做第一个插件</div>\n</div>",
      "react": "LayerCard",
      "rules": [
        "不要把分层卡片叠在分层卡片里"
      ],
      "a11y": [
        "次要区里的操作按钮有文字或 aria-label",
        "层级靠底色和分隔线，读屏按 DOM 顺序读：主体在前"
      ]
    },
    {
      "name": "Layout",
      "zh": "布局预设",
      "category": "内容",
      "classes": [
        "xxd-layout",
        "xxd-surface"
      ],
      "summary": "常用的两栏 / 三栏布局，720px 以下自动叠成一栏；面分三层：base（纸）、sunken（洗色）、raised（只给浮层）。",
      "block": true,
      "attributes": {
        "data-cols": "2up | 3up | 4up | 1-2 | 2-1 | 1-3 | side",
        "--layout-gap": "间距",
        "data-level（.xxd-surface）": "base | sunken | raised"
      },
      "markup": "<div class=\"xxd-layout\" data-cols=\"1-2\">\n  <div class=\"xxd-surface\" data-level=\"sunken\" style=\"padding:16px\">侧栏 1</div>\n  <div class=\"xxd-surface\" style=\"padding:16px\">主体 2</div>\n</div>",
      "react": "Grid / Surface",
      "a11y": [
        "布局只是 div，不带语义；页面的 main / nav / aside 由你自己的外层决定",
        "窄屏叠成一栏时 DOM 顺序就是阅读顺序"
      ]
    },
    {
      "name": "TableOfContents",
      "zh": "文章目录",
      "category": "内容",
      "classes": [
        "xxd-toc",
        "xxd-toc__title"
      ],
      "summary": "长文的目录：滚动到哪一节，哪一项高亮（data-spy）。",
      "attributes": {
        "data-spy": "跟着滚动高亮当前节",
        "data-depth=\"3\"（li）": "二级缩进"
      },
      "markup": "<nav aria-label=\"本文目录\"><p class=\"xxd-toc__title\">本文</p>\n<ul class=\"xxd-toc\" data-spy>\n  <li><a href=\"#demo\" aria-current=\"true\">演示</a></li>\n  <li data-depth=\"3\"><a href=\"#props\">属性与类名</a></li>\n  <li><a href=\"#rules\">规则</a></li>\n</ul></nav>",
      "react": "TableOfContents",
      "a11y": [
        "外层 <nav aria-label=\"本文目录\">；当前节 aria-current=\"true\"",
        "链接指向标题的 id；标题本身用真正的 h2 / h3"
      ]
    },
    {
      "name": "Logo",
      "zh": "小小东标志",
      "category": "内容",
      "classes": [
        "xxd-logo",
        "xxd-logo__mark"
      ],
      "summary": "暖黑方块上四个由满到淡的小方块，各插件同一个标；跟随主题（暗色时反过来）。",
      "attributes": {
        "data-size": "sm | （缺省 20px）| lg",
        "--logo-size": "任意尺寸"
      },
      "markup": "<span class=\"xxd-logo\" data-size=\"sm\"><span class=\"xxd-logo__mark\"></span>小小东</span>\n<span class=\"xxd-logo\"><span class=\"xxd-logo__mark\"></span>长文发布</span>\n<span class=\"xxd-logo\" data-size=\"lg\"><span class=\"xxd-logo__mark\"></span>小小东 UI</span>",
      "a11y": [
        "方块标是装饰（aria-hidden），名字是文字",
        "放在链接里时，链接的名字就是产品名"
      ]
    },
    {
      "name": "Prose",
      "zh": "阅读排版",
      "category": "内容",
      "classes": [
        "xxd-prose",
        "xxd-hit"
      ],
      "markup": "<article class=\"xxd-prose\"><h2>编辑式海报</h2><p>克制的编辑式粗野主义。</p><blockquote>动效用来揭示层级。</blockquote></article>",
      "rules": [
        "一个颜色四种强度：100% 指向、40% 线与标记、12% 行内代码、6% 代码块",
        "阅读列 680px"
      ],
      "summary": "长文排版：标题、段落、引用、列表、代码，阅读列宽。",
      "a11y": [
        "用真正的 h2 / h3、ul / ol、blockquote，读屏才能按结构跳",
        "链接有下划线；列表符号用主色只是装饰"
      ]
    },
    {
      "name": "Stat",
      "zh": "统计卡",
      "category": "展示",
      "summary": "标签 · 数字（等宽数字）· 和上期比的变化 · 迷你折线。变化只有涨（成功色）跌（危险色）两种含义色。",
      "classes": [
        "xxd-stats",
        "xxd-stat",
        "xxd-stat__label",
        "xxd-stat__value",
        "xxd-stat__unit",
        "xxd-stat__delta",
        "xxd-stat__spark"
      ],
      "markup": "<div class=\"xxd-stats\" style=\"width:100%;max-width:640px\">\n  <div class=\"xxd-stat\"><p class=\"xxd-stat__label\">本周写入</p><p class=\"xxd-stat__value\">1,284<span class=\"xxd-stat__unit\">篇</span></p><span class=\"xxd-stat__delta\" data-tone=\"up\">↑ 12.4% 比上周</span><span class=\"xxd-stat__spark\" data-spark=\"8,9,7,11,10,13,15\"></span></div>\n  <div class=\"xxd-stat\"><p class=\"xxd-stat__label\">新会员</p><p class=\"xxd-stat__value\">86</p><span class=\"xxd-stat__delta\" data-tone=\"down\">↓ 3 比上周</span><span class=\"xxd-stat__spark\" data-spark=\"12,11,13,10,9,10,8\"></span></div>\n  <div class=\"xxd-stat\"><p class=\"xxd-stat__label\">平均写入耗时</p><p class=\"xxd-stat__value\">2.4<span class=\"xxd-stat__unit\">秒</span></p><span class=\"xxd-stat__delta\">持平</span><span class=\"xxd-stat__spark\" data-spark=\"5,5,6,5,5,4,5\"></span></div>\n</div>",
      "a11y": [
        "迷你折线是 aria-hidden 的装饰；变化要写成文字（↑ 12.4% 比上周），不能只靠颜色。"
      ],
      "js": "el.innerHTML = Xxd.sparkline([3, 5, 4, 8])  // 或者 data-spark=\"3,5,4,8\" 再 Xxd.init()"
    },
    {
      "name": "Chart",
      "zh": "图表",
      "category": "展示",
      "summary": "ECharts + Xxd.chartOptions()：一条重点线用主色，其余是墨色的深浅；发丝网格线；切主题、主色自动重画。也能直接喂给 Kumo 的 <Chart options>。",
      "classes": [
        "xxd-chart",
        "xxd-chart-card",
        "xxd-chart-card__head",
        "xxd-chart-card__title",
        "xxd-chart-card__meta"
      ],
      "markup": "<div style=\"display:grid;gap:16px;width:100%;max-width:720px\">\n<div class=\"xxd-chart-card\">\n  <div class=\"xxd-chart-card__head\"><h3 class=\"xxd-chart-card__title\">每天写入篇数</h3><span class=\"xxd-chart-card__meta\">最近 14 天</span></div>\n  <div class=\"xxd-chart\" id=\"chart-line\"></div>\n</div>\n<div class=\"xxd-chart-card\">\n  <div class=\"xxd-chart-card__head\"><h3 class=\"xxd-chart-card__title\">各插件用户</h3></div>\n  <div class=\"xxd-chart\" id=\"chart-bar\" style=\"--chart-h:220px\"></div>\n</div>\n</div>\n<script>\n  if (window.echarts) {\n    const days = Array.from({ length: 14 }, (_, i) => `${i + 1}日`);\n    const line = echarts.init(document.getElementById(\"chart-line\"));\n    const lineOpt = () => Xxd.chartOptions({ legend: {}, xAxis: { type: \"category\", data: days, boundaryGap: false }, yAxis: { type: \"value\" }, series: [{ name: \"长文发布\", type: \"line\", data: [42, 48, 45, 60, 58, 66, 71, 69, 80, 78, 85, 90, 88, 96] }, { name: \"图片排序\", type: \"line\", data: [20, 22, 25, 24, 28, 27, 30, 33, 31, 35, 34, 36, 38, 40] }] });\n    line.setOption(lineOpt());\n    Xxd.watchChartTheme(line, lineOpt);\n    const bar = echarts.init(document.getElementById(\"chart-bar\"));\n    const barOpt = () => Xxd.chartOptions({ xAxis: { type: \"category\", data: [\"长文发布\", \"排序链接\", \"批量存图\", \"今日待办\"] }, yAxis: { type: \"value\" }, series: [{ type: \"bar\", data: [2410, 830, 5102, 312] }] });\n    bar.setOption(barOpt());\n    Xxd.watchChartTheme(bar, barOpt);\n    new ResizeObserver(() => { line.resize(); bar.resize(); }).observe(document.getElementById(\"chart-line\").parentElement.parentElement);\n  }\n</script>",
      "a11y": [
        "图表旁边要有一句话结论或一张表；颜色之外，线宽也区分重点线。"
      ],
      "js": "chart.setOption(Xxd.chartOptions(option))\nXxd.watchChartTheme(chart, () => Xxd.chartOptions(option))\n// React：<Chart echarts={echarts} options={Xxd.chartOptions(option)} />"
    }
  ]
}