フロントエンド カラーミーショップ

デザイントークンが正しく利用できていることを開発サイクルで継続的に検査する

フロントエンド カラーミーショップ

ウェブアプリケーションにおける一貫性のあるUIを提供し、結果として快適なUXを実現するために、昨年からエンジニアとデザイナーでペパボ共通基盤デザインシステムである「Inhouse」のアプリケーションへの導入を推進しています。

この取り組みの中で、「デザイントークンがどの程度アプリケーションで活用されているか分からない」という課題が見えてきました。 導入の対象となるアプリケーションはコードの規模も大きいことから、既存のスタイルを壊さないように徐々に進めていく必要があります。 このため、日々の開発の中で置き換えを進めていくのですが、その速度を把握することは今後の導入の作戦を決定するためにも重要です。

また、デザイントークンの存在を知らないフロントエンドエンジニアやデザイナーが利用する機会を逃したり、本来の意図とは異なる使い方をする可能性もあります。

そこで、Stylelintを活用して、デザイントークンが正しく利用できていることをチェックするようにし始めました。

この取り組みによって、ルール違反件数の総数から導入の進捗が分かるようになり、かつ日々の開発の中でデザイントークンの存在を知ることができるようになると期待しています。

デザイントークンの利用をチェックするカスタムルール

InhouseのデザイントークンはStyle Dictionaryで記述されています。 今回対象となるアプリケーションはSassでスタイルを記述していますが、InhouseにはデザイントークンをSass Functionから取得する仕組みがあります。

たとえば、フォントサイズのデザイントークンを参照するには、このようにコーディングします。

@use "@pepabo-inhouse/adapter" as adapter;

.foo {
  font-size: adapter.get-font-size(m);
}

一方で、次のように記述されていた場合には、本来デザイントークンから取得すべき値がハードコードされていることになります。

.foo {
  font-size: 1.2rem;
}

つまり、あるSassのプロパティに対応する値をチェックし、デザイントークンを使うべき場面においてはルール違反であるというルールを記述すればよいことになります。

これは、例えば以下のようなカスタムルールによって検査できます。

import stylelint, { type Rule } from "stylelint"

export const ruleName = "stylelint-pepabo-design-standard/font-size-must-use-get-font-size"
export const messages = stylelint.utils.ruleMessages(ruleName, {
  // ルールに適合しない場合のメッセージでどのデザイントークンを利用すべきかを開発者に示す
  expected: "Expected font-size to use @function get-font-size($level: xs | s | m | l | xl | xxl)"
})

const rule: Rule = () => (postcssRoot, postcssResult) => {
  postcssRoot.walkDecls("font-size", decl => {
    // 値にデザイントークンを利用していることを検査する
    if (!decl.value.includes("get-font-size(")) {
      stylelint.utils.report({
        message: messages.expected,
        node: decl,
        result: postcssResult,
        ruleName
      })
    }
  })
}

rule.ruleName = ruleName
rule.messages = messages

export const fontSizeMustUseGetFontSize = stylelint.createPlugin(ruleName, rule)

ルールを効率よく記述する

ペパボではエンジニアやデザイナーがGitHub CopilotあるいはCursorを選択して利用できます。 これらのAIコーディングツールを用いて、自然言語で仕様を規定した上でコードを生成をして開発しています。

以下のようなコーディングルールをカスタムルールのリポジトリに含めます。

## Stylelintのルールの追加
以下の手順に従い、ルールを追加してください。

### 準備
`src/` 配下にルール名と同様のディレクトリをひとつ作成します。
ルール本体のファイルである `index.ts` および、テスト `index.test.ts` および、ルールを説明する `README.md` ファイルを作ります。

### READMEの記述
READMEには以下のフォーマットでルールを説明するマークダウンを記述します。

> # (rule name)
> ルールの概要をここに書く
>
> ## 例
>
> ### ❌ 無効
> (ここにルール違反のスタイルの例を書く)
>
> ### ✅ 有効
> (ここにルールをパスするスタイルの例を書く)

### ルールの記述
仕様を満たすようにルールを記述します。TypeScriptで書きます。以下に例を示します。

...(省略)...

このコーディングルールをコンテキストに含めて指示します。

@development.md を参考に、以下のルールのstylelintのルールを追加してください。

- `font-size` プロパティの値に `.get-font-size(` が含まれている必要がある

これは、`get-font-size` という、あるパッケージが提供するSass関数の利用を推奨するためのものです。それをふまえて実装してください。

Linterのインタフェースに合わせてルールをコーディングするという性質上、ある程度書きっぷりが決まっているということもあってか、安定した品質のコードを出力できている感覚があります。

このような取り組みを経て、デザイントークンを適用すべきケースを確認し、ルールをコードに落とし込むまでの一連の流れを エンジニアとデザイナーで開催している1時間程度のミーティングで完結できるようになりました。

効果

このような取り組みをした結果、以下のような効果がありました。

  • ルール違反の件数でデザイントークンへの置き換えの速度を把握できるようになった
  • ルールに落とし込む過程の議論によって、デザイントークンを適用すべきケースをより正確に把握できた
  • ルールを適用すべきケースがStylelintのルールから一目瞭然になった

当初解決したい課題だった「置き換えの進捗を把握する」、「デザイントークンが正しく利用されていることを検査する」を一部解決できたほか、 ルール化における議論の副産物として、適用すべきケースを誤解していたり、明確に決まっていない部分があるという課題が明らかになりました。

加えて、AIを活用した高速な実装によって、デザイントークンの適切な適用場面についての議論に時間を割くことができています。

ルールの適用について、現状はPull Request上において差分のあるファイルのみ検査することで、日々の開発で対応すべき範囲を狭めて運用しています。 取り組みを更に進めることで、最終的には、開発者がデザイントークンに熟知していなくても、仕組みによって一貫性のあるUIを提供できるようになることを目指していきたいです。