20
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マテリアルカラー、やめました。

Last updated at Posted at 2025-06-10

私たちはKotlinMultiplatformでアプリを開発しています。
クロスプラットフォームでの設計と実装を進める中で、UI設計において避けては通れないのが「デザインシステム」の選定です。

その中でもよく採用されるのが、Googleが提唱する「Material Design」。この記事では、

  • なぜマテリアルカラーを採用しなかったのか
  • 代わりにどのようにテーマを構築したのか

について、実体験をもとにお話しします。

1. そもそもマテリアルカラーとは?

マテリアルデザインは、Googleが提唱するデザインシステムで、UI設計におけるガイドラインやコンポーネント、そして配色ルールを提供します。

その中でも「マテリアルカラー」は、

  • primary(主要色)
  • secondary(補助色)
  • surfacebackground といった背景用の色

など、“役割ベース”のカラー命名 が特徴です。

💡 利用するメリットは?

  • UI全体に統一感が出る
  • デザイナーとエンジニアの共通言語になる
  • アクセシビリティ(視認性)の基準を満たしやすい

つまり、迷わず使える。そう、迷わなければ

2. なぜマテリアルカラーを使わなかったのか

正直なところ、最初はマテリアルカラーを使っていました。けれど、次第に違和感を覚えるようになったのです。

2.1 命名がフィットしない

たとえば、リンクの色。

「このリンクの色は primary(青)と同じ。でも、これをprimaryと呼ぶのは何か違う……」

マテリアルの命名は「役割ベース」ですが、私たちのUI設計は意味ベース だったのです。進捗バーの青、完了マークの緑、注意喚起の赤──色はその“意味”を伝えるためのものでした。

2.2 デザイン思想とのズレ

マテリアルデザインは、「色でユーザーを誘導する」思想に基づいています。ボタンやFABが浮かび上がり、アクセントカラーで行動を促す設計です。

でも、私たちのアプリでは、あくまで「コンテンツが主役」

色やUIが主張しすぎず、ユーザーがコンテンツに集中できる静かな世界観を重視していました。操作が直感的であれば、派手なボタンで誘導する必要はないのです。

その結果、マテリアルカラーは UI の意図と噛み合わず、命名や実装の両面で迷いが生じてしまったのです。

3. カスタムテーマ、こう実装しました

マテリアルカラーを使わないと決めた以上は、自分たちで配色ルールを設計する必要があります。そこで、以下の方針に基づいてカスタムテーマを実装しました。

3.1 カラースキームの構造定義

まずは、アプリ内で使う色をカテゴリごとに整理し、それぞれのスキーム(色設計)をデータクラスとして定義します。

data class ColorSchemes(
    val layout: LayoutColorScheme,
    val dialog: DialogColorScheme,
    val text: TextColorScheme,
)

data class TextColorScheme(
    val title: Color,
    val link: Color,
)
  • ColorSchemes は、全体のカラースキーマをまとめたエントリーポイント。
  • 各セクション(layout, dialog, text)で使う色をスキームごとに分けています。

3.2 明暗テーマ別のカラー定義

ライトモード/ダークモードに応じて、それぞれのテーマ用カラーを定義します。

fun lightTextColors(): TextColorScheme =
    TextColorScheme(
        title = Color(0xFF000000),
        link = Color(0xFF007AFF),
    )

fun darkTextColors(): TextColorScheme =
    TextColorScheme(
        title = Color(0xFFFFFFFF),
        link = Color(0xFF8AB4F8),
    )

lightLayoutColors()lightDialogColors() は省略していますが、同様に定義します。

3.3 テーマごとの具体的なカラースキームを構築

val CustomLightColors = ColorSchemes(
    layout = lightLayoutColors(),
    dialog = lightDialogColors(),
    text = lightTextColors(),
)

val CustomDarkColors = ColorSchemes(
    layout = darkLayoutColors(),
    dialog = darkDialogColors(),
    text = darkTextColors(),
)

3.4 グローバルで使えるテーマオブジェクトの提供

val LocalCustomColors = staticCompositionLocalOf { CustomLightColors }

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {
    val colors = if (darkTheme) CustomDarkColors else CustomLightColors
    CompositionLocalProvider(LocalCustomColors provides colors) {
        content()
    }
}

object CustomTheme {
    val colors: ColorSchemes
        @Composable
        get() = LocalCustomColors.current
}

  • MyAppTheme でテーマを切り替え、CustomTheme.colors.xxx として各所で使用可能になります。

3.5 UIへの適用例

Text(
    text = text,
    color = CustomTheme.colors.text.title,
)

このように、明確な意味に基づいた命名でカラーを管理することで、「primary か secondary か」といった迷いはなくなり、この色は何のためにあるのか がコード上でも明確になります。

おわりに

マテリアルカラーは非常に優れた仕組みです。しかし、それがすべてのプロジェクトにフィットするとは限りません。

UIに強い色の主張がいらない。意味で色を使い分けたい。そんなときには、自分たちのルールで配色を設計するのも、立派な選択肢です。

デザインと実装の“しっくりくる”を求めて、私たちはカスタムテーマを選びました。

同じように悩んでいる方の参考になれば嬉しいです。

20
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?