基本形
<div className="red">りんご</div>
<div className="blue yellow red">信号機</div>
通常のCSSファイルの読み込み
.red {
color: red;
}
import './styles.css'; // 通常のCSSファイルを読み込む
const Apple = () => {
return <div className="red">りんご</div>;
};
export default Apple;
// <div class="red">りんご</div> HTMLに反映されてスタイルが適用
{ }
の扱いについて
JSX内で{ }
を使った場合、以下はエラーになる
<div className={red}>りんご</div> // red が未定義(undefined)ならエラーになる
{ } の中身を定義せずに JSX 内で使うとエラーになる
{ } の中身はJavaScriptの式として評価される
→ 変数や関数の値として解釈される
const red = "red"; // 変数red を 文字列 "red" で定義
<div className={red}>りんご</div> // <div class="red">りんご</div>
以下、3つはOK
<div className={'foo'}>シングルクォート</div> // OK
<div className={"foo"}>ダブルクォート</div> // OK
<div className={`foo`}>バッククォート</div> // OK
バッククォート(`)で囲まれた文字列はテンプレートリテラルとして扱われ
${ } 構文で変数や式の結果を埋め込める。
const className = "foo";
<div className={`${className} bar`}>OK</div> // <div class="foo bar">OK</div>
CSS ModulesとclassName
ハードコーディングは適用されない?
.red {
color: red;
}
import styles from './styles.module.css'; //CSS Modules形式
const Apple = () => {
return (
<div className="red">りんご</div> // ハードコーディングはスタイルが適用されない
);
};
export default Apple;
CSS Modulesの場合
className="red"
のようなハードコーディングはスタイルが適用されないので注意
import styles from './styles.module.css';
const Apple = () => {
return (
<div className={styles.red}>りんご</div> // CSS Modulesではオブジェクト経由で指定
);
};
export default Apple;
CSS Modulesの場合、ビルド時にハッシュ値を含むユニークな名前に変換されるため、className="red"
のようなハードコーディングだと一致しなくなる。
参考1)添付は本番ビルド
npm run dev(開発環境)
、npm run build(本番ビルド)
といったビルド時の設定やファイル内容の変更で命名表記が変わる場合がある
参考2)next.config.js
で必要に応じて localIdentName を変更することで、クラス名の命名形式をカスタマイズできる
CSS Modulesの特徴
-
ローカルスコープ:
コンポーネントごとにスタイルをスコープ化し、他のコンポーネントとスタイルが競合するのを防ぐ -
ユニークなクラス名:
ビルド時にクラス名がハッシュ値を含んだ一意に変換され競合を防ぐ -
Next.jsサポート:
Next.jsではデフォルトでCSS Modulesがサポートされており、.module.css
拡張子のファイルを使用することで自動的に認識される
ハイフンを含むクラス名はどうする?
.red-text {
color: red;
}
import styles from './styles.module.css';
const Apple = () => {
return (
<div className={styles.red-text}>りんご</div> // ❌ エラーになる
);
};
export default Apple;
CSS Modulesでハイフンを含むクラス名を指定する場合
ブラケット記法(styles['red-text'])でオブジェクトのキーを文字列で指定する。
import styles from './styles.module.css';
const Apple = () => {
return (
<div className={styles['red-text']}>りんご</div>
{/* ハイフンがある場合は文字列キーでアクセス */}
);
};
export default Apple;
三項演算子で動的なclassName
条件に応じて、クラス名を動的に変更したい場合、三項演算子を使う。
<div className={isRed ? 'red' : 'green'}>りんご</div>
// isRed が true なら、<div class="red">りんご</div>
// isRed が false なら、<div class="green">りんご</div>
短絡評価(&&)とclassName
短絡評価(&&)は、左辺が truthy の時に右辺を評価して返す
const result1 = true && 'red'; // "red"
const result2 = false && 'red'; // false
const result3 = 'blue' && 'red'; // "red" → 非空文字列は truthy
<div className={isRed && 'red'}>りんご</div>
isRed === true の場合
<div class="red">りんご</div>
isRed === false の場合
<div>りんご</div> //クラス属性が表示されない
短絡評価(&&)では false, null, undefined はレンダー時に無視されるため、
属性が表示されない。
classNameに短絡評価(&&)の左辺に数値を置かない
const result4 = 0 && 'item'; // 0 (0はfalthyな値)
const result5 = 1 && 'item'; // "item" (1はtruthyなので右辺が返る)
短絡評価(&&)の左辺に数値が入るような時
-
0
の場合 → その式全体が0
という値に評価されるため、意図せず0
が表示されてしまう -
0
以外の数値の場合 → 常に左辺が truthy扱いで表示が固定される
<div className={itemCount && 'item'}>りんごの個数</div>
// 短絡評価(&&)の左辺が 0 の場合 <div class="0">りんごの個数</div>
// 短絡評価(&&)の左辺が 0 以外の数値の場合 <div class="item">りんごの個数</div>
短絡評価(&&)では false, null, undefined はレンダー時に無視されるため、
属性が表示されないが、0は実際そのものが表示されてしまう
解決方法①:itemCount が 0 でない場合のみクラスを適用
<div className={itemCount ? 'item' : ''}>りんごの個数</div>
解決方法②:&& の前に itemCount !== 0 (もしくは、itemCount > 0 )を追加
<div className={itemCount !== 0 && 'item'}>りんごの個数</div>
// もしくは、<div className={itemCount > 0 && 'item'}>りんごの個数</div>
classNameで短絡評価(&&)を使うと意図しない結果になる場合があるため、使用は避ける方がいいかもしれません
テンプレートリテラルとclassName
テンプレートリテラルの中で ${変数}
を使う事で、変数の値を文字列に埋め込める
クラス名の一部に付与する
const PrimaryBtn = () => {
const color = 'primary'
return <button className={`btn btn-${color}`}>Primary</button>
}
// <button class="btn btn-primary">Primary</button>
要素に複数のクラス名を付与したい場合
const ColoredButton = ({ isActive }) => {
const bgColor = isActive ? "blue" : "gray"; // クラス名を動的に変更
const textColor = "white" // クラス名を再利用
return (
<button className={`${bgColor} ${textColor}`}>Colored Button</button>
)
}
// isActive が true なら <button class="blue white">Colored Button</button>
// isActive が false なら <button class="gray white">Colored Button</button>
補足1)join(' ')
を使っても同様の結果になる
const ColoredButton = ({ isActive }) => {
const bgColor = isActive ? "blue" : "gray";
const textColor = "white"
return (
<button className={[bgColor, textColor].join(' ')}>Button</button>
)
}
// isActive が true なら <button class="blue white">Button</button>
// isActive が false なら <button class="gray white">Button</button>
補足2)クラスが増えると可読性が悪くなるため、配列の変数に定義を分けて組み立てる
const ColoredButton = ({ isActive, isLarge }) => {
const classes = [
isActive ? 'bg-blue-500' : 'bg-gray-300', // 三項演算子で動的に色を決定
isLarge ? 'text-xl' : 'text-sm', // 三項演算子で動的にサイズを決定
'rounded', // 固定
'p-4' // 固定
];
return (
<button className={classes.join(' ')}>Button</button>
);
};
固定クラス+条件付きクラス
<div className={`blue ${isBoolean ? 'yellow red' : ''}`}>信号機</div>
// trueなら、<div class="blue yellow red">信号機</div>
// falseなら、<div class="blue ">信号機</div>
falseの場合、開発者ツール上では、以下の画像の様に
<div class="blue ">
となり、blue
の後に空白ができる。
テンプレートリテラルでは、空白や改行はそのまま生かされてしまう
しかし、ブラウザが「クラス名は blue のみ」と判断するので、特に気にしなくてよい。
開発者ツールで末尾に空白が残るが、HTMLの仕様上、空白は無視される。
→ class="blue "
も class="blue"
と同じクラスとして扱われる
参考)document.querySelector('.blue ')として、'.blue '
と書いた場合でも、ブラウザは'.blue'
として扱う。
const el = document.querySelector('.blue ');
console.log(el.className); // "blue"
↓ もし、気になり空白部分を削除したいなら
解決方法①:空白部分も考慮して組み込む
<div className={`blue${isBoolean ? ' yellow red' : ''}`}>信号機</div>
blue ${isBoolean
→ blue${isBoolean
'yellow red'
→ ' yellow red'
として、空白部分を考慮する。
解決方法②:trim()で両端の余白を削除
<div className={`blue ${isBoolean ? 'yellow red' : ''}`.trim()}>信号機</div>
テンプレートリテラルで処理した後に trim() を適用する。
String.prototype.trim() → 両端からホワイトスペースを取り除く
onClickでクラス名の切り替え
onClick イベントで className を切り替える方法。useState で状態管理し、その状態に基づいてクラス名を動的に変更する。
import React, { useState } from "react";
import "./styles.css";
const App = () => {
const [active, setActive] = useState(false);
const classToggle = () => {
setActive(!active) // 切り替え
}
return (
<div>
<h1 className={active ? "red" : ""}>りんご</h1>
<button onClick={classToggle}>切り替え</button>
</div>
);
}
export default App;
.red {
color: red;
}
- 初期状態では
active
は false なので、h1 要素にはクラス名が付与されない
→<h1>りんご</h1>
- ボタンをクリックすると、classToggle 関数が実行され、
active ? "red" : ""
は、active が true になる →<h1 class="red">りんご</h1>
- 再度、クリックすると、
active
は再び false に戻りクラスが付与されない状態に戻る
→<h1>りんご</h1>
className のリスク管理
className に falsyな値(false, 0, "", null, undefined, NaN など)
を直接渡すと、HTMLのクラス属性に空白が含まれたり、不要なクラスが適用されて予期せぬ結果を引き起こす可能性がある。これを回避するはに、filter メソッドを使用する事で falsy な値を除外し対応できる。
filter メソッドの基本的な使い方
filter メソッドは、配列を元に新しい配列を作成するメソッドで、コールバック関数の戻り値が true の要素だけが新しい配列に含まれる。
const arr = [1, 2, 3, 4]
const newArr = arr.filter((v) => v % 2 === 0)
console.log(newArr); // [2, 4]
① filter(v=>v)
filter メソッドのコールバック関数で true の要素を返す
const randomArr = [1, undefined, false, 4, null]
const newRandomArr = randomArr.filter(v => v)
// null, undefined, false は true の要素ではないため新しい配列には含まれない
console.log(newRandomArr) // [1, 4]
② filter(Boolean)
filter メソッドに Boolean 関数をコールバック関数として渡すことで、配列から falty な値を除外する。
filter(Boolean)
Boolean 関数が、falsyな値(false
, 0
, ""
, null
, undefined
, NaN
など)をすべて false に変換するため除外できる
const randomArr = [1, undefined, false, 4, null]
const newRandomArr = randomArr.filter(Boolean)
console.log(newRandomArr) // [1, 4]
filter メソッドは、配列から falsy な値を除外できるため、className に使う際に便利。
className に使用する時は ①、② どちらでも同じ結果になる。
const App = ({isBoolean}) => {
const arr = ["red", isBoolean && "big"]
return (
<div className={arr.filter(v => v).join(' ')}>りんご</div>
// もしくは、<div className={arr.filter(Boolean).join(' ')}>りんご</div>
);
};
// isBoolean が trueの場合、<div class="red big">りんご</div>
// isBoolean が falseの場合、<div class="red">りんご</div>
配列.filter(Boolean)
は 配列.filter(v=>Boolean(v))
と同じ結果でもある。
// function
const newArray = array.filter(function(v) {
return Boolean(v);
});
// アロー関数(returnあり)
const newArray = array.filter((v) => {
return Boolean(v);
});
// アロー関数(省略形)
const newArray = array.filter((v) => Boolean(v));
// 関数参照(最短形)
const newArray = array.filter(Boolean);
const randomArr = [1, undefined, false, 4, null];
const newRandomArr1 = randomArr.filter(Boolean); // [1, 4]
const newRandomArr2 = randomArr.filter(v => Boolean(v)); // [1, 4]
参考