跳到内容
栏目
语言,当前是简体中文
Rastrillo 设计系统

展示

拼出一个页面的那些小块语汇:状态、身份、进度,以及唯一的那种提示形状。

每个示例都能真实操作,但其中的链接不会跳转到任何地方。 HTML 显示可复制的标记。

status-pill

记录状态显示为带底色的胶囊标签,有颜色也有文字。

预览

neutral

<span rst-status rst-tone="neutral">草稿</span>

positive

<span rst-status rst-tone="positive">已发布</span>

warning

<span rst-status rst-tone="warning">已排期</span>

negative

<span rst-status rst-tone="negative">失败</span>

neutral

{{template "status-pill" dict "Label" "草稿"}}

positive

{{template "status-pill" dict "Label" "已发布" "Tone" "positive"}}

warning

{{template "status-pill" dict "Label" "已排期" "Tone" "warning"}}

negative

{{template "status-pill" dict "Label" "失败" "Tone" "negative"}}

neutral Tone 默认是 neutral,所以这个调用省略了它。

badge

一枚大写字母的带边框小标记。它和 status-pill 不同:后者表示记录的状态。

预览

无色调(安静的默认值)

<span rst-badge>草稿</span>

neutral

<span rst-badge="neutral">内部</span>

positive

<span rst-badge="positive">上线</span>

warning

<span rst-badge="warning">测试版</span>

negative

<span rst-badge="negative">已售罄</span>

无色调(安静的默认值)

{{template "badge" dict "Label" "草稿"}}

neutral

{{template "badge" dict "Label" "内部" "Tone" "neutral"}}

positive

{{template "badge" dict "Label" "上线" "Tone" "positive"}}

warning

{{template "badge" dict "Label" "测试版" "Tone" "warning"}}

negative

{{template "badge" dict "Label" "已售罄" "Tone" "negative"}}

meter

带数字的容量条。

预览

空

<span rst-meter>
  <meter rst-meter-bar value="0" min="0" max="100" aria-hidden="true"></meter>
  <span rst-meter-num>0/500</span>
</span>

进行到一半

<span rst-meter>
  <meter rst-meter-bar value="42" min="0" max="100" aria-hidden="true"></meter>
  <span rst-meter-num>210/500</span>
</span>

快满了

<span rst-meter>
  <meter rst-meter-bar value="82" min="0" max="100" aria-hidden="true"></meter>
  <span rst-meter-num>412/500</span>
</span>

已满

<span rst-meter>
  <meter rst-meter-bar value="100" min="0" max="100" aria-hidden="true"></meter>
  <span rst-meter-num>500/500</span>
</span>

超出额度

<span rst-meter>
  <meter rst-meter-bar value="100" min="0" max="100" aria-hidden="true"></meter>
  <span rst-meter-num>700/500</span>
</span>

空

{{template "meter" dict "Percent" 0 "Text" "0/500"}}

进行到一半

{{template "meter" dict "Percent" 42 "Text" "210/500"}}

快满了

{{template "meter" dict "Percent" 82 "Text" "412/500"}}

已满

{{template "meter" dict "Percent" 100 "Text" "500/500"}}

超出额度

{{template "meter" dict "Percent" 140 "Text" "700/500"}}

超出额度 填充在 partial 里被限制在 100%。

stat

仪表板上的一个数字,带标签,以及它变化了多少。

那个大数字

预览

把这段放进 <div rst-stats> 里。

<div rst-stat="lead">
  <span rst-stat-label>本月收入</span>
  <span rst-stat-num>€48,210</span>
  <span rst-stat-delta rst-tone="positive">&#43;12%</span>
  <span rst-stat-note>较上月</span>
</div>

把这段放进 <div rst-stats> 里。

{{template "stat" dict
    "Label" "本月收入"
    "Value" "€48,210"
    "Delta" "+12%"
    "Tone" "positive"
    "Note" "较上月"
    "Lead" true}}

给这一排里的某个数字加上 Lead,它就会变大。还是同一个组件,只是更大。

较小的数字

预览

把这段放进 <div rst-stats> 里。

<div rst-stat>
  <span rst-stat-label>未付发票</span>
  <span rst-stat-num>24</span>
</div>

把这段放进 <div rst-stats> 里。

{{template "stat" dict "Label" "未付发票" "Value" "24"}}

你只需要一个标签和一个数字。其余都是可选的。

下降是好事的时候

预览

把这段放进 <div rst-stats> 里。

<div rst-stat>
  <span rst-stat-label>首次回复用时</span>
  <span rst-stat-num>1小时12分</span>
  <span rst-stat-delta rst-tone="positive">−38%</span>
  <span rst-stat-note>较上周</span>
</div>

把这段放进 <div rst-stats> 里。

{{template "stat" dict
    "Label" "首次回复用时"
    "Value" "1小时12分"
    "Delta" "−38%"
    "Tone" "positive"
    "Note" "较上周"}}

颜色由你决定,不是由组件决定。这里数字下降了,而这是好消息,所以变化显示为绿色。

上升是坏事的时候

预览

把这段放进 <div rst-stats> 里。

<div rst-stat>
  <span rst-stat-label>失败的付款</span>
  <span rst-stat-num>9</span>
  <span rst-stat-delta rst-tone="negative">&#43;3</span>
  <span rst-stat-note>较上周</span>
</div>

把这段放进 <div rst-stats> 里。

{{template "stat" dict
    "Label" "失败的付款"
    "Value" "9"
    "Delta" "+3"
    "Tone" "negative"
    "Note" "较上周"}}

这里数字上升了,而这是坏消息,所以变化显示为红色。

说不准的时候

预览

把这段放进 <div rst-stats> 里。

<div rst-stat>
  <span rst-stat-label>平均会话时长</span>
  <span rst-stat-num>4分12秒</span>
  <span rst-stat-delta rst-tone="neutral">&#43;18秒</span>
  <span rst-stat-note>自周五以来</span>
</div>

把这段放进 <div rst-stats> 里。

{{template "stat" dict
    "Label" "平均会话时长"
    "Value" "4分12秒"
    "Delta" "+18秒"
    "Note" "自周五以来"}}

不设颜色,变化就是灰色的。说不准是好是坏时,就这样做。

缩短过的数字

预览

把这段放进 <div rst-stats> 里。

<div rst-stat>
  <span rst-stat-label>已发送消息</span>
  <span rst-stat-num><data value="4120">4.1k</data></span>
</div>

把这段放进 <div rst-stats> 里。

{{template "stat" dict "Label" "已发送消息" "Value" "4.1k" "Exact" "4120"}}

显示 4.1k 而不是 4120?把完整数字放进 Exact。屏幕阅读器只读屏幕上的内容,所以人需要的信息要留在数字本身里。

person

首字母头像、姓名和邮箱。用来快速辨认代表人的记录。

姓名和邮箱

预览
<a rst-person href="/people/1">
  <span rst-person-av aria-hidden="true">G</span>
  <span rst-person-meta>
    <span rst-person-name>Grace Hopper</span>
    <span rst-person-email>grace@example.com</span>
  </span>
</a>
{{template "person" dict
    "Href" "/people/1"
    "Name" "Grace Hopper"
    "Email" "grace@example.com"
    "Initial" "G"}}

大号,详情页页眉的尺寸

预览
<a rst-person="lg" href="/people/2">
  <span rst-person-av aria-hidden="true">A</span>
  <span rst-person-meta>
    <span rst-person-name>Ada Lovelace</span>
    <span rst-person-email>ada@example.com</span>
  </span>
</a>
{{template "person" dict
    "Href" "/people/2"
    "Name" "Ada Lovelace"
    "Email" "ada@example.com"
    "Initial" "A"
    "Large" true}}

没有首字母

预览
<a rst-person href="/people/3">
  <span rst-person-av="empty" aria-hidden="true"></span>
  <span rst-person-meta>
    <span rst-person-name>未接受的邀请</span>
    <span rst-person-email>invited@example.com</span>
  </span>
</a>
{{template "person" dict
    "Href" "/people/3"
    "Name" "未接受的邀请"
    "Email" "invited@example.com"}}

actor

谁做了某件事,用文字表示:一个人的名字,或者代理以及它代表的人。没有徽章,也没有图标。

代表某人行动的代理

预览
<span rst-actor>Claude(代表 Grace Hopper)</span>
{{template "actor" dict "Actor" .Actor}}

启动时设置一次 ui.ActorName,把主体转成名字。应用本身,也就是零值 Actor,不会渲染任何内容。

一个人

预览
<span rst-actor>Grace Hopper</span>
{{template "actor" dict "Actor" .Actor}}

callout

唯一的一套提示语汇:着色条、色调图标、可选的粗体标题、一段正文。

info

预览
<div rst-callout rst-tone="info">
  <span rst-callout-ic aria-hidden="true">
    <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
  </span>
  <div rst-callout-body>
    <p>在网站发布之前,文章仅对会员可见。</p>
  </div>
</div>
{{template "callout" dict "Body" "在网站发布之前,文章仅对会员可见。" "Tone" "info"}}

positive

预览
<div rst-callout rst-tone="positive">
  <span rst-callout-ic aria-hidden="true">
    <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>
  </span>
  <div rst-callout-body>
    <strong>付款已连接</strong>
    <p>现在可以收取门票款项了。</p>
  </div>
</div>
{{template "callout" dict
    "Body" "现在可以收取门票款项了。"
    "Title" "付款已连接"
    "Tone" "positive"}}

warning

预览
<div rst-callout rst-tone="warning">
  <span rst-callout-ic aria-hidden="true">
    <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
  </span>
  <div rst-callout-body>
    <strong>连接付款即可开始销售</strong>
    <p>你的活动已上线,但还不能收款。</p>
  </div>
</div>
{{template "callout" dict
    "Body" "你的活动已上线,但还不能收款。"
    "Title" "连接付款即可开始销售"
    "Tone" "warning"}}

negative

预览
<div rst-callout rst-tone="negative">
  <span rst-callout-ic aria-hidden="true">
    <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>
  </span>
  <div rst-callout-body>
    <strong>上一次导入失败了</strong>
    <p>没有做任何更改。修正第 91 行后重试。</p>
  </div>
</div>
{{template "callout" dict
    "Body" "没有做任何更改。修正第 91 行后重试。"
    "Title" "上一次导入失败了"
    "Tone" "negative"}}

negative,并作为警报播报

预览
<div rst-callout rst-tone="negative" role="alert">
  <span rst-callout-ic aria-hidden="true">
    <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>
  </span>
  <div rst-callout-body>
    <strong>导入已停止</strong>
    <p>没有做任何更改。修正第 91 行后重试。</p>
  </div>
</div>
{{template "callout" dict
    "Body" "没有做任何更改。修正第 91 行后重试。"
    "Title" "导入已停止"
    "Tone" "negative"
    "Alert" true}}

alert 会加上 role="alert",用于显示重要信息。

detail-list

详情页上关于某条记录的定义列表。等宽标记用于偏机器的值。

四条信息,其中一条偏机器

预览

把这段放进 <section rst-box> 里。

<dl rst-detail>
  <dt>受众</dt>
  <dd>会员</dd>
  <dt>首页</dt>
  <dd>否</dd>
  <dt>已发布</dt>
  <dd>
    <time datetime="2026-08-02">2026年8月2日</time>
  </dd>
  <dt>编号</dt>
  <dd class="rst-mono">post_01H9ZQ</dd>
</dl>

把这段放进 <section rst-box> 里。

{{template "detail-list" dict
    "Items" (list
        (dict "Label" "受众" "Value" "会员")
        (dict "Label" "首页" "Value" "否")
        (dict "DateTime" "2026-08-02" "Label" "已发布" "Value" "2026年8月2日")
        (dict "Label" "编号" "Mono" true "Value" "post_01H9ZQ"))}}

notice

重定向之后的一行确认信息。它接受一个普通字符串而不是 dict,字符串为空时什么也不渲染。

一条“已保存”的消息

预览
<p rst-notice role="status">文章已保存。</p>
{{template "notice" "文章已保存。"}}

region:notice

每个外壳都保留在页面上的通知区,显示页面数据中的 Notice,这样屏幕阅读器能听到提示消息,live.js 也能原地填入内容。

一条“已保存”的消息

预览
<div id="notice" role="status">
  <p rst-notice>文章已保存。</p>
</div>
{{template "region:notice" dict "Notice" "文章已保存。"}}

form-error

表单的错误摘要行,role="alert"。接受一个普通字符串;没有错误时什么也不渲染。

一次被拒绝的提交

预览
<p rst-form-error role="alert">这个标题已被使用。</p>
{{template "form-error" "这个标题已被使用。"}}

job-status

后台任务的轮询片段。只有在任务还在运行时才会输出 data-poll 属性:省略它就是轮询停止的方式。

进行中

预览

<div rst-job data-poll="/jobs/1/fragment" data-poll-every="2">
  <span rst-spin aria-hidden="true"></span> <bdi><strong>导入</strong></bdi>正在运行:<bdi>400 中的 128</bdi>…
  <progress rst-job-bar value="32" min="0" max="100" aria-hidden="true"></progress>
</div>
{{template "job-status" dict
    "Name" "导入"
    "Percent" 32
    "PollSeconds" 2
    "PollURL" "/jobs/1/fragment"
    "Progress" "400 中的 128"
    "Status" "running"}}

这一个会轮询:它是页面上唯一会向网络要东西的样例,而在静态站点上,它永远等不到答复。

已完成

预览

<div rst-job>
  <bdi><strong>导入</strong></bdi>已完成。
</div>
{{template "job-status" dict "Name" "导入" "Status" "done"}}

失败

预览

<div rst-job>
  <bdi><strong>导入</strong></bdi>失败:<bdi>row 91: no such column &#34;published&#34;</bdi>
</div>
{{template "job-status" dict
    "Err" "row 91: no such column \"published\""
    "Name" "导入"
    "Status" "failed"}}