Перейти к содержимому
Разделы
Язык, сейчас Русский
Дизайн-система 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

Одна строка списка: имя записи как основная ссылка, строка сведений и второстепенное действие.

Начальная метка, статус и одно действие

Предпросмотр

Поместите это внутрь <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>Опубликовано 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" "Опубликовано 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 в одну форму. Каждый флажок отправляет значение Select своей плитки под именем "id"; задайте 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" "К этой записи пока нет комментариев."}}