🎨

Next.js(app router)とrecoilで動的にfont-sizeを切り替える

2023/09/10に公開

TL;DR

demo

source code
https://github.com/neco3s/next-playwright/tree/dynamic_fontsize

ざっくりとした流れ

  1. htmlのfont-sizeにrecoilで管理された動的な値を適用します。
  2. サイト全体でfont-sizeの指定にremを使用します。
  3. ボタンのクリックなどでrecoilで管理された動的な値を変更する。
    ※remはhtmlのfont-sizeをベースにしてfont-sizeを計算するので、サイト全体でfont-size指定にremを使用していればhtmlのfont-sizeの値を変更するだけでサイト全体に変更が反映されます。
app/layout.tsx
import "./globals.css";
import type { Metadata } from "next";
import FooterContainer from "@/app/_components/Footer/FooterContainer";
import StatesProvider from "@/app/_states/statesProvider";
import ThemesProvider from "@/app/_themes/themesProvider";
import HeaderContainer from "@/app/_components/Header/HeaderContainer";

export const metadata: Metadata = {
  title: "Next.js + TypeScript + Tailwind CSS + Playwright",
  description: "sandbox for Next.js + TypeScript + Tailwind CSS + Playwright",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <StatesProvider>
      <ThemesProvider>
        <HeaderContainer />
        {children}
        <FooterContainer />
      </ThemesProvider>
    </StatesProvider>
  );
}

上記のような構成になっております。htmlのfont-sizeにrecoilの値を適用したいので

  • StatesProvider(<RecoilRoot>)
    • ThemesProvider(<html style={recoilの動的な値}>)
      • {children} // */page.tsx

という構成になっております。recoilはブラウザのメモリ上に(※recoil-persist使えばローカルストレージに情報が保存されデータが永続化されます)情報を保存する仕組みなのでStatesProviderとThemesProviderはどちらも"use client" ディレクティブを使用してClient Componentとして定義されています。それぞれのProviderのpropsとしてReactNodeを渡す(Composition Pattern)ことでapp/layout.tsx(全てのページに適用される)をserver componentに保ちつつhtmlのfont-sizeに動的な値をセットしサイト全体にfont-sizeの変更を反映できるようになっております。

app/_states/statesProvider.tsx
"use client";

import { ReactNode } from "react";
import { RecoilRoot } from "recoil";

function StatesProvider({ children }: { children: ReactNode }) {
  return <RecoilRoot>{children}</RecoilRoot>;
}

export default StatesProvider;
app/_themes/themesProvider.tsx
"use client";

import { ReactNode } from "react";
import { Noto_Sans_JP } from "next/font/google";
import { useRecoilValue } from "recoil";
import { baseFontSizeState, baseLineHeightState } from "@/app/_states/themes";

const noto_sans_jp = Noto_Sans_JP({ subsets: ["latin"] });
function ThemesProvider({ children }: { children: ReactNode }) {
  const baseFontSize: number = useRecoilValue(baseFontSizeState);
  const baseLineHeight: number = useRecoilValue(baseLineHeightState);

  return (
    <html
      lang="ja"
      style={{
        fontSize: `${baseFontSize}px`,
        lineHeight: `${baseLineHeight}px`,
      }}
    >
      <body className={noto_sans_jp.className}>{children}</body>
    </html>
  );
}

export default ThemesProvider;

小話

以下のアクセシビリティのpdfを読んでフォントサイズを動的に設定できるようにしようと思いました! まだまだ10分の1程度も導入できていないのですが、1つずつ対応してより多くの人がアクセスできるようなサイトやサービスを作っていきたいと思います!

https://www.digital.go.jp/resources/introduction-to-web-accessibility-guidebook/

ではまた明日!

Discussion