{
  "schema": 1,
  "tokensVersion": "1.0.0",
  "tokensCss": "tokens.css",
  "count": 374,
  "categories": [
    { "name": "入场", "count": 8 },
    { "name": "加载", "count": 7 },
    { "name": "微交互", "count": 6 },
    { "name": "光标", "count": 6 },
    { "name": "滚动", "count": 6 },
    { "name": "过渡", "count": 6 },
    { "name": "拖拽", "count": 6 },
    { "name": "布局", "count": 6 },
    { "name": "数据", "count": 13 },
    { "name": "图表", "count": 18 },
    { "name": "反馈", "count": 23 },
    { "name": "排印", "count": 7 },
    { "name": "无障碍", "count": 12 },
    { "name": "表单", "count": 27 },
    { "name": "导航", "count": 24 },
    { "name": "浮层", "count": 11 },
    { "name": "列表", "count": 15 },
    { "name": "手势", "count": 28 },
    { "name": "内容", "count": 39 },
    { "name": "长文", "count": 10 },
    { "name": "表格", "count": 21 },
    { "name": "状态页", "count": 8 },
    { "name": "媒体", "count": 33 },
    { "name": "SVG 与路径", "count": 10 },
    { "name": "3D 与透视", "count": 17 },
    { "name": "滚动驱动", "count": 7 }
  ],
  "items": [
    {"id":"blur-in","cat":"入场","t":"模糊聚焦","en":"blur-in","d":"内容从模糊到清晰，适合弹窗、图片、AI 生成结果。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/blur-in.html","css":"blocks/blur-in.css","js":"blocks/blur-in.js"}},
    {"id":"choreography","cat":"入场","t":"错峰编排","en":"choreography","d":"一个面板打开时，标题先、正文后、按钮最后；顺序对了才显得\"有秩序\"。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/choreography.html","css":"blocks/choreography.css","js":"blocks/choreography.js"}},
    {"id":"enter-exit","cat":"入场","t":"进出场配对","en":"enter/exit pair","d":"进的曲线要弹、出的曲线要快；同一条曲线进出就会\"黏\"。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/enter-exit.html","css":"blocks/enter-exit.css","js":"blocks/enter-exit.js"}},
    {"id":"fade-stagger","cat":"入场","t":"错峰淡入","en":"staggered fade-up","d":"列表/卡片依次出现，每项延迟 40ms；最基础也最耐看的入场。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/fade-stagger.html","css":"blocks/fade-stagger.css","js":"blocks/fade-stagger.js"}},
    {"id":"line-draw-title","cat":"入场","t":"标题描线","en":"line draw title","d":"线用 scaleX 而不是 width，宽度每帧都要重排，标题会跟着抖一下；280ms 走完再让副标题淡入，延迟正好等于线的时长，两者不在中间叠——读者先看到一条线把版面定住，再读出下面那行小字。记忆点：先画线，再说话。","motion":"印刷","tags":["标题","描线","副标题","入场"],"hasInit":true,"files":{"html":"blocks/line-draw-title.html","css":"blocks/line-draw-title.css","js":"blocks/line-draw-title.js"}},
    {"id":"mask-reveal","cat":"入场","t":"遮罩揭示","en":"clip reveal","d":"用 clip-path 从上往下「掀开」，适合标题与大幅图片。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/mask-reveal.html","css":"blocks/mask-reveal.css"}},
    {"id":"pop-spring","cat":"入场","t":"弹簧弹出","en":"spring pop","d":"弹出确认、新建成功这类「有物理感」的时刻；别用在长列表上。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/pop-spring.html","css":"blocks/pop-spring.css","js":"blocks/pop-spring.js"}},
    {"id":"stagger-grid","cat":"入场","t":"网格错峰","en":"stagger grid","d":"3×3 按到中心的切比雪夫距离分三圈：0 / 120 / 280ms，第二圈的延迟等于第一圈的时长、第三圈又等于第二圈，队列首尾相接不重叠；中心先落位是因为视线本来就先落在正中，四角最后补齐，读者感到方格是从中间铺开的，而不是从左上角扫过来。记忆点：从中心炸开。","motion":"物理","tags":["入场","网格","错峰","中心"],"hasInit":true,"files":{"html":"blocks/stagger-grid.html","css":"blocks/stagger-grid.css","js":"blocks/stagger-grid.js"}},
    {"id":"four-states","cat":"加载","t":"四态转场","en":"empty / loading / error / success","d":"同一个容器里换内容，容器不跳；这是\"稳\"的来源。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/four-states.html","css":"blocks/four-states.css","js":"blocks/four-states.js"}},
    {"id":"optimistic","cat":"加载","t":"乐观上屏","en":"optimistic send","d":"按下发送立刻出现半透明消息，失败再标红；这是聊天里最重要的一个感。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/optimistic.html","css":"blocks/optimistic.css","js":"blocks/optimistic.js"}},
    {"id":"progress-ring","cat":"加载","t":"环形进度","en":"progress ring","d":"未知时长用无限转；已知进度用 determinate。","motion":"机械","tags":[],"hasInit":false,"files":{"html":"blocks/progress-ring.html","css":"blocks/progress-ring.css"}},
    {"id":"skeleton","cat":"加载","t":"骨架到内容","en":"skeleton → content","d":"先给形状再给内容，比转圈更稳；加载超过 300ms 才出现。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/skeleton.html","css":"blocks/skeleton.css","js":"blocks/skeleton.js"}},
    {"id":"skeleton-set","cat":"加载","t":"骨架三种","en":"skeleton variants","d":"列表 / 卡片 / 对话三种骨架，和真实内容同形状。","motion":"机械","tags":[],"hasInit":false,"files":{"html":"blocks/skeleton-set.html","css":"blocks/skeleton-set.css"}},
    {"id":"sweep-glow","cat":"加载","t":"边框流光","en":"border sweep","d":"AI 生成中/保存中的轻提示；一圈光沿着边框走，不闪屏。","motion":"机械","tags":[],"hasInit":false,"files":{"html":"blocks/sweep-glow.html","css":"blocks/sweep-glow.css"}},
    {"id":"typing","cat":"加载","t":"打字指示","en":"typing dots","d":"AI 正在生成但还没出字时的等待感。","motion":"机械","tags":[],"hasInit":false,"files":{"html":"blocks/typing.html","css":"blocks/typing.css"}},
    {"id":"checkbox-draw","cat":"微交互","t":"勾选手绘","en":"checkbox draw","d":"勾是一条 pathLength=1 的描边，280ms 画完；再叠一条偏移 1px、四成透明的同形线当套印偏差——机器画的勾没有这层错位，而正是它让勾像手画的。","motion":"印刷","tags":["微交互","表单","勾选","套印"],"hasInit":false,"files":{"html":"blocks/checkbox-draw.html","css":"blocks/checkbox-draw.css"}},
    {"id":"copy-check","cat":"微交互","t":"复制变对勾","en":"copy → check morph","d":"复制类按钮把图标原地换成对勾，2 秒后复原；别弹 toast 打断。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/copy-check.html","css":"blocks/copy-check.css","js":"blocks/copy-check.js"}},
    {"id":"icon-morph-menu","cat":"微交互","t":"汉堡变叉","en":"hamburger morph","d":"三条 1px 线在 40px 按钮里留 6px 间距，比 4px 更接近字排的行距；中间那条先退 120ms（它没法参与 X），上下两条再用物理曲线 280ms 合拢——三条一起转会像图案翻转，不像折叠。","motion":"物理","tags":["微交互","菜单","汉堡","折叠"],"hasInit":true,"files":{"html":"blocks/icon-morph-menu.html","css":"blocks/icon-morph-menu.css","js":"blocks/icon-morph-menu.js"}},
    {"id":"press-depth","cat":"微交互","t":"按压下沉","en":"press depth","d":"按下时缩小 + 阴影收起，模拟真实按键。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/press-depth.html","css":"blocks/press-depth.css"}},
    {"id":"ripple","cat":"微交互","t":"水波按压","en":"ripple","d":"点击处扩散一圈，给「按到了」最直接的反馈。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/ripple.html","css":"blocks/ripple.css","js":"blocks/ripple.js"}},
    {"id":"toggle-spring","cat":"微交互","t":"弹簧开关","en":"spring switch","d":"开关的滑块带一点过冲，比线性更「活」。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/toggle-spring.html","css":"blocks/toggle-spring.css","js":"blocks/toggle-spring.js"}},
    {"id":"cursor-grid-light","cat":"光标","t":"光标点亮网格","en":"grid spotlight","d":"亮度按与光标的欧氏距离分三档（1 / 2.2 / 3.2 格），不用连续值：连续亮度在 180ms 里会糊成一片，分档才看得出一圈一圈；最近一圈不加延迟，加了就变成追着光标跑。","motion":"机械","tags":["光标","网格","跟随","距离"],"hasInit":true,"files":{"html":"blocks/cursor-grid-light.html","css":"blocks/cursor-grid-light.css","js":"blocks/cursor-grid-light.js"}},
    {"id":"cursor-state-machine","cat":"光标","t":"光标状态机","en":"cursor states","d":"本例让光标在悬停、按下与等待状态间变化，帮助理解操作进度。尺寸、形状、颜色、时长与曲线均可重定；等待应由真实任务状态驱动。","motion":"机械","tags":["光标","状态","拖拽","等待"],"hasInit":true,"files":{"html":"blocks/cursor-state-machine.html","css":"blocks/cursor-state-machine.css","js":"blocks/cursor-state-machine.js"}},
    {"id":"custom-cursor","cat":"光标","t":"自定义光标","en":"custom cursor","d":"一个跟随的小圆点，按下时变大；页面内才生效。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/custom-cursor.html","css":"blocks/custom-cursor.css","js":"blocks/custom-cursor.js"}},
    {"id":"magnetic","cat":"光标","t":"磁吸按钮","en":"magnetic button","d":"指针靠近时按钮轻微跟随，离开回弹；只给一个主按钮用。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/magnetic.html","css":"blocks/magnetic.css","js":"blocks/magnetic.js"}},
    {"id":"tilt","cat":"光标","t":"3D 倾斜卡","en":"pointer tilt","d":"卡片随指针轻微倾斜；角度控制在 6° 内，否则廉价。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/tilt.html","css":"blocks/tilt.css","js":"blocks/tilt.js"}},
    {"id":"tooltip-follow","cat":"光标","t":"跟随提示","en":"cursor-follow tooltip","d":"提示跟着指针走，比固定在元素上更少遮挡。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/tooltip-follow.html","css":"blocks/tooltip-follow.css","js":"blocks/tooltip-follow.js"}},
    {"id":"parallax","cat":"滚动","t":"视差分层","en":"parallax layers","d":"前景动得多、背景动得少；只做 2–3 层，多了会晕。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/parallax.html","css":"blocks/parallax.css","js":"blocks/parallax.js"}},
    {"id":"progress-scroll","cat":"滚动","t":"滚动进度条","en":"scroll progress","d":"阅读进度；用 CSS 滚动驱动动画，不跑 JS。","motion":"机械","tags":[],"hasInit":false,"files":{"html":"blocks/progress-scroll.html","css":"blocks/progress-scroll.css"}},
    {"id":"reveal-scroll","cat":"滚动","t":"滚动揭示","en":"IntersectionObserver reveal","d":"进入视口才出现；首屏以下的内容不要做，容易被误认为加载失败。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/reveal-scroll.html","css":"blocks/reveal-scroll.css","js":"blocks/reveal-scroll.js"}},
    {"id":"scrolly","cat":"滚动","t":"滚动叙事","en":"scrollytelling","d":"滚到第几屏就点亮第几步；适合讲\"这件事怎么发生的\"。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/scrolly.html","css":"blocks/scrolly.css","js":"blocks/scrolly.js"}},
    {"id":"snap-carousel","cat":"滚动","t":"吸附轮播","en":"scroll-snap","d":"一屏一卡、停得准；比 JS 轮播轻，触屏手感天然好。","motion":"物理","tags":[],"hasInit":false,"files":{"html":"blocks/snap-carousel.html","css":"blocks/snap-carousel.css"}},
    {"id":"sticky-stack","cat":"滚动","t":"粘性堆叠","en":"sticky stack","d":"卡片一张张叠上来（sticky + top 递增），列表有层级感。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/sticky-stack.html","css":"blocks/sticky-stack.css"}},
    {"id":"flip-list","cat":"过渡","t":"列表重排","en":"FLIP reorder","d":"排序/筛选时元素交换位置而不是瞬间跳变；用 transform 记首末位。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/flip-list.html","css":"blocks/flip-list.css","js":"blocks/flip-list.js"}},
    {"id":"morph-button","cat":"过渡","t":"容器变形","en":"morph button → input","d":"按钮被点后原地变成输入框；宽度、圆角、内边距一起过渡，不是闪现。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/morph-button.html","css":"blocks/morph-button.css","js":"blocks/morph-button.js"}},
    {"id":"shared-element","cat":"过渡","t":"共享元素转场","en":"shared element","d":"同一张卡从列表\"长\"进详情；靠 view-transition-name 让浏览器接管首末位。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/shared-element.html","css":"blocks/shared-element.css","js":"blocks/shared-element.js"}},
    {"id":"theme-crossfade","cat":"过渡","t":"主题过渡","en":"theme crossfade","d":"换深浅色时整页平滑过渡；用 View Transitions + 一次性覆盖。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/theme-crossfade.html","css":"blocks/theme-crossfade.css","js":"blocks/theme-crossfade.js"}},
    {"id":"transition-wipe-diagonal","cat":"过渡","t":"斜切擦除","en":"diagonal wipe","d":"边界斜 20%（顶边比底边多走 20% 宽），斜边一有角度，眼睛先看到「被刮开」而不是「淡入」；新内容同时从 -10px 推到 0，才像被带过来。整版靛蓝是本批两处破格之一——擦除的边只有色差才看得见。","motion":"印刷","tags":["过渡","擦除","对角线","换版"],"hasInit":true,"files":{"html":"blocks/transition-wipe-diagonal.html","css":"blocks/transition-wipe-diagonal.css","js":"blocks/transition-wipe-diagonal.js"}},
    {"id":"view-transition","cat":"过渡","t":"视图过渡","en":"View Transitions API","d":"页面/列表切换的整块过渡；支持的浏览器自动生效，不支持就直接切。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/view-transition.html","css":"blocks/view-transition.css","js":"blocks/view-transition.js"}},
    {"id":"drag-deck","cat":"拖拽","t":"卡片堆拖拽","en":"drag deck","d":"卡片堆通过拖动距离与速度决定回位或甩出，避免轻微调整触发误操作。序号、层间比例、间距与倾斜只是本例参数，可按内容与设备调整，无需刻意破格。","motion":"物理","tags":["拖拽","卡片","甩出","堆叠"],"hasInit":true,"files":{"html":"blocks/drag-deck.html","css":"blocks/drag-deck.css","js":"blocks/drag-deck.js"}},
    {"id":"drag-ghost-follow","cat":"拖拽","t":"拖拽残影","en":"ghost trail","d":"残影每帧按 0.3 追指针：0.5 就变成钉在指针上、没有重量，0.15 会一直掉半个身位、像卡住了。原件压到 0.28 不透明度也有上下限——再高会和残影抢注意力，再低就看不出「它原来在哪」，而「原来在哪」正是这条要传达的信息。","motion":"物理","tags":["拖拽","残影","跟随","重量"],"hasInit":true,"files":{"html":"blocks/drag-ghost-follow.html","css":"blocks/drag-ghost-follow.css","js":"blocks/drag-ghost-follow.js"}},
    {"id":"drag-scroll","cat":"拖拽","t":"拖拽滚动","en":"drag to scroll","d":"横向列表按住拖动；触屏天然支持，桌面补这一段。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/drag-scroll.html","css":"blocks/drag-scroll.css","js":"blocks/drag-scroll.js"}},
    {"id":"drag-snap-grid","cat":"拖拽","t":"网格吸附","en":"snap grid","d":"吸附半径 26px 是照 48px 的格子算的：半格 24px 再加 2px 容错——半径只要小于半格，手指压着方块偏一格就永远吸不进去，人会觉得这功能坏了。对齐提示线只画 1px：它的作用是解释落点，不是参与构图，粗一格就把格子本身的秩序压掉了。格子松手用 280ms 落位，比 180ms 慢一点，落点才看得出是「归位」而不是被甩进去。","motion":"物理","tags":["拖拽","吸附","网格","对齐"],"hasInit":true,"files":{"html":"blocks/drag-snap-grid.html","css":"blocks/drag-snap-grid.css","js":"blocks/drag-snap-grid.js"}},
    {"id":"drag-swap","cat":"拖拽","t":"两卡互换","en":"drag to swap","d":"判定用「两张卡中心相距 58px 以内」而不是「碰到边缘」：手指会盖住接触点，按接触判定永远慢半拍，人会觉得没反应。目标卡只让 22px——正好是两卡间隙的一半，再多它就贴到画框边上，看着像目标卡先跑了；让位用 180ms、落位用 280ms，先让后落才有先后顺序。","motion":"物理","tags":["拖拽","互换","排序","卡片"],"hasInit":true,"files":{"html":"blocks/drag-swap.html","css":"blocks/drag-swap.css","js":"blocks/drag-swap.js"}},
    {"id":"drag-threshold-hint","cat":"拖拽","t":"边界提示","en":"drag threshold","d":"越界后位移按 0.18 折算，是要把「拖不动」做成「拖得动但明显费劲」：折算到 0 就像卡死，到 0.3 又像还能拖出去。箭头要等你推过 6px 才亮——短促的抖手不该惊动提示，那 6px 就是「手抖」和「想出去」的分界。松手 280ms 弹回，比 180ms 多留一点被拉回去的感觉。","motion":"物理","tags":["拖拽","边界","阻力","提示"],"hasInit":true,"files":{"html":"blocks/drag-threshold-hint.html","css":"blocks/drag-threshold-hint.css","js":"blocks/drag-threshold-hint.js"}},
    {"id":"accordion","cat":"布局","t":"手风琴展开","en":"accordion","d":"高度自动过渡（用 grid-template-rows 0fr→1fr，不需要知道内容高度）。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/accordion.html","css":"blocks/accordion.css","js":"blocks/accordion.js"}},
    {"id":"bento-grid","cat":"布局","t":"便当格","en":"bento grid","d":"大小不一的格子拼成一屏；重点用面积表达，不用再加一个“重点”标签。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/bento-grid.html","css":"blocks/bento-grid.css"}},
    {"id":"container-query","cat":"布局","t":"容器查询","en":"container query","d":"组件跟着「自己所在栏宽」变形，而不是跟着窗口。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/container-query.html","css":"blocks/container-query.css"}},
    {"id":"sidebar-collapse","cat":"布局","t":"侧栏折叠","en":"collapsible sidebar","d":"图标条 ↔ 文字栏，宽度过渡 + 文字淡出；折叠后仍能点。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/sidebar-collapse.html","css":"blocks/sidebar-collapse.css","js":"blocks/sidebar-collapse.js"}},
    {"id":"splitter","cat":"布局","t":"可拖分栏","en":"resizable splitter","d":"两栏之间拖宽窄，双击复位；编辑器/对话列表常用。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/splitter.html","css":"blocks/splitter.css","js":"blocks/splitter.js"}},
    {"id":"table-to-cards","cat":"布局","t":"表格转卡片","en":"table → cards","d":"窄屏时表格变成一张张卡；不是缩小表格，是换一种读法。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/table-to-cards.html","css":"blocks/table-to-cards.css","js":"blocks/table-to-cards.js"}},
    {"id":"bars-grow","cat":"数据","t":"柱状生长","en":"bars grow","d":"图表首次出现时从 0 长到目标值，错峰 40ms。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/bars-grow.html","css":"blocks/bars-grow.css"}},
    {"id":"calendar-day-distance","cat":"数据","t":"日历日期间隔","en":"calendar day distance","d":"按 UTC 日历日计算日期差，避免夏令时造成少一天；不包含结束日，可切换包含首尾。","motion":"无动效","tags":["数据","日历日期间隔","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/calendar-day-distance.html","css":"blocks/calendar-day-distance.css","js":"blocks/calendar-day-distance.js"}},
    {"id":"count-up","cat":"数据","t":"数字滚动","en":"count-up","d":"金额、计数变化时滚一下；时长 500–900ms，别超过 1s。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/count-up.html","css":"blocks/count-up.css","js":"blocks/count-up.js"}},
    {"id":"donut","cat":"数据","t":"环形占比","en":"donut chart","d":"一眼看构成；中心放总数，别放图例。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/donut.html","css":"blocks/donut.css"}},
    {"id":"duration-list-sum","cat":"数据","t":"多段时长累计","en":"duration list sum","d":"按 分:秒 累加多段时长，秒数必须小于 60；总时长支持超过一小时。","motion":"无动效","tags":["数据","多段时长累计","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/duration-list-sum.html","css":"blocks/duration-list-sum.css","js":"blocks/duration-list-sum.js"}},
    {"id":"flip-number","cat":"数据","t":"数字翻牌","en":"flip number","d":"数字变化时上下翻，而不是整体替换；计数、时钟有\"机械感\"。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/flip-number.html","css":"blocks/flip-number.css","js":"blocks/flip-number.js"}},
    {"id":"gauge","cat":"数据","t":"半圆仪表","en":"gauge","d":"范围型指标（完成度/健康度）比柱状更直观。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/gauge.html","css":"blocks/gauge.css"}},
    {"id":"heatmap","cat":"数据","t":"热力格","en":"heatmap","d":"密度型数据（每天活跃/提交量）；颜色要单调递进。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/heatmap.html","css":"blocks/heatmap.css"}},
    {"id":"numeric-series-summary","cat":"数据","t":"数列基础统计","en":"numeric series summary","d":"逗号或换行分隔有限数字，拒绝无效项；计数、合计、均值和中位数来自输入数列。","motion":"无动效","tags":["数据","数列基础统计","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/numeric-series-summary.html","css":"blocks/numeric-series-summary.css","js":"blocks/numeric-series-summary.js"}},
    {"id":"stat-spark","cat":"数据","t":"指标卡 + 迷你折线","en":"stat card + sparkline","d":"一个数 + 一条趋势线，比纯数字有信息量。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/stat-spark.html","css":"blocks/stat-spark.css"}},
    {"id":"timeline","cat":"数据","t":"竖向时间线","en":"timeline","d":"按时间讲过程；适合\"这件事怎么一步步走到今天\"。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/timeline.html","css":"blocks/timeline.css"}},
    {"id":"unit-price-comparison","cat":"数据","t":"单位成本比较","en":"unit price comparison","d":"按价格除以数量比较样例方案，数量必须大于零；显示输入假设，不构成真实报价。","motion":"无动效","tags":["数据","单位成本比较","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/unit-price-comparison.html","css":"blocks/unit-price-comparison.css","js":"blocks/unit-price-comparison.js"}},
    {"id":"weighted-score-calculator","cat":"数据","t":"自定义权重评分","en":"weighted score calculator","d":"评分与权重均由用户输入，权重无需凑满 100，但总权重必须为正；结果展示计算依据。","motion":"无动效","tags":["数据","自定义权重评分","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/weighted-score-calculator.html","css":"blocks/weighted-score-calculator.css","js":"blocks/weighted-score-calculator.js"}},
    {"id":"annotation-band","cat":"图表","t":"目标带与标注","en":"annotation band","d":"目标不是一个点，是一个可接受区间：画成一条线，人只会盯着「差 0.3」，画成带子，才知道一直在区间里。事件标注用一根细竖线加一句短语，不写长句——标注是路标，不是注释文章。","motion":"无动效","tags":["目标带","标注","趋势","图表"],"hasInit":true,"files":{"html":"blocks/annotation-band.html","css":"blocks/annotation-band.css","js":"blocks/annotation-band.js"}},
    {"id":"area-stack","cat":"图表","t":"堆叠面积","en":"stacked area","d":"堆叠面积图用于比较总量与各部分贡献，单层高度受下层累积影响，不宜按普通折线读取。颜色、明度或纹理可按项目选择，并提供图例与可读标签。","motion":"印刷","tags":["面积","堆叠","构成","图表"],"hasInit":true,"files":{"html":"blocks/area-stack.html","css":"blocks/area-stack.css","js":"blocks/area-stack.js"}},
    {"id":"bar-grouped","cat":"图表","t":"分组柱","en":"grouped bars","d":"两根柱之间留 4px、组与组之间留 18px：间距就是分组信号，两组一样宽反而分不出谁跟谁比。基線从 0 起，不给「截断轴」——截断能让差距看着翻倍，那是骗人。悬停整组变色，是为了让你比较一组里的两根，而不是单根。","motion":"印刷","tags":["柱状","对比","分组","图表"],"hasInit":true,"files":{"html":"blocks/bar-grouped.html","css":"blocks/bar-grouped.css","js":"blocks/bar-grouped.js"}},
    {"id":"bullet-chart","cat":"图表","t":"子弹图","en":"bullet chart","d":"一根横条同时给三件事：实际走到哪、目标在哪、量程多大。背景三段是量程分区（差 / 中 / 好），不是三个数据；把分区做成一样深，它们就会跟实际值抢读。目标线是竖线，不画成第二根条——它只是一个位置。","motion":"无动效","tags":["子弹图","目标","量程","图表"],"hasInit":false,"files":{"html":"blocks/bullet-chart.html","css":"blocks/bullet-chart.css"}},
    {"id":"chart-empty-states","cat":"图表","t":"图表三态","en":"chart empty states","d":"没有数据、正在加载、取数失败，是三种完全不同的处境，不能都用一句「暂无数据」。加载态给骨架轮廓，位置和真图一样，出来时不跳；失败态必须给重试和一句人话原因；空态告诉人下一步做什么，不是只说「空」。","motion":"机械","tags":["空态","加载","错误","图表"],"hasInit":true,"files":{"html":"blocks/chart-empty-states.html","css":"blocks/chart-empty-states.css","js":"blocks/chart-empty-states.js"}},
    {"id":"chart-small-multiples","cat":"图表","t":"小多图","en":"small multiples","d":"同一个指标切成六组并排，共用一条纵轴：各自缩放会让六张小图看起来一样高，那就什么都比不出来了。小图不画轴、不画网格，只在左下角留一个组名——它们是同一张图的六个切片，不是六张图。","motion":"无动效","tags":["小多图","分组","对比","图表"],"hasInit":true,"files":{"html":"blocks/chart-small-multiples.html","css":"blocks/chart-small-multiples.css","js":"blocks/chart-small-multiples.js"}},
    {"id":"donut-progress","cat":"图表","t":"环形进度","en":"donut progress","d":"环只画一个数：68% 这种单一指标适合环，是因为环天然对应「一圈里占了多少」。多指标别用多个环并排，人比不出两个环的面积差；换成条形更准。缺口从 12 点方向开始，顺时针走，跟表盘一致。","motion":"机械","tags":["环","进度","单指标","图表"],"hasInit":true,"files":{"html":"blocks/donut-progress.html","css":"blocks/donut-progress.css","js":"blocks/donut-progress.js"}},
    {"id":"heatmap-calendar","cat":"图表","t":"日历热力","en":"calendar heatmap","d":"日历热力图按日期组织等面积单元，用数值映射表现活动差异。时间范围、色阶数量与配色由数据和设备决定，零值与缺失值应能区分。","motion":"无动效","tags":["日历","热力","活跃度","图表"],"hasInit":true,"files":{"html":"blocks/heatmap-calendar.html","css":"blocks/heatmap-calendar.css","js":"blocks/heatmap-calendar.js"}},
    {"id":"histogram","cat":"图表","t":"直方图","en":"histogram","d":"柱子之间不留缝，因为横轴是连续的数值区间，不是几个并列的类别；留缝就会让人以为组与组之间有空白。均值线画在柱子上层、虚线、比柱子深一档，位置对得上才算数。区间宽度取 5：太细看不出形状，太粗只剩三根柱子。","motion":"无动效","tags":["直方图","分布","区间","图表"],"hasInit":true,"files":{"html":"blocks/histogram.html","css":"blocks/histogram.css","js":"blocks/histogram.js"}},
    {"id":"legend-toggle","cat":"图表","t":"图例联动","en":"legend toggle","d":"点图例关掉一条线，纵轴不跟着缩：一缩，剩下的线立刻被拉高，看起来像涨了，其实是删了数据。关掉的那条在图例上留一道删除线，随时能点回来——图例既是开关，也是当前状态的记录。","motion":"印刷","tags":["图例","开关","趋势","图表"],"hasInit":true,"files":{"html":"blocks/legend-toggle.html","css":"blocks/legend-toggle.css","js":"blocks/legend-toggle.js"}},
    {"id":"line-chart","cat":"图表","t":"折线趋势","en":"line chart","d":"12 个月只留 4 个横轴标签：每月都标，字会挤成一片，反而看不出趋势。悬停时十字线吸附到最近的点而不是跟着鼠标飘——读数要落在真实数据上，落在两点之间就是假数据。线用 1.5px，网格 1px 且压到很淡：图是主角，不是网格。","motion":"机械","tags":["折线","趋势","悬停","图表"],"hasInit":true,"files":{"html":"blocks/line-chart.html","css":"blocks/line-chart.css","js":"blocks/line-chart.js"}},
    {"id":"radar-chart","cat":"图表","t":"雷达图","en":"radar chart","d":"只放两组、五个维度：再多几条线就互相盖住，读不出谁是谁。轴从中心 0 起，别为了让形状变好看把最小值抬高——那会把「全都不行」画成一个饱满的多边形。两组用实线和虚线，不靠颜色区分：复印和色盲都不受影响。","motion":"无动效","tags":["雷达","多维","对比","图表"],"hasInit":true,"files":{"html":"blocks/radar-chart.html","css":"blocks/radar-chart.css","js":"blocks/radar-chart.js"}},
    {"id":"scatter-plot","cat":"图表","t":"散点相关","en":"scatter plot","d":"四十六个点里只标一个离群点：全标等于没标，人会先去读标注而不是先看形状。点用 2px 实心、不加描边，叠在一起还能看出密度；加描边就把密度糊成一团。横纵轴范围从 0 起，因为这两个量没有负值，硬拉开只会让相关性看着更强。","motion":"无动效","tags":["散点","相关","离群","图表"],"hasInit":true,"files":{"html":"blocks/scatter-plot.html","css":"blocks/scatter-plot.css","js":"blocks/scatter-plot.js"}},
    {"id":"slope-chart","cat":"图表","t":"斜率图","en":"slope chart","d":"只看两个时间点之间的变化，不画中间过程：斜率图回答的是「谁上去了、谁掉下来了」，不是「怎么变的」。交叉的线最显眼，所以顺序按终点排、不按起点排，一眼就能看出谁反超。两条线就够了，三条以上开始乱；真要三组，换成分组柱。","motion":"无动效","tags":["斜率","变化","反超","图表"],"hasInit":true,"files":{"html":"blocks/slope-chart.html","css":"blocks/slope-chart.css","js":"blocks/slope-chart.js"}},
    {"id":"sparkline-grid","cat":"图表","t":"迷你趋势","en":"sparkline grid","d":"表格里的趋势不该占一整个图表位：一条 28px 高的线，只画形状不画轴，配一个涨跌数字就够了。线不填充、端点不留圆点——在 28px 里加任何装饰都会盖住形状本身。数值涨用墨、跌用朱，别用红绿：这套纸色里没有交通灯。","motion":"无动效","tags":["迷你图","表格","趋势","图表"],"hasInit":true,"files":{"html":"blocks/sparkline-grid.html","css":"blocks/sparkline-grid.css","js":"blocks/sparkline-grid.js"}},
    {"id":"stacked-100","cat":"图表","t":"百分比堆叠条","en":"100% stacked bar","d":"一行就是 100%，段与段之间留 1px 缝：不留缝时两块深色挨在一起就并成一条，看不出是三类。只在占比超过 12% 的段里写字，小段写字会溢出——小段靠图例，不靠标签。四行并排比高低，比四个饼图准得多。","motion":"无动效","tags":["百分比","堆叠","构成","图表"],"hasInit":false,"files":{"html":"blocks/stacked-100.html","css":"blocks/stacked-100.css"}},
    {"id":"treemap","cat":"图表","t":"矩形树图","en":"treemap","d":"面积就是占比，所以只能拿面积比，不能拿长短比——人估面积误差大，这张图适合看「谁明显更大」，不适合读精确数值。只在能放下字的块里写标签，放不下就留空，硬塞会把字压成两个字。块按大小从左上角排，眼睛先落在那里。","motion":"无动效","tags":["树图","占比","面积","图表"],"hasInit":true,"files":{"html":"blocks/treemap.html","css":"blocks/treemap.css","js":"blocks/treemap.js"}},
    {"id":"waterfall","cat":"图表","t":"瀑布拆解","en":"waterfall chart","d":"瀑布图按前一步落点衔接增减，帮助理解起点如何到达终点。连接线、配色与纹理均可按项目调整，并以符号、文字或几何关系补充增减含义。","motion":"无动效","tags":["瀑布","拆解","增减","图表"],"hasInit":true,"files":{"html":"blocks/waterfall.html","css":"blocks/waterfall.css","js":"blocks/waterfall.js"}},
    {"id":"approval-state-flow","cat":"反馈","t":"审阅审批状态流","en":"approval state flow","d":"状态决定可用操作，草稿提交后才可批准或退回；所有操作仅改变演示状态。","motion":"无动效","tags":["反馈","审阅审批状态流","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/approval-state-flow.html","css":"blocks/approval-state-flow.css","js":"blocks/approval-state-flow.js"}},
    {"id":"autosave-x","cat":"反馈","t":"自动保存状态","en":"autosave","d":"写完停一下就保存，状态从“未保存”变成“已保存”；不用一个保存按钮。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/autosave-x.html","css":"blocks/autosave-x.css","js":"blocks/autosave-x.js"}},
    {"id":"badge-pop","cat":"反馈","t":"角标跳动","en":"badge pop","d":"未读/待办数字变化时弹一下，提醒但不打断。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/badge-pop.html","css":"blocks/badge-pop.css","js":"blocks/badge-pop.js"}},
    {"id":"batched-status-announcer","cat":"反馈","t":"批量操作状态播报","en":"batched status announcement","d":"批量处理后只播报一次汇总结果，避免读屏器被逐项通知淹没，同时保留可见的计数。","motion":"无动效","tags":["批量","播报","无障碍"],"hasInit":true,"files":{"html":"blocks/batched-status-announcer.html","css":"blocks/batched-status-announcer.css","js":"blocks/batched-status-announcer.js"}},
    {"id":"cancelable-task-progress","cat":"反馈","t":"任务暂停与取消","en":"cancelable task progress","d":"暂停保留进度，取消停止计时并清空进度，避免用户离开后仍收到完成提示。","motion":"无动效","tags":["进度","暂停","取消"],"hasInit":true,"files":{"html":"blocks/cancelable-task-progress.html","css":"blocks/cancelable-task-progress.css","js":"blocks/cancelable-task-progress.js"}},
    {"id":"connection-state-control","cat":"反馈","t":"连接状态与重连","en":"connection state control","d":"把断开、连接中和已连接明确区分，重连期间禁用重复操作；状态来自演示按钮而不冒充真实服务。","motion":"无动效","tags":["连接","重连","状态"],"hasInit":true,"files":{"html":"blocks/connection-state-control.html","css":"blocks/connection-state-control.css","js":"blocks/connection-state-control.js"}},
    {"id":"dependency-check-runner","cat":"反馈","t":"按依赖检查任务","en":"dependency check runner","d":"后续检查依赖前序条件，区分失败和因依赖未满足而跳过；修改条件后可重新运行。","motion":"无动效","tags":["反馈","按依赖检查任务","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/dependency-check-runner.html","css":"blocks/dependency-check-runner.css","js":"blocks/dependency-check-runner.js"}},
    {"id":"dirty-field-summary","cat":"反馈","t":"已修改字段摘要","en":"dirty field summary","d":"按字段与最近确认值比较，列出实际修改项；恢复或确认后重新建立当前基线。","motion":"无动效","tags":["反馈","已修改字段摘要","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/dirty-field-summary.html","css":"blocks/dirty-field-summary.css","js":"blocks/dirty-field-summary.js"}},
    {"id":"draft-conflict-resolution","cat":"反馈","t":"草稿冲突选择","en":"draft conflict resolution","d":"并列展示本地与远端版本，让用户明确选择保留哪份，解决前不把任意一方静默覆盖。","motion":"无动效","tags":["冲突","草稿","版本"],"hasInit":true,"files":{"html":"blocks/draft-conflict-resolution.html","css":"blocks/draft-conflict-resolution.css","js":"blocks/draft-conflict-resolution.js"}},
    {"id":"empty-actions","cat":"反馈","t":"空态带动作","en":"empty state with action","d":"空态不只说\"没有\"，直接给下一步按钮。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/empty-actions.html","css":"blocks/empty-actions.css"}},
    {"id":"error-retry","cat":"反馈","t":"错误卡 + 重试","en":"error state with retry","d":"失败要给出\"为什么 + 怎么办\"；只写\"出错了\"等于没说。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/error-retry.html","css":"blocks/error-retry.css","js":"blocks/error-retry.js"}},
    {"id":"inline-valid","cat":"反馈","t":"行内校验","en":"inline validation","d":"边填边提示，错误贴在字段上；不要等提交才报。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/inline-valid.html","css":"blocks/inline-valid.css","js":"blocks/inline-valid.js"}},
    {"id":"notification-inbox-state","cat":"反馈","t":"未读通知收件箱","en":"notification inbox state","d":"未读数量从实际通知计算，支持逐条已读和全部已读；演示通知不代表真实业务消息。","motion":"无动效","tags":["反馈","未读通知收件箱","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/notification-inbox-state.html","css":"blocks/notification-inbox-state.css","js":"blocks/notification-inbox-state.js"}},
    {"id":"offline-write-queue","cat":"反馈","t":"离线写入队列","en":"offline write queue","d":"把待发送与已送达分开计数，恢复连接后再处理队列，避免离线时误报保存成功。","motion":"无动效","tags":["离线","队列","保存"],"hasInit":true,"files":{"html":"blocks/offline-write-queue.html","css":"blocks/offline-write-queue.css","js":"blocks/offline-write-queue.js"}},
    {"id":"retry-countdown","cat":"反馈","t":"可取消的重试等待","en":"cancelable retry countdown","d":"等待期间展示剩余时间并允许取消，防止用户反复点击产生多次重试。","motion":"无动效","tags":["重试","倒计时","取消"],"hasInit":true,"files":{"html":"blocks/retry-countdown.html","css":"blocks/retry-countdown.css","js":"blocks/retry-countdown.js"}},
    {"id":"sequential-task-runner","cat":"反馈","t":"串行任务与中途停止","en":"sequential task runner","d":"每次只执行一个演示任务，停止会保留已完成项；重跑从头开始并禁止重复启动。","motion":"无动效","tags":["反馈","串行任务与中途停止","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/sequential-task-runner.html","css":"blocks/sequential-task-runner.css","js":"blocks/sequential-task-runner.js"}},
    {"id":"step-status","cat":"反馈","t":"步骤状态","en":"step status","d":"每一步有等待、进行、完成、失败；失败后能重试，不用从头再来。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/step-status.html","css":"blocks/step-status.css","js":"blocks/step-status.js"}},
    {"id":"submit-state-button","cat":"反馈","t":"防重复提交状态","en":"submit state button","d":"提交期间锁定同一个动作，失败后可重试；演示不会发送请求或保存真实项目。","motion":"无动效","tags":["提交","重复请求","重试"],"hasInit":true,"files":{"html":"blocks/submit-state-button.html","css":"blocks/submit-state-button.css","js":"blocks/submit-state-button.js"}},
    {"id":"success","cat":"反馈","t":"成功勾 + 礼花","en":"success draw + confetti","d":"完成不可逆动作时给一次正反馈；只放一次。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/success.html","css":"blocks/success.css","js":"blocks/success.js"}},
    {"id":"toast-stack","cat":"反馈","t":"堆叠提示","en":"toast stack","d":"多条提示向上叠、自动消失、悬停暂停。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/toast-stack.html","css":"blocks/toast-stack.css","js":"blocks/toast-stack.js"}},
    {"id":"undo-redo-history","cat":"反馈","t":"多步撤销与重做","en":"undo redo history","d":"保留操作快照支持连续撤销；撤销后发生新编辑时清除旧的重做分支，避免恢复错误状态。","motion":"无动效","tags":["撤销","重做","历史"],"hasInit":true,"files":{"html":"blocks/undo-redo-history.html","css":"blocks/undo-redo-history.css","js":"blocks/undo-redo-history.js"}},
    {"id":"undo-snackbar","cat":"反馈","t":"撤销提示","en":"undo snackbar","d":"删了先别问，给五秒撤销；比“确定要删除吗”少一次打断。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/undo-snackbar.html","css":"blocks/undo-snackbar.css","js":"blocks/undo-snackbar.js"}},
    {"id":"upload-progress","cat":"反馈","t":"上传进度","en":"upload progress","d":"进度、暂停、重来都在一个位置；失败之前先让人能停。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/upload-progress.html","css":"blocks/upload-progress.css","js":"blocks/upload-progress.js"}},
    {"id":"gradient-sweep","cat":"排印","t":"渐变扫光","en":"gradient sweep","d":"标题上一道光扫过；只在「生成完成」时放一次，常驻会廉价。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/gradient-sweep.html","css":"blocks/gradient-sweep.css"}},
    {"id":"split-text","cat":"排印","t":"逐字揭示","en":"split text","d":"标题逐字浮起；只给首屏主标题用，正文别做。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/split-text.html","css":"blocks/split-text.css","js":"blocks/split-text.js"}},
    {"id":"text-line-mask","cat":"排印","t":"逐行遮罩","en":"line mask","d":"行用 overflow:hidden 遮住、字上移 112%（多出的 12% 盖住字头，否则行顶会漏一条白缝），每行错 120ms——正好是一行中文读完的时间，像排字工一行行把字放上版。","motion":"印刷","tags":["排印","遮罩","错峰","标题"],"hasInit":true,"files":{"html":"blocks/text-line-mask.html","css":"blocks/text-line-mask.css","js":"blocks/text-line-mask.js"}},
    {"id":"text-scramble","cat":"排印","t":"乱码解码","en":"scramble","d":"乱码每 120ms 定住一位，未定的位换成同宽等宽字符，因为等宽决定换值时整行不抖；定型从左往右，读者才有「这句到底要说哪个字」的预期。","motion":"机械","tags":["排印","乱码","生成中","字符"],"hasInit":true,"files":{"html":"blocks/text-scramble.html","css":"blocks/text-scramble.css","js":"blocks/text-scramble.js"}},
    {"id":"text-strike-draw","cat":"排印","t":"删除线手绘","en":"strike draw","d":"线用 scaleX 从 0 拉到 1（不写 width，避免每帧重排把字挤动），两端各探出 2px、落在字高中线偏下 1px——手划删除线不会正好停在笔画边缘。","motion":"印刷","tags":["排印","删除线","手绘","价格"],"hasInit":true,"files":{"html":"blocks/text-strike-draw.html","css":"blocks/text-strike-draw.css","js":"blocks/text-strike-draw.js"}},
    {"id":"text-width-collapse","cat":"排印","t":"字宽压缩","en":"width collapse","d":"字宽只压到 .75、放到 1.18：超过 ±25% 读者先看到「变形」而不是「宽窄」；基线锚在左侧，缩放都往右长，字才不像被整体缩放。900ms 是唯一能看清中间态的时长——这是连续量，不是反馈。34px 是本条唯一的破格，字号阶里的展示档只为一句话用。","motion":"印刷","tags":["排印","字宽","可变字体","对照"],"hasInit":true,"files":{"html":"blocks/text-width-collapse.html","css":"blocks/text-width-collapse.css","js":"blocks/text-width-collapse.js"}},
    {"id":"variable-weight","cat":"排印","t":"字重呼吸","en":"variable font weight","d":"悬停时字重变粗；需要可变字体，没有就退化成颜色变化。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/variable-weight.html","css":"blocks/variable-weight.css"}},
    {"id":"contrast-badge","cat":"无障碍","t":"对比度自检","en":"contrast check","d":"正文 ≥ 7:1、次要 ≥ 4.5:1；这张卡片本身就是示例。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/contrast-badge.html","css":"blocks/contrast-badge.css"}},
    {"id":"contrast-toggle","cat":"无障碍","t":"高对比模式","en":"contrast toggle","d":"两态之间不做淡入淡出：拨杆用 steps(6) 走 120ms，一格一格换挡，像老印刷机的调墨杆——过渡会让人怀疑「是不是还在切」。真正只换三样：正文落到 --ink、描边从 --line 加深到 --ink、0 1px 0 的底边阴影清零；颜色一变立刻可读，不用等动画结束。","motion":"机械","tags":["高对比","无障碍","状态机","开关"],"hasInit":true,"files":{"html":"blocks/contrast-toggle.html","css":"blocks/contrast-toggle.css","js":"blocks/contrast-toggle.js"}},
    {"id":"delete-item-focus-recovery","cat":"无障碍","t":"移除项目后的焦点恢复","en":"delete item focus recovery","d":"移除按钮所在项目后优先聚焦下一项，其次上一项；清空后焦点回到添加入口。","motion":"无动效","tags":["无障碍","移除项目后的焦点恢复","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/delete-item-focus-recovery.html","css":"blocks/delete-item-focus-recovery.css","js":"blocks/delete-item-focus-recovery.js"}},
    {"id":"focus-ring","cat":"无障碍","t":"焦点环","en":"focus-visible","d":"键盘 Tab 才有环、鼠标点没有；环要 2px 且与背景有对比。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/focus-ring.html","css":"blocks/focus-ring.css"}},
    {"id":"focus-trap-dialog","cat":"无障碍","t":"焦点陷阱","en":"focus trap","d":"模态对话框打开后焦点进入，Tab 在当前层循环，关闭后返回触发位置。游标、入场形式与全部视觉参数只是示例，实际项目可使用其他清楚的焦点提示。","motion":"机械","tags":["焦点","陷阱","弹层","键盘"],"hasInit":true,"files":{"html":"blocks/focus-trap-dialog.html","css":"blocks/focus-trap-dialog.css","js":"blocks/focus-trap-dialog.js"}},
    {"id":"linked-field-description","cat":"无障碍","t":"按需关联字段说明","en":"linked field description","d":"展开说明时通过 aria-describedby 关联输入，收起后移除关联；每个实例生成独立标识。","motion":"无动效","tags":["无障碍","按需关联字段说明","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/linked-field-description.html","css":"blocks/linked-field-description.css","js":"blocks/linked-field-description.js"}},
    {"id":"motion-scale","cat":"无障碍","t":"动效强度档","en":"motion scale","d":"关 / 克制 / 标准 三档，用户自己选；比\"全有或全无\"好。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/motion-scale.html","css":"blocks/motion-scale.css","js":"blocks/motion-scale.js"}},
    {"id":"reading-text-spacing","cat":"无障碍","t":"阅读字距与行距控制","en":"reading text spacing","d":"分别调整字距和行距以检查阅读适配，恢复按钮回到示例值；不规定项目字体或排版。","motion":"无动效","tags":["无障碍","阅读字距与行距控制","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/reading-text-spacing.html","css":"blocks/reading-text-spacing.css","js":"blocks/reading-text-spacing.js"}},
    {"id":"reduced-motion","cat":"无障碍","t":"尊重减少动效","en":"prefers-reduced-motion","d":"系统开启「减少动态效果」时，动画时长归零但状态仍变化。这是硬要求。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/reduced-motion.html","css":"blocks/reduced-motion.css","js":"blocks/reduced-motion.js"}},
    {"id":"roving-toolbar-controls","cat":"无障碍","t":"跳过禁用项的键盘工具栏","en":"roving toolbar controls","d":"工具栏单一 Tab 入口，左右方向键和 Home/End 移动焦点，禁用操作自动跳过。","motion":"无动效","tags":["无障碍","跳过禁用项的键盘工具栏","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/roving-toolbar-controls.html","css":"blocks/roving-toolbar-controls.css","js":"blocks/roving-toolbar-controls.js"}},
    {"id":"scoped-shortcut-manager","cat":"无障碍","t":"可关闭的局部快捷键","en":"scoped shortcut manager","d":"快捷键仅在组件获得焦点时使用，文本输入时不拦截；可以关闭快捷键并始终使用按钮。","motion":"无动效","tags":["无障碍","可关闭的局部快捷键","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/scoped-shortcut-manager.html","css":"blocks/scoped-shortcut-manager.css","js":"blocks/scoped-shortcut-manager.js"}},
    {"id":"skip-nav","cat":"无障碍","t":"跳到主内容","en":"skip link","d":"跳转链接帮助键盘用户越过重复导航，激活后到达可识别的正文位置。入口与目标焦点都应可见，位置、样式和动效按项目需要调整。","motion":"机械","tags":["键盘","跳转","焦点环","导航"],"hasInit":true,"files":{"html":"blocks/skip-nav.html","css":"blocks/skip-nav.css","js":"blocks/skip-nav.js"}},
    {"id":"allocation-total-form","cat":"表单","t":"总额约束分配表单","en":"allocation total form","d":"多字段共同受总额约束，超额时指出差额并阻止确认；小数先转整数分计算。","motion":"无动效","tags":["表单","总额约束分配表单","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/allocation-total-form.html","css":"blocks/allocation-total-form.css","js":"blocks/allocation-total-form.js"}},
    {"id":"autocomplete-x","cat":"表单","t":"联想输入","en":"autocomplete","d":"边输边给候选，点到就填上；列表最多四条，别把屏幕占满。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/autocomplete-x.html","css":"blocks/autocomplete-x.css","js":"blocks/autocomplete-x.js"}},
    {"id":"bounded-checkbox-group","cat":"表单","t":"有数量限制的复选组","en":"bounded checkbox group","d":"最多选择两项，达到上限时禁用未选项；取消一项后恢复可选，不丢失已选结果。","motion":"无动效","tags":["表单","有数量限制的复选组","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/bounded-checkbox-group.html","css":"blocks/bounded-checkbox-group.css","js":"blocks/bounded-checkbox-group.js"}},
    {"id":"char-count","cat":"表单","t":"字数统计","en":"character count","d":"写的时候就知道还剩多少；接近上限再提醒，不要一开始就红。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/char-count.html","css":"blocks/char-count.css","js":"blocks/char-count.js"}},
    {"id":"clearable-input","cat":"表单","t":"可清空输入","en":"clearable input","d":"清空动作只在有内容时可用，执行后将焦点交回输入框，便于连续重输而不改变键盘路径。","motion":"无动效","tags":["输入","清空","焦点"],"hasInit":true,"files":{"html":"blocks/clearable-input.html","css":"blocks/clearable-input.css","js":"blocks/clearable-input.js"}},
    {"id":"conditional-fields","cat":"表单","t":"条件字段","en":"conditional required fields","d":"只有相关选项被选中时才展示并启用附加字段，隐藏字段不应继续参与校验或提交。","motion":"无动效","tags":["条件","必填","表单"],"hasInit":true,"files":{"html":"blocks/conditional-fields.html","css":"blocks/conditional-fields.css","js":"blocks/conditional-fields.js"}},
    {"id":"confirmation-field-match","cat":"表单","t":"双字段一致性校验","en":"confirmation field match","d":"两次输入逐字符一致才允许继续；不修改用户原始输入，空值不视为确认。","motion":"无动效","tags":["表单","双字段一致性校验","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/confirmation-field-match.html","css":"blocks/confirmation-field-match.css","js":"blocks/confirmation-field-match.js"}},
    {"id":"currency-input","cat":"表单","t":"金额格式化","en":"currency input","d":"输入时自动加千分位；存的是数字，显示的是人能一眼读懂的金额。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/currency-input.html","css":"blocks/currency-input.css","js":"blocks/currency-input.js"}},
    {"id":"date-pick","cat":"表单","t":"日期选择","en":"date picker","d":"一个月一张网格，前后翻月；选中的日期要一直看得见。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/date-pick.html","css":"blocks/date-pick.css","js":"blocks/date-pick.js"}},
    {"id":"date-range-validation","cat":"表单","t":"起止日期校验","en":"date range validation","d":"起止日期放在同一处判断，开始日期变化时同步结束日期下限，并明确指出顺序错误。","motion":"无动效","tags":["日期","范围","校验"],"hasInit":true,"files":{"html":"blocks/date-range-validation.html","css":"blocks/date-range-validation.css","js":"blocks/date-range-validation.js"}},
    {"id":"dependent-selects","cat":"表单","t":"联动选项","en":"dependent selects","d":"上级分类变化后重建下级选项并重置旧值，防止把不适用的导出格式带入下一步。","motion":"无动效","tags":["联动","选择","格式"],"hasInit":true,"files":{"html":"blocks/dependent-selects.html","css":"blocks/dependent-selects.css","js":"blocks/dependent-selects.js"}},
    {"id":"dropzone","cat":"表单","t":"拖拽上传区","en":"dropzone","d":"整体高亮 + 松手提示；本组件库里附件暂不可用，这类交互可以留给「服务端执行」的文件操作。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/dropzone.html","css":"blocks/dropzone.css","js":"blocks/dropzone.js"}},
    {"id":"float-label","cat":"表单","t":"浮动标签","en":"floating label","d":"聚焦或有内容时标签缩小上浮，省一行高度、又不会丢失上下文。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/float-label.html","css":"blocks/float-label.css"}},
    {"id":"multi-step","cat":"表单","t":"多步表单","en":"multi-step form","d":"步骤间横向滑动，校验不过不放行；比一长条输入友好。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/multi-step.html","css":"blocks/multi-step.css","js":"blocks/multi-step.js"}},
    {"id":"otp","cat":"表单","t":"验证码分格","en":"OTP input","d":"4–6 格自动跳位，退格回退；粘贴整串能自动分配。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/otp.html","css":"blocks/otp.css","js":"blocks/otp.js"}},
    {"id":"password-visibility","cat":"表单","t":"密码显隐","en":"password visibility","d":"切换显隐时保留输入值和光标位置，按钮同时说明当前状态，避免重新输入或误判是否已隐藏。","motion":"无动效","tags":["密码","显隐","输入"],"hasInit":true,"files":{"html":"blocks/password-visibility.html","css":"blocks/password-visibility.css","js":"blocks/password-visibility.js"}},
    {"id":"quantity-stepper","cat":"表单","t":"有边界的数量步进","en":"bounded quantity stepper","d":"加减按钮与直接输入使用相同的边界和步长，到达边界时禁用对应按钮，避免显示无法提交的数量。","motion":"无动效","tags":["数量","边界","步进"],"hasInit":true,"files":{"html":"blocks/quantity-stepper.html","css":"blocks/quantity-stepper.css","js":"blocks/quantity-stepper.js"}},
    {"id":"radio-cards","cat":"表单","t":"卡片单选","en":"radio cards","d":"把单选项做成卡片，选项自带说明；比一排小圆点更容易扫读。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/radio-cards.html","css":"blocks/radio-cards.css","js":"blocks/radio-cards.js"}},
    {"id":"range-dual","cat":"表单","t":"双端范围","en":"dual range","d":"最低和最高一起选；两端不能交叉，数值实时写出来。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/range-dual.html","css":"blocks/range-dual.css","js":"blocks/range-dual.js"}},
    {"id":"rating","cat":"表单","t":"星级评分","en":"star rating","d":"悬停预览、点击定值；常用于反馈与质量打分。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/rating.html","css":"blocks/rating.css","js":"blocks/rating.js"}},
    {"id":"repeatable-contact-fields","cat":"表单","t":"可增删的联系人字段","en":"repeatable contact fields","d":"为重复字段生成独立标签，删除后将焦点移回相邻可用操作；至少保留一行。","motion":"无动效","tags":["表单","可增删的联系人字段","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/repeatable-contact-fields.html","css":"blocks/repeatable-contact-fields.css","js":"blocks/repeatable-contact-fields.js"}},
    {"id":"segmented","cat":"表单","t":"分段选择","en":"segmented control","d":"2–4 个互斥选项，滑块跟随；比 radio 更省地方。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/segmented.html","css":"blocks/segmented.css","js":"blocks/segmented.js"}},
    {"id":"strength","cat":"表单","t":"密码强度","en":"password strength","d":"边输边告诉用户差在哪；比事后报错体验好。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/strength.html","css":"blocks/strength.css","js":"blocks/strength.js"}},
    {"id":"tag-input","cat":"表单","t":"标签输入","en":"tag input","d":"回车加一个标签，退格删最后一个；比下拉多选快。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/tag-input.html","css":"blocks/tag-input.css","js":"blocks/tag-input.js"}},
    {"id":"textarea-auto-grow","cat":"表单","t":"文本框随内容增高","en":"auto growing textarea","d":"短备注不占用大块空白，长内容逐步扩展到上限后才滚动，删除内容时也能回缩。","motion":"无动效","tags":["备注","文本框","自适应"],"hasInit":true,"files":{"html":"blocks/textarea-auto-grow.html","css":"blocks/textarea-auto-grow.css","js":"blocks/textarea-auto-grow.js"}},
    {"id":"unit-conversion-input","cat":"表单","t":"双向单位换算输入","en":"unit conversion input","d":"厘米与英寸以最后编辑的字段为准，保留其输入；无效输入清空对应换算结果。","motion":"无动效","tags":["表单","双向单位换算输入","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/unit-conversion-input.html","css":"blocks/unit-conversion-input.css","js":"blocks/unit-conversion-input.js"}},
    {"id":"validation-summary","cat":"表单","t":"可定位的错误摘要","en":"focusable error summary","d":"多个字段同时出错时先给完整摘要，每条错误能把焦点送到对应字段，修正后保留其他输入。","motion":"无动效","tags":["错误摘要","焦点","校验"],"hasInit":true,"files":{"html":"blocks/validation-summary.html","css":"blocks/validation-summary.css","js":"blocks/validation-summary.js"}},
    {"id":"breadcrumb","cat":"导航","t":"面包屑折叠","en":"collapsed breadcrumb","d":"层级深时把中间收成「…」，悬停展开；标题栏里最常用。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/breadcrumb.html","css":"blocks/breadcrumb.css"}},
    {"id":"editable-path-breadcrumb","cat":"导航","t":"可编辑路径面包屑","en":"editable path breadcrumb","d":"路径按层级拆分为可返回的按钮，编辑路径可跳转；所有动作仅改变本地路径。","motion":"无动效","tags":["导航","可编辑路径面包屑","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/editable-path-breadcrumb.html","css":"blocks/editable-path-breadcrumb.css","js":"blocks/editable-path-breadcrumb.js"}},
    {"id":"fab","cat":"导航","t":"悬浮操作钮","en":"floating action button","d":"主操作悬浮在右下，点开扇出次操作；移动端高频。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/fab.html","css":"blocks/fab.css","js":"blocks/fab.js"}},
    {"id":"filter-url-state","cat":"导航","t":"筛选写入地址","en":"shareable filter URL","d":"把当前筛选保存在地址参数中，复制地址或刷新后可以恢复相同视图，同时保留其他参数。","motion":"无动效","tags":["地址","筛选","恢复"],"hasInit":true,"files":{"html":"blocks/filter-url-state.html","css":"blocks/filter-url-state.css","js":"blocks/filter-url-state.js"}},
    {"id":"gated-review-navigation","cat":"导航","t":"有前置条件的审阅导航","en":"gated review navigation","d":"只有确认准备项才能进入下一阶段，回退后保留选择；条件取消会同步关闭后续入口。","motion":"无动效","tags":["导航","有前置条件的审阅导航","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/gated-review-navigation.html","css":"blocks/gated-review-navigation.css","js":"blocks/gated-review-navigation.js"}},
    {"id":"hash-section-router","cat":"导航","t":"片段地址驱动视图","en":"hash section router","d":"三个视图共用明确的片段前缀，刷新时读取片段；浏览器前进后退同步当前视图。","motion":"无动效","tags":["导航","片段地址驱动视图","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/hash-section-router.html","css":"blocks/hash-section-router.css","js":"blocks/hash-section-router.js"}},
    {"id":"hide-header","cat":"导航","t":"滚动隐藏顶栏","en":"hide on scroll","d":"往下读时收起顶栏，往上滑再出来；给正文让位置，但不拿走导航。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/hide-header.html","css":"blocks/hide-header.css","js":"blocks/hide-header.js"}},
    {"id":"mega-menu","cat":"导航","t":"大菜单","en":"mega menu","d":"一个入口展开多列；分类多但层级浅的产品，比层层点开的下拉快。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/mega-menu.html","css":"blocks/mega-menu.css","js":"blocks/mega-menu.js"}},
    {"id":"mobile-tabbar","cat":"导航","t":"底部标签栏","en":"mobile tabbar","d":"手机上把四个主入口放到底部；拇指够得着，比汉堡菜单快一步。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/mobile-tabbar.html","css":"blocks/mobile-tabbar.css","js":"blocks/mobile-tabbar.js"}},
    {"id":"navigation-history","cat":"导航","t":"局部前进后退","en":"local navigation history","d":"局部工具面板保留访问栈，后退后打开新位置会丢弃旧的前进分支，避免展示不存在的路径。","motion":"无动效","tags":["导航","历史","返回"],"hasInit":true,"files":{"html":"blocks/navigation-history.html","css":"blocks/navigation-history.css","js":"blocks/navigation-history.js"}},
    {"id":"nested-action-menu","cat":"导航","t":"分层操作入口","en":"nested action disclosure","d":"将同类但不常用的操作放在可展开层级里，保留原生键盘操作，不把所有动作挤成一排。","motion":"无动效","tags":["分层","操作","原生控件"],"hasInit":true,"files":{"html":"blocks/nested-action-menu.html","css":"blocks/nested-action-menu.css","js":"blocks/nested-action-menu.js"}},
    {"id":"overflow-tab-scroll","cat":"导航","t":"溢出页签滚动","en":"overflowing tab navigation","d":"页签数量超过可用宽度时保留横向浏览和当前选中项，不通过缩小文字掩盖溢出。","motion":"无动效","tags":["页签","溢出","导航"],"hasInit":true,"files":{"html":"blocks/overflow-tab-scroll.html","css":"blocks/overflow-tab-scroll.css","js":"blocks/overflow-tab-scroll.js"}},
    {"id":"pagination","cat":"导航","t":"分页器","en":"pagination","d":"列表很长时的兜底；首末页与当前页永远可见。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/pagination.html","css":"blocks/pagination.css","js":"blocks/pagination.js"}},
    {"id":"palette","cat":"导航","t":"命令面板","en":"command palette","d":"Ctrl/⌘+K 唤起，按\"要做什么\"搜；比记菜单路径快。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/palette.html","css":"blocks/palette.css","js":"blocks/palette.js"}},
    {"id":"recent-destinations-list","cat":"导航","t":"最近访问去重导航","en":"recent destinations list","d":"最近访问记录置顶并去重，限定本次会话最多三项；清空不会改变当前页面。","motion":"无动效","tags":["导航","最近访问去重导航","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/recent-destinations-list.html","css":"blocks/recent-destinations-list.css","js":"blocks/recent-destinations-list.js"}},
    {"id":"scroll-return-control","cat":"导航","t":"滚动后返回顶部","en":"scroll return control","d":"只有离开顶部后才提供返回操作，按钮控制自己的滚动区域，并在减少动效模式下直接定位。","motion":"无动效","tags":["滚动","返回顶部","减少动效"],"hasInit":true,"files":{"html":"blocks/scroll-return-control.html","css":"blocks/scroll-return-control.css","js":"blocks/scroll-return-control.js"}},
    {"id":"search-paged-results","cat":"导航","t":"搜索结果分页","en":"search paged results","d":"搜索改变时回到第一页，空结果隐藏分页；页数来自实际匹配数量。","motion":"无动效","tags":["导航","搜索结果分页","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/search-paged-results.html","css":"blocks/search-paged-results.css","js":"blocks/search-paged-results.js"}},
    {"id":"shortcut-sheet","cat":"导航","t":"快捷键面板","en":"shortcut sheet","d":"按 ? 弹出，分组列出；键盘流要有地方查。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/shortcut-sheet.html","css":"blocks/shortcut-sheet.css","js":"blocks/shortcut-sheet.js"}},
    {"id":"skip-target","cat":"导航","t":"跳到正文","en":"skip link","d":"键盘用户按 Tab 时最先出现的链接；跳过导航，直达正文。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/skip-target.html","css":"blocks/skip-target.css","js":"blocks/skip-target.js"}},
    {"id":"stepper","cat":"导航","t":"步骤条","en":"stepper","d":"多步流程显式告诉用户\"还有几步\"；比一长条表单友好。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/stepper.html","css":"blocks/stepper.css","js":"blocks/stepper.js"}},
    {"id":"tabs-keyboard-roving","cat":"导航","t":"键盘页签","en":"roving keyboard tabs","d":"Tab 只进入当前页签，方向键在同组切换并同步面板，避免每个页签都占一次 Tab。","motion":"无动效","tags":["页签","键盘","焦点"],"hasInit":true,"files":{"html":"blocks/tabs-keyboard-roving.html","css":"blocks/tabs-keyboard-roving.css","js":"blocks/tabs-keyboard-roving.js"}},
    {"id":"tabs-pill","cat":"导航","t":"滑动页签","en":"sliding tabs","d":"选中态是一块跟着走的底；比换色更有\"位置感\"。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/tabs-pill.html","css":"blocks/tabs-pill.css","js":"blocks/tabs-pill.js"}},
    {"id":"tree-keyboard-navigation","cat":"导航","t":"键盘树导航","en":"keyboard tree navigation","d":"折叠层级仍保留当前位置，方向键展开、回到父级和移动到可见节点，长目录不必逐项 Tab。","motion":"无动效","tags":["树","键盘","目录"],"hasInit":true,"files":{"html":"blocks/tree-keyboard-navigation.html","css":"blocks/tree-keyboard-navigation.css","js":"blocks/tree-keyboard-navigation.js"}},
    {"id":"unsaved-navigation-guard","cat":"导航","t":"未保存离开确认","en":"unsaved navigation guard","d":"只有草稿确实改变时才阻止切换，允许继续编辑或明确丢弃，并把焦点交回原位置。","motion":"无动效","tags":["草稿","离开确认","焦点"],"hasInit":true,"files":{"html":"blocks/unsaved-navigation-guard.html","css":"blocks/unsaved-navigation-guard.css","js":"blocks/unsaved-navigation-guard.js"}},
    {"id":"confirm","cat":"浮层","t":"输入确认删除","en":"type-to-confirm","d":"不可逆操作要求手打关键词，防误触；比\"再点一次\"更稳。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/confirm.html","css":"blocks/confirm.css","js":"blocks/confirm.js"}},
    {"id":"ctxmenu","cat":"浮层","t":"右键菜单","en":"context menu","d":"对着对象右键，动作就有上下文；桌面端高频。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/ctxmenu.html","css":"blocks/ctxmenu.css","js":"blocks/ctxmenu.js"}},
    {"id":"dialog-decision-result","cat":"浮层","t":"带返回值的决策对话框","en":"dialog decision result","d":"明确区分保留、另存和取消三种结果，Escape 归为取消，不把关闭误当作确认。","motion":"无动效","tags":["浮层","带返回值的决策对话框","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/dialog-decision-result.html","css":"blocks/dialog-decision-result.css","js":"blocks/dialog-decision-result.js"}},
    {"id":"dialog-filter-draft","cat":"浮层","t":"先编辑再应用筛选","en":"dialog filter draft","d":"对话框维护临时条件，应用才改变结果；取消和 Escape 均丢弃临时修改。","motion":"无动效","tags":["浮层","先编辑再应用筛选","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/dialog-filter-draft.html","css":"blocks/dialog-filter-draft.css","js":"blocks/dialog-filter-draft.js"}},
    {"id":"dropdown","cat":"浮层","t":"下拉菜单","en":"dropdown menu","d":"点开出现、点外部关闭、Esc 关闭；菜单项支持危险色。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/dropdown.html","css":"blocks/dropdown.css","js":"blocks/dropdown.js"}},
    {"id":"exclusive-help-disclosures","cat":"浮层","t":"互斥帮助展开组","en":"exclusive help disclosures","d":"展开一条帮助会收起其他条目，所有条目也可全部收起；原生 summary 支持键盘。","motion":"无动效","tags":["浮层","互斥帮助展开组","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/exclusive-help-disclosures.html","css":"blocks/exclusive-help-disclosures.css","js":"blocks/exclusive-help-disclosures.js"}},
    {"id":"modal-create-record","cat":"浮层","t":"对话框创建记录","en":"modal create record","d":"原生模态对话框在提交前检查名称，取消不写入，关闭后焦点回到创建按钮。","motion":"无动效","tags":["浮层","对话框创建记录","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/modal-create-record.html","css":"blocks/modal-create-record.css","js":"blocks/modal-create-record.js"}},
    {"id":"nested-dialog-focus-return","cat":"浮层","t":"嵌套对话框焦点返回","en":"nested dialog focus return","d":"子层关闭回到父层触发按钮，父层关闭回到外部入口，使用浏览器原生模态栈。","motion":"无动效","tags":["浮层","嵌套对话框焦点返回","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/nested-dialog-focus-return.html","css":"blocks/nested-dialog-focus-return.css","js":"blocks/nested-dialog-focus-return.js"}},
    {"id":"popover","cat":"浮层","t":"气泡浮层","en":"popover","d":"带小箭头、指哪说哪；比 tooltip 能装更多内容。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/popover.html","css":"blocks/popover.css","js":"blocks/popover.js"}},
    {"id":"sheet","cat":"浮层","t":"底部抽屉","en":"bottom sheet","d":"手机上比居中弹窗更好用；支持下拉关闭。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/sheet.html","css":"blocks/sheet.css","js":"blocks/sheet.js"}},
    {"id":"toolbar-overflow","cat":"浮层","t":"工具栏溢出折叠","en":"toolbar overflow","d":"工具栏定宽 280px：再宽就轮不到折叠；展开时三项依次错 120ms、用 steps(6) 出现，是让人看清有哪三项，而不是一次糊上来。更多键上留着数字 3——折起来的东西要能数得清。","motion":"机械","tags":["浮层","工具栏","溢出","折叠"],"hasInit":true,"files":{"html":"blocks/toolbar-overflow.html","css":"blocks/toolbar-overflow.css","js":"blocks/toolbar-overflow.js"}},
    {"id":"archive-filter-list","cat":"列表","t":"归档与恢复列表","en":"archive filter list","d":"归档项目保留在本地列表中，可筛选查看并恢复；不执行永久删除。","motion":"无动效","tags":["列表","归档与恢复列表","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/archive-filter-list.html","css":"blocks/archive-filter-list.css","js":"blocks/archive-filter-list.js"}},
    {"id":"avatar-stack","cat":"列表","t":"头像叠 + 在线点","en":"avatar stack + presence","d":"多人协作一眼看清有谁、谁在；超过 4 个用 +N。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/avatar-stack.html","css":"blocks/avatar-stack.css"}},
    {"id":"chips","cat":"列表","t":"筛选标签","en":"filter chips","d":"一排可点标签，带选中态与计数；比下拉直观。","motion":"印刷","tags":[],"hasInit":false,"files":{"html":"blocks/chips.html","css":"blocks/chips.css"}},
    {"id":"drag-reorder","cat":"列表","t":"拖拽排序","en":"drag reorder","d":"按住拖动换顺序；任务卡、看板列必备。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/drag-reorder.html","css":"blocks/drag-reorder.css","js":"blocks/drag-reorder.js"}},
    {"id":"dual-list-transfer","cat":"列表","t":"双列表移入移出","en":"dual list transfer","d":"分别选择候选与已选项目后移动，保持项目唯一；按钮根据实际选中项启用。","motion":"无动效","tags":["列表","双列表移入移出","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/dual-list-transfer.html","css":"blocks/dual-list-transfer.css","js":"blocks/dual-list-transfer.js"}},
    {"id":"infinite-scroll","cat":"列表","t":"无限滚动","en":"infinite scroll","d":"底部哨兵进入视口就加载下一批；带\"正在加载\"细行，不要只有转圈。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/infinite-scroll.html","css":"blocks/infinite-scroll.css","js":"blocks/infinite-scroll.js"}},
    {"id":"kanban","cat":"列表","t":"看板列","en":"kanban board","d":"列间拖卡、列头计数、列内滚动；任务流的骨架。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/kanban.html","css":"blocks/kanban.css","js":"blocks/kanban.js"}},
    {"id":"list-deduplicate-preview","cat":"列表","t":"逐行去重预览","en":"list deduplicate preview","d":"去除空行与首尾空白，保留首次出现的顺序；预览不覆盖输入，按纯文本显示。","motion":"无动效","tags":["列表","逐行去重预览","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/list-deduplicate-preview.html","css":"blocks/list-deduplicate-preview.css","js":"blocks/list-deduplicate-preview.js"}},
    {"id":"list-priority-position","cat":"列表","t":"按目标位置调整顺序","en":"list priority position","d":"输入目标位置后移动指定项目，位置越界时拒绝操作，避免只能拖动才能排序。","motion":"无动效","tags":["列表","按目标位置调整顺序","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/list-priority-position.html","css":"blocks/list-priority-position.css","js":"blocks/list-priority-position.js"}},
    {"id":"multiselect","cat":"列表","t":"多选操作条","en":"multi-select toolbar","d":"选中若干项后浮出一条操作栏；批量处理的核心。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/multiselect.html","css":"blocks/multiselect.css","js":"blocks/multiselect.js"}},
    {"id":"range-select-list","cat":"列表","t":"Shift 连续范围选择","en":"range select list","d":"点击设定锚点，Shift 点击选中连续区间；同时提供全选和清空，触屏也可完成操作。","motion":"无动效","tags":["列表","Shift 连续范围选择","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/range-select-list.html","css":"blocks/range-select-list.css","js":"blocks/range-select-list.js"}},
    {"id":"row-expand","cat":"列表","t":"行内展开","en":"inline row expand","d":"点行在原地展开详情，不跳页；列表浏览不打断。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/row-expand.html","css":"blocks/row-expand.css","js":"blocks/row-expand.js"}},
    {"id":"sticky-group","cat":"列表","t":"分组吸顶","en":"sticky group headers","d":"按日期分组、组头吸顶；长消息流按天分开最清楚。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/sticky-group.html","css":"blocks/sticky-group.css"}},
    {"id":"sticky-table","cat":"列表","t":"粘性表头","en":"sticky table header","d":"长表格滚动时表头不动；数据核对场景必备。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/sticky-table.html","css":"blocks/sticky-table.css"}},
    {"id":"swipe-delete","cat":"列表","t":"左滑删除","en":"swipe to delete","d":"移动端列表的高频手势；要有\"再滑一下\"的阈值，不能一碰就删。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/swipe-delete.html","css":"blocks/swipe-delete.css","js":"blocks/swipe-delete.js"}},
    {"id":"double-tap-zoom","cat":"手势","t":"双击放大","en":"double tap zoom","d":"双击从点中的位置放大，不是从中心：手指点哪儿，哪儿就该留在原地，从中心放大会把想看的那块推出屏幕。两次点击间隔 300ms 内才算双击；第二次点击后再放大，避免单击时的闪动。","motion":"物理","tags":["双击","缩放","图片","手势"],"hasInit":true,"files":{"html":"blocks/double-tap-zoom.html","css":"blocks/double-tap-zoom.css","js":"blocks/double-tap-zoom.js"}},
    {"id":"drag-handle-resize","cat":"手势","t":"拖动调整高度","en":"drag handle resize","d":"上下两块之间的把手给 14px 高，是因为手指按上去会挡住 10px：比这更薄，用户只能盲拖。拖动时有最小高度（80px）和最大（面板的七成），到边继续拖只回弹 4px，手感上知道到底了。松手不吸附——这是自由高度，不是分档。","motion":"物理","tags":["拖拽","调整","面板","手势"],"hasInit":true,"files":{"html":"blocks/drag-handle-resize.html","css":"blocks/drag-handle-resize.css","js":"blocks/drag-handle-resize.js"}},
    {"id":"drag-scrub","cat":"手势","t":"拖动擦洗","en":"drag scrub","d":"在时间条上拖动时，预览缩略图跟着手指走，但播放头只按整秒跳：拖到 3.7 秒就显示第 3 秒，不显示半帧——擦洗是为了找位置，不是为了逐帧。松手才把播放头设到当前位置，拖着的时候不改播放状态。","motion":"机械","tags":["擦洗","时间条","预览","媒体"],"hasInit":true,"files":{"html":"blocks/drag-scrub.html","css":"blocks/drag-scrub.css","js":"blocks/drag-scrub.js"}},
    {"id":"drag-sheet-snap","cat":"手势","t":"抽屉分档","en":"drag sheet snap","d":"抽屉只停三档：露头、半屏、全屏。档位固定，是因为自由高度会让每次打开都要重新找位置；松手后按「离哪档近 + 甩的方向」决定落到哪，甩得够快时方向比距离更重要。拖到最上面再往上不再动，但会有 24px 的阻尼，告诉你到头了。","motion":"物理","tags":["抽屉","分档","拖拽","手势"],"hasInit":true,"files":{"html":"blocks/drag-sheet-snap.html","css":"blocks/drag-sheet-snap.css","js":"blocks/drag-sheet-snap.js"}},
    {"id":"drag-to-close","cat":"手势","t":"下拉关闭","en":"drag to close","d":"弹层随手指往下走，背景同步变淡，松手时超过 1/4 高度才关：只有跟手的位移才让人相信「这是可以被推走的」。关的速度比开的快（180ms 对 280ms）——撤销总是应该比进入干脆。","motion":"物理","tags":["下拉","关闭","弹层","手势"],"hasInit":true,"files":{"html":"blocks/drag-to-close.html","css":"blocks/drag-to-close.css","js":"blocks/drag-to-close.js"}},
    {"id":"drop-target","cat":"手势","t":"拖到目标区","en":"drop target","d":"拖卡进某一列时，目标列高亮并出现占位影子。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/drop-target.html","css":"blocks/drop-target.css","js":"blocks/drop-target.js"}},
    {"id":"edge-peek","cat":"手势","t":"边缘窥视","en":"edge peek","d":"从屏幕左缘往里拉会露出上一页的一角，松手弹回；拉过 40% 才真的返回。只露出一角、不整页跟手，是为了让人随时能放弃——整页跟手时，手一抖就回不去了。右缘不做，避免和系统手势打架。","motion":"物理","tags":["边缘","返回","窥视","手势"],"hasInit":true,"files":{"html":"blocks/edge-peek.html","css":"blocks/edge-peek.css","js":"blocks/edge-peek.js"}},
    {"id":"edge-swipe-back","cat":"手势","t":"边缘右滑返回","en":"edge swipe","d":"30% 是「划到这里心里已经决定要走」的位置：低于它松手算误触，高于它手指已经到屏幕中间，再弹回就显得很突兀。判定只在左边缘 18px 内起手，否则页面里正常的横向拖动会被当成返回，这一条是整条手势能不能用的关键。进度用 1px 的尺子加一根刻度表示，人在拖的时候需要知道「还差多少」，而不是一个百分比数字。","motion":"物理","tags":["手势","返回","边缘","阈值"],"hasInit":true,"files":{"html":"blocks/edge-swipe-back.html","css":"blocks/edge-swipe-back.css","js":"blocks/edge-swipe-back.js"}},
    {"id":"flick-velocity","cat":"手势","t":"轻扫判定","en":"flick velocity","d":"快速轻扫（超过 0.6px/ms）即使只走 40px 也触发，慢慢推到底反而不触发：手势里速度比距离更接近意图。阈值用「每毫秒像素」而不是总行程，因为总行程会被按住不动的时间稀释。界面上把当前速度直接写出来，是为了让人看见判定依据。","motion":"物理","tags":["轻扫","速度","阈值","手势"],"hasInit":true,"files":{"html":"blocks/flick-velocity.html","css":"blocks/flick-velocity.css","js":"blocks/flick-velocity.js"}},
    {"id":"gesture-hint","cat":"手势","t":"手势引导","en":"gesture hint","d":"提示只在第一次出现，滑一次就消失：每次都出现会挡住内容，而不出现人就永远不知道可以滑。提示是一根手指加一条轨迹，动两秒停住，不循环——循环的提示会变成背景噪音。底下留一个「再看一次」，是给快速划过去的人。","motion":"物理","tags":["引导","提示","首次","手势"],"hasInit":true,"files":{"html":"blocks/gesture-hint.html","css":"blocks/gesture-hint.css","js":"blocks/gesture-hint.js"}},
    {"id":"hold-to-record","cat":"手势","t":"按住录音","en":"hold to record","d":"按下去才开始计时，松手立刻结束并给出时长：录音类交互最怕「按了没录上」，所以按压的 120ms 内就出红点和计时，不用等。上滑超过 60px 取消录音，这是手指不离屏也能反悔的唯一办法。","motion":"物理","tags":["按住","录音","取消","手势"],"hasInit":true,"files":{"html":"blocks/hold-to-record.html","css":"blocks/hold-to-record.css","js":"blocks/hold-to-record.js"}},
    {"id":"inertia","cat":"手势","t":"拖拽惯性","en":"inertia drag","d":"松手后按速度继续滑一小段再停；列表、轮播的\"重\"感。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/inertia.html","css":"blocks/inertia.css","js":"blocks/inertia.js"}},
    {"id":"long-press-copy","cat":"手势","t":"长按复制","en":"long press copy","d":"长按 480ms 弹出菜单，比系统的 500ms 略短，是因为网页里常有人按得轻。菜单出现在按下的位置上方，放不下就翻到下方——位置不对，人第一反应是「没反应」。复制后菜单不立刻消失，改成显示「已复制」，因为消失得太快会让人不确定到底成没成。","motion":"物理","tags":["长按","复制","菜单","手势"],"hasInit":true,"files":{"html":"blocks/long-press-copy.html","css":"blocks/long-press-copy.css","js":"blocks/long-press-copy.js"}},
    {"id":"long-press-menu","cat":"手势","t":"长按菜单","en":"long press","d":"按 420ms：300ms 会被滑动起步的惯性误触，500ms 又像卡顿；比 iOS 的 400ms 多 20ms，是因为这里同时画一条走满才弹的进度线，线到 420ms 刚好走完，两条时间轴合得上。移动超过 8px 立刻取消——长按和拖动只能二选一，否则手指一抖两件事都做一半。整块主墨也是故意的：420ms 的停留需要一个实心面来承重，描边卡撑不住这个时长，本组只此一处大色块。","motion":"物理","tags":["手势","长按","菜单","阈值"],"hasInit":true,"files":{"html":"blocks/long-press-menu.html","css":"blocks/long-press-menu.css","js":"blocks/long-press-menu.js"}},
    {"id":"momentum-gallery","cat":"手势","t":"惯性相册","en":"momentum gallery","d":"松手后按最后一段速度继续滑，摩擦每帧 0.94：手指快划时能连翻三四张，慢拖则停在原地。停下来的位置吸附到最近的整张，因为半张卡停在中间既不能看也不能点。单张宽度取容器 78%，露出下一张的一角，人才知道后面还有。","motion":"物理","tags":["惯性","相册","吸附","手势"],"hasInit":true,"files":{"html":"blocks/momentum-gallery.html","css":"blocks/momentum-gallery.css","js":"blocks/momentum-gallery.js"}},
    {"id":"overscroll-bounce","cat":"手势","t":"过度滚动回弹","en":"overscroll bounce","d":"端点后的位移按 0.3 折算，并且封顶 40px：折算到 0.5 像列表从手里滑走，到 0.15 又几乎看不出被拉住；40px 是 116px 视窗里还能看见最后一行、又明显变形的临界，超过就只剩一条空白。回弹给 280ms 而不是 180ms，因为 180ms 会有一声「啪」的惊吓感，橡皮筋要慢一点才像有弹性。","motion":"物理","tags":["手势","滚动","回弹","橡皮筋"],"hasInit":true,"files":{"html":"blocks/overscroll-bounce.html","css":"blocks/overscroll-bounce.css","js":"blocks/overscroll-bounce.js"}},
    {"id":"pinch-map","cat":"手势","t":"地图式缩放","en":"pinch map","d":"缩放围绕两指中点，不是画布中心：手指想放大的地方就该留在指尖下。倍率卡在 1–3，缩到 1 再缩会停住而不是乱飘；平移范围跟着倍率算，放得越大能拖的越多。滚轮用 Ctrl 当捏合，桌面也能试。","motion":"物理","tags":["缩放","地图","双指","手势"],"hasInit":true,"files":{"html":"blocks/pinch-map.html","css":"blocks/pinch-map.css","js":"blocks/pinch-map.js"}},
    {"id":"pinch-zoom-tile","cat":"手势","t":"捏合缩放","en":"pinch zoom","d":"上下限锁 1.00 和 2.40：1 是「刚到边」而不是随便定的下限，2.40 是这张 200px 的图放大后网点还认得出的极限。越界先按 0.25 折算再回弹，所以推到 3.00 也不会突然停住——突然停住人会以为手势坏了，而不是以为到底了。回弹 280ms 用带过冲的物理曲线，越界那一刻的「拽回来」才感觉得到。","motion":"物理","tags":["手势","缩放","边界","回弹"],"hasInit":true,"files":{"html":"blocks/pinch-zoom-tile.html","css":"blocks/pinch-zoom-tile.css","js":"blocks/pinch-zoom-tile.js"}},
    {"id":"press-hold-fill","cat":"手势","t":"按住确认","en":"press and hold","d":"长按 900ms 填满才生效：短到 500ms 会误触，长到 1.5 秒手会酸。填充用环而不是进度条，因为手指正压着按钮中间，环形能在不移动视线的前提下被余光看到。中途松手立刻退回，没有半途生效这回事。","motion":"物理","tags":["长按","确认","环形","手势"],"hasInit":true,"files":{"html":"blocks/press-hold-fill.html","css":"blocks/press-hold-fill.css","js":"blocks/press-hold-fill.js"}},
    {"id":"pull-refresh","cat":"手势","t":"下拉刷新","en":"pull to refresh","d":"下拉出现进度弧，松手回弹；移动端高频。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/pull-refresh.html","css":"blocks/pull-refresh.css","js":"blocks/pull-refresh.js"}},
    {"id":"rotary-dial","cat":"手势","t":"旋钮","en":"rotary dial","d":"旋钮按角度算，不按位移算：拖得远不等于转得多，手指绕着中心画弧才是转。每 30° 一档、一共 12 档，吸附让人不用盯着看；指针拖出界后角度继续跟手但刻度不动，是告诉你到头了。","motion":"物理","tags":["旋钮","角度","吸附","手势"],"hasInit":true,"files":{"html":"blocks/rotary-dial.html","css":"blocks/rotary-dial.css","js":"blocks/rotary-dial.js"}},
    {"id":"shake-undo","cat":"手势","t":"摇一摇撤销","en":"shake to undo","d":"本例模拟摇动触发撤销，并保留普通按钮作为替代入口。阈值与时间窗口是示例，实际设备还需检查传感器能力、权限和误触情况。","motion":"物理","tags":["摇一摇","撤销","兜底","手势"],"hasInit":true,"files":{"html":"blocks/shake-undo.html","css":"blocks/shake-undo.css","js":"blocks/shake-undo.js"}},
    {"id":"swipe-actions-row","cat":"手势","t":"左滑操作","en":"swipe actions","d":"横滑露出当前行的操作，释放后按阈值回位或展开。操作宽度、阈值、颜色与排列按内容和设备调整，危险后果需明确说明，不能只靠颜色。","motion":"物理","tags":["左滑","操作","列表","手势"],"hasInit":true,"files":{"html":"blocks/swipe-actions-row.html","css":"blocks/swipe-actions-row.css","js":"blocks/swipe-actions-row.js"}},
    {"id":"swipe-tab-pager","cat":"手势","t":"横滑切页","en":"swipe tab pager","d":"翻页用「三分之一屏」做阈值，不是整屏：手腕一抖就换页在手机上很烦，走满三分之一才认。页与页之间不留空隙，手指到哪儿内容到哪儿，松手再补完剩下的一段——中间加过渡会让人觉得页面在打滑。","motion":"物理","tags":["滑动","分页","标签","手势"],"hasInit":true,"files":{"html":"blocks/swipe-tab-pager.html","css":"blocks/swipe-tab-pager.css","js":"blocks/swipe-tab-pager.js"}},
    {"id":"swipe-to-dismiss","cat":"手势","t":"侧滑关闭","en":"swipe to dismiss","d":"滑过 96px 或松手时速度超过 0.5px/ms 才算关掉，两个条件缺一个就弹回去：只看距离会把「手抖一下」当成关闭，只看速度会把慢慢推到底的动作忽略。位移跟手但压到 0.55 倍，手感是「拉着有阻力」，不是图片在飘。","motion":"物理","tags":["滑动","关闭","通知","手势"],"hasInit":true,"files":{"html":"blocks/swipe-to-dismiss.html","css":"blocks/swipe-to-dismiss.css","js":"blocks/swipe-to-dismiss.js"}},
    {"id":"tap-feedback","cat":"手势","t":"触点反馈","en":"tap feedback","d":"涟漪按落点生成、随输入类型变强度：手指按得重一点，圈就大一点（用 pointer 的 pressure），鼠标固定一档。圈从 0 开始、扩到 36px 就停，再大就像加载动画。同一处连点会叠出三层，这是有意的——快连点本来就该看到密度。","motion":"物理","tags":["触点","反馈","涟漪","手势"],"hasInit":true,"files":{"html":"blocks/tap-feedback.html","css":"blocks/tap-feedback.css","js":"blocks/tap-feedback.js"}},
    {"id":"two-finger-pan","cat":"手势","t":"双指平移","en":"two finger pan","d":"画布用两根手指才平移，一根手指留给「选中」：单指也能拖的画布，在手机上永远选不中东西。平移有边界，边到边就停，回弹只留 30px；滚轮兜底用 Shift 当第二根手指。","motion":"物理","tags":["双指","平移","画布","手势"],"hasInit":true,"files":{"html":"blocks/two-finger-pan.html","css":"blocks/two-finger-pan.css","js":"blocks/two-finger-pan.js"}},
    {"id":"two-finger-rotate","cat":"手势","t":"双指旋转","en":"two-finger rotate","d":"角度按「指针相对卡心的夹角」算，不按横向位移：夹角等于真的两根手指在转，位移算法一转过头就会反向跳，人立刻知道是假的。松手时 6° 内吸到 15° 的刻度上——手很难停准，吸一下才有「卡进档位」的手感，而 15° 是排版里斜线最常用的档，再密就分不出区别。读数放卡外，是因为卡在转，字跟着转就没法看。","motion":"物理","tags":["手势","旋转","角度","吸附"],"hasInit":true,"files":{"html":"blocks/two-finger-rotate.html","css":"blocks/two-finger-rotate.css","js":"blocks/two-finger-rotate.js"}},
    {"id":"author-bio","cat":"内容","t":"作者小传","en":"author bio","d":"作者信息放文末，一行头像加两行字：资历写具体年份和做过什么，不写「资深」「热爱」。头像用姓氏一个字，因为虚构作者的真人照片会抢注意力，而首字母没有任何信息负担。","motion":"无动效","tags":["作者","小传","长文","内容"],"hasInit":false,"files":{"html":"blocks/author-bio.html","css":"blocks/author-bio.css"}},
    {"id":"callout-note","cat":"内容","t":"提示块三型","en":"callout variants","d":"提示块只分三型：注意、例外、做法。四型以上就开始凭感觉挑。三型靠左侧竖线的颜色和标签字区分，不靠底色——三块不同底色放在一起，页面会花。提示块不用感叹号，感叹号多了等于没有重点。","motion":"无动效","tags":["提示","标注","长文","内容"],"hasInit":false,"files":{"html":"blocks/callout-note.html","css":"blocks/callout-note.css"}},
    {"id":"caption-zoom","cat":"内容","t":"图注联动","en":"caption link","d":"图注列锁死 120px、不随字数伸缩，换行时右边缘不动，图注才不会把版面顶来顶去；上移只给 2px 配物理曲线的过冲，多 1px 像在跳，少 1px 看不出和热点是一对。热点用 --x/--y 百分比定位，换一张图不必重算坐标，这套联动才能直接搬走。","motion":"物理","tags":["图注","热点","联动","悬停"],"hasInit":true,"files":{"html":"blocks/caption-zoom.html","css":"blocks/caption-zoom.css","js":"blocks/caption-zoom.js"}},
    {"id":"checklist-progress","cat":"内容","t":"清单完成进度","en":"checklist completion progress","d":"进度直接来自实际勾选项，取消勾选会回退；显示完成数量而不靠颜色猜测。","motion":"无动效","tags":["清单","进度","检查"],"hasInit":true,"files":{"html":"blocks/checklist-progress.html","css":"blocks/checklist-progress.css","js":"blocks/checklist-progress.js"}},
    {"id":"code-line-highlight","cat":"内容","t":"代码行定位","en":"code line focus","d":"逐行定位时保留完整上下文，用文本和选中状态同时标识当前行，便于解释某一行的职责。","motion":"无动效","tags":["代码","行号","定位"],"hasInit":true,"files":{"html":"blocks/code-line-highlight.html","css":"blocks/code-line-highlight.css","js":"blocks/code-line-highlight.js"}},
    {"id":"comparison-inline","cat":"内容","t":"正文内对比","en":"inline comparison","d":"正文里的对比用两列小表，不用卡片：卡片会把两样东西推得很远，而对比需要眼睛来回扫。差异项加一行底色标记，不加红绿——红绿在纸色上对比不足。表头只有两列，第三列会开始像决策表。","motion":"无动效","tags":["对比","表格","正文","内容"],"hasInit":false,"files":{"html":"blocks/comparison-inline.html","css":"blocks/comparison-inline.css"}},
    {"id":"content-search-marks","cat":"内容","t":"正文匹配与跳转","en":"in content search marks","d":"在原文里标出所有匹配并提供前后跳转，搜索词按纯文本处理，不会作为正则或 HTML 执行。","motion":"无动效","tags":["正文","搜索","高亮"],"hasInit":true,"files":{"html":"blocks/content-search-marks.html","css":"blocks/content-search-marks.css","js":"blocks/content-search-marks.js"}},
    {"id":"content-version-preview","cat":"内容","t":"历史版本只读预览","en":"content version preview","d":"历史版本与当前草稿分开显示，预览不会覆盖正在编辑的内容；恢复需要明确点击。","motion":"无动效","tags":["内容","历史版本只读预览","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/content-version-preview.html","css":"blocks/content-version-preview.css","js":"blocks/content-version-preview.js"}},
    {"id":"copy-code-block","cat":"内容","t":"代码复制与降级","en":"copy code with fallback","d":"复制源文本而不是带编号的显示内容，自动复制不可用时提供可手动选中的完整代码。","motion":"无动效","tags":["代码","复制","降级"],"hasInit":true,"files":{"html":"blocks/copy-code-block.html","css":"blocks/copy-code-block.css","js":"blocks/copy-code-block.js"}},
    {"id":"correction-notice","cat":"内容","t":"更正声明","en":"correction notice","d":"更正放在正文最上方、用一条竖线标出、写清哪里错了、改成什么、什么时候改的：把更正藏到文末，等于等读者发现。竖线用警示色，正文仍用正常色——错的是事实，不是整篇文章。不写「感谢指正」这类客套，一句足够。","motion":"无动效","tags":["更正","声明","长文","内容"],"hasInit":false,"files":{"html":"blocks/correction-notice.html","css":"blocks/correction-notice.css"}},
    {"id":"diff-view","cat":"内容","t":"差异视图","en":"diff view","d":"红删绿增、上下文折叠；版本对比最需要。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/diff-view.html","css":"blocks/diff-view.css","js":"blocks/diff-view.js"}},
    {"id":"download-card","cat":"内容","t":"文件下载卡","en":"download card","d":"文件卡写清三件事：文件名、格式与大小、拿来干嘛。只写「下载」的卡片，用户得点开才知道是不是自己要的。格式和大小放在同一行等宽字，是为了对齐着扫。下载中改成进度与取消，不让按钮一直转圈。","motion":"机械","tags":["下载","文件","内容"],"hasInit":true,"files":{"html":"blocks/download-card.html","css":"blocks/download-card.css","js":"blocks/download-card.js"}},
    {"id":"editable-heading","cat":"内容","t":"标题就地编辑","en":"editable heading","d":"编辑保留在标题原位，Enter 保存、Escape 取消；空标题不被接受，取消不会覆盖原文。","motion":"无动效","tags":["标题","编辑","取消"],"hasInit":true,"files":{"html":"blocks/editable-heading.html","css":"blocks/editable-heading.css","js":"blocks/editable-heading.js"}},
    {"id":"endnote-list","cat":"内容","t":"文末注释","en":"endnotes","d":"文末注释通过编号与正文位置对应，并提供返回阅读位置的入口。注释也可按项目改为脚注或其他结构，字体和编号样式服务于辨识与溯源。","motion":"无动效","tags":["注释","脚注","长文","内容"],"hasInit":true,"files":{"html":"blocks/endnote-list.html","css":"blocks/endnote-list.css","js":"blocks/endnote-list.js"}},
    {"id":"figure-caption","cat":"内容","t":"图注与编号","en":"figure caption","d":"图注放在图下面、比正文小一档、编号和说明分开：编号用等宽字，一眼能找到；说明用正文字体，读起来不跳。图注不写「如图所示」，那句话不提供信息。图注和正文之间留一行空，避免被读成正文的第一句。","motion":"无动效","tags":["图注","编号","长文","内容"],"hasInit":false,"files":{"html":"blocks/figure-caption.html","css":"blocks/figure-caption.css"}},
    {"id":"file-tree","cat":"内容","t":"文件树","en":"file tree","d":"展开/折叠、选中、显示大小；/browse 的数据就能直接喂它。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/file-tree.html","css":"blocks/file-tree.css","js":"blocks/file-tree.js"}},
    {"id":"glossary-tooltip","cat":"内容","t":"术语解释","en":"glossary tooltip","d":"术语用虚线下划线，不改成蓝色链接：蓝色会让人以为点了会跳走。解释浮层里第一句是人话，第二句才给数字。键盘 Tab 也能打开，因为不是所有人用鼠标。点空白处关掉，不用非得再点一次术语。","motion":"印刷","tags":["术语","浮层","长文","内容"],"hasInit":true,"files":{"html":"blocks/glossary-tooltip.html","css":"blocks/glossary-tooltip.css","js":"blocks/glossary-tooltip.js"}},
    {"id":"image-annotation","cat":"内容","t":"图上标注","en":"image annotation","d":"图像标注把定位点与说明对应，避免长文字遮挡关键信息。标注大小、形状、字体和说明位置按画面与设备调整，并检查对比度和触达。","motion":"无动效","tags":["标注","图片","正文","内容"],"hasInit":false,"files":{"html":"blocks/image-annotation.html","css":"blocks/image-annotation.css"}},
    {"id":"image-viewer","cat":"内容","t":"图片查看器","en":"image viewer","d":"缩略图点开大图，可缩放、左右翻、Esc 关；聊天里贴截图必备。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/image-viewer.html","css":"blocks/image-viewer.css","js":"blocks/image-viewer.js"}},
    {"id":"inline-definition","cat":"内容","t":"行内定义","en":"inline definition","d":"术语第一次出现时在括号里给一句定义，之后再出现就不重复：每次都解释会打断阅读，一次不解释则后文全废。定义用正文同字号、淡化颜色，不用斜体——中文斜体是伪斜体，看起来像出错。","motion":"无动效","tags":["定义","术语","正文","内容"],"hasInit":false,"files":{"html":"blocks/inline-definition.html","css":"blocks/inline-definition.css"}},
    {"id":"inline-toc","cat":"内容","t":"正文小目录","en":"inline toc","d":"正文目录帮助理解文章结构并跳到章节。位置、章节数量与当前节提示按内容和屏宽决定，避免自动移动打断正在执行的导航。","motion":"无动效","tags":["目录","长文","导航","内容"],"hasInit":true,"files":{"html":"blocks/inline-toc.html","css":"blocks/inline-toc.css","js":"blocks/inline-toc.js"}},
    {"id":"json-inspector","cat":"内容","t":"JSON 结构查看","en":"JSON structure inspector","d":"将有效 JSON 按字段展开，解析失败时保留上一次结果；所有值按文本显示，不执行输入内容。","motion":"无动效","tags":["JSON","结构","解析"],"hasInit":true,"files":{"html":"blocks/json-inspector.html","css":"blocks/json-inspector.css","js":"blocks/json-inspector.js"}},
    {"id":"key-takeaways","cat":"内容","t":"要点摘要","en":"key takeaways","d":"摘要提取真实要点，帮助决定是否继续阅读。数量、长度、位置与是否使用图标或编号由内容关系决定，不为凑数补造信息。","motion":"无动效","tags":["摘要","要点","长文","内容"],"hasInit":false,"files":{"html":"blocks/key-takeaways.html","css":"blocks/key-takeaways.css"}},
    {"id":"lede-paragraph","cat":"内容","t":"导语段","en":"lede paragraph","d":"导语交代正文范围与阅读价值，字号、字重、背景、行距与宽度按内容和品牌决定，检查它与标题、正文和提示信息之间的关系。","motion":"无动效","tags":["导语","长文","排版","内容"],"hasInit":false,"files":{"html":"blocks/lede-paragraph.html","css":"blocks/lede-paragraph.css"}},
    {"id":"line-position-diff","cat":"内容","t":"逐行位置差异核对","en":"line position diff","d":"按相同行号比较两份文本，区分缺失与修改；不把它冒充自动对齐的最小编辑差异算法。","motion":"无动效","tags":["内容","逐行位置差异核对","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/line-position-diff.html","css":"blocks/line-position-diff.css","js":"blocks/line-position-diff.js"}},
    {"id":"pull-stat","cat":"内容","t":"数字引述","en":"pull stat","d":"强调正文数字时附上理解所需的单位、口径和来源，不虚构统计。字号、字重、颜色与排布可按重要性选择，不限定固定尺寸或材质。","motion":"无动效","tags":["数字","引述","长文","内容"],"hasInit":false,"files":{"html":"blocks/pull-stat.html","css":"blocks/pull-stat.css"}},
    {"id":"quote-block","cat":"内容","t":"引段块","en":"quote block","d":"竖线只在左侧留 1px、不加粗，因为引文靠留白和字号分层，线一粗就变成警示条；出处压在 10px 等宽字上，比引文低两档，读者先读句子后读来源。「上下文」默认合起、展开走 280ms 印刷曲线，高度只放 44px——正好两行批注，再多这一块就压过原文。","motion":"印刷","tags":["引用","出处","展开","竖线"],"hasInit":true,"files":{"html":"blocks/quote-block.html","css":"blocks/quote-block.css","js":"blocks/quote-block.js"}},
    {"id":"quote-portrait","cat":"内容","t":"带头像引语","en":"quote with portrait","d":"引语配头像时，头像放在引语下方而不是上方：先读话，再确认是谁说的。加粗引号用文字标点而不是图形，这样缩放时不会错位。引语太长就截断，一句半以内最像人说的话。","motion":"无动效","tags":["引语","头像","长文","内容"],"hasInit":false,"files":{"html":"blocks/quote-portrait.html","css":"blocks/quote-portrait.css"}},
    {"id":"read-more-clamp","cat":"内容","t":"长摘要展开与收起","en":"expandable clamped text","d":"先显示摘要开头，展开后保留原位置与按钮，读者可以检查全文再收回，不会丢失上下文。","motion":"无动效","tags":["摘要","展开","长文"],"hasInit":true,"files":{"html":"blocks/read-more-clamp.html","css":"blocks/read-more-clamp.css","js":"blocks/read-more-clamp.js"}},
    {"id":"reading-font-control","cat":"内容","t":"正文阅读字号","en":"reading font control","d":"允许读者单独调整正文大小，控件和导航保持稳定；改动只作用于当前阅读区域。","motion":"无动效","tags":["阅读","字号","长文"],"hasInit":true,"files":{"html":"blocks/reading-font-control.html","css":"blocks/reading-font-control.css","js":"blocks/reading-font-control.js"}},
    {"id":"reading-meta","cat":"内容","t":"阅读信息","en":"reading meta","d":"阅读时长按每分钟三百字算，不写「约」：写了「约」就等于承认不准。更新日期和初发日期都写，改了什么都不说，只写「已更新」等于没说。三个信息用一根细线隔开，不堆成一行点号。","motion":"无动效","tags":["阅读","元信息","长文","内容"],"hasInit":false,"files":{"html":"blocks/reading-meta.html","css":"blocks/reading-meta.css"}},
    {"id":"related-links","cat":"内容","t":"相关阅读","en":"related links","d":"相关阅读给三条、每条带一句为什么相关：只给标题等于让人猜。三条以内是有理由的——再多，读者会认为这是推荐位而不是延伸阅读。链接文字用标题本身，不写「点击阅读」。","motion":"无动效","tags":["相关","阅读","长文","内容"],"hasInit":false,"files":{"html":"blocks/related-links.html","css":"blocks/related-links.css"}},
    {"id":"ribbon-note","cat":"内容","t":"旁注便签","en":"ribbon note","d":"便签歪 1.5° 是「贴上去而不是插进去」的最小可见角度，再多就像坏了；1px 硬边不改成软阴影，便签才留在纸面上而不浮起来。展开只放 54px、用 280ms 印刷曲线，因为旁注的职责是补一句话，写长就成了第二篇正文。","motion":"印刷","tags":["旁注","便签","旋转","展开"],"hasInit":true,"files":{"html":"blocks/ribbon-note.html","css":"blocks/ribbon-note.css","js":"blocks/ribbon-note.js"}},
    {"id":"safe-markdown-subset","cat":"内容","t":"安全的轻量文本预览","en":"safe markdown subset","d":"只识别标题、列表和普通段落，输入中的 HTML 按文本显示，不执行标签或脚本。","motion":"无动效","tags":["内容","安全的轻量文本预览","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/safe-markdown-subset.html","css":"blocks/safe-markdown-subset.css","js":"blocks/safe-markdown-subset.js"}},
    {"id":"series-nav","cat":"内容","t":"系列导航","en":"series nav","d":"系列文章在文末给出上一篇和下一篇，标明第几篇共几篇：只给标题，读者不知道自己在哪儿。当前篇不用高亮列表，只写「第 3 篇 / 共 6 篇」，一行就够。链接方向用文字「上一篇」，不用箭头。","motion":"无动效","tags":["系列","导航","长文","内容"],"hasInit":false,"files":{"html":"blocks/series-nav.html","css":"blocks/series-nav.css"}},
    {"id":"spoiler-reveal","cat":"内容","t":"剧透折叠","en":"spoiler reveal","d":"剧透默认模糊而不是直接隐藏：隐藏会让人以为那段不存在，模糊保留长度和排版，说明「这里有内容，但你可能不想看」。点一下变清晰，不做动画——动画会让被遮住的字先闪一下。","motion":"无动效","tags":["剧透","折叠","正文","内容"],"hasInit":true,"files":{"html":"blocks/spoiler-reveal.html","css":"blocks/spoiler-reveal.css","js":"blocks/spoiler-reveal.js"}},
    {"id":"text-case-transform","cat":"内容","t":"文本大小写与空白转换","en":"text case transform","d":"转换结果单独展示，原文保留；折叠空白和大小写模式分别选择，支持空输入。","motion":"无动效","tags":["内容","文本大小写与空白转换","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/text-case-transform.html","css":"blocks/text-case-transform.css","js":"blocks/text-case-transform.js"}},
    {"id":"timeline-block","cat":"内容","t":"时间线片段","en":"timeline block","d":"时间线竖向排、左侧一条细线、节点是小圆点：横向时间线在窄屏一定会被压扁。时间放左边、内容放右边，年份用等宽字对齐，扫一眼就能比较间隔。只给有转折的年份，平年不写。","motion":"无动效","tags":["时间线","历程","长文","内容"],"hasInit":false,"files":{"html":"blocks/timeline-block.html","css":"blocks/timeline-block.css"}},
    {"id":"word-frequency-counter","cat":"内容","t":"词频与字数统计","en":"word frequency counter","d":"统计非空白字符和以空格分隔的词频，明确中文分词边界；大小写归一后排序。","motion":"无动效","tags":["内容","词频与字数统计","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/word-frequency-counter.html","css":"blocks/word-frequency-counter.css","js":"blocks/word-frequency-counter.js"}},
    {"id":"column-flow","cat":"长文","t":"分栏切换","en":"column flow","d":"宽屏分两栏，窄屏回一栏；一行四十五到七十五个字，读起来最省力。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/column-flow.html","css":"blocks/column-flow.css","js":"blocks/column-flow.js"}},
    {"id":"drop-cap","cat":"长文","t":"首字下沉","en":"drop cap","d":"长文开头把第一个字放大，视线有了明确起点；比加粗整段安静。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/drop-cap.html","css":"blocks/drop-cap.css"}},
    {"id":"footnote-pop","cat":"长文","t":"脚注浮层","en":"footnote popover","d":"正文不打断，点右上角的小数字才出现注释；适合引用和来源。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/footnote-pop.html","css":"blocks/footnote-pop.css","js":"blocks/footnote-pop.js"}},
    {"id":"pull-quote-x","cat":"长文","t":"引文浮出","en":"pull quote","d":"把一句重要的话从正文里拎出来，左边一条线，读者会自然停一下。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/pull-quote-x.html","css":"blocks/pull-quote-x.css"}},
    {"id":"read-progress-x","cat":"长文","t":"阅读进度","en":"reading progress","d":"长文里的一条细线，告诉读者还剩多少；比百分比数字安静。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/read-progress-x.html","css":"blocks/read-progress-x.css","js":"blocks/read-progress-x.js"}},
    {"id":"reading-time","cat":"长文","t":"阅读时长","en":"reading time","d":"按字数估一个阅读时间；先告诉读者要花多久，比催他“快读”有用。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/reading-time.html","css":"blocks/reading-time.css","js":"blocks/reading-time.js"}},
    {"id":"sidenote-x","cat":"长文","t":"边注","en":"sidenote","d":"正文提到一个数字，旁边给一条不打断阅读的边注；手机上收进正文。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/sidenote-x.html","css":"blocks/sidenote-x.css","js":"blocks/sidenote-x.js"}},
    {"id":"text-annotate","cat":"长文","t":"划词标记","en":"text annotate","d":"选中一句话，浮出「标记」；给长文加荧光笔，不改原文。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/text-annotate.html","css":"blocks/text-annotate.css","js":"blocks/text-annotate.js"}},
    {"id":"text-wrap","cat":"长文","t":"文字绕图","en":"shape-outside","d":"让文字绕着图形走；排版不必永远是方块，轮廓也能被行盒认识。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/text-wrap.html","css":"blocks/text-wrap.css"}},
    {"id":"toc-follow","cat":"长文","t":"目录跟随","en":"toc follow","d":"左边目录，右边正文；滚到哪一节，目录就亮哪一条。长文导航的基本功。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/toc-follow.html","css":"blocks/toc-follow.css","js":"blocks/toc-follow.js"}},
    {"id":"col-pin","cat":"表格","t":"固定首列","en":"pinned column","d":"横向滚动时把首列钉住；表格宽过屏幕，读者仍然知道自己在看谁。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/col-pin.html","css":"blocks/col-pin.css"}},
    {"id":"col-resize","cat":"表格","t":"拖拽列宽","en":"column resize","d":"拖表头边缘改列宽，双击回到默认；数据表最容易被忽略的基本功。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/col-resize.html","css":"blocks/col-resize.css","js":"blocks/col-resize.js"}},
    {"id":"density-toggle","cat":"表格","t":"密度切换","en":"density toggle","d":"舒适和紧凑两档，行高和字号一起变；让用户按屏幕和任务自己选。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/density-toggle.html","css":"blocks/density-toggle.css","js":"blocks/density-toggle.js"}},
    {"id":"filter-builder","cat":"表格","t":"状态筛选","en":"status filter","d":"先按状态收窄，再读列表；筛选按钮上带数量，眼睛不用来回数。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/filter-builder.html","css":"blocks/filter-builder.css","js":"blocks/filter-builder.js"}},
    {"id":"inline-edit","cat":"表格","t":"行内编辑","en":"inline edit","d":"点单元格直接改，回车保存；比开一个弹窗快，也比弹窗不容易迷路。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/inline-edit.html","css":"blocks/inline-edit.css","js":"blocks/inline-edit.js"}},
    {"id":"row-select","cat":"表格","t":"行选择与批量条","en":"row selection","d":"勾选若干行，底部浮出批量操作；选中数量和动作永远一起出现。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/row-select.html","css":"blocks/row-select.css","js":"blocks/row-select.js"}},
    {"id":"table-cell-copy","cat":"表格","t":"单元格复制","en":"copy table cell","d":"只复制被点中的字段而不夹带整行内容；浏览器不能自动复制时展示可选中的原文。","motion":"无动效","tags":["复制","单元格","降级"],"hasInit":true,"files":{"html":"blocks/table-cell-copy.html","css":"blocks/table-cell-copy.css","js":"blocks/table-cell-copy.js"}},
    {"id":"table-column-filters","cat":"表格","t":"组合列筛选","en":"combined column filters","d":"文本列与状态列共同缩小结果范围，清除操作同时复位所有条件，避免隐藏筛选继续生效。","motion":"无动效","tags":["列筛选","组合","重置"],"hasInit":true,"files":{"html":"blocks/table-column-filters.html","css":"blocks/table-column-filters.css","js":"blocks/table-column-filters.js"}},
    {"id":"table-column-toggle","cat":"表格","t":"显示与隐藏列","en":"column visibility","d":"允许暂时隐藏次要列，把比较空间留给当前任务；行数据保留，重新显示时不需要重新加载。","motion":"无动效","tags":["列","显示","表格"],"hasInit":true,"files":{"html":"blocks/table-column-toggle.html","css":"blocks/table-column-toggle.css","js":"blocks/table-column-toggle.js"}},
    {"id":"table-csv-preview","cat":"表格","t":"CSV 导出预览","en":"CSV export preview","d":"导出前展示字段顺序与转义后的文本，逗号和引号不会拆错列，并对可能被表格执行的公式前缀作文本处理。","motion":"无动效","tags":["CSV","导出","转义"],"hasInit":true,"files":{"html":"blocks/table-csv-preview.html","css":"blocks/table-csv-preview.css","js":"blocks/table-csv-preview.js"}},
    {"id":"table-global-search","cat":"表格","t":"跨列表格搜索","en":"global table search","d":"在同一次输入中匹配所有可见字段，保留表头并报告无结果，便于在短表里快速定位。","motion":"无动效","tags":["搜索","表格","空结果"],"hasInit":true,"files":{"html":"blocks/table-global-search.html","css":"blocks/table-global-search.css","js":"blocks/table-global-search.js"}},
    {"id":"table-group-toggle","cat":"表格","t":"分组行折叠","en":"collapsible row groups","d":"按拍摄区域折叠成组记录，组标题保留数量和展开状态，适合先浏览分组再查看明细。","motion":"无动效","tags":["分组","折叠","明细"],"hasInit":true,"files":{"html":"blocks/table-group-toggle.html","css":"blocks/table-group-toggle.css","js":"blocks/table-group-toggle.js"}},
    {"id":"table-multi-sort","cat":"表格","t":"双字段排序","en":"two key table sorting","d":"先按工作状态归组，再按时长排序，让相同时长和相同状态的记录仍有可预测的顺序。","motion":"无动效","tags":["排序","多字段","表格"],"hasInit":true,"files":{"html":"blocks/table-multi-sort.html","css":"blocks/table-multi-sort.css","js":"blocks/table-multi-sort.js"}},
    {"id":"table-page-size","cat":"表格","t":"分页与每页条数","en":"table pagination size","d":"切换每页条数时回到第一页，并在边界禁用翻页，避免留在已经不存在的页码。","motion":"无动效","tags":["分页","条数","边界"],"hasInit":true,"files":{"html":"blocks/table-page-size.html","css":"blocks/table-page-size.css","js":"blocks/table-page-size.js"}},
    {"id":"table-reconcile-keys","cat":"表格","t":"按编号核对两个清单","en":"table reconcile keys","d":"集合核对只比较非空完整编号，区分共同项和单边缺失，不靠行号猜测对应关系。","motion":"无动效","tags":["表格","按编号核对两个清单","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/table-reconcile-keys.html","css":"blocks/table-reconcile-keys.css","js":"blocks/table-reconcile-keys.js"}},
    {"id":"table-row-compare","cat":"表格","t":"两行字段对照","en":"table row compare","d":"选择两个不同记录，对齐字段并用文字指出差异；同一记录不能作为对照双方。","motion":"无动效","tags":["表格","两行字段对照","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/table-row-compare.html","css":"blocks/table-row-compare.css","js":"blocks/table-row-compare.js"}},
    {"id":"table-row-validation","cat":"表格","t":"逐行数据检查","en":"table row validation","d":"检查编号重复与时长范围，按行输出具体原因；修改后重新检查即可恢复通过状态。","motion":"无动效","tags":["表格","逐行数据检查","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/table-row-validation.html","css":"blocks/table-row-validation.css","js":"blocks/table-row-validation.js"}},
    {"id":"table-sort","cat":"表格","t":"表头排序","en":"sortable table","d":"点表头排序，箭头表示方向；表格的第一层交互，比再画一个筛选器有用。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/table-sort.html","css":"blocks/table-sort.css","js":"blocks/table-sort.js"}},
    {"id":"table-transpose-view","cat":"表格","t":"行列转置查看","en":"table transpose view","d":"将同一份数据在横向记录与纵向字段间切换，转置后仍保留表头语义。","motion":"无动效","tags":["表格","行列转置查看","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/table-transpose-view.html","css":"blocks/table-transpose-view.css","js":"blocks/table-transpose-view.js"}},
    {"id":"table-visible-totals","cat":"表格","t":"按可见行汇总","en":"table visible totals","d":"筛选与合计共用当前可见记录，空结果的合计明确为零，不混入隐藏行。","motion":"无动效","tags":["表格","按可见行汇总","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/table-visible-totals.html","css":"blocks/table-visible-totals.css","js":"blocks/table-visible-totals.js"}},
    {"id":"virtual-list","cat":"表格","t":"虚拟滚动","en":"virtual list","d":"一千行只渲染看得见的十几行；长表格不卡，滚动条高度还是对的。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/virtual-list.html","css":"blocks/virtual-list.css","js":"blocks/virtual-list.js"}},
    {"id":"empty-search","cat":"状态页","t":"搜索无结果","en":"empty search","d":"没有结果时给三条能点的建议；空态不是句号，是下一步。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/empty-search.html","css":"blocks/empty-search.css","js":"blocks/empty-search.js"}},
    {"id":"error-retry-x","cat":"状态页","t":"失败与重试","en":"error retry","d":"失败时说清楚“什么没有发生”，再给一个重试；不要只写“出错了”。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/error-retry-x.html","css":"blocks/error-retry-x.css","js":"blocks/error-retry-x.js"}},
    {"id":"maintenance-x","cat":"状态页","t":"维护倒计时","en":"maintenance","d":"维护页给一个可见的等待；有进度和出口，比“请稍后”有诚意。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/maintenance-x.html","css":"blocks/maintenance-x.css","js":"blocks/maintenance-x.js"}},
    {"id":"offline-banner","cat":"状态页","t":"离线横幅","en":"offline banner","d":"断网时顶上出现一条，说明改动会留在本地；恢复后自己消失。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/offline-banner.html","css":"blocks/offline-banner.css","js":"blocks/offline-banner.js"}},
    {"id":"page-403","cat":"状态页","t":"无权限页","en":"403 page","d":"说清楚为什么打不开、谁能开；给一个申请入口，而不是一句“禁止访问”。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/page-403.html","css":"blocks/page-403.css","js":"blocks/page-403.js"}},
    {"id":"page-404","cat":"状态页","t":"404 页","en":"404 page","d":"找不到页面时，先承认，再给一个能走的方向；不要放一个巨大的插画然后没有出口。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/page-404.html","css":"blocks/page-404.css","js":"blocks/page-404.js"}},
    {"id":"session-expired","cat":"状态页","t":"会话过期","en":"session expired","d":"过期不是错误，是提醒；保留现场，重新登录后回到原地。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/session-expired.html","css":"blocks/session-expired.css","js":"blocks/session-expired.js"}},
    {"id":"success-next","cat":"状态页","t":"成功与下一步","en":"success next steps","d":"成功页不只打勾，还告诉人接下来能做什么；否则用户会停在原地。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/success-next.html","css":"blocks/success-next.css","js":"blocks/success-next.js"}},
    {"id":"aspect-ratio-reducer","cat":"媒体","t":"尺寸约分为画幅比例","en":"aspect ratio reducer","d":"用最大公约数约分整数尺寸，同时计算目标宽度对应高度；不强制任何画幅。","motion":"无动效","tags":["媒体","尺寸约分为画幅比例","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/aspect-ratio-reducer.html","css":"blocks/aspect-ratio-reducer.css","js":"blocks/aspect-ratio-reducer.js"}},
    {"id":"aspect-toggle","cat":"媒体","t":"画面比例切换","en":"aspect ratio toggle","d":"16:9 与 9:16 切换时容器高度真的变，不是把画面压扁：压扁会把脸拉长，是最容易看出来的假。竖屏时两侧留出纸色底，而不是黑边——黑边像老电视，纸色是这套系统里「没有内容」的颜色。","motion":"印刷","tags":["比例","竖屏","容器","媒体"],"hasInit":true,"files":{"html":"blocks/aspect-toggle.html","css":"blocks/aspect-toggle.css","js":"blocks/aspect-toggle.js"}},
    {"id":"audio-peaks","cat":"媒体","t":"峰值条跳转","en":"audio peaks seek","d":"峰值条每一根都能量，点是跳转不是装饰：波形能点的概率比一条细进度条高得多，因为它看起来就是内容。已播部分只降不压暗背景，压暗会让整条看起来是灰的。播放时峰值跟着走过，滚动条不动。","motion":"机械","tags":["峰值","跳转","音频","媒体"],"hasInit":true,"files":{"html":"blocks/audio-peaks.html","css":"blocks/audio-peaks.css","js":"blocks/audio-peaks.js"}},
    {"id":"audio-transcript","cat":"媒体","t":"音频转写跟随","en":"audio transcript","d":"转写按句高亮，跟着播放进度走；点某一句直接跳过去。高亮不用整块底色，只在左侧加一条 2px 竖线——整块底色会让人以为那句被选中了。播放时高亮句自动滚动到中间偏上，不滚到正中间，留一眼下一句。","motion":"机械","tags":["转写","跟随","音频","媒体"],"hasInit":true,"files":{"html":"blocks/audio-transcript.html","css":"blocks/audio-transcript.css","js":"blocks/audio-transcript.js"}},
    {"id":"audio-wave","cat":"媒体","t":"音频波形","en":"audio waveform","d":"播放时波形动，暂停时停；没有真实音频，也能表达“正在响”。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/audio-wave.html","css":"blocks/audio-wave.css","js":"blocks/audio-wave.js"}},
    {"id":"before-after","cat":"媒体","t":"前后对比滑杆","en":"before / after","d":"一根滑杆把改版前后叠在一起；比两张图并排更容易看出差别。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/before-after.html","css":"blocks/before-after.css","js":"blocks/before-after.js"}},
    {"id":"caption-cue","cat":"媒体","t":"字幕逐句上屏","en":"caption cue","d":"本例按句展示字幕，以字数估计停留时间；实际应用应结合语音时间轴、语言和阅读速度调整。换句形式与样式可修改，避免在切换中影响阅读。","motion":"无动效","tags":["字幕","逐句","节奏","媒体"],"hasInit":true,"files":{"html":"blocks/caption-cue.html","css":"blocks/caption-cue.css","js":"blocks/caption-cue.js"}},
    {"id":"caption-cue-editor","cat":"媒体","t":"带重叠检查的字幕条目","en":"caption cue editor","d":"新增字幕检查起止时间和区间重叠，按开始时间排序；逐条移除不影响其他字幕。","motion":"无动效","tags":["媒体","带重叠检查的字幕条目","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/caption-cue-editor.html","css":"blocks/caption-cue-editor.css","js":"blocks/caption-cue-editor.js"}},
    {"id":"caption-offset-adjuster","cat":"媒体","t":"字幕整体偏移预览","en":"caption offset adjuster","d":"每行以 秒数|字幕 录入，统一加减偏移；任何结果早于零则整批拒绝，不改原文。","motion":"无动效","tags":["媒体","字幕整体偏移预览","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/caption-offset-adjuster.html","css":"blocks/caption-offset-adjuster.css","js":"blocks/caption-offset-adjuster.js"}},
    {"id":"chapter-markers","cat":"媒体","t":"章节标记","en":"chapter markers","d":"章节标记画在进度条上，不另开列表：看片时想跳的是「哪里」，不是「第几章」。每条标记有一根 2px 的刻度，悬停出名字；正在播放的那一章在下方文字里显示，不占画面。章节按秒切，不做淡入。","motion":"机械","tags":["章节","进度","跳转","媒体"],"hasInit":true,"files":{"html":"blocks/chapter-markers.html","css":"blocks/chapter-markers.css","js":"blocks/chapter-markers.js"}},
    {"id":"duration-badge","cat":"媒体","t":"时长角标","en":"duration badge","d":"时长放右下角、压在画面上，用半透明底：放画外面会被当成标题的一部分。已看过的再加一条进度条贴在底部，比角标更省地方。角标不随悬停消失，信息一直在那里。","motion":"无动效","tags":["时长","角标","进度","媒体"],"hasInit":false,"files":{"html":"blocks/duration-badge.html","css":"blocks/duration-badge.css"}},
    {"id":"gallery-strip","cat":"媒体","t":"缩略图条","en":"gallery strip","d":"缩略图条只在主图下方放一行，选中项用 2px 边框标出，不用放大：放大缩略图会推开两边的图，滑动时整条都在动。键盘左右键也能换图，因为看片的人手常在键盘上。","motion":"无动效","tags":["缩略图","相册","键盘","媒体"],"hasInit":true,"files":{"html":"blocks/gallery-strip.html","css":"blocks/gallery-strip.css","js":"blocks/gallery-strip.js"}},
    {"id":"hotspots","cat":"媒体","t":"图片热点","en":"hotspots","d":"在图上点一个小点看说明；适合产品图、平面图和教学图。","motion":"无动效","tags":[],"hasInit":true,"files":{"html":"blocks/hotspots.html","css":"blocks/hotspots.css","js":"blocks/hotspots.js"}},
    {"id":"image-fit-dimensions","cat":"媒体","t":"容器适配尺寸计算","en":"image fit dimensions","d":"从素材与容器尺寸计算 contain 或 cover 的实际缩放结果和裁切量，不依赖外部图片。","motion":"无动效","tags":["媒体","容器适配尺寸计算","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/image-fit-dimensions.html","css":"blocks/image-fit-dimensions.css","js":"blocks/image-fit-dimensions.js"}},
    {"id":"lazy-media","cat":"媒体","t":"懒加载占位","en":"lazy media","d":"没进视口先占位，进来再加载；列表再长，首屏也不会被拖慢。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/lazy-media.html","css":"blocks/lazy-media.css","js":"blocks/lazy-media.js"}},
    {"id":"live-indicator","cat":"媒体","t":"直播标记","en":"live indicator","d":"直播的红点只在真的在播时闪：暂停了还闪，等于告诉观众「现在是直播」，这是假信息。旁边写延迟秒数，因为直播总归有延迟，不写会有人在弹幕里问「怎么慢半拍」。画面上加一道扫描线，是唯一允许的低成本「在播」信号。","motion":"机械","tags":["直播","延迟","状态","媒体"],"hasInit":true,"files":{"html":"blocks/live-indicator.html","css":"blocks/live-indicator.css","js":"blocks/live-indicator.js"}},
    {"id":"masonry-x","cat":"媒体","t":"瀑布流","en":"masonry","d":"列高不齐的卡片墙；适合图片、笔记、灵感板，不适合需要逐行对齐的数据。","motion":"无动效","tags":[],"hasInit":false,"files":{"html":"blocks/masonry-x.html","css":"blocks/masonry-x.css"}},
    {"id":"media-error-retry","cat":"媒体","t":"媒体加载失败","en":"media error retry","d":"失败时保留播放器外框和时长，只在画面位置写原因：整块换成一个错误页，用户会以为跳走了。原因写人话（「文件在，但解不开」而不是 error code），下面给重试和「换个清晰度」两条路，因为失败常常只是这一档码率的问题。","motion":"机械","tags":["失败","重试","媒体","状态"],"hasInit":true,"files":{"html":"blocks/media-error-retry.html","css":"blocks/media-error-retry.css","js":"blocks/media-error-retry.js"}},
    {"id":"media-skeleton","cat":"媒体","t":"播放器骨架","en":"media skeleton","d":"加载时先铺一层和视频同尺寸的骨架，比例锁死 16:9：骨架和真视频尺寸不一致，加载完页面会跳一下。骨架用呼吸而不是扫光，扫光更像广告位。超过 2 秒才出现「还在加载」的文字，快的时候不闪一下。","motion":"机械","tags":["骨架","加载","播放器","媒体"],"hasInit":true,"files":{"html":"blocks/media-skeleton.html","css":"blocks/media-skeleton.css","js":"blocks/media-skeleton.js"}},
    {"id":"pip-window","cat":"媒体","t":"画中画小窗","en":"picture in picture","d":"小窗固定在右下角、占主画面 28%：再小看不清表情，再大就把主画面挡了。小窗带一个「回主画面」的角标，不然用户不知道能点回去。拖动能挪位置，但松手会吸附到最近的角——飘在中间的小窗总是挡字幕。","motion":"物理","tags":["画中画","小窗","拖动","媒体"],"hasInit":true,"files":{"html":"blocks/pip-window.html","css":"blocks/pip-window.css","js":"blocks/pip-window.js"}},
    {"id":"playback-rate","cat":"媒体","t":"倍速切换","en":"playback rate","d":"倍速只在 1 / 1.5 / 2 三档循环，不做 0.75：审片要的是快，慢放另有逐帧。当前倍速直接写在按钮上，不藏进菜单——正在用哪个速度是播放状态的一部分。切倍速不打断播放，音高不变。","motion":"机械","tags":["倍速","播放","审片","媒体"],"hasInit":true,"files":{"html":"blocks/playback-rate.html","css":"blocks/playback-rate.css","js":"blocks/playback-rate.js"}},
    {"id":"player-skin","cat":"媒体","t":"播放器皮肤","en":"player skin","d":"播放、暂停、进度和时间；视频控件也可以不长得像系统默认。","motion":"机械","tags":[],"hasInit":true,"files":{"html":"blocks/player-skin.html","css":"blocks/player-skin.css","js":"blocks/player-skin.js"}},
    {"id":"playlist-next","cat":"媒体","t":"播放列表接续","en":"playlist next","d":"当前这首在列表里高亮并保持可见；播完自动接下一首，中间不留「播放结束」的空档。列表项显示时长而不是序号，因为下一首该放哪儿，人是用时长判断的。手动点任意一首会立刻切，不排队。","motion":"机械","tags":["播放列表","自动","媒体","音频"],"hasInit":true,"files":{"html":"blocks/playlist-next.html","css":"blocks/playlist-next.css","js":"blocks/playlist-next.js"}},
    {"id":"poster-hover-preview","cat":"媒体","t":"海报悬停预览","en":"poster hover preview","d":"悬停 400ms 后才开始预览，鼠标只是划过去不会播：一划过就动的卡片会让人不敢移动鼠标。预览只放 3 秒，且不循环——循环预览会让人误以为这本来就是一段动图。移开立刻回到海报，不给残留。","motion":"物理","tags":["悬停","预览","海报","媒体"],"hasInit":true,"files":{"html":"blocks/poster-hover-preview.html","css":"blocks/poster-hover-preview.css","js":"blocks/poster-hover-preview.js"}},
    {"id":"ratio-switch","cat":"媒体","t":"比例容器","en":"aspect-ratio","d":"同一块内容在 1:1、16:9、4:5 之间切换；先定比例，再谈裁切。","motion":"印刷","tags":[],"hasInit":true,"files":{"html":"blocks/ratio-switch.html","css":"blocks/ratio-switch.css","js":"blocks/ratio-switch.js"}},
    {"id":"scrub-thumbnails","cat":"媒体","t":"擦洗缩略图","en":"scrub thumbnails","d":"拖动进度时在手指上方弹出一帧缩略图，比只显示时间好找：人能认出画面，认不出「03:14」。缩略图跟着手指但不出边界，出边界时贴边——弹到屏幕外等于没有。松手才真正跳转，拖的时候不打断播放。","motion":"机械","tags":["进度","缩略图","擦洗","媒体"],"hasInit":true,"files":{"html":"blocks/scrub-thumbnails.html","css":"blocks/scrub-thumbnails.css","js":"blocks/scrub-thumbnails.js"}},
    {"id":"subtitle-toggle","cat":"媒体","t":"字幕开关","en":"subtitle toggle","d":"字幕开关只做两件事：开、关；字号放在一个三档里。把设置做成菜单，看片时没人会点进去。字幕底色用半透明黑而不是纯黑，画面亮的时候纯黑底会像贴了一块膏药。","motion":"无动效","tags":["字幕","开关","播放器","媒体"],"hasInit":true,"files":{"html":"blocks/subtitle-toggle.html","css":"blocks/subtitle-toggle.css","js":"blocks/subtitle-toggle.js"}},
    {"id":"timestamp-seconds-converter","cat":"媒体","t":"时间戳与秒数转换","en":"timestamp seconds converter","d":"小时可超过两位，分钟秒数须小于 60；保留到毫秒，明确换算格式。","motion":"无动效","tags":["媒体","时间戳与秒数转换","交互","本地演示"],"hasInit":true,"files":{"html":"blocks/timestamp-seconds-converter.html","css":"blocks/timestamp-seconds-converter.css","js":"blocks/timestamp-seconds-converter.js"}},
    {"id":"video-controls-minimal","cat":"媒体","t":"极简播放控件","en":"minimal player controls","d":"一条进度条上画三层：已播、缓冲、总长。缓冲不画出来，加载时用户只会看到「卡住」。播放键只放在左下角一个，时长放右下角——控件越少，画面越像片子而不是网页。空格键也能播，是因为看片的人手本来就在键盘上。","motion":"机械","tags":["播放器","进度","键盘","媒体"],"hasInit":true,"files":{"html":"blocks/video-controls-minimal.html","css":"blocks/video-controls-minimal.css","js":"blocks/video-controls-minimal.js"}},
    {"id":"video-frame-step","cat":"媒体","t":"逐帧步进","en":"frame step","d":"逐帧按 1/24 秒走，显示到两位小数：审片要的是「再往前一格」，不是「再来半秒」。按住不放会连续步进但不能快过 24fps，否则屏幕只在闪。左右方向键也能走，和剪辑软件一致。","motion":"机械","tags":["逐帧","审片","键盘","媒体"],"hasInit":true,"files":{"html":"blocks/video-frame-step.html","css":"blocks/video-frame-step.css","js":"blocks/video-frame-step.js"}},
    {"id":"volume-popover","cat":"媒体","t":"音量浮层","en":"volume popover","d":"点喇叭弹出竖直滑杆，不是横在按钮下面：竖条占的横向空间小，手指上下滑也更自然。滑杆里画出「当前音量」的填充段，光有一个小圆点时人不知道现在多大声。静音后图标变叉，再点恢复上一次的音量，不是回到满格。","motion":"无动效","tags":["音量","浮层","播放器","媒体"],"hasInit":true,"files":{"html":"blocks/volume-popover.html","css":"blocks/volume-popover.css","js":"blocks/volume-popover.js"}},
    {"id":"waveform-trim","cat":"媒体","t":"波形选区","en":"waveform trim","d":"波形上的选区两端各有一个把手，拖动改边界、拖中间整体平移。选区外压暗到 35%，不是只画框：压暗才看得出「这段不要」。选区时长实时写在中间，剪辑时最先看的就是这个数字。","motion":"机械","tags":["波形","选区","裁剪","媒体"],"hasInit":true,"files":{"html":"blocks/waveform-trim.html","css":"blocks/waveform-trim.css","js":"blocks/waveform-trim.js"}},
    {"id":"zoom-view","cat":"媒体","t":"缩放与拖看","en":"zoom & pan","d":"点倍率放大，按住拖动看细节；比直接打开原图更适合核对画面。","motion":"物理","tags":[],"hasInit":true,"files":{"html":"blocks/zoom-view.html","css":"blocks/zoom-view.css","js":"blocks/zoom-view.js"}},
    {"id":"svg-blob-morph","cat":"SVG 与路径","t":"有机形变","en":"blob morph","d":"八个极径按曲线插值再拼成 Q 路径，而不是给 d 做过渡——d 只有 Chromium 能补间；曲线在 1.56 那里过冲，半径会多鼓一点再回来，这才像一个会呼吸的墨点，等比缩放是死板的。半径上限压到 1.14 倍，过冲也不会蹭到边框。","motion":"物理","tags":["有机","形变","呼吸","路径"],"hasInit":true,"files":{"html":"blocks/svg-blob-morph.html","css":"blocks/svg-blob-morph.css","js":"blocks/svg-blob-morph.js"}},
    {"id":"svg-clip-reveal","cat":"SVG 与路径","t":"图形裁剪揭示","en":"svg clip reveal","d":"本例通过裁剪区域展示内容，变换原点与坐标系需对应，避免从错误位置展开。方向、形状、曲线与是否使用揭示动画由内容决定。","motion":"印刷","tags":["裁剪","揭示","路径","角"],"hasInit":true,"files":{"html":"blocks/svg-clip-reveal.html","css":"blocks/svg-clip-reveal.css","js":"blocks/svg-clip-reveal.js"}},
    {"id":"svg-dash-progress","cat":"SVG 与路径","t":"圆环路径进度","en":"dash progress","d":"环和数字共用同一个 requestAnimationFrame 时钟，才不会出现环走到 78%、数字还停在 65% 的错位；周长用 getTotalLength() 现算而不是写死 126，换成椭圆或非圆路径也不用手改数字。","motion":"机械","tags":["进度","圆环","描边","数字"],"hasInit":true,"files":{"html":"blocks/svg-dash-progress.html","css":"blocks/svg-dash-progress.css","js":"blocks/svg-dash-progress.js"}},
    {"id":"svg-draw-line","cat":"SVG 与路径","t":"描边生长","en":"stroke draw","d":"先用 2/4 的虚线铺一遍画稿，墨线再压着它走一趟——读者认的是「起稿→上墨」这个作画顺序，而不是一条线忽然出现；dasharray 取 271 是这条曲线的实长上取整，短一截就会在收笔处提前断掉。","motion":"印刷","tags":["描边","路径","生长","印刷"],"hasInit":true,"files":{"html":"blocks/svg-draw-line.html","css":"blocks/svg-draw-line.css","js":"blocks/svg-draw-line.js"}},
    {"id":"svg-fill-wipe","cat":"SVG 与路径","t":"填充扫过","en":"fill wipe","d":"用一张会长高的白板当遮罩，而不是给 fill 做透明度过渡——fill 淡入一定会出现中间灰调，而刮墨落纸是硬边；板子以底边为原点，墨就从下往上被刮过去，露出边上那圈 1px 的印版线。","motion":"印刷","tags":["填充","遮罩","印刷","揭示"],"hasInit":true,"files":{"html":"blocks/svg-fill-wipe.html","css":"blocks/svg-fill-wipe.css","js":"blocks/svg-fill-wipe.js"}},
    {"id":"svg-morph-icon","cat":"SVG 与路径","t":"图标路径变形","en":"icon path morph","d":"两条箭头的 d 命令数与顺序完全一样，只有端点互换，所以交叉淡入的那一帧在空间里几乎重合，看不出是两个图形；命令数不同的图标硬切会在中途抖一下，这就是必须先对齐 d 结构的原因。","motion":"物理","tags":["图标","形变","路径","切换"],"hasInit":true,"files":{"html":"blocks/svg-morph-icon.html","css":"blocks/svg-morph-icon.css","js":"blocks/svg-morph-icon.js"}},
    {"id":"svg-motion-path","cat":"SVG 与路径","t":"沿路径运动","en":"motion path","d":"本例让对象沿路径运动；自动朝向可按图形与任务开启或关闭，不能把本例的角度和曲线当作固定要求。保留清楚的路径关系并尊重减少动效偏好。","motion":"机械","tags":["路径","运动","曲线","匀速"],"hasInit":true,"files":{"html":"blocks/svg-motion-path.html","css":"blocks/svg-motion-path.css","js":"blocks/svg-motion-path.js"}},
    {"id":"svg-noise-grain","cat":"SVG 与路径","t":"颗粒质感","en":"noise grain","d":"颗粒层比色块大 6px 再用同形状的 clip 裁住，抖动只发生在这 6px 余量里，边缘才不会露白；alpha 从噪声的 .6R+.6G+.6B-.55 取，只有噪声高的地方落墨，看着才是撒点而不是蒙一层灰。整页最大的一块实色留在这里，因为它得当印刷样张。","motion":"机械","tags":["颗粒","噪点","印刷","网点"],"hasInit":true,"files":{"html":"blocks/svg-noise-grain.html","css":"blocks/svg-noise-grain.css","js":"blocks/svg-noise-grain.js"}},
    {"id":"svg-stagger-dots","cat":"SVG 与路径","t":"点阵依次点亮","en":"staggered dots","d":"六格、每格迟 120ms：最后一格在 600ms 亮起，赶在第一格 900ms 复亮之前把这一轮走完，再加一格就会和第一轮撞上，先后顺序就读不出来了。点用实心圆不用方，12px 的方块会被眼睛读成表格线。","motion":"机械","tags":["点阵","延迟","节奏","扫描"],"hasInit":true,"files":{"html":"blocks/svg-stagger-dots.html","css":"blocks/svg-stagger-dots.css","js":"blocks/svg-stagger-dots.js"}},
    {"id":"svg-text-on-path","cat":"SVG 与路径","t":"文字沿弧线","en":"text on path","d":"startOffset 50% 配 text-anchor middle，字串的中点才落在弧顶；中文字面比西文高，dominant-baseline 设 middle 才能让弧线从字腰穿过去，不然整行会漂在弧线上方一截。21px 是本条唯一的大字号，因为弧形排字本来就该占版面。","motion":"印刷","tags":["排印","文字","弧线","路径"],"hasInit":true,"files":{"html":"blocks/svg-text-on-path.html","css":"blocks/svg-text-on-path.css","js":"blocks/svg-text-on-path.js"}},
    {"id":"card-flip-3d","cat":"3D 与透视","t":"卡片翻面","en":"card flip","d":"翻面用 280ms：180° 要让眼睛看清「转的是同一张卡」，再短只剩闪一下，900ms 又会两面叠在半途、分不清哪面是新的。参数表放背面，因为翻面这个动作已经说明是同一张卡的另一面，不必再换位置。","motion":"物理","tags":["翻面","卡片","参数","立体"],"hasInit":true,"files":{"html":"blocks/card-flip-3d.html","css":"blocks/card-flip-3d.css","js":"blocks/card-flip-3d.js"}},
    {"id":"carousel-coverflow","cat":"3D 与透视","t":"封面轮播","en":"coverflow","d":"侧过身 34° 是「明显侧着」又不至于把标题折到看不见的角度，两侧再缩到 .82、压到 72% 不透就分出了主次。中间那张铺满靛墨是本组唯一的大色块：轮播的活是挑出一张，只靠描边看不出谁被选中。","motion":"物理","tags":["轮播","封面","透视","立体"],"hasInit":true,"files":{"html":"blocks/carousel-coverflow.html","css":"blocks/carousel-coverflow.css","js":"blocks/carousel-coverflow.js"}},
    {"id":"cube-flip","cat":"3D 与透视","t":"立方体翻转","en":"cube flip","d":"本例用两面翻转展示内容切换，面间几何关系需要保持衔接；尺寸、圆角、颜色与文字层级均可修改，转场后仍需读清内容并尊重减少动效偏好。","motion":"物理","tags":["立方体","翻转","换面","立体"],"hasInit":true,"files":{"html":"blocks/cube-flip.html","css":"blocks/cube-flip.css","js":"blocks/cube-flip.js"}},
    {"id":"depth-parallax","cat":"3D 与透视","t":"分层视差","en":"depth parallax","d":"三层靠 translateZ 分景深，不手算每层位移：场景绕 Y 轴转 11° 时，近层在屏幕上走过的像素自然多于远层，跟真实透视一致。11° / 9° 是上限，再转背板边缘就会露出空边；方向键一次 3°，和鼠标手感同档。","motion":"物理","tags":["视差","景深","指针","立体"],"hasInit":true,"files":{"html":"blocks/depth-parallax.html","css":"blocks/depth-parallax.css","js":"blocks/depth-parallax.js"}},
    {"id":"drawer-perspective","cat":"3D 与透视","t":"抽屉透视推出","en":"drawer perspective","d":"拉出用 translateZ 而不是 translateY：只有朝观察者推，边框才会出现轻微斜切，跟真抽屉抽出来一样；再叠 6° rotateX 让上沿先出来一点，超过 8° 就开始像在翻书。底层始终留一条 1px 缝，提醒下面还有东西。","motion":"物理","tags":["抽屉","透视","拉出","立体"],"hasInit":true,"files":{"html":"blocks/drawer-perspective.html","css":"blocks/drawer-perspective.css","js":"blocks/drawer-perspective.js"}},
    {"id":"extruded-text","cat":"3D 与透视","t":"立体文字","en":"extruded text","d":"本例通过同字分层偏移模拟厚度，并让指针改变视角。层数、偏移、配色与运动幅度可修改，检查文字可读性、性能和减少动效偏好。","motion":"物理","tags":["文字","厚度","指针","标题"],"hasInit":true,"files":{"html":"blocks/extruded-text.html","css":"blocks/extruded-text.css","js":"blocks/extruded-text.js"}},
    {"id":"hover-lift-shadow","cat":"3D 与透视","t":"悬浮与影子","en":"hover lift shadow","d":"抬起 12px 的同时影子放大 8px、变虚、变淡：影子不是装饰，是告诉你它离桌面多高。只抬不散影，看起来像整个卡片在往上滚；只散影不抬，像地上有水汽。落回去用 180ms 比抬起快，手感才对——按住东西落下的速度本来就比松开快。","motion":"物理","tags":["悬浮","影子","卡片","质感"],"hasInit":false,"files":{"html":"blocks/hover-lift-shadow.html","css":"blocks/hover-lift-shadow.css"}},
    {"id":"isometric-stack","cat":"3D 与透视","t":"等距图层","en":"isometric stack","d":"等距不是随便斜一下：X 轴压到 cos(60°)、Y 轴压到 sin(60°)，比例不对就不像等距而像被拉歪的平面。抽出时每层间距 26px，是 1px 描边在这个尺度下还能数清层数的最小值。三层封顶——再往上加，抽出来就飞出框了。","motion":"物理","tags":["等距","图层","架构","展开"],"hasInit":true,"files":{"html":"blocks/isometric-stack.html","css":"blocks/isometric-stack.css","js":"blocks/isometric-stack.js"}},
    {"id":"layer-explode","cat":"3D 与透视","t":"分层爆炸图","en":"layer explode","d":"三层沿 Z 轴拉开 26px，是 1px 描边在 640px 透视下还能认出「这是三层」的最小间隙，再挤就糊成一张卡；指针进入某层时它抬到 64px、其余退到 -18px 并压到 45% 不透明度，读者先看清被抽出的那一层，再回头对它在原图里的位置。记忆点：抽屉式抽层。","motion":"物理","tags":["分层","爆炸图","悬停","透视"],"hasInit":true,"files":{"html":"blocks/layer-explode.html","css":"blocks/layer-explode.css","js":"blocks/layer-explode.js"}},
    {"id":"orbit-chart","cat":"3D 与透视","t":"可转立体图表","en":"orbit chart","d":"柱子高度直接写进每根自己的变量里，换数据只改数字，不用动布局；地面网格给 5 格是为了让眼睛有刻度，不给格子就只能看个大概。拖到侧面时后排柱子会被前排挡住一点，这是真三维该有的遮挡，不是 bug——反过来，如果完全不遮，说明只是在斜切一张平面图。","motion":"物理","tags":["图表","柱状","拖拽","三维"],"hasInit":true,"files":{"html":"blocks/orbit-chart.html","css":"blocks/orbit-chart.css","js":"blocks/orbit-chart.js"}},
    {"id":"particle-depth","cat":"3D 与透视","t":"粒子景深","en":"particle depth","d":"远近决定三件事：大小、速度、亮度。远的粒子又小又慢还压暗，近的又大又快，这样看起来才是往屏幕外飞，而不是一堆点在平移。数量按面积给，340×180 只放 46 个，多了就糊成噪点。离开屏幕就停，不白烧电。","motion":"机械","tags":["粒子","景深","画布","背景"],"hasInit":true,"files":{"html":"blocks/particle-depth.html","css":"blocks/particle-depth.css","js":"blocks/particle-depth.js"}},
    {"id":"pointer-light","cat":"3D 与透视","t":"指针当光源","en":"pointer light","d":"光是跟着指针走的一个圆形渐变，中心亮度只到 18%：再亮就盖住了卡面文字，读者先看见光斑再看见内容。光斑用两层——紧的一层是灯，散的一层是反射；只做一层会像一块脏印子。指针离开立刻灭，因为光源跟着人走了，不是留在原地。","motion":"物理","tags":["光","高光","指针","材质"],"hasInit":true,"files":{"html":"blocks/pointer-light.html","css":"blocks/pointer-light.css","js":"blocks/pointer-light.js"}},
    {"id":"scroll-camera-dolly","cat":"3D 与透视","t":"滚动推镜","en":"scroll camera dolly","d":"把滚动量直接接到纵深上，等于相机往前推：近层涨得比远层快，是因为它本来离你近，不是给每层各设一个放大系数。推到顶时停在 150px，再往前近层会糊出框；回滚不弹，因为镜头不该有回弹。","motion":"机械","tags":["推镜","滚动","纵深","相机"],"hasInit":true,"files":{"html":"blocks/scroll-camera-dolly.html","css":"blocks/scroll-camera-dolly.css","js":"blocks/scroll-camera-dolly.js"}},
    {"id":"scroll-parallax-depth","cat":"3D 与透视","t":"滚动分层视差","en":"scroll parallax depth","d":"三层给 0.18 / 0.42 / 0.8 三个系数，不是随便取的：远景只跟四分之一，近景几乎贴身，中间那层压在大约一半，读者才信这三层在同一个空间里。层内文字靠左对齐同一条线，滚动时错开的是距离不是位置，所以看起来是你在往纵深里走，不是三张图各飘各的。","motion":"印刷","tags":["视差","滚动","景深","整页"],"hasInit":true,"files":{"html":"blocks/scroll-parallax-depth.html","css":"blocks/scroll-parallax-depth.css","js":"blocks/scroll-parallax-depth.js"}},
    {"id":"scroll-tilt-reveal","cat":"3D 与透视","t":"滚动立起","en":"scroll tilt reveal","d":"躺到立走 68° 而不是 90°：90° 是纯平贴地，滚到一半会像一块被翻过来的板子，68° 还留着一眼能读到的厚度。每块按自己的位置算进度，不按整页滚动量算，所以长页面上第一块立完第二块才开始，不会一起倒。","motion":"印刷","tags":["滚动","立起","揭示","纵深"],"hasInit":true,"files":{"html":"blocks/scroll-tilt-reveal.html","css":"blocks/scroll-tilt-reveal.css","js":"blocks/scroll-tilt-reveal.js"}},
    {"id":"tag-sphere","cat":"3D 与透视","t":"标签球","en":"tag sphere","d":"每个词的位置用等距球面点算，不手写坐标：手写的坐标会挤在赤道。远小近大按 260 的焦距算，透明度跟着深度掉，最远的降到 35%——不是为了好看，是为了让眼睛分得清前后。拖一下会带着惯性转，松手后慢慢停，停的位置由松手那一下的速度决定。","motion":"物理","tags":["标签球","球面","拖拽","惯性"],"hasInit":true,"files":{"html":"blocks/tag-sphere.html","css":"blocks/tag-sphere.css","js":"blocks/tag-sphere.js"}},
    {"id":"tilt-stack","cat":"3D 与透视","t":"倾斜叠层","en":"tilt stack","d":"三张卡只错 8px 与 16px，再大就会被读成三张独立的卡，而不是一叠；指针移动时整叠只偏 8°（纵轴 6°），超过这个角度卡面文字开始难读。最上层抬 4px 是为了让「手正按着一叠纸」这件事有落点。记忆点：纸叠被指尖压得一歪，上面那张先翘起来。","motion":"物理","tags":["3D","叠层","指针","透视"],"hasInit":true,"files":{"html":"blocks/tilt-stack.html","css":"blocks/tilt-stack.css","js":"blocks/tilt-stack.js"}},
    {"id":"scroll-counters","cat":"滚动驱动","t":"滚动计数","en":"scroll counters","d":"数字不做补间，而是用 scroll-timeline 把 0 到目标值切成 6 格（steps(6)），126 / 18 / 96 除得尽，所以每格分别是 21 / 3 / 16，读数永远落在整数上——仪器式的计数不该出现小数抖动。记忆点：往下滚，三个数字像计数器一样一格一格跳。","motion":"机械","tags":["计数","滚动","数字","仪表"],"hasInit":false,"files":{"html":"blocks/scroll-counters.html","css":"blocks/scroll-counters.css"}},
    {"id":"scroll-horizontal-pin","cat":"滚动驱动","t":"横向锁定","en":"horizontal pin","d":"本例用粘性区域把纵向滚动映射为横向内容移动。滚动行程应根据实际内容尺寸计算，避免未展示完整或长时间停滞；映射比例与表现可调整。","motion":"机械","tags":["滚动","横向","锁定","叙事"],"hasInit":false,"files":{"html":"blocks/scroll-horizontal-pin.html","css":"blocks/scroll-horizontal-pin.css"}},
    {"id":"scroll-range-fade","cat":"滚动驱动","t":"区间淡出","en":"range fade","d":"把退场钉在 cover 55%–95%：卡片在视口里全墨走完大半段，贴上沿才淡到只剩轮廓——淡出绑在「已经在离场」的那一段，读者一进来不会先看到一张半透明的卡。用 animation-range 而不是手算 top<40%，换字号、换行高都不用重算数值。","motion":"印刷","tags":["滚动","区间","淡出","印刷"],"hasInit":false,"files":{"html":"blocks/scroll-range-fade.html","css":"blocks/scroll-range-fade.css"}},
    {"id":"scroll-snap-sections","cat":"滚动驱动","t":"分屏吸附","en":"snap sections","d":"每段高度正好等于滚动框内高（148px），所以 scroll-snap 一定停在段首，不会停在半段；右侧刻度按同样的 28px 步距排三点，指针用 linear 180ms 平移——刻度是读数，加弹簧反而让人以为还有惯性没走完。记忆点：翻页式的段落，右边有个跟着走的指针。","motion":"机械","tags":["分屏","吸附","滚动","刻度"],"hasInit":true,"files":{"html":"blocks/scroll-snap-sections.html","css":"blocks/scroll-snap-sections.css","js":"blocks/scroll-snap-sections.js"}},
    {"id":"scroll-timeline-bar","cat":"滚动驱动","t":"滚动刻度进度","en":"scroll timeline","d":"已有 progress-scroll 也是 CSS 驱动的细线，只回答「到哪了」；这条把 0–100% 切成 6 档并画上刻度，读数靠对齐刻度而不是估长度——它是仪表，那条是书签。用 steps(6) 而不是 linear，因为刻度本来就是一格一格，连续增长的条和下面的 6 个刻线对不上。","motion":"机械","tags":["滚动","进度","刻度","仪表"],"hasInit":false,"files":{"html":"blocks/scroll-timeline-bar.html","css":"blocks/scroll-timeline-bar.css"}},
    {"id":"scroll-velocity-skew","cat":"滚动驱动","t":"速度倾斜","en":"velocity skew","d":"倾斜 6° 封顶：再大文字基线就开始像被压扁，人先感到晃而不是快。速度按「一帧走了多少像素」算，不是按 wheel 一次跳多少——wheel 一跳 100px 不代表真速度，除以帧间隔才是；每帧靠近目标 45%，所以停下后 120ms 内自然回正。","motion":"机械","tags":["滚动","速度","倾斜","惯性"],"hasInit":true,"files":{"html":"blocks/scroll-velocity-skew.html","css":"blocks/scroll-velocity-skew.css","js":"blocks/scroll-velocity-skew.js"}},
    {"id":"scroll-view-reveal","cat":"滚动驱动","t":"视口进入揭示","en":"view timeline","d":"用 view() 而不是 IntersectionObserver：揭示进度交给滚动位置，滚到一半停下卡片就停在半透明，往上滚还会退回去——JS 一次性加 class 只会「已经出现过了」一格，回滚无法复原。区间取 entry 10% 到 cover 45%，这样卡片露出三分之一时才真正看清。","motion":"印刷","tags":["滚动","揭示","视口","免监听"],"hasInit":false,"files":{"html":"blocks/scroll-view-reveal.html","css":"blocks/scroll-view-reveal.css"}}
  ]
}
