跳到內容
欄目
語言,而家係廣東話
Rastrillo 設計系統

路由

呢啲片段本身就係一整個回應,唔係回應入面嘅一塊。

每個例子都真係用得,不過入面啲連結唔會帶你去邊度。 HTML 顯示可以複製嘅標記。

error-page

錯誤回應嘅整個主體:狀態、一句老實話、一條返轉頭嘅路,同管理員用得着嘅 500 參考編號。

404:搵唔到

預覽
<div rst-error>
  <p rst-error-status>404</p>
  <h1 rst-error-title>搵唔到嗰個頁面</h1>
  <p rst-error-body>個連結可能已經過時,或者個頁面已經搬咗。請檢查下網址,或者返去起始頁。</p>
  <p rst-error-cta>
    <a rst-btn="primary" href="/">起始頁</a>
  </p>
</div>
{{template "error-page" dict "Status" 404 "HomeHref" "/"}}

403:冇權限

預覽
<div rst-error>
  <p rst-error-status>403</p>
  <h1 rst-error-title>你睇唔到呢個</h1>
  <p rst-error-body>你個帳戶喺呢度冇權限。如果你覺得應該有,請問下管理呢個網站嘅人。</p>
  <p rst-error-cta>
    <a rst-btn="primary" href="/">起始頁</a>
    <a rst-btn href="/posts">返去</a>
  </p>
</div>
{{template "error-page" dict "Status" 403 "HomeHref" "/" "BackHref" "/posts"}}

422:處理唔到

預覽
<div rst-error>
  <p rst-error-status>422</p>
  <h1 rst-error-title>送唔出去</h1>
  <p rst-error-body>送出嘅內容有啲唔啱。請返去再試一次;冇儲存任何嘢。</p>
  <p rst-error-cta>
    <a rst-btn="primary" href="/">起始頁</a>
    <a rst-btn href="/posts/1/edit">返去</a>
  </p>
</div>
{{template "error-page" dict
    "Status" 422
    "HomeHref" "/"
    "BackHref" "/posts/1/edit"}}

500:伺服器出錯

預覽
<div rst-error>
  <p rst-error-status>500</p>
  <h1 rst-error-title>我哋呢邊出咗問題</h1>
  <p rst-error-body>唔關你事。問題已經記錄咗。請等陣再試;如果一直咁樣,請提供下面嘅參考編號。</p>
  <p rst-error-cta>
    <a rst-btn="primary" href="/">起始頁</a>
  </p>
  <p class="rst-mono" rst-error-ref dir="auto">參考編號:k3f9tq</p>
</div>
{{template "error-page" dict "Status" 500 "HomeHref" "/" "Ref" "k3f9tq"}}

500 係唯一有參考編號嘅狀態:管理員可以用佢喺日誌度 grep。

503:用唔到

預覽
<div rst-error>
  <p rst-error-status>503</p>
  <h1 rst-error-title>我哋暫時停咗服務</h1>
  <p rst-error-body>網站更新緊,或者太多人用緊。等一分鐘再試吓。</p>
  <p rst-error-cta>
    <a rst-btn="primary" href="/">起始頁</a>
  </p>
</div>
{{template "error-page" dict "Status" 503 "HomeHref" "/"}}

其他任何情況:通用嗰對

預覽
<div rst-error>
  <p rst-error-status>418</p>
  <h1 rst-error-title>有啲唔妥</h1>
  <p rst-error-body>呢個要求完成唔到。請返去再試一次。</p>
  <p rst-error-cta>
    <a rst-btn="primary" href="/">起始頁</a>
  </p>
</div>
{{template "error-page" dict "Status" 418 "HomeHref" "/"}}

五個狀態嘅講法寫咗喺框架目錄度。第六個會退返去通用嗰對,而唔係將一個唔存在嘅鍵名擺喺讀者面前。

back-nav

上返一層嘅連結,擺喺詳情或者編輯畫面上面。

返去一筆記錄

預覽
<nav rst-back-nav>
  <a href="/orders/1">← 訂單 AB3PX</a>
</nav>
{{template "back-nav" dict "Href" "/orders/1" "Label" "訂單 AB3PX"}}

locale-menu

語言切換器:一個 details 選單,入面係寄去 /_locale 嘅單一欄位 POST 表單,噉揀嘅結果就會入咗 locale cookie,而讀者仲留喺同一條路徑度。

十二種語言,目前嗰種會標示出嚟。

預覽
<details rst-dropdown rst-locale 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>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="en">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="en">English</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="ga">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="ga">Gaeilge</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="zh-Hans">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="zh-Hans">简体中文</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="es">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="es">Español</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="hi">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="hi">हिन्दी</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="pt-PT">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="pt-PT">Português</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="bn">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="bn">বাংলা</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="ru">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="ru">Русский</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="ja">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="ja">日本語</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="yue">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="yue" aria-current="true">廣東話</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="vi">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="vi">Tiếng Việt</button>
    </form>
    <form method="post" action="/_locale">
      <input type="hidden" name="locale" value="ar">
      <input type="hidden" name="return" value="/">
      <button type="submit" lang="ar">العربية</button>
    </form>
  </div>
</details>
{{template "locale-menu" dict "Items" .Locales "Return" "/"}}

呢個會 POST 去 /_locale,所以要有伺服器。佢嘅 Items 嚟自 rastrillo.LocaleItems(r)。本頁自己個頁首嘅切換器係淨係用連結嘅版本。

scheme-menu

配色切換器:一個 details 選單,入面係送去 /_theme 嘅單欄位 POST 表單,分別係淺色、深色同跟系統,咁選擇就會存入配色 cookie,讀者仍然留喺同一頁。

三個選擇,目前係深色。

預覽
<details rst-dropdown rst-scheme 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 role="group" aria-label="配色">
    <form method="post" action="/_theme">
      <input type="hidden" name="scheme" value="light">
      <input type="hidden" name="return" value="/">
      <button type="submit">淺色</button>
    </form>
    <form method="post" action="/_theme">
      <input type="hidden" name="scheme" value="dark">
      <input type="hidden" name="return" value="/">
      <button type="submit" aria-current="true">深色</button>
    </form>
    <form method="post" action="/_theme">
      <input type="hidden" name="scheme" value="system">
      <input type="hidden" name="return" value="/">
      <button type="submit">跟系統</button>
    </form>
  </div>
</details>
{{template "scheme-menu" dict "Scheme" "dark" "Return" "/"}}

呢個會 POST 去 /_theme,所以要有伺服器。佢嘅 Scheme 嚟自 rastrillo.SchemeFrom(r),由 ui.ForRequest 帶住。本頁自己個頂欄嘅切換器係畫廊自己嘅。

api-tokens

由 apitoken 嘅 PageData 嚟嘅權杖頁面:一次性顯示、建立表格、清單同撤銷掣。

四個權杖,其中兩個有效

預覽
<div rst-tokens>
  <section rst-box>
    <form rst-form method="post" action="/tokens">
      <div rst-field>
        <label rst-field-label for="agent">代理名稱</label>
        <input rst-input type="text" id="agent" name="agent" maxlength="40">
      </div>
      <label rst-switch>
        <input type="checkbox" name="for_self" value="on">
        <span rst-switch-track aria-hidden="true"></span>
        呢個權杖係畀我自己用,唔係畀代理
      </label>
      <fieldset rst-choice>
        <legend>存取權</legend>
        <div rst-choice-cards>
          <label>
            <input type="radio" name="access" value="write" checked>
            <span>
              <span rst-choice-title>讀寫</span>
            </span>
          </label>
          <label>
            <input type="radio" name="access" value="read">
            <span>
              <span rst-choice-title>只讀</span>
            </span>
          </label>
        </div>
      </fieldset>
      <div rst-field>
        <label rst-field-label for="expiry">有效期至</label>
        <select rst-input="short" id="expiry" name="expiry">
          <option value="30">30 日</option>
          <option value="90" selected>90 日</option>
          <option value="365">1 年</option>
          <option value="never">永不</option>
        </select>
      </div>
      <div rst-form-foot>
        <button rst-btn="primary lg" type="submit">建立權杖</button>
      </div>
    </form>
  </section>
  <div rst-card rst-tokens-list>
    <div rst-lrow="head">
      <span>權杖</span>
      <span class="rst-m-hide">存取權</span>
      <span class="rst-m-hide">建立時間</span>
      <span class="rst-m-hide">上次使用</span>
      <span class="rst-m-hide">有效期至</span>
      <span></span>
      <span></span>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Claude(代表 Grace Hopper)</span>
        <small>
          <code>k3Jx9QpL2mWa</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>讀寫</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span><time datetime="2026-08-11">2026-08-11</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span><time datetime="2026-10-31">2026-10-31</time></span>
      <span rst-tokens-state></span>
      <details rst-row-menu name="rst-menus">
        <summary aria-label="Claude(代表 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>
          <form method="post" action="/tokens/revoke">
            <input type="hidden" name="id" value="k3Jx9QpL2mWa">
            <button type="submit">撤銷</button>
          </form>
        </div>
      </details>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Grace Hopper</span>
        <small>
          <code>Qm2vR8sT4xYb</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>只讀</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span>永不</span>
      <span rst-tokens-state></span>
      <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>
          <form method="post" action="/tokens/revoke">
            <input type="hidden" name="id" value="Qm2vR8sT4xYb">
            <button type="submit">撤銷</button>
          </form>
        </div>
      </details>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Codex(代表 Grace Hopper)</span>
        <small>
          <code>Zp7wN1cH6kLd</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>讀寫</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span>永不</span>
      <span rst-tokens-state><span rst-status rst-tone="neutral">已撤銷</span></span>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Nightly import(代表 Grace Hopper)</span>
        <small>
          <code>Vb4tG9yK3nMe</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>只讀</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span><time datetime="2026-08-11">2026-08-11</time></span>
      <span rst-tokens-state><span rst-status rst-tone="warning">已過期</span></span>
    </div>
  </div>
  <div rst-tokens-revoke>
    <form method="post" action="/tokens/revoke-agent">
      <input type="hidden" name="agent" value="Claude">
      <button rst-btn type="submit">撤銷呢個代理嘅全部權杖: Claude</button>
    </form>
    <form method="post" action="/tokens/revoke-all">
      <button rst-btn="danger" type="submit">撤銷全部權杖</button>
    </form>
  </div>
</div>
{{template "api-tokens" dict "Page" .Page "Path" "/tokens"}}

將 Handlers 交畀 Render 嘅嘢用 Page 傳入,再將頁面自己嘅路徑用 Path 傳入。撤銷表格會提交去 Path/revoke、Path/revoke-agent 同 Path/revoke-all;想改其中一個,就設定 RevokePath、RevokeAgentPath 或者 RevokeAllPath。

啱啱建立:秘密只顯示一次

預覽
<div rst-tokens>
  <div rst-callout rst-tone="positive" tabindex="-1" autofocus>
    <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>
        <code rst-tokens-secret>harbour_k3Jx9QpL2mWa_6bD2qK9xWz4rT8vN1mP3sL7yF5hJ0cG2aE4uB</code>
      </p>
    </div>
  </div>
  <section rst-box>
    <form rst-form method="post" action="/tokens">
      <div rst-field>
        <label rst-field-label for="agent">代理名稱</label>
        <input rst-input type="text" id="agent" name="agent" maxlength="40">
      </div>
      <label rst-switch>
        <input type="checkbox" name="for_self" value="on">
        <span rst-switch-track aria-hidden="true"></span>
        呢個權杖係畀我自己用,唔係畀代理
      </label>
      <fieldset rst-choice>
        <legend>存取權</legend>
        <div rst-choice-cards>
          <label>
            <input type="radio" name="access" value="write" checked>
            <span>
              <span rst-choice-title>讀寫</span>
            </span>
          </label>
          <label>
            <input type="radio" name="access" value="read">
            <span>
              <span rst-choice-title>只讀</span>
            </span>
          </label>
        </div>
      </fieldset>
      <div rst-field>
        <label rst-field-label for="expiry">有效期至</label>
        <select rst-input="short" id="expiry" name="expiry">
          <option value="30">30 日</option>
          <option value="90" selected>90 日</option>
          <option value="365">1 年</option>
          <option value="never">永不</option>
        </select>
      </div>
      <div rst-form-foot>
        <button rst-btn="primary lg" type="submit">建立權杖</button>
      </div>
    </form>
  </section>
  <div rst-card rst-tokens-list>
    <div rst-lrow="head">
      <span>權杖</span>
      <span class="rst-m-hide">存取權</span>
      <span class="rst-m-hide">建立時間</span>
      <span class="rst-m-hide">上次使用</span>
      <span class="rst-m-hide">有效期至</span>
      <span></span>
      <span></span>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Claude(代表 Grace Hopper)</span>
        <small>
          <code>k3Jx9QpL2mWa</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>讀寫</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span><time datetime="2026-08-11">2026-08-11</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span><time datetime="2026-10-31">2026-10-31</time></span>
      <span rst-tokens-state></span>
      <details rst-row-menu name="rst-menus">
        <summary aria-label="Claude(代表 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>
          <form method="post" action="/tokens/revoke">
            <input type="hidden" name="id" value="k3Jx9QpL2mWa">
            <button type="submit">撤銷</button>
          </form>
        </div>
      </details>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Grace Hopper</span>
        <small>
          <code>Qm2vR8sT4xYb</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>只讀</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span>永不</span>
      <span rst-tokens-state></span>
      <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>
          <form method="post" action="/tokens/revoke">
            <input type="hidden" name="id" value="Qm2vR8sT4xYb">
            <button type="submit">撤銷</button>
          </form>
        </div>
      </details>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Codex(代表 Grace Hopper)</span>
        <small>
          <code>Zp7wN1cH6kLd</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>讀寫</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span>永不</span>
      <span rst-tokens-state><span rst-status rst-tone="neutral">已撤銷</span></span>
    </div>
    <div rst-lrow>
      <span class="rst-nm"><span rst-actor>Nightly import(代表 Grace Hopper)</span>
        <small>
          <code>Vb4tG9yK3nMe</code>
        </small>
      </span>
      <span rst-tokens-fact><span rst-tokens-fact-label>存取權</span>只讀</span>
      <span rst-tokens-fact><span rst-tokens-fact-label>建立時間</span><time datetime="2026-08-02">2026-08-02</time></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>上次使用</span></span>
      <span rst-tokens-fact><span rst-tokens-fact-label>有效期至</span><time datetime="2026-08-11">2026-08-11</time></span>
      <span rst-tokens-state><span rst-status rst-tone="warning">已過期</span></span>
    </div>
  </div>
  <div rst-tokens-revoke>
    <form method="post" action="/tokens/revoke-agent">
      <input type="hidden" name="agent" value="Claude">
      <button rst-btn type="submit">撤銷呢個代理嘅全部權杖: Claude</button>
    </form>
    <form method="post" action="/tokens/revoke-all">
      <button rst-btn="danger" type="submit">撤銷全部權杖</button>
    </form>
  </div>
</div>
{{template "api-tokens" dict "Page" .Page "Path" "/tokens"}}

用設定咗 Created 嘅建立表格回應嚟顯示,唔好重新導向:秘密只係喺嗰個回應入面,其他地方冇。

仲未有權杖

預覽
<div rst-tokens>
  <section rst-box>
    <form rst-form method="post" action="/tokens">
      <div rst-field>
        <label rst-field-label for="agent">代理名稱</label>
        <input rst-input type="text" id="agent" name="agent" maxlength="40">
      </div>
      <label rst-switch>
        <input type="checkbox" name="for_self" value="on">
        <span rst-switch-track aria-hidden="true"></span>
        呢個權杖係畀我自己用,唔係畀代理
      </label>
      <fieldset rst-choice>
        <legend>存取權</legend>
        <div rst-choice-cards>
          <label>
            <input type="radio" name="access" value="write" checked>
            <span>
              <span rst-choice-title>讀寫</span>
            </span>
          </label>
          <label>
            <input type="radio" name="access" value="read">
            <span>
              <span rst-choice-title>只讀</span>
            </span>
          </label>
        </div>
      </fieldset>
      <div rst-field>
        <label rst-field-label for="expiry">有效期至</label>
        <select rst-input="short" id="expiry" name="expiry">
          <option value="30">30 日</option>
          <option value="90" selected>90 日</option>
          <option value="365">1 年</option>
          <option value="never">永不</option>
        </select>
      </div>
      <div rst-form-foot>
        <button rst-btn="primary lg" type="submit">建立權杖</button>
      </div>
    </form>
  </section>
  <div rst-empty>
    <p rst-empty-body>仲未有權杖。</p>
  </div>
</div>
{{template "api-tokens" dict "Page" .Page "Path" "/tokens"}}