シェル
hc-shell は管理画面 / 業務アプリケーションの外側のレイアウトです:
常設のサイドバー、ヘッダー、スクロールするメイン領域、
そして任意のセカンダリな aside とフッター。レイアウトは純
CSS Grid です。唯一の JavaScript — installShell() — は、プラット
フォームがフォーカスと解除を代わりに管理してくれないモバイルの
ナビゲーションオーバーレイだけを動かします。デスクトップではシェルに
スクリプトは一切不要です。
デフォルトではヘッダーがサイドバーの上で全幅にまたがります
(SAP Fiori / Google / Salesforce の配置)。
data-layout="sidebar-first" で全高のサイドバー(Slack / Notion /
VS Code スタイル)に切り替わります —
レイアウトモードを参照。
レイアウトユーティリティの 上に構築され、既存のカラーとスペーシングのトークンを再利用するため、 アクティブなテーマ、密度、カラーを自動で採用します。
別名: アプリシェル、アプリケーションレイアウト、サイドバーレイアウト。
ブラウザのベースライン
Section titled “ブラウザのベースライン”| プリミティブ | 状況 |
|---|---|
CSS Grid + grid-template-areas | Baseline(すべてのエバーグリーンブラウザ) |
100dvh 動的ビューポート単位 | Baseline 2023 |
:has()(任意の aside の第 3 カラム) | Baseline 2023 |
リージョンはこのソース順で書いてください — モバイルではサイドバーが オーバーレイとして持ち上がる一方、残りはフローに積まれるため、順序が 重要です:
<div class="hc-shell"> <header class="hc-shell__header"> <button class="hc-button hc-shell__toggle" data-variant="ghost" data-hc-shell-toggle aria-label="Open navigation" type="button">≡</button> <strong>Acme Admin</strong> </header>
<nav class="hc-shell__sidebar" aria-label="Primary"> <a href="/dashboard">Dashboard</a> <a href="/orders">Orders</a> <a href="/settings">Settings</a> </nav>
<main class="hc-shell__main"> <!-- page content --> </main>
<footer class="hc-shell__footer">…</footer></div>ヘッダーの data-hc-shell-toggle ボタンはデスクトップでは非表示
です — モバイルのブレークポイントでだけ現れます。
レイアウトモード
Section titled “レイアウトモード”デフォルトではヘッダーとフッターが全幅にまたがり(上端と下端)、 左にサイドバーを持つ中間の帯を挟みます — SAP Fiori、Google Workspace、 Salesforce が使う配置で、グローバルなクローム(ブランド、グローバル 検索、アカウント)が文脈的なナビゲーションの周りで製品全体の関心事を 運ぶ形です。上のデモがそれです。
data-layout="sidebar-first" を設定すると反転します:
サイドバーが左で全高にまたがり、ヘッダーとフッターは main の
上だけに載ります — Slack / Notion / VS Code のスタイルで、サイドバーが
主要な面であるアプリに向いています。
<div class="hc-shell" data-layout="sidebar-first"> <header class="hc-shell__header">…</header> <nav class="hc-shell__sidebar" aria-label="Primary">…</nav> <main class="hc-shell__main">…</main> <footer class="hc-shell__footer">…</footer></div>それ以外はどちらのモードも同じです — 折りたたみのアイコンレール、 任意の aside、モバイルのオフキャンバスオーバーレイはすべて同じように 振る舞います。
| クラス | 要素(推奨) | 役割 |
|---|---|---|
hc-shell__header | <header> | 上部バー。モバイルのトグルを収めます。 |
hc-shell__sidebar | <nav> | 常設ナビゲーション(sidebar-first レイアウトでは全高)。 |
hc-shell__main | <main> | 主コンテンツ。独立にスクロールします。 |
hc-shell__aside | <aside> | 任意のセカンダリパネル(第 3 カラムを追加)。 |
hc-shell__footer | <footer> | 任意のフッター。 |
hc-shell__toggle | <button> | モバイルのハンバーガー(data-hc-shell-toggle とともに使用)。 |
サイドバーのナビゲーション項目
Section titled “サイドバーのナビゲーション項目”シェルは意図的にサイドバーの子をスタイルしません — ナビ項目は
hc-item です。<a>
要素として描画すればホバーのハイライトとフォーカスリングが自動で付き、
アクティブなリンクの aria-current="page" は選択時の見た目になります
(これはアクセシビリティのシグナルでもあります — 一致するルートで
サーバサイドに設定してください):
<nav class="hc-shell__sidebar" aria-label="Primary"> <a class="hc-item" href="/ops"> <span class="hc-item__media" aria-hidden="true">⚙</span> <span class="hc-item__title hc-shell__label">Operations</span> </a> <a class="hc-item" href="/studio" aria-current="page"> <span class="hc-item__media" aria-hidden="true">▦</span> <span class="hc-item__title hc-shell__label">Studio</span> </a> <a class="hc-item" href="/iam"> <span class="hc-item__media" aria-hidden="true">⛨</span> <span class="hc-item__title hc-shell__label">IAM Admin</span> </a></nav>リンクテキストを(上のように).hc-shell__label で包むと、
折りたたまれたアイコンレールでは
アクセシビリティツリーに残ったまま自動で隠れます。
ブロックページに、この方法で
組み立てた完全なサイドバーがあります。素の <a> の子も動作します —
ただ自前のホバー / カレントのスタイルを持たないだけです。
URL からアクティブ項目に印を付ける — data-hc-nav-current
Section titled “URL からアクティブ項目に印を付ける — data-hc-nav-current”一致するルートでサーバサイドに aria-current="page" を設定するのが
正統な方法です。それが不便なとき — ページ間で共有される静的な
サイドバーや、htmx のスワップをまたいで持続するもの — は、ナビの
コンテナに data-hc-nav-current を足すと、installNavCurrent()
ビヘイビアがインライン JS なしで URL から正しいリンクに印を付けます
(そのため厳格な Content-Security-Policy: default-src 'self' の下でも
動きます):
<nav class="hc-shell__sidebar" data-hc-nav-current aria-label="Primary"> <a class="hc-item" href="/app/explorer">Explorer</a> <a class="hc-item" href="/app/docs">Docs</a> <a class="hc-item" href="/app/docs/coverage">Coverage</a></nav>コンテナ内の同一オリジンの a[href] のうち、pathname が
location.pathname に等しいリンク、それがなければ — 完全一致に失敗
したとき — その最長のパスセグメント接頭辞であるリンクの pathname
に aria-current="page" を設定します。そのためセクションのリンク
(/app/docs)はそのサブページ(/app/docs/coverage)でもカレントの
ままです。ルート / は完全一致のみで、接頭辞としては決して一致しま
せん。htmx の履歴ナビゲーション(htmx:pushedIntoHistory)と
戻る / 進む(popstate)の後に再マークし、以前に設定したリンクを
クリアするため、カレントは常にちょうど 1 つです。
import { installNavCurrent } from '@hypermedia-components/core';installNavCurrent(); // or the auto-init bundle: @hypermedia-components/core/behaviorsdata-hc-nav-current はオプトインで、シェルのサイドバーに限らず
どんなナビコンテナでも動きます — .hc-item(または
[aria-current="page"] でスタイルする任意の要素)と組み合わせて
ください。JavaScript がなくてもリンクは動作し、アクティブの印が
欠けるだけです — プログレッシブエンハンスメント。
ヘッダーのアクションと戻るリンク
Section titled “ヘッダーのアクションと戻るリンク”ヘッダーは flex の行(gap つき)なので、ページごとの追加要素は
カスタム CSS なしで構成できます: コンテンツを分けたい位置に
.hc-spacer を置くと、その後のすべて — 戻るリンク、アクションリンク、
バッジ — はインラインの末尾に寄ります:
<header class="hc-shell__header"> <button class="hc-button hc-shell__toggle" data-variant="ghost" data-hc-shell-toggle aria-label="Open navigation" type="button">≡</button> <strong>Outbox events</strong> <span class="hc-badge" data-variant="warning">3 pending</span> <span class="hc-spacer"></span> <nav class="hc-cluster" aria-label="Page actions"> <a class="hc-button" data-variant="ghost" data-size="sm" href="/ops/console">← Back</a> <a class="hc-button" data-size="sm" href="/ops/console/outbox/export">Export</a> </nav></header>任意の aside
Section titled “任意の aside”直下の子として <aside class="hc-shell__aside"> を足すと、グリッドは
自動で第 3 カラムを生やします(:has() で検出)。モバイルでは通常
フローで main の下に落ちます。
<div class="hc-shell"> <header class="hc-shell__header">…</header> <nav class="hc-shell__sidebar">…</nav> <main class="hc-shell__main">…</main> <aside class="hc-shell__aside">…</aside></div>レスポンシブの挙動
Section titled “レスポンシブの挙動”- デスクトップ(≥ 60rem): ビューポートを満たす CSS Grid
(
block-size: 100dvh)。デフォルトではヘッダーが全幅にまたがり、 その下にサイドバー。data-layout="sidebar-first"では代わりに サイドバーが左で全高にまたがります。いずれにせよ main はクロームとは 独立にスクロールします。 - モバイル(< 60rem): リージョンは通常フローに積まれ、ページが スクロールします。サイドバーは固定のオフキャンバスオーバーレイに なり、ヘッダーは上部に貼り付き、ハンバーガーがオーバーレイをトグル します。
リテラルの 60rem ブレークポイントを使っています(メディアクエリの
条件はカスタムプロパティを読めません)。これはシステムがコンテナ
クエリではなくビューポートのブレークポイントを使う唯一の場所です —
シェルは定義上ビューポートを満たすため、ビューポート幅がすなわち
コンテナ幅です。内側のリージョンは引き続きコンテナレスポンシブな
レイアウトユーティリティを
使います。
ビヘイビア — installShell()
Section titled “ビヘイビア — installShell()”import { installShell } from '@hypermedia-components/core';installShell(); // or the auto-init bundle: @hypermedia-components/core/behaviors[data-hc-shell-toggle] ボタンと .hc-shell__sidebar を持つシェルに
対して、installShell() は:
- シェルの
data-sidebar="open"をトグルし、aria-expanded/aria-controlsを同期し続けます。 - 開いたときにフォーカスをサイドバーへ移し、その中に
Tabを トラップします。 Escape、サイドバー外のクリック(スクリム)、サイドバー内のリンクの アクティベートで閉じます — フォーカスをトグルに戻しながら。- ビューポートがデスクトップに戻ったら強制的に閉じるため、オーバーレイ が開いたまま固まることはありません。
冪等で、アンインストーラを返し、後から DOM に追加されたシェルも
(MutationObserver 経由で)拾います — つまり htmx でスワップされた
コンテンツでも動きます。ネットワークには決して触れません。
折りたたみ可能なサイドバー
Section titled “折りたたみ可能なサイドバー”デスクトップでは、サイドバーを細いアイコンレールに折りたため
ます。.hc-shell__sidebar の data-collapsible でオプトインし、
シェル内の任意の場所に [data-hc-shell-collapse] ボタンを足し、
(任意で)data-persist="<key>" で状態を localStorage に記憶させ
ます:
<div class="hc-shell"> <header class="hc-shell__header"> <button class="hc-button" data-variant="ghost" data-hc-shell-collapse type="button" aria-label="Collapse sidebar">⇔</button> </header>
<nav class="hc-shell__sidebar" aria-label="Primary" data-collapsible data-persist="app.sidebar"> <a class="hc-item" href="/dashboard"> <span class="hc-item__media" aria-hidden="true">▦</span> <span class="hc-item__title hc-shell__label">Dashboard</span> </a> <a class="hc-item" href="/orders"> <span class="hc-item__media" aria-hidden="true">▤</span> <span class="hc-item__title hc-shell__label">Orders</span> </a> </nav>
<main class="hc-shell__main">…</main></div>installShell() はシェルの data-sidebar-collapsed をトグルし
(CSS がグリッドのカラムを --hc-shell-sidebar-width から
--hc-shell-sidebar-collapsed-width、デフォルト 4rem に狭めます)、
ボタンの aria-expanded を同期し続けます。各ナビ項目のテキストは
.hc-shell__label で包んでください。レールでは視覚的に隠れつつ
アクセシビリティツリーには残るため、アイコンだけが見えていても
リンクはアクセシブルな名前を保ちます。
方向つきの折りたたみアイコン。 シェブロンは動く方向を指すべきです
— 展開中は «(折りたたむ)、折りたたみ後は »(展開する)。
グリフを .hc-shell__collapse-icon で包むと、レールが折りたたまれた
ときに自動で反転します(data-sidebar-collapsed をキーにした純 CSS。
追加スクリプトなし):
<button data-hc-shell-collapse type="button" aria-label="Toggle sidebar"> <span class="hc-shell__collapse-icon">«</span></button>方向を持たないグリフ(例: 上の ⇔ やハンバーガー)ではラッパーを
省いてください — 動かないままにすべきです。ボタンのアクセシブルな
名前は一定のまま(「Toggle sidebar」)で構いません。開閉の状態は
installShell() が aria-expanded で伝えます。
これはデスクトップ限定です — レスポンシブのブレークポイント未満
ではサイドバーは代わりにオフキャンバスオーバーレイを使い、折りたたみ
ボタンは隠れます。data-persist があれば、折りたたみ状態は次の訪問で
最初の描画前に復元されます(localStorage の失敗は静かに劣化)。
htmx での利用
Section titled “htmx での利用”htmx でのシェルの定番は boost されたアプリシェルです: シェルに
data-hx-boost="true" を置くとサイドバーの各リンクが AJAX
ナビゲーションになり、継承される data-hx-target / data-hx-select
によって各リンクはメインリージョンだけを置き換えます — クローム
(ヘッダー、サイドバー、そのスクロール位置)は再描画されません:
<div class="hc-shell" data-hx-boost="true" data-hx-target=".hc-shell__main" data-hx-select=".hc-shell__main" data-hx-swap="outerHTML show:window:top"> <header class="hc-shell__header">…</header> <nav class="hc-shell__sidebar" data-hc-nav-current aria-label="Primary"> <a class="hc-item" href="/orders">Orders</a> <a class="hc-item" href="/settings">Settings</a> </nav> <main class="hc-shell__main"><!-- swapped per navigation --></main></div>サーバはフルページを返し続けます — data-hx-select がレスポンス
から新しいページの .hc-shell__main を抽出するため、直接のロード、
リロード、no-JS のクリックはそのまま動きます。boost された
ナビゲーションは新しい URL を履歴にプッシュし、
installNavCurrent()
はまさにそのシグナル(htmx:pushedIntoHistory、加えて戻る / 進む用の
popstate)で再マークします — つまり data-hc-nav-current を持つ
サイドバーの aria-current="page" は、追加の配線なしにすべての
boost ナビゲーションに追随します。
アクセシビリティ
Section titled “アクセシビリティ”- 本物のランドマークを使ってください:
<header>、<nav aria-label="…">、<main>、<aside>、<footer>。シェルは レイアウトを提供するだけです。 - モバイルのオーバーレイはダイアログのようにフォーカスを管理します:
フォーカスはサイドバーに入り、
Tabはトラップされ、Escapeで 解除され、フォーカスはトグルに戻ります。 - トグルにはアクセシブルな名前を与えてください(
aria-labelか 見えるテキスト)。どちらもなければinstallShell()がフォールバック のラベルを足します。
テーマ用トークン
Section titled “テーマ用トークン”シェルは固有の component トークンを定義しません: 色とスペーシング
は共有のセマンティックな --hc-color-* / --hc-space-* トークンから
来るため、テーマ、アクセント(data-color)、密度は自動で適用され
ます。下の CSS 変数はテーマ可能なトークンではなくレイアウトのノブ
です。
CSS 変数
Section titled “CSS 変数”生成される CSS 変数を表示
これらはテーマ可能な視覚トークンではなくレイアウトのノブです —
シェルにインラインで、またはラッパーのルールで設定してください。色と
スペーシングは共有の --hc-color-* / --hc-space-* トークンから
来ます。
| 変数 | デフォルト | 制御対象 |
|---|---|---|
--hc-shell-sidebar-width | 16rem | サイドバーのカラム幅(デスクトップ)/ オーバーレイ幅(モバイル、80vw が上限)。 |
--hc-shell-sidebar-collapsed-width | 4rem | レールに折りたたまれたときのサイドバーのカラム幅(data-sidebar-collapsed)。 |
--hc-shell-aside-width | 20rem | aside のカラム幅。 |
--hc-shell-pad | var(--hc-space-4) | リージョンの内側パディング。 |
- レイアウトユーティリティ — シェルの土台となるプリミティブ。