Next.jsで作ったブログにStyleを適用していく

published: 2020/9/7 update: 2020/9/9

Table of Contents
  - TL;DR
  - Styleの適用
    - Prism.js && Github markdown css
    - amp-mathml
    - material-ui
  - 感想

TL;DR

前回の記事で、markdownをうまくレンダリングできるようになったので、次はStyleを適用していく。適用すべき対象は、最初の記事でに書いたように、

  • material-ui
  • Prism.jsでのcode syntax
  • amp-mathmlによる数式
  • Github markdown css

である。AMP対応するには鬼門である。

Styleの適用

Prism.js && Github markdown css

prism.js公式サイトからcssをダウンロードしておく。github-markdown-cssからダウンロードする。github-markdown-cssの方は自動生成なので!importantとかが使われていてAMPに対応できないのでそのへんは除いてしまう。そのあと、raw-loaderを使ってcssを_app.tsxでimportして、直接埋め込む。できるならMarkdownのページだけで読み込みたいが...

ちょっとmaterial-ui成分も入ってしまっているが、_document.jsは以下の感じ。

import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheets } from "@material-ui/core/styles";

import theme from "@libs/theme";

// @ts-ignore
import css from "!!raw-loader!../styles/github_markdown.css";
// @ts-ignore
import prismCss from "!!raw-loader!../styles/prism.css";
// @ts-ignore
import globalCss from "!!raw-loader!../styles/global.css";

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="ja">
        <Head>
          {/* PWA primary color */}
          <meta name="theme-color" content={theme.palette.primary.main} />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
  const sheets = new ServerStyleSheets();
  const originalRenderPage = ctx.renderPage;

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
    });

  const initialProps = await Document.getInitialProps(ctx);

  return {
    ...initialProps,
    // Styles fragment is rendered after the app and page rendering finish.
    styles: [
      ...React.Children.toArray(initialProps.styles),
      <style
        key="custom"
        dangerouslySetInnerHTML={{
          __html: `${globalCss}\n${css}\n${prismCss}`,
        }}
      />,
      sheets.getStyleElement(),
    ],
  };
};

custom loaderでcodeをTokenに落とす作業をしておけばAMPでもコードがハイライトされる。順番の関係か、prismjsはダーク系のテーマにしたのに黒くならなかったので、github-markdown-css側で背景を黒にしておいた。

example

const MOD: usize = 1e9 as usize + 7;
>>> import pandas as pd
>>> pd.read_csv("/path/to/file.csv")

amp-mathml

KatexはAMPに対応できない。custom loaderを使って、type === "math"type === "inlineMath"に対応するamp-mathmlを埋め込む(先にremark-mathを使っておく必要がある)。インラインの数式はparagraphのchildrenなので注意が必要。

example

インライン数式

普通の数式

material-ui

yarn add @material-ui/core

で終わりだと思ってたんだけど、そんなことはなかった。

というのは、サーバーサイドレンダリングをnext.jsでするときに、CSSの読み込みがリセットされてしまうことがあるらしい(参考)。実際に自分の画面でも崩れていて、結構時間を溶かした。 幸いなことに、material-uiの公式がテンプレート例を作成してくれているので(javascript, typescript)、参考にしながら_app.tsx_document.tsxを書き換えておく。あとnext.jsのリンクとmaterial-uiのリンクもclassNameの問題とかでうまく行かないことがあるので、Linkコンポーネントを作っておく。

それとコンポーネントの中に!importantを生み出すやつがいるので、生み出されたらnext build && exportでhtml作って原因コンポーネントは削除しておく。InputBase的なやつが怪しかった。

感想

スタイルの適用はこんな感じ。しかし、material-uiは結構がっつりcssっぽいものを触らないとだめで結構難しい。Bootstrapはだいたいよしなにやってくれていたので、css力が本当に無い。

記事に間違い等ありましたら、お気軽に以下までご連絡ください

E-mail: illumination.k.27|gmail.com ("|" replaced to "@")

Twitter: @illuminationK

Privacy Policy

Copyright © illumination-k 2021.