本文へ移動
セクション
言語、現在は日本語
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

検索フォームの本物の送信ボタンです。目には見えませんがタブ順には入っていて、キーボードでフォーカスすると姿を現します。

既定のラベル

プレビュー

これは <section rst-box> の中に置きます。

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

これは <section rst-box> の中に置きます。

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

Tab キーで入るまで、ここには何も表示されません。

list-row-action

一覧の1行。レコード名を主リンクに、補足の1行、そして副次的な操作がひとつ。

先頭の目印、状態、そしてひとつの操作

プレビュー

これは <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">8月のリリースノート</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="「8月のリリースノート」を編集">編集</a>
</div>

これは <div rst-list> の中に置きます。

{{template "list-row-action" dict
    "Href" "/posts/1"
    "Main" "8月のリリースノート"
    "Sub" "8月2日公開 · 4分で読めます"
    "ActionHref" "/posts/1/edit"
    "ActionLabel" "編集"
    "ActionAria" "「8月のリリースノート」を編集"
    "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

選択モードのヘッダー帯で、一覧バーと入れ替わります。件数、選択を広げるリンク、そして操作メニュー。

3 行を選択中

プレビュー

これは <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>3月のショップ上部の帯。</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" "3月のショップ上部の帯。" "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>まだ投稿はありません。最初の1件から始めましょう。</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" "まだ投稿はありません。最初の1件から始めましょう。"
    "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" "この投稿にはまだコメントがありません。"}}