Next.js×TypeScript×CSS Modules×Sass×sass-mq

2022-04-23

導入にかなり苦戦したのですが、ようやく納得の行く形で組めたので書いていこうと思います。


前提

実装

breakpoints.scss

まずはブレークポイント(src/styles/breakpoints.scss)をば。

$breakpoints: (
  vga: 640px,
  svga: 800px,
  xga: 1024px,
  uxga: 1600px,
);

今回はわかりやすく書くために、すべて上書きしています。

mq-settings.scss

つぎに設定ファイル(src/styles/mq-settings.scss)です。

@use "styles/breakpoints" as *;

$show-breakpoints: (vga, svga, xga, uxga);

@use "sass-mq" with (
  $breakpoints: $breakpoints,
  $show-breakpoints: $show-breakpoints
);

mq.scss

大事なルートファイル(src/styles/mq.scss)となります。

@use "styles/breakpoints" as *;

@forward "sass-mq" with (
  $breakpoints: $breakpoints
);

ここで forward を使えば良いことにやたら時間を取られました…。

_app.tsx

Next.js でブレークポイントの表示の出し分けを行います。

import type { AppProps } from "next/app";

if (process.env.NODE_ENV !== "production") {
  require("styles/mq-settings.scss");
}

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

export default MyApp;

本番環境では表示したくないので、動的にインポートを行うようにしています。

next.config.js

webpack を介して Sass の設定を上書きします。

const path = require("path");

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  sassOptions: {
    additionalData: async (content, { resourcePath }) => {
      if (resourcePath.endsWith("mq-settings.scss")) {
        return content;
      }

      return "@use 'styles/mq' as mq;" + content;
    },
    includePaths: [path.join(__dirname, "src/styles")],
  },
};

module.exports = nextConfig;

当初 additionalData でなく prependData で実装していたのですが。

prependData の場合はパスによる出し分けが行えないようです。(Next.js の仕様?)

*.module.scss

てきとうな CSS Modules ファイルになります。

.hoge {
  @include mq.mq($until: xga) {
    color: red;
    font-size: 3rem;
  }

  @include mq.mq($from: xga) {
    color: blue;
    font-size: 4rem;
  }
}

今回は next.config.jssrc/styles/mq.scss を読み込むようにしているので、そのまま mixin が書けるようにしています。

もちろん設定側で @use の処理を省いて、都度 @use "styles/mq" as mq と書いて読み込んでも問題ないです。

tsconfig.json

最後に TypeScript でスタイルシートの絶対パスによる読み込みを行えるようにしてあげます。

{
  "compilerOptions": {
    ...
    "baseUrl": "src",
    ...
    "paths": {
      "styles/*": [
        "styles/*"
      ]
    },
    ...
}

もちろん設定が必須ではないです、相対パス派はよしなにしていただければと。


実装に結構時間がかかったのですが @forward の仕様さえ把握してしまえばあっという間でした。