6.6 KiB
6.6 KiB
ダークモード視認性ガイドライン
目的
ダークモード実装時に「背景と同化して見えない」問題を防ぐための開発ガイドラインです。
基本原則
1. 色の明示的指定
❌ NG: Theme.of(context).primaryColor をダークモードで直接使用
// NG例: ダークモードでは #8AB4F6 になり、暗い背景で見えにくい
color: Theme.of(context).primaryColor
✅ OK: brightness チェックで明示的に明るい色を指定
// OK例: ダークモードでは明るい青 (#64B5F6) を使用
color: Theme.of(context).brightness == Brightness.dark
? const Color(0xFF64B5F6) // より明るい青
: Theme.of(context).primaryColor // #376495
2. 推奨カラーパレット
アクセント色(選択状態、重要なUI要素)
| 用途 | ライトモード | ダークモード |
|---|---|---|
| プライマリ(通常) | #376495 (AppTheme.posimaiBlue) |
#64B5F6 (明るい青) |
| プライマリ(強調) | AppTheme.posimaiBlue |
#8AB4F6 (Theme.primaryColor) |
| チェックマーク | Theme.primaryColor |
#64B5F6 |
| 選択中チップ | AppTheme.posimaiBlue |
colorScheme.primary (#8AB4F6) |
テキスト色
| 用途 | ライトモード | ダークモード |
|---|---|---|
| 本文 | Colors.black87 |
Colors.white |
| 副見出し | Colors.grey[600] |
Colors.grey[300] |
| 薄い表示 | Colors.grey[400] |
Colors.grey[500] |
背景・ボーダー
| 用途 | ライトモード | ダークモード |
|---|---|---|
| カード背景 | Colors.white |
#1E1E1E |
| ダイアログ背景 | Colors.white |
#2C2C2C |
| ボーダー(通常) | Colors.grey[300] |
Colors.grey[700] |
| ボーダー(強調) | Colors.grey[400] |
Colors.grey[600] |
3. コンポーネント別パターン
ダイアログ選択肢(SimpleDialogOption)
Icon(
isSelected ? Icons.check : Icons.circle_outlined,
color: isSelected
? (Theme.of(context).brightness == Brightness.dark
? const Color(0xFF64B5F6) // ダークモード用の明るい青
: Theme.of(context).primaryColor)
: Colors.grey[400],
)
FilterChip(フィルタチップ)
FilterChip(
selected: isSelected,
selectedColor: Theme.of(context).brightness == Brightness.dark
? colorScheme.primary // #8AB4F6
: AppTheme.posimaiBlue,
side: isSelected
? BorderSide(
color: Theme.of(context).brightness == Brightness.dark
? colorScheme.primary
: AppTheme.posimaiBlue,
width: 1.5,
)
: null,
// 非選択時の明示的なスタイル指定
backgroundColor: isDark
? Colors.grey[800]?.withValues(alpha: 0.5)
: null,
side: BorderSide(
color: isDark
? Colors.grey[700]!
: colorScheme.outline.withValues(alpha: 0.5),
),
)
ボタン(AlertDialog内のElevatedButton)
ElevatedButton(
style: ElevatedButton.styleFrom(
// ダークモードでも見えるよう固定色を使用
backgroundColor: AppTheme.posimaiBlue, // #376495
foregroundColor: Colors.white,
),
child: const Text('確認'),
)
SnackBar
SnackBar(
content: Text(
'メッセージ',
style: TextStyle(
color: isDark ? Colors.white : Colors.white, // 明示的に白を指定
),
),
backgroundColor: isDark
? const Color(0xFF2C2C2C) // ダーク背景
: Colors.grey[850],
behavior: SnackBarBehavior.floating,
)
バッジカウンター
Container(
decoration: BoxDecoration(
color: Theme.of(context).brightness == Brightness.dark
? const Color(0xFF64B5F6).withValues(alpha: 0.15)
: Theme.of(context).primaryColor.withValues(alpha: 0.1),
border: Border.all(
color: Theme.of(context).brightness == Brightness.dark
? const Color(0xFF64B5F6).withValues(alpha: 0.4)
: Theme.of(context).primaryColor.withValues(alpha: 0.3),
),
),
child: Text(
'2 / 3',
style: TextStyle(
color: Theme.of(context).brightness == Brightness.dark
? const Color(0xFF64B5F6)
: Theme.of(context).primaryColor,
),
),
)
開発時のチェックリスト
新機能実装時
- ライトモードで表示確認
- ダークモードで表示確認(必須)
- 以下の要素が見えるか確認:
- テキスト(タイトル、本文、ラベル)
- アイコン(選択状態、非選択状態)
- ボーダー・区切り線
- ボタン(通常、選択、無効)
- カウンター・バッジ表示
コードレビュー時
Theme.of(context).primaryColorの使用箇所でbrightness チェックがあるかconst TextStyle()に明示的な color 指定があるかColors.greyのような曖昧な色ではなく、明示的な階調(Colors.grey[300]等)を使用しているか
よくある問題と解決策
問題1: チェックマークが見えない
原因: Theme.of(context).primaryColor がダークモードで薄くなる
解決: 明示的に #64B5F6 を指定
問題2: バッジカウンターが見えない
原因: 背景色とテキスト色のコントラストが不足
解決: ダークモード用に明るい色 (#64B5F6) とアルファ値を調整した背景を使用
問題3: ボタンが見えない
原因: Theme.of(context).primaryColor が背景と同化
解決: 固定色 AppTheme.posimaiBlue (#376495) を使用
問題4: SnackBarメッセージが読めない
原因: テキスト色が明示されておらず、デフォルトが暗い色になる
解決: color: Colors.white を明示的に指定
色のコントラスト比基準
WCAG 2.1 AA基準(最低限):
- 通常テキスト: 4.5:1 以上
- 大きいテキスト(18pt以上、14pt太字以上): 3:1 以上
推奨ツール:
実装例リファレンス
実装済みファイル:
lib/screens/soul_screen.dart: ダイアログチェックマークlib/widgets/gamification/badge_case.dart: バッジカウンターlib/widgets/settings/backup_settings_section.dart: ダイアログボタンlib/screens/camera_screen.dart: SnackBarメッセージlib/widgets/home/sake_filter_chips.dart: フィルタチップ
更新日: 2026-01-18 バージョン: 1.0