🌀

Vite+React+Emotionに、リセットCSSを導入する。

2022/02/28に公開

前回は、Viteで作成したRactアプリにEmotionを導入しました。
Vite+React+TypeScriptで、CSSスタイルについて調べて、Emotionに流れ着いた

今回は、リセットCSS( normalize.css、sanitize.css、 reset.css )のどれを取り入れようか迷ってsanitize.cssを選んだ経緯と導入方法についてです。

normalize.css、sanitize.css、reset.cssってそもそも何か?

ブラウザにはデフォルトCSSとよばれるあらかじめ適用されたスタイルがあります。

  • normalize.css
    デフォルトスタイルはブラウザごとにバグや微妙な差があり、この差を調整するのが normalize.css です。
  • sanitize.css
    sanitize.css は normalize.css を拡張したもので、ブラウザごとの差異を調整した上で、よく使うCSSを指定してくれています。sanitize.css と normalize.css は同期しています。
  • reset.css
    デフォルトスタイルをほぼすべてをリセットします。

まずはデフォルトのスタイルを normalize.css、sanitize.css、reset.css などで調整してから、アプリの土台となるCSSを書いていくと、ブラウザによって見え方が違うということが防げます。

https://www.npmjs.com/package/normalize.css
https://www.npmjs.com/package/sanitize.css
https://www.npmjs.com/package/reset.css

normalize.css、sanitize.css、reset.css どれを選べばいい?

好みの問題のようです。
normalize.cssを適用しつて、不要な要素だけ自分でリセットしたりするようです。

npm trendsで見てみると sanitize.css のインストール数が多いです。
normalize.css vs reset.css vs sanitize.css

また、githunを確認してみると、sanitize.css はよくメンテナンスされています。

emotion(Styled JSX)環境下で normalize.css を導入する方法

前回 emotion(Styled JSX) を採用したので、emotion 環境下で normalize.css を導入する方法です。
create-react-app で Reactアプリを作った場合、デフォルトで normalize.cssは入っていますが、vite init でReactアプリを作った場合、normalize.cssは入っていません。
まずは normalize.css をインストールします。

npm install normalize.css

次に App.tsx などでグローバルにスタイルにセットします。
String-Style で指定する方法です。

App.tsx
import { Global, css } from '@emotion/react'
import normalize from 'normalize.css'

const styles = css`
  ${normalize}
  body {
    color: hotpink; // ここにアプリ固有のベースになるスタイルを書く
  }
`
export function App() {
 return ( <Global styles={styles} />)
}

Object-Style で指定する方法です。

App.tsx
import { Global } from '@emotion/react'
import normalize from 'normalize.css'

// ここにアプリ固有のベースになるスタイルを書く
const styles = {
  body: {
    color: 'hotpink'
  }
}

export function App() {
  return(
  <>
    <Global styles={normalize} />
    <Global styles={styles} />
  </>
  )
}

動作確認

デフォルトスタイルでは article要素 や section 要素内で h1 のフォントサイズが効きません。
確認用にApp.tsを変更し動作確認してみます。

App.ts
<h1>デフォルトの h1 要素</h1>
<article>
  <h1>article内の h1 要素</h1>
</article>

article要素内のh1のフォントサイズがあきらかに小さいです。

normalize.css を 導入した結果です。
h1のフォントサイズが修正されています。

emotion に sanitize.css を導入する方法

sanitize.cssをインストールします。

npm install sanitize.css 

インストールすると下記の7つのcssがインストールされますので、必要なもののみ使用します。

sanitize.css

詳細
/* Document
 * ========================================================================== */

/**
 * 1.すべてのブラウザでbox-sizing: border-boxを追加する(opinionated)。
 * 2.デフォルトで背景を繰り返さないようにする(opinionated)。
 */

*,
::before,
::after {
  box-sizing: border-box; /* 1 */
  background-repeat: no-repeat; /* 2 */
}

/**
 * 1.すべてのブラウザでtext-decoration: inheritを追加する(opinionated)。
 * 2.すべてのブラウザでvertical-align: inheritを追加する(opinionated)
 */

::before,
::after {
  text-decoration: inherit; /* 1 */
  vertical-align: inherit; /* 2 */
}

/**
 * 1.すべてのブラウザでデフォルトのカーソルを使用する(opinionated)。
 * 2.すべてのブラウザで行の高さを変更する(opinionated)。
 * 3.すべてのブラウザでオーバーフローを防ぐために単語を区切る(opinionated)。
 * 4.すべてのブラウザで、タブ幅を4スペースにする(opinionated)。
 * 5.iOS のリンクのグレイハイライトを削除する(opinionated)。
 * 6.iOS で向きが変わってもフォントサイズが変わらないようにする。
 */

:where(:root) {
  cursor: default; /* 1 */
  line-height: 1.5; /* 2 */
  overflow-wrap: break-word; /* 3 */
  -moz-tab-size: 4; /* 4 */
  tab-size: 4; /* 4 */
  -webkit-tap-highlight-color: transparent; /* 5 */
  -webkit-text-size-adjust: 100%; /* 6 */
}

/* Sections
 * ========================================================================== */

/**
 * すべてのブラウザーで余白をなくす (opinionated).
 */

:where(body) {
  margin: 0;
}

/**
 * Chrome、Edge、Firefox、Safari で
 * section内、article内の`h1`要素および`h2`要素のフォントサイズとマージンを修正する。
 */

:where(h1) {
  font-size: 2em;
  margin: 0.67em 0;
}

/* Grouping content
 * ========================================================================== */

/**
 * Chrome、Edge、Safariでネストしたリストのマージンを削除する
 */

:where(dl, ol, ul) :where(dl, ol, ul) {
  margin: 0;
}

/**
 * 1.Firefoxのボーダーカラーのinheritに修正する。
 * 2.Firefoxで正しいボックスサイズを追加する。
 */

:where(hr) {
  color: inherit; /* 1 */
  height: 0; /* 2 */
}

/**
 * すべてのブラウザーで、ナビゲーションリストのリストスタイルを削除する (opinionated).
 */

:where(nav) :where(ol, ul) {
  list-style-type: none;
  padding: 0;
}

/**
 * SafariでVoiceOverがリストのセマンティクスを無視しないようにする。 (opinionated).
 */

:where(nav li)::before {
  content: "\200B";
  float: left;
}

/**
 * 1. すべてのブラウザで、文字サイズの継承と拡大縮小を修正する。
 * 2. すべてのブラウザで、変な`em`フォントのサイズ変更を修正する。
 * 3. すべてのブラウザでコンテナのオーバーフローを防止する (opinionated).
 */

:where(pre) {
  font-family: monospace, monospace; /* 1 */
  font-size: 1em; /* 2 */
  overflow: auto; /* 3 */
}

/* Text-level semantics
 * ========================================================================== */

/**
 * Safariで正しい文字装飾を追加する。
 */

:where(abbr[title]) {
  text-decoration: underline;
  text-decoration: underline dotted;
}

/**
 * Chrome、Edge、Safariで、正しいフォントのウェイトを追加する。
 */

:where(b, strong) {
  font-weight: bolder;
}

/**
 * 1. すべてのブラウザで、文字サイズの継承と拡大縮小を修正する。
 * 2. すべてのブラウザーで、変な`em`のフォントサイズを修正する。
 */

:where(code, kbd, samp) {
  font-family: monospace, monospace; /* 1 */
  font-size: 1em; /* 2 */
}

/**
 * すべてのブラウザーで正しいフォントサイズを追加する。
 */

:where(small) {
  font-size: 80%;
}

/* Embedded content 
 * ========================================================================== */

/*
 * すべてのブラウザーでメディア要素のアライメントを変更する (opinionated).
 */

:where(audio, canvas, iframe, img, svg, video) {
  vertical-align: middle;
}

/**
 * すべてのブラウザーでiframeのボーダーを削除する (opinionated).
 */

:where(iframe) {
  border-style: none;
}

/**
 * すべてのブラウザで文字色に合わせた塗りつぶし色に変更する (opinionated).
 */

:where(svg:not([fill])) {
  fill: currentColor;
}

/* Tabular data
 * ========================================================================== */

/**
 * 1. すべてのブラウザでボーダーの間隔を縮める (opinionated).
 * 2. Chrome、Edge、Safariのすべてで、テーブルのボーダーカラーをinheritに修正する
 * 3. Chrome、Edge、Safariのすべてで、テーブルの内容からテキストのインデントを削除する。
 */

:where(table) {
  border-collapse: collapse; /* 1 */
  border-color: inherit; /* 2 */
  text-indent: 0; /* 3 */
}

/* Forms
 * ========================================================================== */

/**
 * Safariのコントロールのマージンを削除する。
 */

:where(button, input, select) {
  margin: 0;
}

/**
 * iOSとSafariでボタンにスタイルを設定できないのを修正する。
 */

:where(button, [type="button" i], [type="reset" i], [type="submit" i]) {
  -webkit-appearance: button;
}

/**
 * すべてのブラウザーで一貫性のない外観を変更する (opinionated).
 */

:where(fieldset) {
  border: 1px solid #a0a0a0;
}

/**
 * Chrome、Edge、Firefoxで正しい縦並びを追加する。
 */

:where(progress) {
  vertical-align: baseline;
}

/**
 * 1. FirefoxとSafariのマージンを削除する。
 * 3. 全ブラウザでリサイズ方向を変更する (opinionated).
 */

:where(textarea) {
  margin: 0; /* 1 */
  resize: vertical; /* 3 */
}

/**
 * 1. Chrome、Edge、Safariで変な表示になるのを修正する。
 * 2. Safariのアウトラインスタイルを修正する。
 */

:where([type="search" i]) {
  -webkit-appearance: textfield; /* 1 */
  outline-offset: -2px; /* 2 */
}

/**
 * Safariの増分ボタンと減分ボタンのカーソルスタイルを修正する。
 */

::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
  height: auto;
}

/**
 * Chrome、Edge、Safariのプレースホルダーのテキストスタイルを修正する。
 */

::-webkit-input-placeholder {
  color: inherit;
  opacity: 0.54;
}

/**
 * macOSのChrome、Edge、Safariで内側のパディングを削除する。
 */

::-webkit-search-decoration {
  -webkit-appearance: none;
}

/**
 * 1. iOSとSafariでアップロードボタンにスタイルを設定できないのを修正する。
 * 2. Safariのフォントプロパティを「inherit」に変更する。
 */

::-webkit-file-upload-button {
  -webkit-appearance: button; /* 1 */
  font: inherit; /* 2 */
}

/* Interactive
 * ========================================================================== */

/*
 * Safariで正しいスタイルを追加する。
 */

:where(dialog) {
  background-color: white;
  border: solid;
  color: black;
  height: -moz-fit-content;
  height: fit-content;
  left: 0;
  margin: auto;
  padding: 1em;
  position: absolute;
  right: 0;
  width: -moz-fit-content;
  width: fit-content;
}

:where(dialog:not([open])) {
  display: none;
}

/*
 * Safariで正しい表示を追加する。
 */

:where(details > summary:first-of-type) {
  display: list-item;
}

/* Accessibility
 * ========================================================================== */

/**
 * すべてのブラウザーでビジー状態の要素にカーソルが表示されるように変更する 
 * (opinionated).
 */

:where([aria-busy="true" i]) {
  cursor: progress;
}

/*
 * すべてのブラウザーでコントロール要素のカーソルを変更する (opinionated).
 */

:where([aria-controls]) {
  cursor: pointer;
}

/*
 * すべてのブラウザで、無効、編集不可、その他操作不能な要素のカーソルを変更する  
 * (opinionated).
 */

:where([aria-disabled="true" i], [disabled]) {
  cursor: not-allowed;
}

/*
 * すべてのブラウザーで
 * 視覚的に隠されたアクセシブルな要素の表示を変更する (opinionated).
 */

:where([aria-hidden="false" i][hidden]) {
  display: initial;
}

:where([aria-hidden="false" i][hidden]:not(:focus)) {
  clip: rect(0, 0, 0, 0);
  position: absolute;
}

forms.css

フォームコントロールを副作用なく正規化する独立したスタイルシート。

詳細
/**
 * 1.すべてのブラウザで一貫性のない外観を変更する(opinionated)。
 * 2. 全ブラウザでタイポグラフィの継承を追加する(opinionated)。
 */

:where(button, input, select, textarea) {
  background-color: transparent; /* 1 */
  border: 1px solid WindowFrame; /* 1 */
  color: inherit; /* 1 */
  font: inherit; /* 2 */
  letter-spacing: inherit; /* 2 */
  padding: 0.25em 0.375em; /* 1 */
}

/**
 * すべてのブラウザで一貫性のない外観を変更する(opinionated)。
 */

:where(select) {
  appearance: none;
  background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='4'%3E%3Cpath d='M4 0h6L7 4'/%3E%3C/svg%3E") no-repeat right center / 1em;
  border-radius: 0;
  padding-right: 1em;
}

/**
 * 複数選択時に矢印を表示しない
 */

:where(select[multiple]) {
  background-image: none;
}

/**
 * すべてのブラウザでボーダーとパディングを削除する(opinionated)
 */

:where([type="color" i], [type="range" i]) {
  border-width: 0;
  padding: 0;
}

assets.css

プレーンな文書に快適な手段を適用するための独立したスタイルシート。

詳細
/**
 * すべてのブラウザで、サイジングをページ幅に制限する(opinionated)。
 */

:where(iframe, img, input, video, select, textarea) {
  height: auto;
  max-width: 100%;
}

typography.css

システムインターフェースフォントを使用したタイポグラフィを正規化するための独立したスタイルシート。

詳細
/**
 * すべてのブラウザでデフォルトのユーザーインターフェースフォントを使用する(opinionated)。
 */

html {
  font-family:
    system-ui,
    /* macOS 10.11-10.12 */ -apple-system,
    /* Windows 6+ */ "Segoe UI",
    /* Android 4+ */ "Roboto",
    /* Ubuntu 10.10+ */ "Ubuntu",
    /* Gnome 3+ */ "Cantarell",
    /* KDE Plasma 5+ */ "Noto Sans",
    /* fallback */ sans-serif,
    /* macOS emoji */ "Apple Color Emoji",
    /* Windows emoji */ "Segoe UI Emoji",
    /* Windows emoji */ "Segoe UI Symbol",
    /* Linux emoji */ "Noto Color Emoji";
}

/**
 * すべてのブラウザでデフォルトの等幅ユーザーインターフェースフォントを使用する(opinionated)。
 */

code,
kbd,
samp,
pre {
  font-family:
    ui-monospace,
    /* macOS 10.10+ */ "Menlo",
    /* Windows 6+ */ "Consolas",
    /* Android 4+ */ "Roboto Mono",
    /* Ubuntu 10.10+ */ "Ubuntu Monospace",
    /* KDE Plasma 5+ */ "Noto Mono",
    /* KDE Plasma 4+ */ "Oxygen Mono",
    /* Linux/OpenOffice fallback */ "Liberation Mono",
    /* fallback */ monospace,
    /* macOS emoji */ "Apple Color Emoji",
    /* Windows emoji */ "Segoe UI Emoji",
    /* Windows emoji */ "Segoe UI Symbol",
    /* Linux emoji */ "Noto Color Emoji";
}

reduce-motion.css

ユーザーがシステムレベルで要求した場合に、動きを制限するための別のスタイルシート。

詳細
/*
 * 1. モーションが小さくなったらアニメーションを削除する(opinionated)。
 * 2. モーションが小さくなると、固定された背景の添付ファイルを削除する(opinionated)。
 * 3. 3. モーション低下時の時間差スクロールの動作を削除する (opinionated)。
 * 4.モーション縮小時のトランジション削除(opinionated)
 */

@media (prefers-reduced-motion: reduce) {
  *,
  ::before,
  ::after {
    animation-delay: -1ms !important; /* 1 */
    animation-duration: 1ms !important; /* 1 */
    animation-iteration-count: 1 !important; /* 1 */
    background-attachment: initial !important; /* 2 */
    scroll-behavior: auto !important; /* 3 */
    transition-delay: 0s !important; /* 4 */
    transition-duration: 0s !important; /* 4 */
  }
}

system-ui.css

Firefox で system-ui を使用するためのサポートを追加する独立したスタイルシートです。

詳細
/**
 * Firefoxで正しいsystem-uiのfont-familyを追加しました。
 */

@font-face {
  font-family: system-ui;
  src: local(".AppleSystemUIFont"), local("Segoe UI"), local("Ubuntu"), local("Roboto-Regular"), local("HelveticaNeue");
}

@font-face {
  font-family: system-ui;
  font-style: italic;
  src: local(".AppleSystemUIFont"), local("Segoe UI Italic"), local("Ubuntu-Italic"), local("Roboto-Italic"), local("HelveticaNeue-Italic");
}

@font-face {
  font-family: system-ui;
  font-weight: bold;
  src: local(".AppleSystemUIFont"), local("Segoe UI Bold"), local("Ubuntu-Bold"), local("Roboto-Bold"), local("HelveticaNeue-Bold");
}

@font-face {
  font-family: system-ui;
  font-style: italic;
  font-weight: bold;
  src: local(".AppleSystemUIFont"), local("Segoe UI Bold Italic"), local("Ubuntu-BoldItalic"), local("Roboto-BoldItalic"), local("HelveticaNeue-BoldItalic");
}

ui-monospace.css

Chrome、Edge、Firefoxでui-monospaceを使用するためのサポートを追加する独立したスタイルシートです。

詳細
/**
 * Chrome、Edge、Firefoxで、正しいsystem-uiのfont-familyを追加する。
 */
 
@font-face {
  font-family: ui-monospace;
  src: local(".AppleSystemUIFontMonospaced-Regular"), local("Segoe UI Mono"), local("UbuntuMono"), local("Roboto-Mono"), local("Menlo");
}

@font-face {
  font-family: ui-monospace;
  font-style: italic;
  src: local(".AppleSystemUIFontMonospaced-RegularItalic"), local("Segoe UI Mono Italic"), local("UbuntuMono-Italic"), local("Roboto-Mono-Italic"), local("Menlo-Italic");
}

@font-face {
  font-family: ui-monospace;
  font-weight: bold;
  src: local(".AppleSystemUIFontMonospaced-Bold"), local("Segoe UI Mono Bold"), local("UbuntuMono-Bold"), local("Roboto-Mono-Bold"), local("Menlo-Bold");
}

@font-face {
  font-family: ui-monospace;
  font-style: italic;
  font-weight: bold;
  src: local(".AppleSystemUIFontMonospaced-BoldItalic"), local("Segoe UI Mono Bold Italic"), local("UbuntuMono-BoldItalic"), local("Roboto-Mono-BoldItalic"), local("Menlo-BoldItalic");
}

例えば、emotion(Styled JSX) 環境下に sanitize.css と typography.css を導入するには、下記のように設定します。

String-Style で指定する方法です。

App.tsx
import { Global, css } from '@emotion/react'
import sanitize from 'sanitize.css'
import typography from 'sanitize.css/typography.css'

const styles = css`
  ${sanitize}
  ${typography}
  body {
    color: hotpink; // ここにアプリ固有のベースになるスタイルを書く
  }
`
export function App() {
 return ( <Global styles={styles} />)
}

Object-Style で指定する方法です。

App.tsx
import { Global } from '@emotion/react'
import sanitize from 'sanitize.css'
import typography from 'sanitize.css/typography.css'

const styles = {
  body: {
    color: 'hotpink'  // ここにアプリ固有のベースになるスタイルを書く
  }
}

export function App() {
  return(
  <>
    <Global styles={sanitize} />
    <Global styles={typography} />
    <Global styles={styles} />
  </>
  )
}

まとめ

normalize.css と sanitize.css とで迷いました。
normalize.css は box-sizing: border-box が設定されていないなど、追加で設定しておきたい項目がありますが、私自身あまりデザインに詳しくないのと、デザインにそこまでこだわらなくていいアプリなので、今回は sanitize.css を使ってみようと思います。

Discussion