无头组件系统 · 新资源

Bits UI 设计系统.

Bits UI 提供无头 Svelte 组件,通过原生部件和 Svelte 5 状态进行组合。示例将派生的混合状态复选框、可独立展开的手风琴面板和基于 portal 的评审对话框组合在一起,并附上完整的样式配置。

OpenDesign 指南9 个章节28 个参考条目原始应用指南
01

Bits UI 设计系统界面预览

标记单项检查,查看批量控件的混合状态,检查未完成的进度,然后完成并保存本地清单。

人工界面审核打开完整示例
Bits UI 2.19.5 Checkbox、Accordion 和 Dialog,搭配 Svelte 5.57.1。所有视觉样式均为原创。· 实现参考
02

Bits UI 设计系统设计参考

Bits UI 2.19.5,搭配 Svelte 5.57.1。Bits UI 是无头库:所有 --app-* 颜色、字体和间距值均为本示例附带的明确原创配置,并非 Bits UI 官方设计令牌。原生数据属性与组件属性在下文分别说明。

原创语义化 CSS 配置

来源
名称 / 规则值 / 配置用途
--app-canvas#f4f3f8 页面背景。
--app-surface#ffffff 面板和对话框表面。
--app-ink#2d2938 正文。
--app-muted#746d80 辅助性文本。
--app-line#e3dfeb 分隔线和边框。
--app-action#604c88 选中控件和主要操作。
--app-on-action#fff操作按钮上的文字。
--app-focus#9a80ce 清晰可见的键盘焦点。
--app-soft#eee8f7 可选的柔和表面角色。

原创间距与几何尺寸

来源
名称 / 规则值 / 配置用途
--app-space-14px最小的命名间距单位。
--app-space-28px紧凑间距。
--app-space-312px控件与标签之间的间距。
--app-space-416px面板内侧窄边距。
--app-space-624px桌面端面板内边距和栏间距。
--app-space-832px桌面端页面内边距。
Checkbox geometry22×22px; 5px radius; 1px border明确的原创控件样式。
Focus ring3px solid #9a80ce; 3px offset应用于键盘焦点可见时的按钮状态。
Dialog widthmin(440px, 100% − 28px)明确的响应式覆盖层几何尺寸。
Dialog maximum height100vh − 28px with overflow:auto仅对覆盖层施加视口约束;不随页面高度变化。
Type hierarchy34px title / 29px mobile; 16px accordion; 13px checks; 12px guidance原创系统字体层级。

原生组件契约

来源
名称 / 规则值 / 配置用途
Checkbox.Root checkedboolean已完成或未完成状态。
Checkbox.Root indeterminateboolean部分检查已标记时,批量控件会显示混合状态。
Checkbox data-statechecked / unchecked / indeterminate由样式表直接使用。
Accordion.Root typemultiple可同时展开多个审核分区。
Accordion valuestring[]由 Svelte 绑定的已展开组 ID 列表。
Dialog.Root openboolean, two-way bound控制审核内容的显示状态。
Dialog.Title / DescriptionNative title and description relationships无需手动添加重复的 ARIA ID。
Dialog.PortalOverlay and content rendered outside the normal panel通过面板的文档流避免内容裁切。
03

Bits UI 设计系统完整指南

实用指南,介绍如何为真实界面选择、应用和评审此系统。

DESIGN-bits-ui.md

完整指导、实现起点和源代码链接。

下载指南

Bits UI 设计系统:围绕 Svelte 5 状态组合原生部件

围绕 Svelte 5 状态组合原生部件

此清单使用 $state 保存各项答案、已展开的分区 ID 和对话框的显示状态。$derived 推导出的完成项数同时用于显示进度和批量复选框。这样一来,真实数据源集中在一个 Svelte 模型中,而 Bits UI 则为各个组件提供原生交互。

摘自本 OpenDesign 指南

Checkbox、Accordion 和 Dialog 均由各自的部件组合而成。审核对话框无需管理清单答案,手风琴分区关闭时也无需丢弃这些答案。对于多分区表单,这种分离方式是实用的起点:即使界面中可见的部件发生变化,模型中的数据也会保留。

阅读配套规范

Bits UI 设计系统:配合使用 Svelte 5 组件与样式资源

配合使用 Svelte 5 组件与样式资源

此示例使用 Svelte 5 runes:用 $state 管理分组、展开的分区、对话框显示状态和保存状态;用 $derived 推导完成项数。直接导入 Bits UI 2.19.5。无需 Tailwind 插件、主题提供程序或图标库,即可实现这套原创视觉样式。

完整的 App.svelte 和 style.css 可在标准 Vite Svelte 项目中运行。入口使用 Svelte 的 mount,而非旧式的 new App 构造函数。移植此示例时,请同时保留组件的 runes 语法和 Bits UI 2.19.5 依赖;此处展示的绑定与回调并非旧版 Svelte 组件 API。

阅读配套规范

Bits UI 设计系统:根据各项检查结果推导真正的混合状态

根据各项检查结果推导真正的混合状态

六个布尔检查项分布在三个分组中。只有六项全为 true 时,批量控件才会显示为选中;当计数介于一到五之间时,则显示为不确定状态。其 onCheckedChange 回调会更新所有检查项,因此混合状态是计算得出的结果,而非单独存储、可能与实际状态不一致的标记。

单项控件会调用一个小型变更函数来更新其模型,并使已保存的提示失效。选中一项后,渲染出的原生批量复选框会显示 aria-checked=mixed。样式表根据 data-state=checked 和 data-state=indeterminate 作出响应,使行为与外观保持一致,并由同一原生状态驱动。

  1. 选中第一项焦点指示器检查,并查看批量控件中的短横线。
  2. 打开“审核”清单,并确认“保存已完成审核”处于禁用状态。
  3. 返回,标记所有检查项,并保存完整的本地快照。

阅读配套规范

Bits UI 设计系统:允许各个分区独立操作,同时保留各自的答案

允许各个分区独立操作,同时保留各自的答案

Accordion.Root 使用 type=multiple,并绑定已展开组 ID 的数组。初始时各分区均展开,且可以分别展开“表单校正”和“结果反馈”分区。折叠分区不会清除其中的检查项,因为这些值保存在父级状态中。

每个触发器都会显示该分区已完成的项目数,因此即使内容已折叠,进度依然可见。原生 Header、Trigger 和 Content 部件保留了预期的关联关系和键盘行为。加号标记会根据触发器的 data-state 旋转,并遵循减少动态效果偏好,而不会通过单独的点击处理程序控制展开。

阅读配套规范

Bits UI 设计系统:组合完整的对话框结构,并由其管理焦点

组合完整的对话框结构,并由其管理焦点

审核界面使用 Dialog.Root、Trigger、Portal、Overlay、Content、Title、Description 和 Close。这些标题和描述都是真正的原生部件,因此 Bits UI 能够建立它们之间的无障碍关联。Portal 会将覆盖层放到清单面板的正常文档流之外。

对话框内容设有响应式宽度限制,覆盖层高度也设有最大值;审核内容较长时可滚动查看。按 Escape 可关闭对话框,而不会清除清单。请保留触发器作为焦点返回目标,不要在覆盖层打开时将其移除。

阅读配套规范

Bits UI 设计系统:明确并复用应用的视觉规则

明确并复用应用的视觉规则

CSS 将原生的选中和不确定状态映射到同一操作角色,但为每种状态使用不同的图形。单独的焦点角色用于表示键盘焦点位置,因此控件可以在保持选中状态的同时,指示下一次键盘操作将发生的位置。

命名间距比例为 4、8、12、16、24 和 32 像素。桌面端面板使用 24 像素间距,窄屏则使用 16 像素。规范表将这些具体选择记录为原始配置,方便读者复现或替换,也避免将演示样式误认为包所保证的默认值。

阅读配套规范

Bits UI 设计系统:区分手动标记与自动化测试结果

区分人工标记与自动化测试结果

此清单记录读者标记的项目,不会检查其他页面、执行键盘测试或判定是否符合无障碍标准。简介文案和审核说明都明确了这一范围;这一点很重要,因为六项检查涉及实际的无障碍行为。

只有六项全部标记后,保存按钮才会启用。保存后的提示说明六项均由读者标记,且结果保存在本地。读者仍可打开未完成的审核以查看缺漏,但无法生成审核已完成的提示。这样的区分让示例中的状态转换更有意义。

阅读配套规范

Bits UI 设计系统 证据变更时,清除完成提示

证据发生变化时,清除完成提示

任何单项或批量更改都会清除已保存状态,避免取消某项检查后仍显示已完成的确认信息。“全部清除”操作还会恢复初始展开的分区并关闭审核对话框,让所有相关状态回到明确的初始状态。

状态区域位于叠加层之外,因此关闭对话框后,已保存的提示仍会保留在文档中。提示描述的是页面本地状态,不会让人误以为报告已提交。重新加载页面会清除清单,这对于这个独立演示来说是合适的。

阅读配套规范

Bits UI 设计系统 将组件部件关联到可见标签

将组件部件关联到可见标签

每个复选框都有稳定的 ID,并关联一个可见标签。选中或不确定状态由原生控件表示;勾号和短横线仅用于装饰。批量控件应同时关联布尔型的选中值和派生的不确定值,避免将部分选中状态误报为普通的未选中项。

Dialog.Title 和 Dialog.Description 通过原生结构提供审核名称和说明。Portal 将叠加层与手风琴的布局分开,而 Dialog.Close 则是明确的关闭操作。叠加层关闭后,外部状态区域仍会保留已保存的结果,因此不会随对话框内容一同消失。

  • 确保可见标签与其控件相关联。
  • 应用自定义样式后,仍保留明显的焦点可见环。
  • 重新设置对话框样式时,不要移除标题或描述部件。

阅读配套规范

Bits UI 设计系统组件示例

完整的 Svelte 5 清单

原生 Bits UI 组合,使用 $state/$derived 模型、受控复选框和基于 Portal 的审核对话框。

svelte
<script>
import {Accordion,Checkbox,Dialog} from 'bits-ui';
let groups=$state([{id:'keyboard',name:'Keyboard flow',description:'Move through the interface without a pointer.',checks:[{id:'focus',label:'Every action has a visible focus indicator.',done:false},{id:'order',label:'Focus follows the visual reading order.',done:false}]},{id:'forms',name:'Form correction',description:'Make a mistake and recover from it.',checks:[{id:'labels',label:'Each input has a persistent visible label.',done:false},{id:'errors',label:'Errors identify the field and explain the correction.',done:false}]},{id:'feedback',name:'Result feedback',description:'Confirm that the outcome is understandable.',checks:[{id:'status',label:'A status message describes the completed action.',done:false},{id:'color',label:'Meaning remains clear without color alone.',done:false}]}]);
let expanded=$state(['keyboard']),open=$state(false),saved=$state(false);
const complete=$derived(groups.reduce((sum,g)=>sum+g.checks.filter(c=>c.done).length,0));
function change(check,value){check.done=value;saved=false}
function all(value){groups.forEach(g=>g.checks.forEach(c=>c.done=value));saved=false}
function reset(){all(false);expanded=['keyboard'];open=false}
</script>
<main class="check-shell"><header><p class="eyebrow">INTERFACE REVIEW · MANUAL CHECKLIST</p><h1>Check the experience between the screens.</h1><p>Work through six practical checks, then review what you have marked. This is a personal checklist, not an automated accessibility test.</p></header><div class="check-layout"><section class="check-panel"><div class="bulk"><Checkbox.Root id="all-checks" checked={complete===6} indeterminate={complete>0&&complete<6} onCheckedChange={all} class="checkbox"><span aria-hidden="true">{complete===6?'✓':complete>0?'−':''}</span></Checkbox.Root><label for="all-checks">Mark all checks</label><span class="count">{complete}/6</span></div><Accordion.Root type="multiple" bind:value={expanded} class="accordion">{#each groups as group}<Accordion.Item value={group.id} class="accordion-item"><Accordion.Header class="accordion-heading"><Accordion.Trigger class="accordion-trigger"><span>{group.name}<small>{group.checks.filter(c=>c.done).length} of 2 checked</small></span><span class="plus" aria-hidden="true">+</span></Accordion.Trigger></Accordion.Header><Accordion.Content class="accordion-content"><p class="group-description">{group.description}</p>{#each group.checks as check}<div class="check-row"><Checkbox.Root id={check.id} checked={check.done} onCheckedChange={value=>change(check,value)} class="checkbox"><span aria-hidden="true">{check.done?'✓':''}</span></Checkbox.Root><label for={check.id}>{check.label}</label></div>{/each}</Accordion.Content></Accordion.Item>{/each}</Accordion.Root><Dialog.Root bind:open><Dialog.Trigger class="primary">Review checklist</Dialog.Trigger><Dialog.Portal><Dialog.Overlay class="overlay"/><Dialog.Content class="dialog"><Dialog.Title class="dialog-title">Your review snapshot</Dialog.Title><Dialog.Description class="dialog-description">{complete} of 6 checks are marked. Saving records your own review state; it does not certify the interface.</Dialog.Description><ul class="review-summary">{#each groups as group}<li><strong>{group.name}</strong><span>{group.checks.filter(c=>c.done).length} / 2</span></li>{/each}</ul>{#if complete<6}<p class="remaining">Return to the checklist to finish the remaining {6-complete} checks.</p>{/if}<div class="dialog-actions"><Dialog.Close class="secondary">Back to checks</Dialog.Close><button class="primary" disabled={complete<6} onclick={()=>{saved=true;open=false}}>Save completed review</button></div></Dialog.Content></Dialog.Portal></Dialog.Root><button class="reset" onclick={reset}>Clear all checks</button><p class="result" role="status">{saved?'Completed review saved locally. All six checks were marked by you.':'Progress stays in this preview until you reload.'}</p></section><aside class="check-notes"><div class="focus-art" aria-hidden="true"><span></span><i></i><b></b></div><p class="eyebrow">WHAT TO LOOK FOR</p><h2>Make the next action clear.</h2><p>A good check leaves evidence: where focus moved, what an error said, or how a result was announced.</p><dl><div><dt>Keyboard</dt><dd>Tab, Shift+Tab, Enter and Escape should support the relevant task.</dd></div><div><dt>Correction</dt><dd>Keep the user’s input and point to a specific next step.</dd></div><div><dt>Feedback</dt><dd>Describe the outcome in words as well as visual styling.</dd></div></dl></aside></div></main>

代码参考

  • 主控件根据六项单独检查推导选中和不确定状态。
  • 是否允许保存取决于检查是否全部完成,而不取决于对话框是否打开。
  • 此手动工作表不会运行无障碍审计。

完整的原创视觉配置

命名语义颜色、间距比例、原生 data-state 选择器和响应式叠加层样式。

css
:root{--app-canvas:#f4f3f8;--app-surface:#ffffff;--app-ink:#2d2938;--app-muted:#746d80;--app-line:#e3dfeb;--app-action:#604c88;--app-on-action:#fff;--app-focus:#9a80ce;--app-soft:#eee8f7;--app-radius:12px;--app-space-1:4px;--app-space-2:8px;--app-space-3:12px;--app-space-4:16px;--app-space-6:24px;--app-space-8:32px}html,body,#app{height:auto;min-height:0;margin:0}*{box-sizing:border-box}body{font-family:system-ui,sans-serif;background:var(--app-canvas);color:var(--app-ink)}button{font:inherit;cursor:pointer}button:disabled{opacity:.45;cursor:default}button:focus-visible{outline:3px solid var(--app-focus);outline-offset:3px}.check-shell{max-width:1080px;margin:auto;padding:var(--app-space-8)}.eyebrow{font-size:10px;letter-spacing:.14em;font-weight:700;color:var(--app-muted)}h1{font-size:34px;line-height:1.15;max-width:710px;margin:12px 0}header>p:last-child{font-size:14px;line-height:1.7;max-width:640px;color:var(--app-muted)}.check-layout{display:grid;grid-template-columns:minmax(0,1fr) 270px;gap:var(--app-space-6);margin-top:28px}.check-panel{background:var(--app-surface);border:1px solid var(--app-line);border-radius:16px;padding:var(--app-space-6);min-width:0}.bulk{display:flex;align-items:center;gap:12px;font-size:14px;font-weight:600;padding-bottom:24px}.count{margin-left:auto;color:var(--app-muted);font-size:12px}.checkbox{width:22px;height:22px;flex-shrink:0;border:1px solid #aaa1ba;background:white;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;padding:0;line-height:1}.checkbox[data-state=checked],.checkbox[data-state=indeterminate]{background:var(--app-action);border-color:var(--app-action);color:var(--app-on-action)}.accordion{margin-bottom:24px}.accordion-item{border-top:1px solid var(--app-line)}.accordion-item:last-child{border-bottom:1px solid var(--app-line)}.accordion-heading{margin:0;font-size:16px}.accordion-trigger{width:100%;border:0;background:transparent;display:flex;align-items:center;justify-content:space-between;text-align:left;padding:20px 0;color:var(--app-ink);font-weight:600}.accordion-trigger small{display:block;font-weight:400;font-size:11px;color:var(--app-muted);margin-top:6px}.plus{font-size:23px;font-weight:400;transition:transform .15s}.accordion-trigger[data-state=open] .plus{transform:rotate(45deg)}.accordion-content{padding:0 0 20px}.group-description{font-size:12px;line-height:1.6;color:var(--app-muted);margin:0 0 16px}.check-row{display:flex;align-items:flex-start;gap:12px;margin-top:14px}.check-row label{font-size:13px;line-height:1.6;cursor:pointer}.primary,.secondary{border:1px solid transparent;border-radius:8px;padding:12px 16px;font-size:13px;font-weight:600}.primary{background:var(--app-action);color:var(--app-on-action)}.secondary{background:white;color:var(--app-ink);border-color:var(--app-line)}.reset{border:0;background:transparent;color:var(--app-muted);font-size:12px;padding:12px}.result{font-size:12px;line-height:1.6;color:var(--app-muted);margin:18px 0 0}.check-notes{min-width:0;padding:0 4px}.check-notes h2{font:400 29px/1.2 Georgia,serif}.check-notes>p:not(.eyebrow){font-size:13px;line-height:1.7;color:var(--app-muted)}.check-notes dl>div{padding:14px 0;border-top:1px solid var(--app-line)}.check-notes dt{font-size:13px;font-weight:600}.check-notes dd{font-size:12px;line-height:1.7;color:var(--app-muted);margin:5px 0 0}.focus-art{height:165px;background:#e6e0ef;border-radius:12px;position:relative;overflow:hidden;margin-bottom:24px}.focus-art span{position:absolute;width:140px;height:85px;left:35px;top:44px;background:white;border-radius:8px;transform:rotate(-9deg);box-shadow:0 0 0 6px #b3a3cd}.focus-art i{position:absolute;width:75px;height:13px;background:#b7adc6;left:60px;top:61px;transform:rotate(-9deg)}.focus-art b{position:absolute;width:72px;height:28px;background:#695587;left:87px;top:91px;border-radius:5px;transform:rotate(-9deg)}.overlay{position:fixed;inset:0;background:#21193180;z-index:20}.dialog{position:fixed;z-index:21;left:50%;top:50%;transform:translate(-50%,-50%);width:min(440px,calc(100% - 28px));max-height:calc(100vh - 28px);overflow:auto;background:white;padding:24px;border-radius:14px;box-shadow:0 20px 80px #23193640}.dialog-title{font-size:23px;margin:0 0 12px}.dialog-description,.remaining{font-size:13px;line-height:1.7;color:var(--app-muted)}.review-summary{padding:0;list-style:none}.review-summary li{display:flex;justify-content:space-between;gap:12px;padding:12px 0;border-bottom:1px solid var(--app-line);font-size:13px}.dialog-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:22px}@media(max-width:740px){.check-layout{grid-template-columns:minmax(0,1fr)}.check-shell{padding:20px}h1{font-size:29px}}@media(max-width:360px){.check-shell{padding:14px}.check-panel,.dialog{padding:16px}.check-panel>.primary,.dialog-actions>*{width:100%}}@media(prefers-reduced-motion:reduce){.plus{transition:none}}

代码参考

  • 这些是原始的 --app-* 变量,并非 Bits 官方调色板。
  • 包含焦点和减少动态效果的样式。
  • 只有叠加层使用相对于视口的最大高度。
04

Bits UI 设计系统 AI Agent 设置

实现起点

已验证 Bits UI 2.19.5 与 Svelte 5.57.1。需要完整的原始样式表;Bits UI 不提供默认颜色或页面布局。

javascript
// npm create vite@latest bits-review -- --template svelte
// cd bits-review && npm install
// npm install [email protected] [email protected]
// Save the complete examples as src/App.svelte and src/style.css.
// src/main.js:
import {mount} from 'svelte';
import App from './App.svelte';
import './style.css';
mount(App, {target:document.querySelector('#app')});
// index.html: <div id="app"></div><script type="module" src="/src/main.js"></script>
// npm run dev

适用于你项目的提示词

Agent 提示词
使用 Bits UI 2.19.5 和 Svelte 5 构建所提供的手动界面审核。将六项检查放入三个 Accordion 分区,根据单项答案推导批量选中和不确定状态,并在分区折叠时保留答案。使用完整的 Dialog 部件组合审核功能,允许查看未完成的审核,但只有六项全部标记后才启用保存。每次答案变更都会使已保存的反馈失效;“全部清除”会恢复初始展开状态并关闭审核。加入完整的 CSS,并说明结果只是由读者标记并保存在本地的检查项,而非自动化无障碍认证。
05

Bits UI 设计系统的来源与范围

Bits UI 2.19.5 与 Svelte 5.57.1。使用原始应用设计令牌和插图。这是一份使用本地状态的手动清单,并非自动化无障碍审计或认证。