199 lines
6.6 KiB
Markdown
199 lines
6.6 KiB
Markdown
|
|
# ダークモード視認性ガイドライン
|
|||
|
|
|
|||
|
|
## 目的
|
|||
|
|
ダークモード実装時に「背景と同化して見えない」問題を防ぐための開発ガイドラインです。
|
|||
|
|
|
|||
|
|
## 基本原則
|
|||
|
|
|
|||
|
|
### 1. **色の明示的指定**
|
|||
|
|
❌ **NG**: `Theme.of(context).primaryColor` をダークモードで直接使用
|
|||
|
|
```dart
|
|||
|
|
// NG例: ダークモードでは #8AB4F6 になり、暗い背景で見えにくい
|
|||
|
|
color: Theme.of(context).primaryColor
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
✅ **OK**: brightness チェックで明示的に明るい色を指定
|
|||
|
|
```dart
|
|||
|
|
// 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)
|
|||
|
|
```dart
|
|||
|
|
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(フィルタチップ)
|
|||
|
|
```dart
|
|||
|
|
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)
|
|||
|
|
```dart
|
|||
|
|
ElevatedButton(
|
|||
|
|
style: ElevatedButton.styleFrom(
|
|||
|
|
// ダークモードでも見えるよう固定色を使用
|
|||
|
|
backgroundColor: AppTheme.posimaiBlue, // #376495
|
|||
|
|
foregroundColor: Colors.white,
|
|||
|
|
),
|
|||
|
|
child: const Text('確認'),
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### SnackBar
|
|||
|
|
```dart
|
|||
|
|
SnackBar(
|
|||
|
|
content: Text(
|
|||
|
|
'メッセージ',
|
|||
|
|
style: TextStyle(
|
|||
|
|
color: isDark ? Colors.white : Colors.white, // 明示的に白を指定
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
backgroundColor: isDark
|
|||
|
|
? const Color(0xFF2C2C2C) // ダーク背景
|
|||
|
|
: Colors.grey[850],
|
|||
|
|
behavior: SnackBarBehavior.floating,
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### バッジカウンター
|
|||
|
|
```dart
|
|||
|
|
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** 以上
|
|||
|
|
|
|||
|
|
推奨ツール:
|
|||
|
|
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
|
|||
|
|
- [Coolors Contrast Checker](https://coolors.co/contrast-checker)
|
|||
|
|
|
|||
|
|
## 実装例リファレンス
|
|||
|
|
|
|||
|
|
実装済みファイル:
|
|||
|
|
- `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
|