SCSS のブレイクポイントの変数を React で使いたかったけど読み込ませるのにハマったのでメモ。
要件
SCSS の変数を JS で読み込んで使う
検索して出てきたサイトを参考に次のように作成しました。
- SCSSの変数をexportしてJavaScript側で読み取ってメソッドで利用する|コーディングブログ|tacs-port
- Share SCSS Variables with Javascript - Today I Learned
- Use Sass Variables In Typescript & Javascript | Matt Ferderer
Webpack の設定
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.(css|scss|sass)$/, use: ["style-loader", "css-loader", "sass-loader"], }, ] }, }
JS で使いたい変数の scss ファイル
// _variables.scss $breakpoints: ( sm: '576px', md: '768px', lg: '992px', ); // JS で読み込むための設定を追加 :export { BreakPoints: $breakpoints }
SCSS の変数を使う JS
import BreakPoints from './styles/_variables.scss'; const { sm, md, lg } = BreakPoints;
Error: isn't a valid CSS value なエラー
サイトを表示してみると次のようなエラーが表示されていました。
SassError: (sm: '576px', md: '768px', lg: '992px') isn't a valid CSS value. >> $grid-breakpoints: ( -------------------^
😇
Sass 構文をチェックできる SassMeister | The Sass Playground! に SCSS ファイルの内容を貼ってみると、BreakPoints: $breakpoints
の部分でエラーになっているようでした。
:export
は CSS Modules の書き方で、 Sass の構文ではないから Sass のコンパイルでエラーになっていた
Unfortunately the SASS/SCSS documentation does not mention anything about
export
, and as such, other than guessing and solving it through trial and error, your best bet is to migrate to SCSS and implement the solution that is widely used (but also not documented).
Personally, I like to import my SCSS variables, and then, export them to JS.
NOTE: It is vitally important that_export.scss
is only utilised in JS files, and is not imported in SCSS files. Importing_export.scss
into your SCSS files will result in a) unecessary style exports in your CSS, and b) duplications of these style exports throughout every compiled SCSS file
cf. vue.js - Cannot Export Sass Variables into Javascript - Stack Overflow
導入しようとしていたサイトは Sass を別途コンパイルしていたので、コンパイルするファイルの中に CSS Modules の構文が入っていたのが原因で Sass のコンパイルがコケてしまいエラーになっていたのが原因でした。
JS で import する SCSS を別のファイルにする
Sass のコンパイルに含まれない JS で使うための export 用の SCSS ファイルを作ってしまえばOK
_export-variables.scss
// JS で使いたい変数のあるファイルを import する @import "./_variables.scss"; :export { BreakPoints: $breakpoints }
wbpack でコンパイルエラー
これで Sass コンパイルのエラーはなくなった筈…
と思ったらまたエラーが表示されました。ぐぬぬぬぬぬ…
😇 😇
Error in webpack compile, details follow below: … Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: (sm: '576px', md: '768px', lg: '992px') isn't a valid CSS value. >> $grid-breakpoints: ( -------------------^
下半分は同じエラーのですが、今回はどうやら sass-loader でうまく処理ができなかったっぽい感じです。
map はそのまま CSS Module で export できないっぽい
map な $breakpoints
をそのまま JS に渡せばいい感じに Object にしてくれると思ったのですが、どうやらそうではなく map なままだとエラーになってしまうみたいです。
/ Define all colours / $theme-colours: ( some-color: #000, another-color: #000, third-color: #000, fourth-color: #000 ) @each $color, $value in $theme-colours { :export{ $color: $value; } }cf. https://stackoverflow.com/questions/56523785/exporting-scss-variables-to-js-auto-looping-variables
map を変改して個別の変数にしてしまいます。
_export-variables.scss
// JS で使いたい変数のあるファイルを import する @import "./_variables.scss"; @each $size, $value in $breakpoints { :export { #{$size}: $value; } }
これでコンパイルエラーは出なくなりました!
import した scss が空オブジェクト {}
になってしまう
もろもろ エラーは出なくなったのですが、今度は import してきた値の中身が空オブジェクト {}
になってしまう問題が発生しました…
😇 😇 😇 なんもわからん…
import BreakPoints from './styles/_export-variables.scss'; console.log(BreakPoints); // => {}
This is not a sass-loader issue, you just forgot to turn on CSS Modules in the css-loader.
cf. i got a empty object when import a sass file. · Issue #206 · webpack-contrib/sass-loader · GitHub
webpack の css-loader に modules
オプションが必要だった
webpack の設定の問題だったようです
webpack.config.js
module.exports = { // ... module: { rules: [ { test: /\.(css|scss|sass)$/, // "css-loader" を "css-loader?modules" に変更 use: ["style-loader", "css-loader?modules", "sass-loader"], }, ] }, }
これで JS で import した scss のオブジェクトに変数が入るようになりました!
import BreakPoints from './styles/_export-variables.scss'; console.log(BreakPoints); // => { sm: '576px', md: '768px', lg: '992px', … }
css-loaderはdefaultがglobal scopeで
:local(.className)
または:local{...}~ と宣言した場合にclassNameがハッシュ化されexportされる。 css-loaderの設定に
modulesオプションを渡すとdefaultでセレクタがscoped cssの対象となりハッシュ化され、逆に
:global` を宣言した場合にglobal scopeとして処理される。
cf. css-loaderでscoped cssを手に入れる - Qiita
:export
で囲っていると modules
オプションがないと、変数が取得できない形に変換されてしまっていたという事?
ちょっと理由が理解しきれてないです。
TypeScript の型の問題
無事 SCSS の変数を JS で読み込んで扱えるようになったのですが、プロジェクトは TypeScript なので今度は読み込んだ SCSS 変数の型の問題が発生です。
import BreakPoints from './styles/_export-variables.scss'; BreakPoints.sm; // Property 'sm' does not exist on type 'typeof import("*.scss")'
インポートしたスタイルのプロパティを使おうとしたら型エラーになりました。
😇 😇 😇 😇 知ってた…
型定義ファイルの作成
そもそも型定義がないので、型定義ファイルを作成します。
型定義ファイルは、{import するファイル名}.d.ts
という名前で作ればOKっぽい。
今回は _export-variables.scss
ファイルなので、型定義ファイルは _export-variables.scss.d.ts
とすれば良いみたい。
/types/styles/_export-variables.scss.d.ts
declare module "*.scss" { type BreakpointsType = { sm: string; md: string; lg: string; }; const BreakPoints: BreakpointsType; export default BreakPoints; }
import BreakPoints from './styles/_export-variables.scss'; BreakPoints.sm; // 👌
ESLint import/no-default-export error
コレで大丈夫かな。と、思ったらら ESLint で default import を禁止にしている import/no-default-export error
… 😇 😇 😇 😇 😇
型定義ファイルの export default BreakPoints;
この部分が問題のようです。
とは言え、SCSS からのインポートはデフォルトなので、export BreakPoints
, import { BreakPoints } from './styles/_export-variables.scss'
とすると BreakPoints
が undefined
になってしまいます。
export =
を使う
export default されているものをインポートする時のimport Express = require('express')
( インポート代入 ) の逆みたいな感じで、 export default XXX
を export = XXX
(エクポート代入) と書き換えることができるようです。
/types/styles/_export-variables.scss.d.ts
declare module "*.scss" { type BreakpointsType = { sm: string; md: string; lg: string; }; const BreakPoints: BreakpointsType; export = BreakPoints; // 👌 }
単に ESLint を誤魔化しているだけな気もしますが、これでコンパイルエラー、TypeScript / ESLint のエラーがなくなり、TypeScript で SCSS の変数が JS として扱えるようになりました!
₍ ᐢ. ̫ .ᐢ ₎👌 ヤッタネ
😇 😇 😇 😇 😇 1日潰れた
おわり。
[参考]
- SCSSの変数をexportしてJavaScript側で読み取ってメソッドで利用する|コーディングブログ|tacs-port
- Share SCSS Variables with Javascript - Today I Learned
- Use Sass Variables In Typescript & Javascript | Matt Ferderer
- webpackのcss-loaderでCSS Modulesをやる - Qiita
- map なままだと JS に export できない
- import した scss が空オブジェクト
{}
になってしまう - TypeScript CSS Module の型
- eslint: import/no-default-export error の解消
プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発
- 作者:Boris Cherny
- 発売日: 2020/03/16
- メディア: 単行本(ソフトカバー)
Paint it, Black (GRAPHICTION BOOKS)
- 作者:よむ
- 発売日: 2020/09/28
- メディア: 大型本