SCSSをトークン化してみた. 1

2025.12.23 10:00
2025.12.26 18:53
SCSSをトークン化してみた. 1

CSSを書いてて一番しんどいのって、
見た目そのものより後から直すときの怖さや、
整合性が合わずに帳尻合わせ地獄になるというのがありました。

例えば色や余白が、ページごとに微妙にズレるとか、
直す場所が分からない(同じ指定が点在してる)とか、
人が増えたときに、書き方が散らかる未来が見えるとかとか、、。

「うまく書けたCSS」じゃなくて、壊れにくい仕組みが欲しくなって色々調べたら、
SCSSの設計を トークン化するのがいいと知り、今回やってみました。

ゴール(今回の着地点)

今回の目的は「全部を綺麗にする」じゃなく。まずはここからはじめました。

  • 色・余白・文字サイズの参照元を1つにする
  • トークン以外の書き方を例外に落とす
  • 誰が書いても同じ方向に寄るルールを作る

これができたら、後の改善がめちゃくちゃ楽になる、、はず!

そもそも「トークン化」って何?

ここで言うトークンは、こんな感じ。

@use './primitives-color' as *;

$color-map: (

  // -------------------------
  // Text
  // -------------------------
  'text': (
    'default': $neutral-900,  // 通常テキスト(最も濃い)
    'sub': $gray-600,     // 補助テキスト
    'faint':  $gray-400,     // 控えめテキスト
    'disabled':  $gray-300,     // 無効状態
  ),
);

例えば文字の色や大きさを一箇所で管理することで、ばらつきを無くし、
デザイン的な統一感を出せるようにする仕組みの一つ。

数値が一定じゃなくバラバラだと、見づらさやダサさに繋がるんですよね。
それを各箇所でやるってのが意外と大変。
そういった問題を解決するための手法がトークン。

今回決めた前提(ここがルールの核)

1) Foundationにトークンを集約する

色・余白・文字など、参照元になる値はfoundationに置く。
コンポーネント側で数値を増やさない。

2) 参照はmixin経由にする(直アクセス禁止寄り)

さっきのトークンを使うための関数郡をmixinに書きます。
トークンはあくまで数値のリストであり、それだけでは使えないので、
それを実際に使えるようにするためにこのmixinを使います。

実際にはこんな感じで使いますが、詳しくは後ほど。

@include color('text', 'default');

3) 例外は“許す”けど、条件を決める

現実には「どうしても直書き」って出る。
だから禁止にしない。代わりに直書きOKの条件を決めるようにしました。

でもできる限りトークンに収める努力はします。

ファイル構成(最低限)

ここはプロジェクトに合わせて調整でOKですが、まずは最低限ということで以下の感じ。
まずはcolor-mapを動かすことに注力します。

assets/scss/
  component/
  foundation/
    _color-map.scss
    _index.scss
  layout/
  mixin/
    _color.scss
    _index.scss
  project/
  utility/
  vendor/

map系はfoundationに置き、実際の関数はmixinに置くきます。
なので、実際の作業時にはmixinディレクトリはほとんどみませんね。
どんなmapが合ったかの確認にfoundationを見ることがほとんど。

トークン設計:最小セット(例)

こんな感じでトークンを組んで行ってみます。
これは例えば文字を「defaut」という通常テキストカラーに指定する、と言った感じです。

.c-test {
  @include color('text', 'default'); // 通常テキスト(最も濃い)
}

.c-test {
  @include color('text', 'sub'); // 補助テキスト
}

.c-test {
  @include color('text', 'sub'); // 控えめテキスト
}

.c-test {
  @include color('text', 'disabled'); // 無効状態
}

    'default':   $neutral-900,  
    'sub': $gray-600,     
    'faint':  $gray-400,     
    'disabled':  $gray-300,     

これはたとえば「.c-test」というパーツで文字色を変えたい場合ですね。
「color」で「text」を「default」にして!ということですね。
そのためにはcolorを指定できるようにmixinを作ります。

// ==================================================
// Mixin/Function: color
// カラーマップから意味ベースで色を取得する
// ==================================================

@use 'sass:map';
@use '../foundation' as *;

@function color($group, $key) {
  @return map.get(map.get($color-map, $group), $key);
}

// 例:@include color('text', 'base');
@mixin color($group: 'text', $key: 'default', $property: null) {
  // プロパティの自動判定
  @if $property == null {
    @if $group == 'text' {
      $property: color;
    } @else {
      $property: color; // fallback
    }
  }

  $val: color($group, $key);

  @if $val == null {
    @warn "[color] No value found for (#{$group}, #{$key})";
  } @else {
    & {
      #{$property}: $val;
    }
  }
}

これで「color」を指定できるようになりました。
次に「text」と「default/sub/faint/disabled」のmapを作ります。

// ==================================================
// Foundation: color-map
// 色の意味・用途ベースで管理(セマンティック設計)
// - 値は _primitive-color.scss(Primitive)
// ==================================================

@use './primitives-color' as *;

$color-map: (

  // -------------------------
  // Text
  // -------------------------
  'text': (
    'default': $neutral-900,  // 通常テキスト(最も濃い)
    'sub': $gray-600,     // 補助テキスト
    'faint':  $gray-400,     // 控えめテキスト
    'disabled':  $gray-300,     // 無効状態
  ),
);

こんな感じです。
上記のようにmapを書いていくことで、colorで指定できるものが増えていきます。

ですが、ここであらたな変数が出てきました。
$neutral-900、$glay-600、$gray-400、$gray-300です。
これはまだない、、ということで用意してあげる必要があります。こんな感じです。

$neutral-900:  #3a2f29;
$gray-300:  #e6eaee;
$gray-400:  #cfd5db;
$gray-600:  #9ca2a9;

ここで初めて実際のカラーコードを入れます。
逆を言うと、色の指定はここでしかやっちゃだめだ、というルールになります。
それによって色が散らばってバランスが崩れることを防ぎます。
もし色を変えたい場合でも、ここだけを変えればサイト全体が変わるというわけですね。

では最後にそれぞれの_index.scssで読み込みを行います。

@forward './primitives-color';
@forward './color-map';
@forward 'color';

ここまでの仕組み、構造的には上のパーツが下のパーツを読み込みに行ってるって感じです。
逆はありません。これで混乱を防ぎます。

「コンポーネントパーツ(mixin/_color.scss)」-> 「_color-map.scss」 -> 「_primitives-color.scss」

このように責務をはっきりと分けることで秩序が保てます。
やり過ぎ感を感じるかもしれませんが、
実際にサイトを作成・運営するとこの秩序がとてもありがたく感じます。

作っていくとどうしても混乱してしまうんですよね、、。
そして帳尻合わせをしていってどんどん壊れる、、
だからこういうルールは大事です。

まずはこの最低限の構成で実際に動くかというのを実感してみて、
そのうえで次に行くのが良いかなと思います。

今回は以上です!