本文へ移動
セクション
言語、現在は日本語
Rastrillo デザインシステム

選択と操作

選択肢から選ぶためのコントロールと、フォームを締めくくる行です。どれも余白のある rst-box の中に入っています。これらの部品がそう前提しているからです。

どの例も実際に動きますが、リンクはどこにも移動しません。 HTML にはコピーできるマークアップがあります。

field-select

ネイティブの select を field で包んだものです。選択肢が十を超えると select は select.js の絞り込みコンボボックスを有効にしますが、フォームが送信するのは変わらずネイティブの select です。

3つの選択肢:ネイティブの select

プレビュー

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

<div rst-field>
  <label rst-field-label for="sel_few">役割</label>
  <select rst-input id="sel_few" name="sel_few">
    <option value="admin" selected>管理者</option>
    <option value="editor">編集者</option>
    <option value="member">メンバー</option>
  </select>
</div>

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

{{template "field-select" dict
    "ID" "sel_few"
    "Name" "sel_few"
    "Label" "役割"
    "Options" (list
        (dict "Label" "管理者" "Selected" true "Value" "admin")
        (dict "Label" "編集者" "Value" "editor")
        (dict "Label" "メンバー" "Value" "member"))}}

12個の選択肢:絞り込みできるコンボボックス

プレビュー

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

<div rst-field>
  <label rst-field-label for="sel_many">都市</label>
  <select rst-input id="sel_many" name="sel_many" data-rst-select data-rst-select-filter="入力して絞り込む" data-rst-select-results="{n} 件" data-rst-select-result-one="1 件" data-rst-select-no-matches="一致する項目はありません" aria-describedby="sel_many-help">
    <option value="dublin" selected>ダブリン</option>
    <option value="lisbon">リスボン</option>
    <option value="madrid">マドリード</option>
    <option value="berlin">ベルリン</option>
    <option value="rome">ローマ</option>
    <option value="athens">アテネ</option>
    <option value="helsinki">ヘルシンキ</option>
    <option value="reykjavík">レイキャビク</option>
    <option value="warsaw">ワルシャワ</option>
    <option value="zagreb">ザグレブ</option>
    <option value="tallinn">タリン</option>
    <option value="valletta">バレッタ</option>
  </select>
  <p rst-field-help id="sel_many-help">入力すると絞り込めます。</p>
</div>

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

{{template "field-select" dict
    "ID" "sel_many"
    "Name" "sel_many"
    "Label" "都市"
    "Options" (list
        (dict "Label" "ダブリン" "Selected" true "Value" "dublin")
        (dict "Label" "リスボン" "Value" "lisbon")
        (dict "Label" "マドリード" "Value" "madrid")
        (dict "Label" "ベルリン" "Value" "berlin")
        (dict "Label" "ローマ" "Value" "rome")
        (dict "Label" "アテネ" "Value" "athens")
        (dict "Label" "ヘルシンキ" "Value" "helsinki")
        (dict "Label" "レイキャビク" "Value" "reykjavík")
        (dict "Label" "ワルシャワ" "Value" "warsaw")
        (dict "Label" "ザグレブ" "Value" "zagreb")
        (dict "Label" "タリン" "Value" "tallinn")
        (dict "Label" "バレッタ" "Value" "valletta"))
    "Help" "入力すると絞り込めます。"}}

選択肢が十を超えると、部品は select.js を有効にします。十未満では有効にしません。ひと握りの項目を検索させるのは、助けではなく家具だからです。

選択肢が 12、Plain:拡張を断った場合

プレビュー

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

<div rst-field>
  <label rst-field-label for="sel_plain">都市</label>
  <select rst-input id="sel_plain" name="sel_plain">
    <option value="dublin" selected>ダブリン</option>
    <option value="lisbon">リスボン</option>
    <option value="madrid">マドリード</option>
    <option value="berlin">ベルリン</option>
    <option value="rome">ローマ</option>
    <option value="athens">アテネ</option>
    <option value="helsinki">ヘルシンキ</option>
    <option value="reykjavík">レイキャビク</option>
    <option value="warsaw">ワルシャワ</option>
    <option value="zagreb">ザグレブ</option>
    <option value="tallinn">タリン</option>
    <option value="valletta">バレッタ</option>
  </select>
</div>

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

{{template "field-select" dict
    "ID" "sel_plain"
    "Name" "sel_plain"
    "Label" "都市"
    "Options" (list
        (dict "Label" "ダブリン" "Selected" true "Value" "dublin")
        (dict "Label" "リスボン" "Value" "lisbon")
        (dict "Label" "マドリード" "Value" "madrid")
        (dict "Label" "ベルリン" "Value" "berlin")
        (dict "Label" "ローマ" "Value" "rome")
        (dict "Label" "アテネ" "Value" "athens")
        (dict "Label" "ヘルシンキ" "Value" "helsinki")
        (dict "Label" "レイキャビク" "Value" "reykjavík")
        (dict "Label" "ワルシャワ" "Value" "warsaw")
        (dict "Label" "ザグレブ" "Value" "zagreb")
        (dict "Label" "タリン" "Value" "tallinn")
        (dict "Label" "バレッタ" "Value" "valletta"))
    "Plain" true}}

必須、エラーつき

プレビュー

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

<div rst-field>
  <label rst-field-label for="sel_error">役割</label>
  <select rst-input id="sel_error" name="sel_error" required aria-invalid="true" aria-describedby="sel_error-error">
    <option value="admin" selected>管理者</option>
    <option value="editor">編集者</option>
    <option value="member">メンバー</option>
  </select>
  <p rst-field-error id="sel_error-error" role="alert">この人の役割を選んでください。</p>
</div>

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

{{template "field-select" dict
    "ID" "sel_error"
    "Name" "sel_error"
    "Label" "役割"
    "Options" (list
        (dict "Label" "管理者" "Selected" true "Value" "admin")
        (dict "Label" "編集者" "Value" "editor")
        (dict "Label" "メンバー" "Value" "member"))
    "Required" true
    "Error" "この人の役割を選んでください。"}}

手書き、optgroup つき

プレビュー

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

<div rst-field>
  <label rst-field-label for="sel_grouped">地域</label>
  <select rst-input id="sel_grouped" name="sel_grouped" data-rst-select data-rst-select-filter="入力して絞り込む" data-rst-select-results="{n} 件" data-rst-select-result-one="1 件" data-rst-select-no-matches="一致する項目はありません">
    <optgroup label="ヨーロッパ">
      <option value="dublin" selected>ダブリン</option>
      <option value="lisbon">リスボン</option>
      <option value="warsaw">ワルシャワ</option>
    </optgroup>
    <optgroup label="南北アメリカ">
      <option value="montreal">モントリオール</option>
      <option value="lima">リマ</option>
      <option value="austin">オースティン</option>
    </optgroup>
    <optgroup label="アジア">
      <option value="osaka">大阪</option>
      <option value="hanoi">ハノイ</option>
      <option value="dhaka">ダッカ</option>
    </optgroup>
    <optgroup label="アフリカ">
      <option value="accra">アクラ</option>
      <option value="nairobi">ナイロビ</option>
      <option value="tunis">チュニス</option>
    </optgroup>
  </select>
</div>

ここでの Options は平坦なリストなので、グループ化した選択肢はアプリ側が書くマークアップです。select.js はグループを捨てずに描画します。手書きの select は data-rst-select="false" で拡張そのものを断ることもできます。

field-check

切り替えの仕組みはこれひとつ。見た目をスイッチにした本物のチェックボックスで、キーボードもスクリーンリーダーもそのまま使えます。

オン

プレビュー

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

<label rst-switch>
  <input type="checkbox" name="notify_on" checked>
  <span rst-switch-track aria-hidden="true"></span>
  返信をメールで知らせる
</label>

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

{{template "field-check" dict
    "Name" "notify_on"
    "Label" "返信をメールで知らせる"
    "Checked" true}}

オフ

プレビュー

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

<label rst-switch>
  <input type="checkbox" name="notify_off">
  <span rst-switch-track aria-hidden="true"></span>
  メンションをメールで知らせる
</label>

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

{{template "field-check" dict "Name" "notify_off" "Label" "メンションをメールで知らせる"}}

無効

プレビュー

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

<label rst-switch>
  <input type="checkbox" name="notify_disabled" disabled>
  <span rst-switch-track aria-hidden="true"></span>
  週ごとのまとめをメールで受け取る
</label>

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

{{template "field-check" dict
    "Name" "notify_disabled"
    "Label" "週ごとのまとめをメールで受け取る"
    "Disabled" true}}

choice-field

選択肢カード。ラジオやチェックボックスの group として推奨される形です。Title は短く保ち、説明は Desc に任せます。

ひとつだけ選ぶ

プレビュー

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

<fieldset rst-choice>
  <legend>プラン</legend>
  <div rst-choice-cards>
    <label>
      <input type="radio" name="plan" value="free">
      <span>
        <span rst-choice-title>無料</span>
        <span rst-choice-desc>プロジェクト1件、コミュニティによるサポート。</span>
      </span>
    </label>
    <label>
      <input type="radio" name="plan" value="pro" checked>
      <span>
        <span rst-choice-title>プロ</span>
        <span rst-choice-desc>プロジェクト無制限、サポート窓口つき。</span>
      </span>
    </label>
  </div>
</fieldset>

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

{{template "choice-field" dict
    "Legend" "プラン"
    "Name" "plan"
    "Options" (list
        (dict "Desc" "プロジェクト1件、コミュニティによるサポート。" "Title" "無料" "Value" "free")
        (dict "Checked" true "Desc" "プロジェクト無制限、サポート窓口つき。" "Title" "プロ" "Value" "pro"))}}

いくつでも選べる

プレビュー

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

<fieldset rst-choice>
  <legend>受け取るもの</legend>
  <div rst-choice-cards>
    <label>
      <input type="checkbox" name="digest" value="replies" checked>
      <span>
        <span rst-choice-title>返信</span>
        <span rst-choice-desc>その都度。</span>
      </span>
    </label>
    <label>
      <input type="checkbox" name="digest" value="weekly">
      <span>
        <span rst-choice-title>週ごとのまとめ</span>
        <span rst-choice-desc>毎週月曜の朝。</span>
      </span>
    </label>
  </div>
</fieldset>

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

{{template "choice-field" dict
    "Legend" "受け取るもの"
    "Name" "digest"
    "Type" "checkbox"
    "Options" (list
        (dict "Checked" true "Desc" "その都度。" "Title" "返信" "Value" "replies")
        (dict "Desc" "毎週月曜の朝。" "Title" "週ごとのまとめ" "Value" "weekly"))}}

form-foot

フォームを締めくくる行です。主となる送信ボタンひとつと、任意のキャンセルリンク。キャンセルがリンクなのは、フォームを離れることが画面移動だからです。

保存とキャンセル

プレビュー

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

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">投稿を保存</button>
  <a rst-btn="lg" href="/posts">投稿一覧に戻る</a>
</div>

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

{{template "form-foot" dict
    "Submit" "投稿を保存"
    "CancelHref" "/posts"
    "CancelLabel" "投稿一覧に戻る"}}

送信のみ

プレビュー

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

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">招待を送信</button>
</div>

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

{{template "form-foot" dict "Submit" "招待を送信"}}

待機中:何も起きていないときのボタン

プレビュー

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

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">公開する</button>
  <a rst-btn="lg" href="/posts">投稿一覧に戻る</a>
</div>

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

{{template "form-foot" dict
    "Submit" "公開する"
    "CancelHref" "/posts"
    "CancelLabel" "投稿一覧に戻る"}}

何かを変えるボタンには読み込み状態が付き、何かを開くだけのボタン(開閉、ドロップダウン、タブなど)には付きません。rastrillo.js はこの規則をすべてのフォームのすべての送信ボタンに適用します。有効にする設定はありません。

処理中:フォームが出ていくときに rastrillo.js が書くもの

プレビュー

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

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit" aria-busy="true" disabled>
    <span rst-spin aria-hidden="true"></span>
    公開する
  </button>
  <a rst-btn="lg" href="/posts">投稿一覧に戻る</a>
</div>

押されたボタンだけです。フォーム内のほかの送信ボタンは name と value をそのまま保ち、フォーム自体は二重送信から守られます。data-busy="false" でこの規則を外せます(フォームにも、ボタン一つにも)。data-busy-label は文言を差し替えます。スクリプトを切ると何も起こらず、フォームはこれまでどおり送信されるので、冪等性は変わらずサーバーの仕事です。

confirm-form

確認ページの操作行です。取り消せない操作にはダイアログではなく専用のルートがあるので、書くべきスクリプトがありません。

取り消せない操作

プレビュー

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

<form rst-form-actions method="post" action="/posts/1/delete">
  <input type="hidden" name="csrf" value="tok-123">
  <input type="hidden" name="id" value="1">
  <a rst-btn="ghost lg" href="/posts/1">キャンセル</a>
  <button type="submit" rst-btn="danger lg">この投稿を削除</button>
</form>

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

{{template "confirm-form" dict
    "Action" "/posts/1/delete"
    "Label" "この投稿を削除"
    "Danger" true
    "Hidden" (list (list "csrf" "tok-123") (list "id" "1"))
    "CancelHref" "/posts/1"}}

通常の場合、キャンセルの文言は既定

プレビュー

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

<form rst-form-actions method="post" action="/posts/1/publish">
  <a rst-btn="ghost lg" href="/posts/1">キャンセル</a>
  <button type="submit" rst-btn="primary lg">公開する</button>
</form>

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

{{template "confirm-form" dict
    "Action" "/posts/1/publish"
    "Label" "公開する"
    "CancelHref" "/posts/1"}}

CancelLabel がないので、キャンセルリンクの文言はカタログから来ます。