zz design system
mono & squared
组件 / 表单控件

表单

控件是页面里的「凹槽」:底用 --zz-bg-deep,比页面更深一层,1px--zz-border-strong 描边,与按钮的 --zz-surface 面明确区分。四个尺寸档位 与按钮严格等高,因此一行里输入框、选择器、按钮的上下边缘永远对齐。

示例

一个最小可用的表单:每个字段由 Field 组合 label、控件与提示文案,错误文案落在最底, 出现错误时整块自动带上 .zz-field--invalid。

会出现在侧栏与部署配置里。

这个地址已被占用。

尺寸

高度 2 / 2.25 / 2.5 / 2.75rem(xs / sm / md / lg),横向内距 0.625rem(xs)与 0.75rem(其余)。 固定高度的控件不写纵向内距 —— 文字位置由 height 决定,写死纵向内距会在 xs 档裁字。 文本域的尺寸改由最小高度表达:4 / 4.5 / 5.5 / 6.5rem。

状态

悬停只把描边加深一档;聚焦把描边换成品牌色,外描边仍是全局的 2px 焦点环;无效态转--zz-danger;禁用态压到 50% 不透明度并移除指针事件。文本类控件不做位移反馈 —— 缩放会让正在输入的文字抖动。

用于接收构建失败通知。

邮箱格式不正确。

可以稍后在设置里修改。

勾选与开关

勾选框与单选框是 1rem 的直角方块,选中时填品牌色、勾与点用 --zz-brand-ink; 开关是 2.25 × 1.25rem 的直角轨道,滑块纯色,整条轨道只有一个 16px 的位移。三者内部都是真实<input>,键盘与表单提交走原生行为。

部署目标

类名

类名作用
.zz-input文本控件底座(input / textarea / select 共用):1px --zz-border-strong 描边、--zz-bg-deep 凹槽底、等宽字体、150ms 过渡
.zz-input--xs | sm | md | lg高度 2 / 2.25 / 2.5 / 2.75rem,与按钮同阶梯;xs 横向内距 0.625rem,其余 0.75rem;lg 字号 0.9375rem
.zz-input--invalid / [aria-invalid='true']描边转 --zz-danger;焦点态也保持危险色
.zz-textarea纵向内距 0.625rem、行高 1.5、可纵向拉伸;尺寸改控制最小高度 4 / 4.5 / 5.5 / 6.5rem
.zz-select选择器外层:相对定位,承载自绘箭头,class 也落在这里
.zz-select__control原生 select:appearance: none、右侧留 2rem 箭头位、cursor: pointer
.zz-select::after自绘 1px 箭头:两条 1px 直角边旋转 45°,不依赖背景图与图片资源
.zz-select--xs箭头内距收窄到 0.625rem(其余尺寸共用 0.75rem)
.zz-select--invalid / .zz-select--disabled箭头转危险色 / 移除指针事件并把箭头压到 50%
.zz-field纵向组合:label + 控件 + hint + 错误文案,间距 0.375rem;单选组可以直接放在 <fieldset> 上,legend 的下间距由 .zz-field > legend 单独补(gap 对 legend 无效)
.zz-field__label0.75rem,--zz-text-secondary
.zz-field__required必填星号,--zz-danger,带 aria-hidden(必填由控件自身的 required 表达)
.zz-field__hint0.75rem,--zz-text-muted,空内容自动隐藏
.zz-field__error错误文案:0.75rem,--zz-danger
.zz-field--invalid整块声明无效,内部控件统一转危险色描边
.zz-check / .zz-radio选项行:flex、0.5rem 间距、user-select: none、按下时视觉层 scale(0.96);width: fit-content 让行宽贴合内容,落在纵向 flex 里也不会被撑满整行
.zz-check__input / .zz-radio__input / .zz-switch__input真实 input:透明但保留焦点、键盘与表单行为
.zz-check__box1rem 直角方块,底 --zz-bg-deep;选中填 --zz-brand,勾用 --zz-brand-ink
.zz-radio__box同上,但 50% 圆形(这套语言里唯一允许的圆角例外),选中态是品牌反色圆点
.zz-switch__track2.25 × 1.25rem 直角轨道,底 --zz-bg-deep;选中填 --zz-brand(开关行同样 width: fit-content)
.zz-switch__thumb0.875rem 纯色滑块,选中位移动 1rem 并转 --zz-brand-ink
.zz-check--disabled / .zz-radio--disabled / .zz-switch--disabledopacity: .5 + pointer-events: none + cursor: not-allowed

token

控件不新增变量:颜色全部来自主题 token,因此深色与亮色两套主题共用一份实现。

token用在哪
--zz-bg-deep控件底、勾选框底、开关轨道底(凹槽)
--zz-border-strong控件 1px 描边;悬停时与 --zz-text 混合一档加深
--zz-text输入的文字、选项标签
--zz-text-mutedplaceholder、选择器箭头、hint、关闭态滑块
--zz-text-secondarylabel
--zz-brand聚焦描边、选中填充、开关开启态轨道
--zz-brand-ink选中态的勾、圆点与滑块
--zz-danger无效描边、错误文案、必填星号
--zz-dur / --zz-ease150ms 与统一缓动,只用在下列属性上

用法

---
import Field from '@/components/ui/Field.astro'
import Input from '@/components/ui/Input.astro'
import Select from '@/components/ui/Select.astro'
---

<Field label="项目名称" for="name" hint="会出现在侧栏里。">
  <Input id="name" name="name" size="md" placeholder="zz-design-system" aria-describedby="name-hint" />
</Field>

<Field label="主题" for="theme" error="必须选一个主题。">
  <Select id="theme" name="theme" placeholder="选择一个主题">
    <option value="tokyo-night">Tokyo Night</option>
    <option value="flexoki">Flexoki</option>
  </Select>
</Field>

可访问性

  • label 与控件必须用 for / id 关联。Field 在给出for 时会把 hint、错误文案的 id 推导为 <id>-hint 与<id>-error,控件要自己挂上对应的 aria-describedby。
  • 错误不要只靠颜色:同时给文案(错误文案插槽)与aria-invalid="true",屏幕阅读器才会播报。
  • 单选框组用 <fieldset> + <legend>(或role="radiogroup" + aria-labelledby),组内各项 name 相同。
  • 勾选框、单选框、开关内部都是真实 <input>:空格与方向键、表单提交、required 校验全部是原生行为;视觉层只负责画方块与勾,因此标记aria-hidden。
  • 全局的 2px 品牌色焦点环保留。勾选类控件的真实 input 是透明的,焦点环会被转发到视觉层 —— 这是让它可见,不是抹掉它。
  • 开关用 role="switch",读屏会播报「开关 / 已打开」而不是「复选框」。
  • 禁用态只是视觉与指针层面(opacity: .5 + pointer-events: none), 真正的禁用与校验仍由原生 disabled / required 属性承担。
  • prefers-reduced-motion: reduce 下所有过渡关闭,开关的位移瞬时完成、不做补间。
  • placeholder 只作示例,不能替代 label。