【Sass】node-sassの終了アナウンスから、dart-sassを使う (2021/09)
Next.js(v11) /TypescriptとStorybookでsass導入と@useを使ったサンプル
Sass(Scss)のnode-sassは、2022年10月に終了のアナウンスがされています。現在の推奨はdart-sassです。
こちらの記事を参照
node-sassの終了に関しては前回の記事で、Sassの導入について、以下のコメを記載していましたが、node-sassの終了に伴い、dart-sass(yarn add sass)によりインストールをしていることが理由です。
sass-loaderに依存するNext.js(v11系)と、Storybook(v6.3.7)へ、「dart-sassの導入」と、「@useを使った簡易なSassコードのサンプル」(@importも使わなくなるため)をおこしています。
@importが使われなくなるため、時期をみて、(前の記事は)@useに修正するかなとは考え中です。
Sassにふりまわされたことが執筆のモチベーション✨
開発情報
- Macbook MacOS BigSur 11系
- docker
- node v16.3.0
実装リスト
- docker
- Next.js(v11)/TypeScript
- Sass(dart-sass) 🌟今回のメインテーマ
- Storybook(v6.3.7)
- Atomic design ※一部だけを構成
- jest ※参考です
導入は自己責任で(お約束文)
全体のステップ
- STEP_1 Dockerの環境構築
- STEP_2 Next.jsの導入 + テスト開発用(jest)の導入
- STEP_3 TypeScriptの導入
- STEP_4 Sass(dart-sass)の導入
- STEP_5 Storybookの導入
- STEP_6 sassを@useを使った簡易モック
- おまけ next/imagesをStorybookで使う
STEP_1 Docker環境の構築
初期フォルダの構成はこちらです。(参考)
ディレクトリ、ファイルの作成
mkdir -p docker/dart_sass
touch docker/dart_sass/Dockerfile
touch docker-compose.yaml
1-0. Dockerfileとdocker-compose.yamlの編集
FROM node:16.3-alpine
RUN yarn install && yarn global add \
create-react-app
WORKDIR /dart_sass
version: '3.8'
services:
dart_sass:
container_name: dart_sass_next_container
build: ./docker/dart_app
ports:
- 13050:3000
- 16056:6006
volumes:
- ./dart_sass:/dart_sass
stdin_open: true
tty: true
environment:
- TZ=Asia/Tokyo
networks:
- default
networks:
default:
dockerステータスのチェックコマンド(参考)
docker images -a
docker ps -a
docker-compose ps -a
1-1.コンテナのビルド
docker-compose build --no-cache
※--no-cacheは念のため
1-2.コンテナの起動
docker-compose up -d
1-3.コンテナに接続
docker exec -it dart_sass_next_container sh
STEP_2 Next.js + テスト開発用(jest)の導入
2-1.create-next-app
npx create-next-app .
2-2. jestの設定
- PKGのインストール
yarn add -D jest @testing-library/react @types/jest @testing-library/jest-dom @testing-library/dom babel-jest @testing-library/user-event jest-css-modules
開発に必要なものもインストールします。
yarn add axios msw swr
package.jsonファイルに、scriptsとjestを追記します。
"scripts": {
<省略>
"test": "jest --env=jsdom --verbose"
},
<省略>
"jest": {
"testPathIgnorePatterns": [
"<rootDir>/.next/",
"<rootDir>/node_modules/"
],
"moduleNameMapper": {
"\\.(css|scss)$": "<rootDir>/node_modules/jest-css-modules"
}
}
テスト用のディレクトリとファイルを作成します。
mkdir __test__
touch __test__/Home.test.tsx
Home.test.tsxファイルを編集します。
Nextの文字をテストする(参考)
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import Home from '../pages/index'
it('Should render text', () => {
render(<Home />)
expect(screen.getByText('Next')).toBeInTheDocument()
})
yarn test
2-3. .babelrcファイルの編集
- .babelrcファイルを作成します
touch .babelrc
- .babelrcファイルにpresetsをコーディングします
{
"presets": ["next/babel"]
}
STEP_3 TypeScriptの導入
3-1.tsconfig.jsonファイルの作成
touch tsconfig.json
3-2.PKGのインストール
yarn add -D typescript @types/react @types/node
3-3.Next.jsを起動
Next.jsを開始します
yarn dev
Next.jsのファイル編集
- pages/_app.jsと、pages/index.jsのファイル拡張子を.tsxに変更する
pages/_app.js
mv pages/_app.js pages/_app.tsx
pages/index.js
mv pages/index.js pages/index.tsx
- pages/_app.tsxを編集する
import { AppProps } from 'next/app'
import '../styles/globals.css'
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
- pages/index.jsを編集する
不要なコードを削除するのと、後ほど、CSS/Sassの動作テスト用に、classNameを付与したbuttonタグをコーディングしています。
const Home: React.FC = () => {
return (
<>
<h1>Next</h1>
</>
)
}
export default Home
STEP_4 Sassの導入
4-1.sassのインストール
dart-sassのインストールを行います。※sass(dart-sass)の導入としてはこれだけです。
yarn add sass
4-2.next.config.jsファイルの編集
こちら公式より、読み込むファイルのパス設定の掲載です。(公式)
const path = require('path'); //追加
module.exports = {
reactStrictMode: true,
// 以下を追加
sassOptions: {
includePaths: [path.join(__dirname, 'styles')],
},
}
{
"extends": [
"next",
"next/core-web-vitals",
"prettier",
"next/babel"
]
}
4-3.各PKGのインストール
- sass-loadrのインストール
yarn add -D sass-loader@^10
- 各種PKGのインストール
yarn add -D webpack @storybook/builder-webpack5 @storybook/manager-webpack5 css-loader style-loader babel-plugin-module-resolver
4-4.パス設定
babel-plugin-module-resolverによりパス設定を行います。
- .babelrcファイルの編集
"plugins": [
[
"module-resolver",
{
"root": [
"."
],
"alias": {
"@": "./"
}
}
]
]
- tsconfig.jsonファイルを編集
"compilerOptions": {
<省略>(以下を追記する)
"baseUrl": ".",
"paths": {
"@/": [
"./*"
]
},
4-5.Sassをテスト
Sassインストール後のファイル動作を少しだけテストします
CatNoseさんを参考にコーディング✨zennをありがとうございます
- styles/globals.cssファイルの拡張子をscssへ変更する
mv styles/globals.css styles/globals.scss
2. styles/globals.scssを、pages/_app.tsxファイルへimportする
pages/_app.tsx
import '../styles/globals.css' // 削除
import '../styles/globals.scss'
styles/globals.scssファイルを、:root{}で囲み、h1タグにvarをコーディングしています。
:root {
--c-sass: pink;
h1 {
color: var(--c-sass);
}
}
colorが変更されていればOKです!
STEP_5 Storybookの導入
5-1.Storybookのインストール
- sb initをします
npx sb init
- 作成されるstoriesは不要なので削除します。※必要な方は削除しなくてokです。
rm -r stories
5-2. 各ファイルの編集
- .storybook/main.jsファイルの編集
const path = require('path');
module.exports = {
"stories": [
"../components/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
'@storybook/addon-links',
'@storybook/addon-essentials',
],
core: {
builder: 'webpack5',
},
webpackFinal: async (config) => {
const rootPath = path.resolve(__dirname, '../');
config.resolve.alias['@'] = rootPath;
config.module.rules.push({
test: /\.scss$/,
sideEffects: true,
use: [
"style-loader",
{
loader: "css-loader",
options: {
importLoaders: 2,
},
},
{
loader: "sass-loader",
options: {
sourceMap: true,
},
},
],
});
return config;
}
}
- .storybook/preview.jsファイルへ、styles/global.scssをimportします
import '@/styles/globals.scss'
- package-jsonファイルへ、storybookのパス設定を追記します
"scripts": {
<省略>
"storybook": "start-storybook -s public -p 6006"
}
yarn storybookの前にモック用のコーディングを行います。
STEP_6 Sassの@useを使った簡易モック
モック用にボタン・コンポーネントをつくり、Sassのテストをします
6-1. 各コーディング
- ボタンコンポーネントの作成
mkdir -p components/atoms
mkdir -p components/atoms/types
touch components/atoms/Button.tsx
touch components/atoms/Button.stories.tsx // storybook用
touch components/atoms/types/Button.types.ts
Button.types.ts
export interface ButtonProps {
label: string,
onClick?: () => void,
}
Button.tsx
import { ButtonProps } from './types/Button.types'
import styles from '../../styles/components/atoms/Button.module.scss'
export const Button = ({
label,
...props
}: ButtonProps) => {
return (
<>
<div className={styles.button_sass_sample}>
<h1>sass</h1>
</div>
<div className={styles.button_container}>
<button>
{label}
</button>
</div>
</>
)
}
Button.stories.tsx
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Button } from './Button';
export default {
title: 'Example/Button',
component: Button,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof Button>;
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
export const PrimaryButton = Template.bind({});
PrimaryButton.args = {
label: 'sample',
};
- Sass(dart-sass)のモックファイルを作成
mkdir -p styles/components/atoms/Button.module.scss
touch styles/_variables.scss
sassの@useサンプルコーディング
①styles/_variables.scssに変数を用意
②styles/components/atoms/Button.module.scssで「@use」で変数を使用
_variables.scss
$sass-test: red;
$sass-test_two: blue;
Button.module.scss
@use '../../variables' as v;
.button_sass_sample {
background-color: v.$sass-test;
}
.button_container {
background-color: v.$sass-test_two;
width: 100px;
height: 50px;
}
これをButton.tsxをimportした、pages/index.tsxで動作を確認します。
import { Button } from '../components/atoms/Button'
const Home: React.FC = () => {
return (
<>
<div>
<h1>Next</h1>
<Button label={"sample-scss"} />
</div>
</>
);
}
export default Home
6-2. Storybookの起動
Storybookを起動します。
yarn storybook
"plugins": [
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
],
[
"@babel/plugin-proposal-private-methods",
{
"loose": true
}
],
[
"@babel/plugin-proposal-private-property-in-object",
{
"loose": true
}
],
<省略>
]
実行結果
Next.js
Storybook上
どちらでもSassの@useを使うことでスタイルが変化していればOKです!
記事が参考になります。
Sassのふかい使い方はこちらの学んで別記事に起こせるように、いつか(きっと)
長文でしたが、最後まで読んでいただき、ありがとうございました。どなたかの助けになりますように。
おまけ
next/imagesを使用できるようにする
Next.js(v11)でnext/imagesを、Storybookで使うための設定です。
エラーばかりにあい、かなりハマりました。こちら参考リンクに感謝です。
- モック用のディレクトリとファイルを作成
mkdir -p .storybook/__mocks
touch .storybook/__mocks/NextImage.tsx
- NextImage.tsxファイルを編集
import * as nextImage from "next/image"
Object.defineProperty(nextImage, "default", {
configurable: true,
value: props => {
const { width, height } = props
const ratio = (height / width) * 100
return (
<div
style={{
paddingBottom: `${ratio}%`,
position: "relative",
}}
>
<img
style={{
objectFit: "cover",
position: "absolute",
minWidth: "100%",
minHeight: "100%",
maxWidth: "100%",
maxHeight: "100%",
}}
{...props}
/>
</div>
)
},
})
- .storybook/preview.jsファイルへimport
import '@/styles/global.scss'
import './__mocks/NextImage'
- コーディング例
import NextImage from 'next/images'
import icon from '../public/vercel-icon.svg'
<NextImage src={icon.src} alt="next/images" height="50px" width="50px" />
next/imagesがStorybookでも利用できるようになります。
が・・・Storybook上で、.png、.svgを表示することができません。(どなたか・・・教えてください)
Discussion