概要 — 何をする機能か
InfraGuard コンソールには ダーク(既定) と ライト の2つのテーマが用意されています。テーマはドキュメントのルート要素(<html>)に付く単一の属性 data-theme で切り替わり、その値が SCSS で定義された --infr-* CSS 変数トークン のセット全体を入れ替えます。すべてのコンポーネントはハードコードした16進数ではなく var(--infr-*) トークンを使うため、単語をひとつ(dark ↔ light)変えるだけで UI 全体が即座に塗り直されます。
既定は ダーク です — NOC(ネットワーク運用センター)のコンソールとして、深いブルーブラックの背景にティールのシグナルアクセントを合わせ、深刻度の色(crit / warn / good)が暗い画面上でくっきり読み取れるようにしています。
<html data-theme="dark"> ← 既定。NOC 向けダーク
<html data-theme="light"> ← ライト(明るいオフィス環境向け)
テーマは 純粋なフロントエンドの設定 です。サーバーに保存されることは一切なく、アカウントやデータベースとも無関係で、ブラウザーの
localStorageの中だけに存在します。ログアウトしても保持され、同じアカウントで別の端末からサインインした場合、その端末はその端末自身の設定に従います。
こんなときに使う
- 夜間運用 / NOC のウォールディスプレイ → ダーク(まぶしさが少なく、深刻度の色のコントラストが高い)。
- 明るいオフィス、プロジェクター、ドキュメント用のスクリーンショット → ライト。
- 個人ごとのコントラストや色覚の好み — ユーザーごとに切り替えます。
一度選べばそのブラウザーに保持されるため、通常は最初に一度設定するだけです。
画面構成
ヘッダーのトグルボタン
コンソールのシェル(layouts/app.blade.php)とログイン画面(layouts/simple.blade.php)のどちらも、トップバーの右側に テーマ切替ボタン を配置しています。
| 項目 | 値 |
|---|---|
| マークアップのフック | <button data-theme-toggle> |
| アイコン | mdi-theme-light-dark(半月) |
| aria-label | msg.action.theme(ローカライズ済み) |
| 動作 | クリックするとダーク ↔ ライトが入れ替わる |
| 位置 | トップバーのアクション領域(言語スイッチャーの隣、ユーザーメニューの左) |
ログイン前(ゲスト)の画面にも同じトグルがあるため、認証しなくてもテーマを変更できます。
カラートークンのグループ
resources/scss/_variables.scss は、同じトークン名を異なる値で2つのブロック — :root[data-theme="dark"] と :root[data-theme="light"] — に定義しています。代表的なトークンは次のとおりです。
| トークン | 用途 | dark | light |
|---|---|---|---|
--infr-bg |
ページ背景 | #0b1220 |
#f6f8fb |
--infr-surface |
パネル / カードの面 | #111b2c |
#ffffff |
--infr-fg |
本文テキスト | #e9eff8 |
#111a28 |
--infr-fg-muted |
補助テキスト | #8493ac |
#63728b |
--infr-border |
罫線 | #1e2c43 |
#dbe2ec |
--infr-accent |
アクセント(ティール) | #2dd4bf |
#0d9488 |
--infr-crit |
深刻度 critical | #fb7185 |
#e11d48 |
--infr-warn |
深刻度 warning | #fbbf24 |
#b45309 |
--infr-good |
深刻度 healthy | #34d399 |
#0f9d6a |
--infr-info |
情報 | #38bdf8 |
#0369a1 |
これら以外にも、各テーマは --infr-surface-2/hi、--infr-border-2、--infr-accent-soft/ink、--infr-on-accent(アクセント上のボタン文字色)、TCP 状態の色(--infr-state-*)、チャートの色(--infr-chart-2)、影 / グロー(--infr-shadow / --infr-glow)、トップバーのガラス表現(--infr-topbar-*)を定義しています。各ブロックは color-scheme: dark|light も宣言しているため、ネイティブのフォームコントロールやスクロールバーもテーマに揃います。
SSH ターミナルのパレットは例外です。
--infr-term-*トークンはdata-themeに かかわらず 深く没入感のあるダークのままです。ライトテーマでも SSH コンソール のターミナルはダークを保ちます。
使い方
切り替える
- トップバー右側の 半月アイコン(テーマ切替) をクリックします。
- 即座にダーク ↔ ライトが入れ替わり、選択は自動的に保存されます。
- 次回のアクセス時やページの再読み込み時には、最後の選択が復元されます。
内部の仕組み(開発者向けリファレンス)
フロントエンドの配線は resources/js/app.js にあります。中心となるロジックは次のとおりです。
const THEME_KEY = 'infr-theme'; // localStorage のキー
const get_theme = () => localStorage.getItem(THEME_KEY) || 'dark'; // 既定はダーク
const set_theme = (t) => {
document.documentElement.setAttribute('data-theme', t); // ルート要素の属性
localStorage.setItem(THEME_KEY, t); // 永続化する
window.dispatchEvent(new CustomEvent('infr:theme', { detail: t })); // チャートを再着色する
};
// トグルボタンを配線する
document.querySelectorAll('[data-theme-toggle]')
.forEach(btn => btn.addEventListener('click',
() => set_theme(get_theme() === 'dark' ? 'light' : 'dark')));
動作のまとめ:
| ステップ | 内容 |
|---|---|
| 保存キー | localStorage['infr-theme'] = "dark" | "light" |
| 適用先 | document.documentElement(<html>)の data-theme 属性 |
| 既定値 | dark(localStorage が空、または読み取れない場合) |
| イベント | 切り替えのたびに window 上で infr:theme CustomEvent を発火(detail = 現在のテーマ) |
| タブ間の同期 | storage イベントを購読 → 同じブラウザーの 他のタブ が自動的に同期する |
スタイル未適用のちらつき(FOUC)の防止 — 描画前スクリプト
<head> の最上部に置いた小さなインラインスクリプト(app.blade.php と simple.blade.php で共通、CSS の @vite 読み込み より前)が、どのスタイルシートよりも先に data-theme を設定します。これにより再読み込み時の「ダーク→ライトのちらつき」がなくなります。
<script>
(function () {
try { document.documentElement.setAttribute('data-theme',
localStorage.getItem('infr-theme') || 'dark'); }
catch (e) { document.documentElement.setAttribute('data-theme', 'dark'); }
})();
</script>
localStorageがブロックされている環境(シークレットウィンドウ、サードパーティストレージのブロックなど)でも、catchが安全に ダーク へフォールバックします。
チャートとエディターもテーマに追従する
- ECharts(
<metric-chart>/ echarts-kit):getComputedStyleで--infr-*トークンを読み取ってチャートの色・軸・ツールチップを組み立て、infr:themeイベントで 再着色 します。is_dark()の判定はdata-theme !== 'light'です(ダークが既定のため)。サーバーの時系列チャート を参照してください。 - CodeMirror(
<cm-editor>)::darkプロパティがoneDark(ダーク)とライトテーマを切り替えます。Vue のページはis_dark_mode = document.documentElement.dataset.theme === 'dark'を計算して:dark="is_dark_mode"として渡し、infr:themeイベントで更新します。バックアップスクリプトビルダー を参照してください。
<cm-editor v-model="script.body" lang="bash" :dark="is_dark_mode"></cm-editor>
データソース
テーマには DB もサーバー側の保存も一切ありません。 使うのは次のものだけです。
| 場所 | 内容 |
|---|---|
localStorage['infr-theme'] |
ブラウザーごとのユーザーの選択(dark/light) |
<html data-theme> |
実行時に適用されている状態 |
resources/scss/_variables.scss |
--infr-* トークンのテーマ別の実際の値(CSS にビルドされる) |
resources/js/app.js |
トグル / 永続化 / イベントの配線 |
パレット設計の正本は CLAUDE.md の 付録 C(ライト / ダークテーマトークン) です。
アクセス権限
テーマは 認証や認可とは無関係の UI 設定 です。
- ゲスト(ログイン前)を含む すべてのユーザー が自由に切り替えられます — レベルによる制限はありません。
- demo=1 / staff=5 / admin=10 のいずれでも同じ挙動です。
- サーバー側には何も書き込まないため、監査ログ(
infr_audit_log)に現れることもありません。
トラブルシューティングと FAQ
Q. 再読み込みするとテーマが元に戻ります。
ブラウザーが localStorage をブロックしている場合(シークレットウィンドウ、厳格なプライバシー設定、サードパーティストレージのブロック)、選択を保存できないため毎回既定の ダーク に戻ります。そのサイトにストレージへのアクセスを許可してください。
Q. 片方のタブで変更したのに、もう片方のタブが更新されません。
切り替えは storage イベントを通じて 同じブラウザーの他のタブ に伝わります。すぐに反映されない場合、そのタブはまだリスナーの配線(app.js)を読み込んでいない可能性があります — 再読み込みしてください。端末やブラウザーが異なる 場合はストレージが別なので同期しません(これは仕様です)。
Q. ライトに切り替えたのに SSH ターミナルはダークのままです。
想定どおりの動作です。ターミナルは、アプリのテーマにかかわらず深いダークを保つ専用の --infr-term-* パレットを使用します(可読性 / 没入感のため)。
Q. あるコンポーネントだけ表示がおかしい(テーマが適用されない)。
そのコンポーネントがトークンではなく16進数をハードコードしています。規約上、コンポーネントは var(--infr-*) トークンだけを使わなければなりません。回帰チェック用の grep で resources/scss/pages に直接の16進数がないか確認してください。
rg -n "#[0-9a-fA-F]{3,6}" resources/scss/pages
Q. OS のダークモードに追従させたいです。
現在の実装は OS の設定(prefers-color-scheme)に 追従しません。初期値は常に dark で、ユーザーがトグルで明示的に選んだものだけを記憶します。OS 連携はロードマップの項目です。
関連項目
- ナビゲーションとレイアウト — トップバーのアクション領域(テーマ / 言語 / ユーザーメニュー)
- 言語 · ロケール — テーマと並ぶもうひとつのフロントエンド設定
- このマニュアルを検索する — /manual の検索と構成
- サーバーの時系列と MetricChart — テーマトークンから再着色されるチャート
- バックアップスクリプトビルダー — CodeMirror の
:darkバインディング - SSH コンソール — アプリのテーマとは独立した固定ダークのターミナル
