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

列表页

通用列表页:标题、工具栏和行,桌面和移动端都适用。

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

page-header

页面的标题栏:一个 h1、可选的副标题,以及一个主操作。

标题、副标题和一个主要操作

预览
<header rst-page-header>
  <div rst-page-header-titles>
    <h1>文章</h1>
    <p rst-page-header-sub>你写过的所有内容,最新的在前。</p>
  </div>
  <a rst-btn="primary" href="/posts/new">
    <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="M5 12h14"/><path d="M12 5v14"/></svg>
    写一篇文章
  </a>
</header>
{{template "page-header" dict
    "Title" "文章"
    "Sub" "你写过的所有内容,最新的在前。"
    "ActionHref" "/posts/new"
    "ActionLabel" "写一篇文章"
    "ActionIcon" "plus"}}

只有标题

预览
<header rst-page-header>
  <div rst-page-header-titles>
    <h1>设置</h1>
  </div>
</header>
{{template "page-header" dict "Title" "设置"}}

list-bar

列表卡片顶部的工具栏,带搜索和筛选。

搜索,外加一个筛选下拉菜单

预览

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

<div rst-lbar>
  <search>
    <form rst-search method="get" action="/posts">
      <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="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
      <input type="search" name="q" value="发布" placeholder="搜索文章" aria-label="搜索文章">
      <a rst-search-clear href="/posts?sort=newest" aria-label="清除搜索">
        <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="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
      </a>
      <input type="hidden" name="sort" value="newest">
      <button class="rst-sr-only" type="submit">搜索</button>
    </form>
  </search>
  <details rst-dropdown name="rst-menus">
    <summary rst-btn aria-label="按状态筛选:已付款">
      已付款
      <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="m6 9 6 6 6-6"/></svg>
    </summary>
    <div rst-dropdown-menu>
      <a href="/posts">全部</a>
      <a href="/posts?status=paid" aria-current="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="M20 6 9 17l-5-5"/></svg></a>
      <a href="/posts?status=free">免费</a>
    </div>
  </details>
</div>

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

{{template "list-bar" dict
    "Filter" (dict "Aria" "按状态筛选:已付款" "Items" (list (dict "Href" "/posts" "Label" "全部") (dict "Current" true "Href" "/posts?status=paid" "Label" "已付款") (dict "Href" "/posts?status=free" "Label" "免费")) "Label" "已付款")
    "Hidden" (list (list "sort" "newest"))
    "Placeholder" "搜索文章"
    "Query" "发布"
    "SearchAction" "/posts"}}

list-search-submit

搜索表单真正的提交按钮:视觉上隐藏,但在 Tab 顺序里,一旦获得键盘焦点就现身。

默认标签

预览

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

<button class="rst-sr-only" type="submit">搜索</button>

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

{{template "list-search-submit" dict}}

在你用 Tab 键进入之前,这里什么都看不到。

list-row-action

一条列表行:记录名称作为主链接、一行附加信息,以及一个次要操作。

前导标记、状态和一个操作

预览

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

<div rst-row>
  <span rst-row-lead data-lead="accent" aria-hidden="true">RN</span>
  <span rst-row-main><a href="/posts/1">八月发布说明</a>
    <small rst-row-sub>8月2日发布 · 阅读约 4 分钟</small>
  </span>
  <span rst-status rst-tone="positive">已发布</span>
  <a rst-row-action href="/posts/1/edit" aria-label="编辑“八月发布说明”">编辑</a>
</div>

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

{{template "list-row-action" dict
    "Href" "/posts/1"
    "Main" "八月发布说明"
    "Sub" "8月2日发布 · 阅读约 4 分钟"
    "ActionHref" "/posts/1/edit"
    "ActionLabel" "编辑"
    "ActionAria" "编辑“八月发布说明”"
    "StatusTone" "positive"
    "StatusLabel" "已发布"
    "Lead" "accent"
    "LeadInitial" "RN"}}

只有名称和辅助信息行

预览

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

<div rst-row>
  <span rst-row-main><a href="/posts/2">我们为什么弃用旧的运行器</a>
    <small rst-row-sub>草稿 · 20 分钟前保存</small>
  </span>
</div>

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

{{template "list-row-action" dict
    "Href" "/posts/2"
    "Main" "我们为什么弃用旧的运行器"
    "Sub" "草稿 · 20 分钟前保存"}}

row-menu

该行的其他操作,收在 ⋮ 按钮后面。破坏性的操作链接到它的确认页面。

链接、表单和一个破坏性项目

预览
<details rst-row-menu name="rst-menus">
  <summary aria-label="Grace Hopper 的操作">
    <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
  </summary>
  <div rst-row-menu-panel>
    <a href="/orders/AB3PX/edit">编辑</a>
    <form method="post" action="/orders/AB3PX/archive">
      <input type="hidden" name="state" value="archived">
      <button type="submit">归档</button>
    </form>
    <hr>
    <a class="rst-danger" href="/orders/AB3PX/delete">删除订单…</a>
  </div>
</details>
{{template "row-menu" dict
    "Name" "Grace Hopper"
    "Items" (list
        (dict "Href" "/orders/AB3PX/edit" "Label" "编辑")
        (dict "Action" "/orders/AB3PX/archive" "Hidden" (list (list "state" "archived")) "Label" "归档")
        (dict "Danger" true "Href" "/orders/AB3PX/delete" "Label" "删除订单…"))}}

把破坏性项目放在最后,标签以 … 结尾。它会打开一个页面,在删除任何内容之前先询问。

仅链接,用于选择表单中的列表

预览
<details rst-row-menu name="rst-menus">
  <summary aria-label="Alan Turing 的操作">
    <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
  </summary>
  <div rst-row-menu-panel>
    <a href="/orders/CD4QY/edit">编辑</a>
    <hr>
    <a class="rst-danger" href="/orders/CD4QY/delete">删除订单…</a>
  </div>
</details>
{{template "row-menu" dict
    "Name" "Alan Turing"
    "Items" (list
        (dict "Href" "/orders/CD4QY/edit" "Label" "编辑")
        (dict "Danger" true "Href" "/orders/CD4QY/delete" "Label" "删除订单…"))}}

在表单内,每个项目都必须是链接。需要提交的操作要经过它自己的页面。

seg-tabs

由服务器渲染的链接组成的分段控件。当前标签就是链接上的 aria-current。

两个分区

预览
<nav rst-seg-tabs aria-label="文章分区">
  <a href="?tab=basics" aria-current="page">基本</a>
  <a href="?tab=advanced">高级</a>
</nav>
{{template "seg-tabs" dict
    "Label" "文章分区"
    "Items" (list
        (dict "Current" true "Href" "?tab=basics" "Label" "基本")
        (dict "Href" "?tab=advanced" "Label" "高级"))}}

dropdown

一个零 JavaScript 的折叠块:一个 summary 按钮,下面是一板普通链接。

已应用一项选择的筛选菜单

预览
<details rst-dropdown name="rst-menus">
  <summary rst-btn aria-label="文章排序:最新在前">
    最新
    <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="m6 9 6 6 6-6"/></svg>
  </summary>
  <div rst-dropdown-menu>
    <a href="/posts?sort=newest" aria-current="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="M20 6 9 17l-5-5"/></svg></a>
    <a href="/posts?sort=oldest">最早</a>
    <a href="/posts?sort=title">按标题</a>
  </div>
</details>
{{template "dropdown" dict
    "Label" "最新"
    "Aria" "文章排序:最新在前"
    "Items" (list
        (dict "Current" true "Href" "/posts?sort=newest" "Label" "最新")
        (dict "Href" "/posts?sort=oldest" "Label" "最早")
        (dict "Href" "/posts?sort=title" "Label" "按标题"))}}

bulk-bar

选择模式下的顶部条,取代列表工具栏:计数、一个扩大选择的链接,以及操作菜单。

选中了三行

预览

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

<div rst-bulkbar>
  <a rst-bulkbar-close href="/posts" aria-label="完成选择">
    <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="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
  </a>
  <span rst-bulkbar-selection><span rst-bulkbar-count>已选 3 项</span><a rst-bulkbar-escalate href="/posts?select=all">选择全部 412 条匹配项</a></span>
  <details rst-dropdown name="rst-menus">
    <summary>
      操作
      <span rst-caret 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="m6 9 6 6 6-6"/></svg>
      </span>
    </summary>
    <div rst-dropdown-menu>
      <button type="submit" name="action" value="export">导出</button>
      <button type="submit" name="action" value="unpublish">取消发布</button>
      <button type="submit" name="action" value="delete" class="rst-danger">删除…</button>
    </div>
  </details>
</div>

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

{{template "bulk-bar" dict
    "DoneHref" "/posts"
    "Count" "已选 3 项"
    "EscalateHref" "/posts?select=all"
    "EscalateLabel" "选择全部 412 条匹配项"
    "Actions" (list
        (dict "Label" "导出" "Value" "export")
        (dict "Label" "取消发布" "Value" "unpublish")
        (dict "Danger" true "Label" "删除…" "Value" "delete"))
    "MenuLabel" "操作"}}

破坏性的那一项放在最后:第一项才是外层表单按 Enter 时的隐含默认。

media-grid

图片图块组成的网格。每个框都是 4:3,所以高的截图和宽的横幅可以整齐地放在同一个网格里。

截图、一个标志和一个横幅

预览

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

<div rst-media>
  <div rst-media-tile>
    <div rst-media-frame>
      <img src="/images/checkout.png" alt="" loading="lazy" decoding="async" width="1280" height="3200">
    </div>
    <div rst-media-caption>
      <div rst-media-name>
        <a href="/images/1">结账,第 2 步</a>
        <details rst-row-menu name="rst-menus">
          <summary aria-label="结账,第 2 步 的操作">
            <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
          </summary>
          <div rst-row-menu-panel>
            <a href="/images/1/edit">编辑</a>
            <hr>
            <a class="rst-danger" href="/images/1/delete">删除…</a>
          </div>
        </details>
      </div>
      <p rst-media-desc>付款步骤,银行卡表单已展开。</p>
      <p rst-media-meta>1280 × 3200 · 412 KB</p>
    </div>
  </div>
  <div rst-media-tile>
    <div rst-media-frame>
      <img src="/images/pricing.png" alt="" loading="lazy" decoding="async" width="1280" height="2400">
    </div>
    <div rst-media-caption>
      <div rst-media-name>
        <a href="/images/2">定价页面</a>
        <details rst-row-menu name="rst-menus">
          <summary aria-label="定价页面 的操作">
            <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
          </summary>
          <div rst-row-menu-panel>
            <a href="/images/2/edit">编辑</a>
            <hr>
            <a class="rst-danger" href="/images/2/delete">删除…</a>
          </div>
        </details>
      </div>
      <p rst-media-desc="none">没有描述</p>
      <p rst-media-meta>1280 × 2400 · 318 KB</p>
    </div>
  </div>
  <div rst-media-tile>
    <div rst-media-frame="contain">
      <img src="/images/logo.svg" alt="Harbour" loading="lazy" decoding="async" width="240" height="240">
    </div>
    <div rst-media-caption>
      <div rst-media-name>
        <a href="/images/3">Harbour 标志</a>
        <details rst-row-menu name="rst-menus">
          <summary aria-label="Harbour 标志 的操作">
            <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
          </summary>
          <div rst-row-menu-panel>
            <a href="/images/3/edit">编辑</a>
            <hr>
            <a class="rst-danger" href="/images/3/delete">删除…</a>
          </div>
        </details>
      </div>
      <p rst-media-desc>单独的标志,适用于深色和浅色背景。</p>
      <p rst-media-meta>240 × 240 · 3 KB</p>
    </div>
  </div>
  <div rst-media-tile>
    <div rst-media-frame="contain">
      <img src="/images/banner.png" alt="" loading="lazy" decoding="async" width="1200" height="200">
    </div>
    <div rst-media-caption>
      <div rst-media-name>
        <a href="/images/4">春季横幅</a>
        <details rst-row-menu name="rst-menus">
          <summary aria-label="春季横幅 的操作">
            <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
          </summary>
          <div rst-row-menu-panel>
            <a href="/images/4/edit">编辑</a>
            <hr>
            <a class="rst-danger" href="/images/4/delete">删除…</a>
          </div>
        </details>
      </div>
      <p rst-media-desc>三月时商店顶部的横条。</p>
      <p rst-media-meta>1200 × 200 · 64 KB</p>
    </div>
  </div>
</div>

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

{{template "media-grid" dict
    "Items" (list
        (dict "Desc" "付款步骤,银行卡表单已展开。" "Height" 3200 "Href" "/images/1" "Menu" (list (dict "Href" "/images/1/edit" "Label" "编辑") (dict "Danger" true "Href" "/images/1/delete" "Label" "删除…")) "Meta" "1280 × 3200 · 412 KB" "Name" "结账,第 2 步" "Src" "/images/checkout.png" "Width" 1280)
        (dict "Desc" "" "Height" 2400 "Href" "/images/2" "Menu" (list (dict "Href" "/images/2/edit" "Label" "编辑") (dict "Danger" true "Href" "/images/2/delete" "Label" "删除…")) "Meta" "1280 × 2400 · 318 KB" "Name" "定价页面" "Src" "/images/pricing.png" "Width" 1280)
        (dict "Alt" "Harbour" "Desc" "单独的标志,适用于深色和浅色背景。" "Fit" "contain" "Height" 240 "Href" "/images/3" "Menu" (list (dict "Href" "/images/3/edit" "Label" "编辑") (dict "Danger" true "Href" "/images/3/delete" "Label" "删除…")) "Meta" "240 × 240 · 3 KB" "Name" "Harbour 标志" "Src" "/images/logo.svg" "Width" 240)
        (dict "Desc" "三月时商店顶部的横条。" "Fit" "contain" "Height" 200 "Href" "/images/4" "Menu" (list (dict "Href" "/images/4/edit" "Label" "编辑") (dict "Danger" true "Href" "/images/4/delete" "Label" "删除…")) "Meta" "1200 × 200 · 64 KB" "Name" "春季横幅" "Src" "/images/banner.png" "Width" 1200))}}

对标志或横幅把 Fit 设为 "contain"。截图保持默认,显示它的顶部。没有 Desc 的图块会显示"没有描述"。

选择模式,选中了一个图块

预览

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

<div rst-media>
  <div rst-media-tile>
    <div rst-media-frame>
      <img src="/images/checkout.png" alt="" loading="lazy" decoding="async" width="1280" height="3200">
      <label rst-selbox>
        <input type="checkbox" name="id" value="1" checked aria-label="选择 结账,第 2 步">
      </label>
    </div>
    <div rst-media-caption>
      <div rst-media-name>
        <a href="/images/1">结账,第 2 步</a>
        <details rst-row-menu name="rst-menus">
          <summary aria-label="结账,第 2 步 的操作">
            <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
          </summary>
          <div rst-row-menu-panel>
            <a href="/images/1/edit">编辑</a>
            <hr>
            <a class="rst-danger" href="/images/1/delete">删除…</a>
          </div>
        </details>
      </div>
      <p rst-media-desc>付款步骤,银行卡表单已展开。</p>
      <p rst-media-meta>1280 × 3200 · 412 KB</p>
    </div>
  </div>
  <div rst-media-tile>
    <div rst-media-frame>
      <img src="/images/pricing.png" alt="" loading="lazy" decoding="async" width="1280" height="2400">
      <label rst-selbox>
        <input type="checkbox" name="id" value="2" aria-label="选择 定价页面">
      </label>
    </div>
    <div rst-media-caption>
      <div rst-media-name>
        <a href="/images/2">定价页面</a>
        <details rst-row-menu name="rst-menus">
          <summary aria-label="定价页面 的操作">
            <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="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
          </summary>
          <div rst-row-menu-panel>
            <a href="/images/2/edit">编辑</a>
            <hr>
            <a class="rst-danger" href="/images/2/delete">删除…</a>
          </div>
        </details>
      </div>
      <p rst-media-desc="none">没有描述</p>
      <p rst-media-meta>1280 × 2400 · 318 KB</p>
    </div>
  </div>
</div>

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

{{template "media-grid" dict
    "Items" (list
        (dict "Desc" "付款步骤,银行卡表单已展开。" "Height" 3200 "Href" "/images/1" "Menu" (list (dict "Href" "/images/1/edit" "Label" "编辑") (dict "Danger" true "Href" "/images/1/delete" "Label" "删除…")) "Meta" "1280 × 3200 · 412 KB" "Name" "结账,第 2 步" "Select" "1" "Selected" true "Src" "/images/checkout.png" "Width" 1280)
        (dict "Desc" "" "Height" 2400 "Href" "/images/2" "Menu" (list (dict "Href" "/images/2/edit" "Label" "编辑") (dict "Danger" true "Href" "/images/2/delete" "Label" "删除…")) "Meta" "1280 × 2400 · 318 KB" "Name" "定价页面" "Select" "2" "Src" "/images/pricing.png" "Width" 1280))}}

把网格和 bulk-bar 放在同一个表单里。每个复选框以 "id" 为名提交其图块的 Select 值;设置 SelectName 可以改名。在表单内,菜单项必须是链接。

pagination

一排带编号的普通链接,所以关掉 JavaScript 也照样能翻页。

长列表的中间

预览
<nav rst-pagination aria-label="文章分页">
  <a href="/posts?page=3" rst-pagination-step="prev"><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="m6 9 6 6 6-6"/></svg><span rst-pagination-word>上一页</span></a>
  <a href="/posts?page=1">1</a>
  <span rst-pagination-gap aria-hidden="true">…</span>
  <a href="/posts?page=3" rst-pagination-wide>3</a>
  <span aria-current="page">4</span>
  <a href="/posts?page=5" rst-pagination-wide>5</a>
  <span rst-pagination-gap aria-hidden="true">…</span>
  <a href="/posts?page=9">9</a>
  <a href="/posts?page=5" rst-pagination-step="next"><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="m6 9 6 6 6-6"/></svg><span rst-pagination-word>下一页</span></a>
</nav>
{{template "pagination" dict
    "Items" (list
        (dict "Href" "/posts?page=3" "Label" "上一页" "Rel" "prev")
        (dict "Href" "/posts?page=1" "Label" "1")
        (dict "Gap" true)
        (dict "Href" "/posts?page=3" "Label" "3")
        (dict "Current" true "Label" "4")
        (dict "Href" "/posts?page=5" "Label" "5")
        (dict "Gap" true)
        (dict "Href" "/posts?page=9" "Label" "9")
        (dict "Href" "/posts?page=5" "Label" "下一页" "Rel" "next"))
    "Label" "文章分页"}}

第一页,使用默认的可访问名称

预览
<nav rst-pagination aria-label="分页">
  <span rst-pagination-disabled rst-pagination-step="prev"><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="m6 9 6 6 6-6"/></svg><span rst-pagination-word>上一页</span></span>
  <span aria-current="page">1</span>
  <a href="/posts?page=2">2</a>
  <a href="/posts?page=2" rst-pagination-step="next"><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="m6 9 6 6 6-6"/></svg><span rst-pagination-word>下一页</span></a>
</nav>
{{template "pagination" dict
    "Items" (list
        (dict "Disabled" true "Label" "上一页" "Rel" "prev")
        (dict "Current" true "Label" "1")
        (dict "Href" "/posts?page=2" "Label" "2")
        (dict "Href" "/posts?page=2" "Label" "下一页" "Rel" "next"))}}

没有 Label,所以这个 nav 的可访问名称取自目录。

empty-state

空状态卡片。空状态,并给出开始的入口。

带一个 POST 的行动召唤

预览
<div rst-empty>
  <h2 rst-empty-title>这里还没有内容</h2>
  <p rst-empty-body>还没有文章。从第一篇开始就很好。</p>
  <form rst-empty-cta method="post" action="/posts/seed">
    <input type="hidden" name="csrf" value="tok-123">
    <button rst-btn="primary" type="submit">添加示例文章</button>
  </form>
</div>
{{template "empty-state" dict
    "Body" "还没有文章。从第一篇开始就很好。"
    "Title" "这里还没有内容"
    "ActionLabel" "添加示例文章"
    "PostAction" "/posts/seed"
    "Hidden" (list (list "csrf" "tok-123"))}}

只有一句话

预览
<div rst-empty>
  <p rst-empty-body>这篇文章还没有评论。</p>
</div>
{{template "empty-state" dict "Body" "这篇文章还没有评论。"}}