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」
このように責務をはっきりと分けることで秩序が保てます。
やり過ぎ感を感じるかもしれませんが、
実際にサイトを作成・運営するとこの秩序がとてもありがたく感じます。
作っていくとどうしても混乱してしまうんですよね、、。
そして帳尻合わせをしていってどんどん壊れる、、
だからこういうルールは大事です。
まずはこの最低限の構成で実際に動くかというのを実感してみて、
そのうえで次に行くのが良いかなと思います。
今回は以上です!