最近、自分のホームページ(兼ブログ)を作っているところですが、テーマカラーの切り替え機能を実装しました。ページにアクセスするたびに、ランダムにカラーテーマを選択して、ページをより活気づけるようにしました。以下のようになります:
これはどのように実現されているのでしょうか?まずはカスタムカラーから始めてみましょう。
WindiCSS のカスタムカラー#
固定のカラーを定義する#
// windi.config.js
export default defineConfig({
theme: {
extend: {
colors: {
primary: "#2196f3",
},
},
},
})
これにより、primary
というカラーが定義され、以降は通常通り使用できます(例:bg-primary
/ text-primary
)。
もちろん、文字列だけでなく、オブジェクトを使用してカラーグループを定義することもできます(例:bg-primary-light
)。
// windi.config.js
export default defineConfig({
theme: {
extend: {
colors: {
primary: {
extralight: "#d3eafd",
light: "#b2dafb",
medium: "#6ebbf7",
DEFAULT: "#2196f3"
},
},
},
},
})
CSS 変数の使用#
カラーを可変にするために、CSS 変数を使用すると便利です。WindiCSS でももちろんサポートされています。
:root {
--color-primary-extralight: #d3eafd;
--color-primary-light: #b2dafb;
--color-primary-medium: #6ebbf7;
--color-primary: #2196f3;
--color-primary-dark: #27415b;
}
// windi.config.js
export default defineConfig({
theme: {
extend: {
colors: {
primary: {
extralight: 'var(--color-primary-extralight)',
light: 'var(--color-primary-light)',
medium: 'var(--color-primary-medium)',
DEFAULT: 'var(--color-primary)',
dark: 'var(--color-primary-dark)',
},
},
},
},
})
これにより、WindiCSS で CSS 変数を基本的に使用できるようになります。ただし、少し問題があります。
WindiCSS は、カラーに透明度を設定することができます。例えば、bg-gray-800/80
やbg-gray-800 bg-opacity-80
のような書き方ができます。上記の設定方法では、このような構文が無効になってしまいます(透明度が失われます)。そのため、CSS 変数を別の形式に変更する必要があります。同時に、変数をラップするための高度なユーティリティ関数も必要です。
:root {
--color-primary-extralight: 211 234 253;
--color-primary-light: 178 218 251;
--color-primary-medium: 110 187 247;
--color-primary: 33 150 243;
--color-primary-dark: 39 65 91;
}
// windi.config.js
function withOpacityValue(variable) {
return val => {
if (val.opacityValue === undefined) {
return `rgb(var(${variable}))`
}
return `rgb(var(${variable}) / ${val.opacityValue})`
}
}
export default defineConfig({
theme: {
extend: {
colors: {
primary: {
extralight: withOpacityValue('--color-primary-extralight'),
light: withOpacityValue('--color-primary-light'),
medium: withOpacityValue('--color-primary-medium'),
DEFAULT: withOpacityValue('--color-primary'),
dark: withOpacityValue('--color-primary-dark'),
},
},
},
},
})
これにより、primary
カラーを使用するたびに、WindiCSS は関数を呼び出してスタイルを生成し、opacityValue
の判定によって透明度の構文をサポートします。
SCSS で CSS 変数を生成する#
明らかに、light
やextralight
などのカラーバリエーションに手動で色を指定することは現実的ではありません。また、RGB の 3 つの数字で色を表現する必要があり、エディタではハイライトが表示されず、直感的ではなく、メンテナンスが困難になります。
ここで、SCSS が役立ちます!SCSS は基本的な CSS データ型、条件分岐、ループ構文を提供しており、さまざまなユーティリティ関数(例:red()
blue()
green()
などのチャネル分離、mix()
などのカラーブレンド)も提供しています。
まず、渡された 16 進数のカラーを RGB の 3 つの数字に変換するユーティリティ関数を実装してみましょう。
@function getColorValue($color) {
@return #{red($color)} #{green($color)} #{blue($color)};
}
/* getColorValue(#2196f3) -> 33 150 243 */
私の予想では、primary
の基本色を指定するだけで、SCSS がlight
やextralight
などのカラーバリエーションをすべて生成してくれるはずです。mix
メソッドを使用して実現します。
@mixin spread-theme-map($map: ()) {
@each $key, $value in $map {
#{"--"+$key}: $value;
}
}
@function theme-primary-map($primary-color: #2196f3) {
@return (
color-primary-dark: getColorValue(mix($primary-color, black, 30%)),
color-primary: getColorValue($primary-color),
color-primary-medium: getColorValue(mix($primary-color, white, 70%)),
color-primary-light: getColorValue(mix($primary-color, white, 35%)),
color-primary-extralight: getColorValue(mix($primary-color, white, 15%))
);
}
/* spread-theme-map(theme-primary-map(#2196f3)) */
これにより、特定のカラーに対応するカラープロパティを生成できます。次に、必要なテーマカラーを配列に定義し、ループを回すだけです(Material Design のドキュメントからいくつかの鮮やかな色を選びました)。
$themeColorList: (
#2196f3,
#f44336,
#9c27b0,
#4caf50,
#3f51b5,
#795548,
#607d8b,
#009688
);
@for $i from 1 through length($themeColorList) {
$color: nth($themeColorList, $i);
.theme-#{$i} {
@include spread-theme-map(theme-primary-map($color));
}
}
VSCode では、次のように表示されます:
明らかに使いやすくなりました。
残りの作業取り消し線を引く#
テーマカラーを変更したい場合は、ルート要素(html
またはbody
)に対応するクラスを追加するだけで済みます(例:theme-1
/ theme-2
)。実現方法はさまざまですが、私は Nuxt.js を使用しているため、次のような解決策を採用しました。
const randomThemeColorIndex = useState('randomThemeColorIndex', () =>
Math.floor(Math.random() * themeColorList.length) + 1
)
useHead({
bodyAttrs: {
class: 'theme-' + randomThemeColorIndex.value,
}
})