より良いユーザー体験を求めて"カラー"を深掘る
多くの開発で以下のような課題に直面したことはありませんか?
-
Color.grey
や#F5F5F5
など、具体的なカラー値をハードコードしている - デザインと実装でカラー名の定義が異なる
- アクセシビリティを考慮したカラー設計になっていない
本記事では、カラーの設計方法やアクセシビリティのコードテストについて深掘りしていきます。
運用しやすいカラー設計を考える
エンジニアがデザイナーを巻き込んでカラーの設計を行なっていくにはどうすれば良いのでしょうか?
デザイン目線で議論するために、まずは2種類のカラーを見ていきます。
カラーを大きく分けると、「青色」「赤色」といった具体的な色と、「primary」「error」「background」といった意味のあるカラーの2つに分類できます。
それぞれをプリミティブカラーとセマンティックカラーと呼びます。
プリミティブカラー
プリミティブカラーとは、純粋に色の値だけを定義したもので、「青」「赤」といった具体的な色をカラーパレットとして用意します。
FlutterではMaterialColor
がプリミティブカラーに該当します。普段使っているColor.blue
なども実はMaterialColor
です。
Color.blue
は青色のカラーパレットのデフォルト値を指しています。
例えば、Color.blue[50]
のように、50~900までのパレットを参照できるようになっています。
セマンティックカラー
セマンティックカラーとは、用途や意味を持たせて定義されたカラーのことで、「primary」「error」「background」などが該当します。
マテリアルデザインにおいてもセマンティックカラーが使われており、例えばprimary
、onPrimary
、primaryContainer
というように役割が明確化されています。
「primary」が主要なカラーであるのに対し、「onPrimary」はその上に表示されるテキストカラーを示します。
よく使うFilledButton
の背景カラーはprimary
で、テキストカラーはonPrimary
です。
①on primary、②primary
プリミティブカラーとセマンティックカラーの関係
画面のデザインや、実装ではセマンティックカラーを使うのが理想です。
このセマンティックカラーはプリミティブカラーを参照したり、直接カラーコードを指定したりします。
const primaryColor = Colors.blue[600];
// もしくは、
const primaryColor = Color(0xFF0000FF);
セマンティックカラーを実装でも使う大きな利点として、コードの可読性だけでなくミスの発見しやすさがあります。
例えば、RGB値の16進数(例:#FF5733
)やプリミティブカラー(例:Colors.blue[700]
)を直接実装した場合、typoがあっても気づきづらいですが、theme.colors.primary
のようなセマンティックな命名を使うと、間違った使用(例:存在しないtheme.colors.eror
)をコンパイル時に検出できます。
デザインと実装のカラーを揃える
カラーの種類を見てきました。
その上で、Figmaのカラー定義と、実装のカラー定義を揃えると開発者体験が向上します。
具体的には、Figmaのカラー名を見るだけで、実装の際にどのカラーを使えば良いかがわかります。
Figmaのカラー定義。on primaryという命名がついているのがわかる。
このカラー名はVariablesという機能を使って管理されています。
FigmaのVariablesとは?
Figmaには実装でいうところのグローバル変数のようなVariables
という機能があります。
カラーはもちろん、数値、文字列、真偽値などの変数を管理できます。
FigmaのVariablesのカラー定義。
Variablesで定義したカラーは、実際のデザインで参照することができ、参照しているフレームにフォーカスすると、Variablesのカラーが表示されます。
このようにVariablesでの命名と実装の変数名を揃えることで、瞬時にどのカラーを使えば良いかわかります。
Variablesにはセマンティックカラーだけではなく、プリミティブカラーも定義されていることがあります。
Variablesのaliasという機能を使えば、セマンティックカラーをプリミティブカラーに紐づけることができます。
セマンティックカラーだけを実装するのか、プリミティブカラーも実装するのかはプロジェクトによると思います。
そのため、どこまでを実装すべきかはエンジニアとデザイナーで相談して進めるのが良いはずです。
そのためにも、Figmaの機能をエンジニアが知り、積極的にコミュニケーションを取ることが重要です。
Flutterで独自のカラーテーマを作る(ThemeExtensionを使う)
ここまではFigma側のカラーの管理方法を見てきました。
実装側のカラーはどのように管理していくのが良いでしょうか?
Flutterのカラーのテーマといえば Theme.of(context).colorScheme.primary
のように使います。
これはマテリアルデザインに沿ったテーマを使えます。
ただ、ほとんどのプロジェクトはマテリアルデザインのカラー設計に沿っていないのではないでしょうか?
独自のカラー設計に対応できるようなFlutterのカラーテーマはThemeExtension
を活用することで柔軟に設定できます。
class CustomColors extends ThemeExtension<CustomColors> {
final Color? primary;
final Color? onPrimary;
const CustomColors({required this.primary, required this.onPrimary});
CustomColors copyWith({Color? primary, Color? onPrimary}) {
return CustomColors(
primary: primary ?? this.primary,
onPrimary: onPrimary ?? this.onPrimary,
);
}
CustomColors lerp(ThemeExtension<CustomColors>? other, double t) {
if (other is! CustomColors) return this;
return CustomColors(
primary: Color.lerp(primary, other.primary, t),
onPrimary: Color.lerp(onPrimary, other.onPrimary, t),
);
}
}
// テーマの適用方法
final theme = ThemeData(
extensions: [
const CustomColors(
primary: Colors.blue,
onPrimary: Colors.white,
),
],
);
// 使い方
final customColors = Theme.of(context).extension<CustomColors>();
print(customColors?.primary);
アクセシビリティを意識したカラー設計
次はアクセシビリティを考慮したカラー設計について深掘りします。
カラーにおいてのアクセシビリティとはなんでしょうか?
また、実装で貢献できることはあるのでしょうか?
プロジェクトでどんな基準を設け、基準を守っていくためのコードテストを見ていきます。
WCAGの基準(A、AA、AAA)
WCAG(Web Content Accessibility Guidelines)はウェブアクセシビリティの基準を示したガイドラインです。
その中にはA、AA、AAAの3段階の基準があります。ほとんどのケースはAAを目指すことが多いのかなと思います。
*大きなテキストとは24 CSS px以上のテキストまたは18 CSS px以上の太字テキストのことです。
レベル | 説明 | 通常のテキスト | 大きなテキスト |
---|---|---|---|
レベルA | 最低限のアクセシビリティ基準。最低限満たすべきレベル。 | 3:1以上 | 3:1以上 |
レベルAA | 標準的に推奨される基準。多くの企業や組織で採用されています。 | 4.5:1以上 | 3:1以上 |
レベルAAA | 最高水準の基準。可能な限り高いアクセシビリティを目指した設計。 | 7:1以上 | 4.5:1以上 |
Flutterでのアクセシビリティテスト
アクセシビリティ観点のカラーは背景色とテキスト色のコントラスト比率だけではチェックできないケースもあります。
例えば背景色が同じでもテキストのサイズが大きい場合はコントラスト比率が低くてもアクセシビリティが高い場合があります。
Flutterでは、Widgetテストでアクセシビリティテストを実装できます。
textContrastGuideline
を使えば、最小テキストコントラストレベルを満たしているかチェックできます。
全ての実装でテストをすることは現実的ではありませんが、共通化されたコンポーネントなどでテストをすることで、実装側でアクセシビリティを担保できます。
import 'package:flutter_test/flutter_test.dart';
import 'package:your_accessible_app/main.dart';
void main() {
testWidgets('Follows a11y guidelines', (tester) async {
final handle = tester.ensureSemantics();
await tester.pumpWidget(const AccessibleApp());
// セマンティックノードが最小テキストコントラストレベルを満たしているかチェックします。
// 推奨されるテキストコントラストは、大きなテキスト(18ポイント以上の標準フォント)の場合
// 3:1以上です。
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
}
もっと深掘りしたい人へ!
長くなりましたが、読んでいただきありがとうございました!
カラーの設計段階からエンジニアが参加することで、開発体験だけではなくアクセシビリティなどのユーザーの体験も向上できます。
実は最近Zennの本を出しました!
この記事のように、技術視点から考えるUI/UXを深掘りしています!
もし興味を持っていただけたら、立ち読みしてもらえると嬉しいです。
また、自分もXでも情報発信しているので良かったらフォローよろしくお願いします。
おしゃべりしましょう!
「良いもの作るためならなんでもする」をモットーにSODAという会社で働いています!
カジュアル面談はエンジニアも出れるので、もし自分と話してみたいと思っていただけたらぜひ!

株式会社SODAの開発組織がお届けするZenn Publicationです。 是非Entrance Bookもご覧ください! → recruit.soda-inc.jp/engineer
Discussion