CSS Variables 化とテーマプラグイン化で変わったこと

1. scssの構造・読み込まれる順番が変化した

  • テーマのプラグイン化に対応するため、テーマに関する scss が存在するディレクトリが分割された
    • 本体側 (/growi/packages/app/src/styles/theme)
      • GROWI の見た目に各テーマで定義された値を適用させるための scss
        • 参照が必要な scss
    • preset-themes (/growi/packages/preset-themes)
      • 各テーマごとの値が定義されている scss
        • 参照が必要な scss
  • これに伴い、各 scss の参照元や最終的に scss が読み込まれる順番が変化している
    • 先に読み込まれた scss の記述が強く、従来の記述のままだと変数の適用がうまくいかない場合がある
    • どうしても上書きされてしまう場合は、適用させたい変数側に!important を使用している

【v5までの scss が読み込まれる順番】

  • ライトモード

    1. 関数や mixin が指定された scss
    2. bootstrap 上書き scss
    3. テーマごとの scss(ライト)
    4. apply-colors.scss
    5. apply-colors-light.scss
  • ダークモード

    1. 関数や mixin が指定されたscss
    2. bootstrap 上書き scss
    3. テーマごとのscss(ダーク)
    4. apply-colors.scss
    5. apply-colors-dark.scss

【書き換え後の scss の読み込まれる順番】

  • 本体側

    1. 関数や mixin が指定された scss
    2. bootstrap 上書き scss
    3. hsl書き換え対応 scss
    4. apply-colors.scss
    5. apply-colors-light.scss
    6. apply-colors-dark.scss
  • preset-themes側

    1. hsl書き換え対応 scss
    2. テーマごとの scss(ライト)
    3. テーマごとのscss(ダーク)

2. 変数の書き方が変わった

  • SASS の変数から、CSS の var() を利用した変数指定に変更された

【書き換え前】

  • body の background-color は $primary に定義されている #175fa5 になる
  • 定義も適用も変数の書き方は変わらない
$primary: #175fa5; body { background-color: $primary; }

【書き換え後】

  • 定義の時は --変数名 、適用させる時は var(--変数名) と記述する
--primary: #175fa5; body { background-color: var(--primary); }
  • CSS の変数の値として SASS の変数を定義したい場合は、#{ }で SASSの変数を囲わないと上手く定義されない
    • SASS でなくて CSS の変数として扱いますよ〜という書き方らしい
$growi-blue: #175fa5; --primary: #{$growi-blue};

3. 特定の変数で、HSLによる色指定・変数を分解しての色指定が必須になった

  • SASS の変数からの卒業に伴い、一部の関数が使えなくなってしまった
  • それに伴い、今までカラーコードで指定されていた色が、特定の変数で HSLによる色指定 に置き換わった
    • 色相(“Hue”)、彩度(“Saturation”)、輝度(“Lightness”、もしくは“Luminance”)
  • そして、ただ hsl() で色を指定するだけではなく、hsとlを指定する値を分解して、それぞれを別の変数で指定して記載する必要がある

分解しての色指定が必須の変数

  • primary
  • secondaly
  • bgcolor-global
  • color-global
  • color-link
  • color-link-wiki
  • bgcolor-navbar
  • bgcolor-search-top-dropdown
  • bgcolor-sidebar
  • color-resize-button
  • color-sidebar-context
  • bgcolor-sidebar-context
  • その他上記の変数に代入される可能性がある変数

【書き換え前】

$primary: #175fa5;

【書き換え後】

  1. $primaryに定義されている色を HSL の形式に変換すると、hsl(216.7,65%,20.2%) となる
  2. HSL での色指定の数値を、--primary-hs--primary-l の2つの変数に分解して定義する
    • --primary-hs: 216.7,65%;
      • 色相と彩度
    • --primary-l: 20.2%;
      • 明度
  3. --primary-hs--primary-l の2つを、--primary で完全な HSL 形式の色として組み合わせて定義する
--primary: hsl(var(--primary-hs),var(--primary-l)); //--primary: hsl(216.7,65%,20.2%); と同じ --primary-hs: 216.7,65%; --primary-l: 20.2%;

4. 特定の変数を代入する関数の記述が変更になった

  • SASS の 変数からの卒業、そして特定の変数での HSL による色指定・変数を分解しての色指定が必須になったことに伴い、3で紹介された変数が代入される下記の関数は従来の表記では動かなくなってしまった
  • そこで、hslに適した独自関数を作成し、書き換えを行った

特定の変数が代入されている場合、独自関数に書き換えが必要な関数

  • SASS からの卒業で使えなくなってしまった関数
    • lighten()
    • darken()
    • color-yiq()
  • HSL による色指定・変数を分解しての色指定が原因のもの
    • rgba()

【書き換え前】

// lighten() body { color: lighten($primary, 50%); } $bgcolor-resize-button-hover: lighten($primary, 50%); // darken() body { color: darken($primary, 50%); } $bgcolor-resize-button-hover: darken($primary, 50%); // color-yiq() body { color: color-yiq($primary); } $bgcolor-resize-button-hover: color-yiq($primary); // rgba() body { color: rgba($primary, 50%); } $bgcolor-resize-button-hover: rgba($primary, 50%);

【書き換え後】

@use './theme/hsl-functions' as hsl; // lighten() body { color: hsl.lighten(var(--primary), 50%); } --bgcolor-resize-button-hover: #{hsl.lighten(var(--primary), 50%)}; // darken() body { color: hsl.darken(var(--primary), 50%); } --bgcolor-resize-button-hover: #{hsl.darken(var(--primary), 50%)}; // color-yiq() body { color: hsl.contrast(var(--primary), 50%); } $bgcolor-resize-button-hover: #{hsl.contrast(var(--primary), 50%)}; // rgba() body { color: hsl.alpha(var(--primary), 50%); } --bgcolor-resize-button-hover: #{hsl.alpha(var(--primary), 50%)};
  • まず、@use './theme/hsl-functions' as hsl;で、独自関数が定義された scss を読み込む
  • 関数名の前にhsl.をつけることで、hsl-functions.scss から関数を呼び出す
    • rgba() と color-yiq() は関数名も変更になっているので注意
  • 注意!
    • CSS の変数に定義する値として利用したい場合は、#{ }で囲わないと色が定義されない
    • 独自関数の中に独自関数で指定した色を代入することはできない!!!!!
      • エラーが起きます
// エラーが発生する例 body { color: hsl.alpha(var(--bgcolor-resize-button-hover), 50%); } --bgcolor-resize-button-hover: #{hsl.lighten(var(--primary), 50%)};

5. 特定の変数を代入する mixin の記述が変更になった

  • 「4. 特定の変数を代入する関数の記述が変更になった」の変更に伴い、特定の関数を利用する mixin も独自のものが必要になった
    • 独自の mixin を記述したscss を読み込んで、そこからの参照に変更する
  • ただし、独自関数に独自関数を代入できないという制約により、完全に再現できているわけではない
    • 独自 mixin に置き換えて表示がおかしくなる場合は、個別に値の指定が必要な場合もあるのでご了承ください

特定の変数が代入されている場合、参照元の変更が必要な mixin

  • hsl-button.scss から参照
    • button-variant
    • button-outline-variant
    • button-svg-icon-variant
  • hsl.badge.scss から参照
    • badge-variant

【書き換え前】

@use '../bootstrap/init' as *; .grw-global-search { .btn-secondary.dropdown-toggle { @include button-variant($bgcolor-search-top-dropdown, $bgcolor-search-top-dropdown); }

【書き換え後】

  • 参照元の scss が変更になっているので、hsl-button.button-variant と記述を変更する
@use './mixins/hsl-button'; .grw-global-search { .btn-secondary.dropdown-toggle { @include hsl-button.button-variant(var(--bgcolor-search-top-dropdown,) var(--bgcolor-search-top-dropdown)); }

6. テーマの CSS がめっちゃ軽くなった

  • HAPPY

【書き換え前】

+ First Load JS shared by all 312 kB └ css/2786c5d561ccc8c6.css 58.3 kB ├ λ /[[...path]] 19.2 kB 743 kB ├ └ css/955e61ea26c0fa66.css 4.92 kB vite v3.2.5 building for production... transforming... ✓ 15 modules transformed. rendering chunks... dist/themes/manifest.json 2.04 KiB dist/themes/assets/blackboard.2e1da1c4.css 125.77 KiB / gzip: 13.79 KiB dist/themes/assets/christmas.a19c4c16.css 120.16 KiB / gzip: 13.37 KiB dist/themes/assets/halloween.cc83ba1b.css 125.90 KiB / gzip: 13.96 KiB dist/themes/assets/antarctic.8848a6db.css 119.85 KiB / gzip: 13.36 KiB dist/themes/assets/island.221f98b0.css 119.97 KiB / gzip: 13.22 KiB dist/themes/assets/kibela.c348f9d8.css 119.44 KiB / gzip: 13.13 KiB dist/themes/assets/spring.02096d12.css 120.75 KiB / gzip: 13.29 KiB dist/themes/assets/wood.6838613c.css 120.15 KiB / gzip: 13.36 KiB dist/themes/assets/future.365177e1.css 125.77 KiB / gzip: 13.89 KiB dist/themes/assets/nature.e5987115.css 119.48 KiB / gzip: 13.16 KiB dist/themes/assets/mono-blue.4560e23a.css 319.53 KiB / gzip: 28.81 KiB dist/themes/assets/fire-red.9adfae87.css 318.81 KiB / gzip: 28.63 KiB dist/themes/assets/hufflepuff.1c1521a3.css 322.43 KiB / gzip: 29.25 KiB dist/themes/assets/default.663371d0.css 318.70 KiB / gzip: 28.80 KiB dist/themes/assets/jade-green.0a17a679.css 318.82 KiB / gzip: 28.61 KiB

【書き換え後】

+ First Load JS shared by all 345 kB └ css/5fac0b84842e6c84.css 91.4 kB ├ λ /[[...path]] 19.2 kB 743 kB ├ └ css/955e61ea26c0fa66.css 4.92 kB vite v3.2.5 building for production... transforming... ✓ 15 modules transformed. rendering chunks... dist/themes/manifest.json 2.04 KiB dist/themes/assets/antarctic.f527516b.css 5.23 KiB / gzip: 1.18 KiB dist/themes/assets/blackboard.09d080ee.css 4.48 KiB / gzip: 0.97 KiB dist/themes/assets/fire-red.d413ddc1.css 8.69 KiB / gzip: 1.17 KiB dist/themes/assets/christmas.de4b0ddf.css 6.19 KiB / gzip: 1.27 KiB dist/themes/assets/default.b5b034ac.css 8.48 KiB / gzip: 1.37 KiB dist/themes/assets/future.4da42c46.css 4.75 KiB / gzip: 1.06 KiB dist/themes/assets/halloween.7b4f86de.css 4.46 KiB / gzip: 1.03 KiB dist/themes/assets/jade-green.5702732d.css 8.40 KiB / gzip: 1.12 KiB dist/themes/assets/island.9050e01b.css 5.40 KiB / gzip: 1.15 KiB dist/themes/assets/hufflepuff.5450e298.css 13.32 KiB / gzip: 1.73 KiB dist/themes/assets/mono-blue.eca9a234.css 9.21 KiB / gzip: 1.35 KiB dist/themes/assets/wood.c8f0f209.css 5.87 KiB / gzip: 1.25 KiB dist/themes/assets/kibela.2799310f.css 4.90 KiB / gzip: 0.99 KiB dist/themes/assets/spring.fcd26fd5.css 5.96 KiB / gzip: 1.24 KiB dist/themes/assets/nature.f3b55429.css 4.35 KiB / gzip: 0.98 KiB

コラム:HSLでの色指定と分解の理由

1. lighten() /darken() への対応

  • SASS の関数の一つに、lighten() と darken() がある
    • 指定した色から何%明るくした or 暗くした色を指定できる便利関数
  • しかしこれが CSS には存在しない、なのに GROWI ではあらゆるところで利用されている
  • CSS で色の輝度を変更するには、輝度を数値で指定できる HSL の形式に変換し、計算しないといけない
  • そこで、hsl-function.scssというファイルで、CSSでも lighten() / darken() 相当の対応ができるように独自の関数を作成している

【書き換え前】

body { background-color: lighten($primary, 10%); } $bgcolor-resize-button-hover : lighten($primary, 10%);

【書き換え後】

@use './theme/hsl-functions' as hsl; body { background-color: hsl.lighten(var(--primary), 10%); } --bgcolor-resize-button-hover : #{hsl.lighten(var(--primary), 10%)};

おまけ:独自関数の中身

  1. hsl.lighten(var(--primary), 10%) と指定する
  2. 変数名だけを取り出し、--primary-hs--primary-l を作成する
  3. --primary-l で定義されている primary の色の輝度に、関数で指定された分の度数を足す
  4. --primary-hs と計算された新しい輝度を hsl() の中に入れることで、指定した度数分 primary から明るい色を指定することができる
    • darken は計算が逆になる
@function lighten($color, $degrees) { $color: bs.str-replace($color, 'var('); $color: bs.str-replace($color, ')'); $color-hs: var(#{$color+'-hs'}); $color-l: var(#{$color+'-l'}); @return hsl($color-hs, calc($color-l + $degrees)); }

2. color-yiq() への対応

  • bootstrap の SASS には color-yiq() というこれまた便利関数がある
    • 指定した色を背景等にした時に、文字色としてちょうど視認性の良い色を黒 or 白から選んでくれる
    • 例えば、テーマによって背景色が変わるボタンの文字色の指定として使われていた
    • 参考:カラーコントラスト external_link
  • しかしこれが CSS には存在しない、なのに GROWI ではあらゆるところで利用されている
    • しかもしかも、あらゆる場面で呼び出される mixin の中にも大量発生している
  • そこで、hsl-function.scssで、CSSでも color-yiq() 相当の対応ができるように独自の関数を作成している
    • ただし、関数の中身が異なるため、color-yiq() と全く同じ色指定は期待できないのは注意
    • ほぼ同じぐらい

【書き換え前】

  • body の color や、color-resize-button の値として、背景色である primary に映える文字色を自動で指定してくれる
    • テーマが変化してprimary に定義される色が変わると文字色も変化する!なんて便利なんでしょう
body { color: color-yiq($primary); background-color: $primary; } $color-resize-button : color-yiq($primary); $bgcolor-resize-button : $primary;

【書き換え後】

@use './theme/hsl-functions' as hsl; body { color: hsl.contrast(var(--primary)); background-color: var(--primary); } $color-resize-button : #{hsl.contrast(var(--primary)); $bgcolor-resize-button : var(--primary);

おまけ:独自関数の中身

  1. var(--primary) から 変数名だけを取り出し、--primary-hs--primary-l を作成する
  2. 謎の計算式で黒 or 白 を指定する
@function contrast($color, $darken-degrees: 0%, $alpha-degrees: 100%) { $color: bs.str-replace($color, 'var('); $color: bs.str-replace($color, ')'); $color-hs: var(#{$color+'-hs'}); $color-l: var(#{$color+'-l'}); @return hsla($color-hs, clamp(10%, calc((100% - $color-l - $darken-degrees - 51% ) * 1000), 95%), $alpha-degrees); }

Commments