
Kaneo 原型变体切换器 The Picker从 Markup、CSS 到行为契约的完整实现规范【免费下载链接】app All you need. Nothing you dont. Open source project management that works for you, not against you.项目地址: https://gitcode.com/GitHub_Trending/app116/app在 Kaneo开源项目管理应用的设计工程技能库中prototype技能负责把一个 UI 片段一个 Toast、一张定价卡、一个按住删除按钮做成多个真正不同的版本并放在可视化切换器背后让用户实时翻看、挑出最合适的一个。而这个切换器本身——PICKER.md 所定义的The Picker——是一份被刻意冻结的规格它是一颗悬浮在页面底部中央的深色玻璃胶囊外观不是设计决策而是评审工具台harness的一部分。读完本文你将掌握这颗 Picker 的完整 Markup、CSS 样式表、四条硬性规则、行为契约与参考接线代码并理解它如何与 prototype 技能的 Phase 4 工作流skills/prototype/SKILL.md衔接以及如何在 React 等框架中按同一行为契约做惯用化移植。Picker 在设计工程流程中的定位在展开代码之前先明确它在整个技能生态里的角色。根据 skills/prototype/SKILL.mdprototype 是一个发散divergence技能它只做一件事——把一段被描述的 UI 构建成几个真正不同的版本放到可视化切换器后面让用户实时翻看并选出赢家。它不做已有 UI 的评审那是 skills/review-animations/SKILL.md、不为已有动效制定修复计划那是 skills/improve-animations/SKILL.md、也不挑选依赖库那是pick-ui-library。在 Phase 4「构建 Picker 工具台」中SKILL.md 明确指示Picker 的 Markup、样式、键盘接线和位置来自 PICKER.md逐字照抄verbatim。PICKER.md 的开篇第一句话就划定了边界The pickers appearance isnot a design decision— it is this spec.也就是说Picker 的外观不是被评审的对象它是评审工具。因此它必须跨项目保持完全一致——永远读起来像工具台镶边harness chrome而不是被评审设计的一部分。不得用项目的 token、字体或配色去重新装饰它。一个关键的设计选择是它是一颗**深色玻璃dark glass**悬浮胶囊底部居中。深色玻璃在浅色和深色页面上都能叠得住因此它不需要感知主题not theme-aware。Markup 结构五个元素的职责拆解Picker 的 DOM 结构是固定的原样照抄即可nav classproto-picker aria-labelPrototype variants span classproto-picker-highlight aria-hiddentrue/span button classproto-picker-item>.proto-picker { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 2147483647; display: flex; align-items: center; gap: 2px; padding: 4px; border-radius: 999px; background: rgba(10, 10, 10, 0.82); -webkit-backdrop-filter: blur(12px) saturate(1.4); backdrop-filter: blur(12px) saturate(1.4); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08) inset, 0 8px 24px rgba(0, 0, 0, 0.24), 0 2px 6px rgba(0, 0, 0, 0.12); font-family: -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif; font-size: 13px; line-height: 1; -webkit-font-smoothing: antialiased; user-select: none; -webkit-user-select: none; } .proto-picker-highlight { position: absolute; top: 4px; left: 0; height: 28px; border-radius: 999px; background: rgba(255, 255, 255, 0.12); will-change: transform; } /* The slide is enabled only after first paint (data-ready), so load doesnt animate. */ .proto-picker[data-ready] .proto-picker-highlight { transition: transform 250ms cubic-bezier(0.23, 1, 0.32, 1), width 250ms cubic-bezier(0.23, 1, 0.32, 1); } media (prefers-reduced-motion: reduce) { .proto-picker[data-ready] .proto-picker-highlight { transition: none; } } .proto-picker-item { position: relative; /* sits above the highlight */ display: flex; align-items: center; height: 28px; padding: 0 12px; border: 0; border-radius: 999px; background: transparent; color: rgba(255, 255, 255, 0.55); font: inherit; cursor: pointer; transition: color 150ms ease-out; } .proto-picker-item:hover { color: rgba(255, 255, 255, 0.85); } .proto-picker-item:active { transform: scale(0.97); } .proto-picker-item:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.4); outline-offset: 2px; } .proto-picker-item[data-active] { color: #fff; } .proto-picker-divider { width: 1px; height: 16px; margin: 0 4px; background: rgba(255, 255, 255, 0.12); } .proto-picker-replay { padding: 0 10px; font-size: 14px; } .proto-picker[data-positiontop] { bottom: auto; top: 24px; }值得逐段说明的设计意图定位与层级position: fixedbottom: 24pxleft: 50%translateX(-50%)实现底部居中z-index: 2147483647是 32 位有符号整数的最大值保证 Picker 永远悬浮在任何内容之上不会被子元素的层叠上下文盖住。深色玻璃质感background: rgba(10, 10, 10, 0.82)配backdrop-filter: blur(12px) saturate(1.4)形成磨砂玻璃-webkit-前缀保证 Safari 兼容。三层box-shadow分别提供 1px 内描边、24px 大阴影与 6px 小阴影塑造胶囊的悬浮立体感。字体独立性font-family使用系统字体栈font-size: 13px明确不继承项目字体——这正呼应了不得用项目 tokens 重装饰的规则。滑动高亮高亮块position: absolute、高 28px、圆角胶囊背景为半透明白rgba(255,255,255,0.12)。它通过transform: translateX()与width在按钮之间滑动。data-ready 门控滑动 transition仅在[data-ready]属性出现后才启用该属性由 JS 在首次绘制后通过双重requestAnimationFrame添加。这样页面加载时高亮块直接落在初始位置不会从左上角滑进来。reduced-motionprefers-reduced-motion: reduce时关闭 transition。这与仓库动画标准skills/review-animations/STANDARDS.md中尊重减少动效偏好的原则一致。按压反馈:active时transform: scale(0.97)是 skills/animate/SKILL.md 中按钮按压反馈标准的直接应用100–160ms、scale(0.95–0.98)。无障碍焦点:focus-visible使用 2px 半透明白描边键盘用户能看到焦点环。唯一允许的位置修改data-positiontop会把胶囊移到顶部top: 24px用于变体本身占据屏幕底部中央Toast 堆栈、底部抽屉、Dock的场景避免 Picker 遮住被评审的工作内容。四条硬性规则逐字照抄Verbatim这些值就是规格本身。不得使用项目字体、品牌色不做主题切换不加额外的阴影或边框。高亮滑动、变体切换保持瞬时活动药丸在按钮之间以 250ms 强缓出曲线动画作为 Picker 自身的空间反馈但被预览的变体切换没有任何过渡。这里width的 transition 是对 transform/opacity 规则的刻意例外该元素高 28px、绝对定位、没有布局依赖绘制成本可忽略。唯一允许的修改如果某个变体占据屏幕底部中央Toast 堆栈、底部抽屉、Dock设置data-positiontop让 Picker 永不遮住工作内容。除此之外任何东西都不得移动或改变。重放按钮是条件渲染的仅当至少一个变体有值得重新触发的入场或状态动画时才渲染重放按钮及其分隔符纯静态对比则得到一颗更短的胶囊。行为契约固定不变的交互语义无论工具台用什么框架渲染行为契约是固定的键盘数字键1–N与←/→切换变体R重放。当焦点位于 input、textarea、select 或 contenteditable 中或按住修饰键meta/ctrl/alt时忽略键盘事件。点击点击某项即切换到它任何时刻恰好一项携带data-active与aria-currenttrue高亮块滑向它。URL 持久化选择通过 URL 参数?v2在刷新后保持缺省回退到变体 1。高亮块初始位置不带动画data-ready在首次绘制后添加。重挂载语义切换会重挂载变体因此入场动画会重新执行按重放键只重挂载、不切换。最后一条是整个 Picker 的核心机制切换即重挂载re-mount从而让每个变体的入场动画都能真实重演——这正是评审动效这一用途的基础。参考接线JavaScript 实现详解以下是独立 HTML 分支的逐字参考实现在框架中保持相同行为但用惯用化表达详见下一节// variants is an array of render functions, one per variant, in picker order. const stage document.getElementById(stage); const picker document.querySelector(.proto-picker); const highlight picker.querySelector(.proto-picker-highlight); const items [...picker.querySelectorAll(.proto-picker-item:not(.proto-picker-replay))]; const replay picker.querySelector(.proto-picker-replay); let current 0; function moveHighlight() { const el items[current]; highlight.style.width el.offsetWidth px; highlight.style.transform translateX(${el.offsetLeft}px); } function mount(i) { stage.innerHTML ; // Clear first, render next frame, so entrance animations re-run. requestAnimationFrame(() { stage.innerHTML variants[i](); }); } function setActive(i) { if (i 0 || i variants.length) return; current i; items.forEach((el, j) { el.toggleAttribute(data-active, j i); if (j i) el.setAttribute(aria-current, true); else el.removeAttribute(aria-current); }); moveHighlight(); const url new URL(location); url.searchParams.set(v, i 1); history.replaceState(null, , url); mount(i); } items.forEach((el, i) el.addEventListener(click, () setActive(i))); replay?.addEventListener(click, () mount(current)); window.addEventListener(resize, moveHighlight); document.addEventListener(keydown, (e) { if (/^(INPUT|TEXTAREA|SELECT)$/.test(e.target.tagName) || e.target.isContentEditable) return; if (e.metaKey || e.ctrlKey || e.altKey) return; const num parseInt(e.key, 10); if (num 1 num variants.length) setActive(num - 1); else if (e.key ArrowRight) setActive((current 1) % variants.length); else if (e.key ArrowLeft) setActive((current - 1 variants.length) % variants.length); else if (e.key r || e.key R) mount(current); }); setActive((parseInt(new URLSearchParams(location.search).get(v), 10) || 1) - 1); // Enable the slide only after first paint, so load doesnt animate. requestAnimationFrame(() requestAnimationFrame(() picker.setAttribute(data-ready, )));关键实现要点moveHighlight()直接用offsetWidth与offsetLeft同步高亮块的宽与位。由于高亮是绝对定位、项目是相对定位position: relativeCSS 注释明确说明坐在高亮之上offsetLeft相对 Picker 容器计算是准确的。窗口resize时也要重算。mount(i)先清空舞台再在下一帧渲染——这就是入场动画重新执行的秘诀。清空后 DOM 已移除旧节点下一帧插入新节点时浏览器会将其视为全新元素CSS transition 与keyframes才会从头触发。setActive(i)越界保护 → 更新current→ 通过toggleAttribute维护data-active与aria-current的恰好一项不变量 → 移动高亮 → 用history.replaceState写入?vN替换而非压栈避免污染浏览器历史→ 重挂载变体。键盘处理器先排除 input/textarea/select/contenteditable 与修饰键再处理数字键、方向键带模运算回绕与R键。这与行为契约逐条对应。初始化从?v读取序号缺省 1并执行双重requestAnimationFrame后设置data-ready确保加载时高亮不滑动。框架移植保持行为契约做惯用化表达PICKER.md 对框架分支给出的指导是保持相同行为但用框架惯用法表达。具体对应关系独立 HTML 写法框架惯用法stage.innerHTML variants[i]()组件状态驱动渲染state而非 innerHTML 字符串拼接requestAnimationFrame清空再渲染keyed 重挂载如 React 中给变体根元素设置变化的key强制 React 卸载旧子树、挂载新子树使入场动效重跑手动查询offsetWidth/offsetLeft移动高亮ref layout effect如 React 的useLayoutEffect在 DOM 布局后测量并设置高亮位置需要注意保持不变的类名proto-picker*、结构顺序、data-active/aria-current语义、data-ready门控、键盘契约、URL 持久化。任何框架适配都不得改变这些可观察行为。与 prototype 技能工作流的衔接在 skills/prototype/SKILL.md 的 Phase 4 中Picker 工具的搭建分两条分支有 dev server 的项目内建一个隔离路由或页面/prototypes/slug或框架等价物每个变体一个文件 一个小型工具台文件。任何东西都不得从原型面导入生产代码对应 Hard Rule 1探索期间绝不触碰生产代码。无项目 / 纯静态场景一个自包含的 HTML 文件内联 CSS/JS用户可直接在浏览器中打开。工具台在 Picker 之外还有一个硬性要求一次只渲染一个变体、全尺寸、放在真实的周边环境中——Toast 需要背后有一页内容卡片需要有兄弟元素按钮需要有一个表单。并排缩略图会扭曲间距与比例绝不在邮票大小的尺寸上评审 UI。切换是瞬时的——翻看是 100/会话的高频动作按频率规则变体切换不带任何动画。整个工作流Phase 1 范围界定 → Phase 2 侦察 → Phase 3 选择方向 → Phase 4 构建工具台 → Phase 5 验证与交接 → Phase 6 提升选中项中Picker 始终是评审工具台而非参赛者选赢家后按 Hard Rule 5 清理原型面。技能的清单skills-lock.json表明这套技能源自 Emil Kowalski 的设计工程技能集与仓库内 skills/animate/SKILL.md动效构建、skills/review-animations/SKILL.md动效评审等技能共享同一套缓动曲线、时长预算与 reduced-motion 原则——Picker 中的cubic-bezier(0.23, 1, 0.32, 1)正是仓库标准里的--ease-out强缓出曲线scale(0.97)按压反馈与 sub-300ms 时长也都与标准表skills/review-animations/STANDARDS.md逐项吻合。小结The Picker 是一份冻结规格Markup、CSS、行为契约与接线代码逐字照抄唯一可变的是变体名称、数量以及极少数场景下的data-positiontop。它刻意不感知主题、不继承项目设计 token以恒定不变的工具台镶边外观保证评审的公平性同时它内部的每次滑动、每次重挂载、每次 URL 写入都精确服务于一个目标——让用户能在一屏之内、纯键盘或纯鼠标地真实、公平地比较多个 UI 方向。理解它的每条规则也就理解了 prototype 技能所追求的发散而不失工艺的设计工程方法论。【免费下载链接】app All you need. Nothing you dont. Open source project management that works for you, not against you.项目地址: https://gitcode.com/GitHub_Trending/app116/app创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考