Skip to content

テーマとレイアウト ​

ユーザーはトップバーでライト / ダークモードを切り替えられるほか、「外観設定」パネルでアクセントカラー、ナビゲーションモード、サイドバーのスタイル、コンテンツ幅、タブバーを有効にするかどうかを選べます。選択内容はすぐに反映され、現在のブラウザに保存されます。

このページでは、これらのオプションの実装方法と、それらがページのコードに求める要件を説明します。

ライト / ダーク ​

  • <html class="dark"> で切り替えます。shadcn / Tailwind の慣例に従っています。
  • ユーザーが選んだモードは localStorage の theme キーに保存されます。まだ選んでいない間はシステムの設定に従い、システムの設定が変わると追従します。
  • 切り替えの瞬間は 1 フレームだけすべての CSS トランジションを無効にし(<html> の theme-switching クラス)、hover や色のトランジションが新旧の色の間でアニメーションせず、ページが一度に切り替わるようにしています。

実装は apps/web/src/context/ThemeContext.tsx にあります。ページでセマンティックカラークラスを使っていれば(フロントエンド を参照)、ダークモードは自動で正しく表示されます。

アクセントカラー ​

外観設定には 6 種類のアクセントカラーがあり、デフォルトはオーシャンです。

ID名前
oceanオーシャン(デフォルト)
violetバイオレット
emeraldエメラルド
roseローズ
amberアンバー
slateスレート

トークンの派生方法 ​

アクセントカラーは <html data-accent="<id>"> の形で適用されます。apps/web/src/index.css では、各プリセットが 6 つの変数を、ライトとダークでそれぞれ 1 組ずつ設定しています。

css
[data-accent='ocean'] { --brand-from: #2563eb; --brand-via: #0284c7; --brand-to: #22d3ee; --brand-primary: #2563eb; --brand-strong-from: #2563eb; --brand-strong-to: #077bba; }
.dark[data-accent='ocean'], .dark [data-accent='ocean'] { --brand-from: #3b82f6; --brand-via: #0ea5e9; --brand-to: #22d3ee; --brand-primary: #3d85f9; --brand-strong-from: #2970e3; --brand-strong-to: #017cb2; }
  • --brand-from / --brand-via / --brand-to:装飾用の 3 つのグラデーションのカラーストップ。
  • --brand-primary:文字を載せる色(リンク、フォーカスリング、--primary-foreground の下の塗り)。すべての背景と brand-soft の上で 4.6:1 以上のコントラストになるように選ばれています。
  • --brand-strong-from / --brand-strong-to:白い文字の下に敷くグラデーション。白に対して 4.6:1 以上です。

アクセントカラーに追従するそのほかのトークンは、すべてこれらから派生します。

トークン派生元
--primary、--ring、--sidebar-primary、--sidebar-ring--brand-primary
--brand-gradient3 つのカラーストップによる線形グラデーション
--brand-gradient-strong--brand-strong-from / --brand-strong-to による線形グラデーション
--brand-soft、--brand-glow、--brand-shadowcolor-mix() でカラーストップを透明色と混ぜる

したがって、ページで primary、brand-* などのセマンティッククラスを使っていれば、アクセントカラーを切り替えたときに自動で追従します。ページ内で特定のアクセントカラーの色値を直接書かないでください。

グラフの系列色 --chart-1 … --chart-5 は、アクセントカラーに追従しない固定のカテゴリ用パレット(ライトとダークで別の値)です。ECharts のグラフは @/lib/chart-theme の useChartColors() で現在の CSS 変数の実際の値を読み取り、テーマやアクセントカラーが切り替わると再計算します。chartBase() はカテゴリ用パレットを適用し、単一系列のグラフではアクセントカラーのカラーストップから作る brandLine() / brandArea() を使います。

アクセントカラーを追加する ​

  1. apps/web/src/lib/appearance.ts の ACCENTS に { id, label } を追加します。label は中国語の原文で、翻訳キーも兼ねます。
  2. apps/web/src/index.css に、対応する [data-accent='<id>'] のライトとダークの 2 つのルールを追加します。どちらも 6 つの変数をすべて設定し、上記のコントラストの基準を満たすようにします。色は 16 進表記のままにしてください。chart-theme.ts が --brand-from を rgba に変換します。
  3. label の英語と日本語の訳文を追加します。

ナビゲーションモード ​

ID名前説明
sidebarサイドバー(デフォルト)左側にメニューツリー全体を表示
topトップメニューをトップバーに表示し、サイドバーは表示しない
mixedミックストップバーに第 1 階層のセクションを表示し、左側に現在のセクション配下のメニューを表示

サイドバーのスタイル ​

shadcn の <Sidebar variant> と 1 対 1 で対応しています。

ID名前
sidebar標準(デフォルト)
floatingフローティング
insetインセット

ナビゲーションモードが「トップ」の場合はサイドバーが表示されないため、このオプションは使えません。

コンテンツ幅 ​

ID名前説明
boxed固定幅コンテンツ領域を中央に配置し、最大幅は 1600px
fluidフル幅(デフォルト)コンテンツ領域を利用可能な幅いっぱいに広げる

モバイル ​

ビューポートの幅が 768px 未満の場合:

  • どのナビゲーションモードでも、メニュー全体を表示するドロワー式のサイドバーになります
  • タブバーは表示されず、ページの状態保持も行いません

タブバーとページの状態保持 ​

タブバーはデフォルトで有効で、外観設定で無効にできます。有効にすると、開いたページがトップバーの下にタブとして表示されます。

  • トップレベルのページ(ホームなど)は先頭に固定され、閉じることはできません
  • タブでは「閉じる」「他を閉じる」「右側を閉じる」「すべて閉じる」「再読み込み」が使えます
  • タブの一覧は sessionStorage の tags-view キーに保存され、現在のブラウザタブでのみ有効です
  • タブに戻ると、前回のクエリパラメーターとスクロール位置が復元されます

状態管理は apps/web/src/context/TagsViewContext.tsx、ページ領域の描画は apps/web/src/components/app/AppLayout.tsx にあります。

状態保持の仕組み ​

タブバーが有効な場合、開いているタブのページはそれぞれ React の <Activity> で包まれ、マウントされたまま保持されます。

状態切り替えて離れたとき(非表示)戻ったとき(表示)
コンポーネントの state(フィルター条件、ページング、フォームの入力)保持そのまま復元
useEffect の副作用クリーンアップ関数を実行再実行

つまり、ページに戻ると useEffect 内のリクエストがもう一度データを取得し、非表示の間はタイマー、ポーリング、WebSocket がクリーンアップ関数によって自動で停止します。

タブを閉じると対応するページはアンマウントされます。タブを再読み込みするとページが再マウントされ、先頭から表示されます。

ページのコードに求められること ​

副作用は effect の中に書き、正しくクリーンアップすること

  • タイマー、ポーリング、購読、WebSocket 接続はすべて useEffect の中で開始し、クリーンアップ関数で停止してください。
  • モジュールのトップレベルやレンダリング中にタイマーを開始しないでください。ページが非表示になっても動き続けてしまいます。
tsx
useEffect(() => {
  const timer = setInterval(refresh, 5000)
  return () => clearInterval(timer)
}, [refresh])

設定の保存先 ​

ストレージキー内容
localStoragethemelight / dark
localStorageappearance{ accent, navMode, sidebarVariant, contentWidth, tagsView }
localStoragelangUI の言語。多言語対応 を参照
sessionStoragetags-view開いているタブ

appearance 内の未知のキーや不正な値は無視され、デフォルト値に戻ります。外観設定パネルには「デフォルトに戻す」ボタンがあります。オプションの唯一の定義は apps/web/src/lib/appearance.ts にあります。

Released under the MIT License.