Remix+Mantineでフロントエンドを作る！（その１）

# はじめに
最近のフロントエンドの知識がほぼゼロの状態から挑戦した、自分用メモです。
Macを使っているので、なんとなく[Ubuntuを入れてそこに環境を作ろうと思った](https://qiita.com/setoh2000/items/a1911183a76a2cb178bd)のですが、Macとのファイル共有とか、色々と課題があって、そもそもメリットがないことに気づいたので、Mac上に直接 Node.jsを入れることにしました。（多分これが一般的かと思います）

# nvmのインストール
Node.jsをインストールする、nvmという便利なものがあることが分かったので、[こちら](https://github.com/nvm-sh/nvm#installation-and-update)を参考に入れました。
```
# zshの場合 (私はこちら)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | zsh

# bashの場合
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
```

# Node.jsのインストール
事前検証の結果、v18やそれ以前だとエラーになったりWarningが出たりしたので、v20以上にするのが良さそうということがわかりました。
とりあえず、思い切ってバージョンを指定せずインストールしてみました。
```
nvm install node
```
実行すると、v23.6.0が入りました！
```
% nvm install node
Downloading and installing node v23.6.0...
Downloading https://nodejs.org/dist/v23.6.0/node-v23.6.0-darwin-arm64.tar.xz...
######################################################################### 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v23.6.0 (npm v10.9.2)
Creating default alias: default -> node (-> v23.6.0)
(base) setoh@MBP13 ~ % node -v
v23.6.0
```
新しすぎて逆に問題が出そうな気もしましたが、nvmだとバージョンを変えたりするのが簡単なので、このままいきます。

# Remix公式のチュートリアルをやってみる
[Tutorial(30m)](https://remix.run/docs/en/main/start/tutorial)とのことなので、30分あれば出来そうということでやってみます。
<em>a pretty bare-bones template</em> ということで非常にシンプルなテンプレートらしいです。

```
npx create-remix@latest --template remix-run/remix/templates/remix-tutorial
```
実行すると、見た目がさみしいページが表示されました。
ここからcssを適用したり、結構色々なことを順番にやっていくことになります。
３０分で終わるかと思ったのですが、わからないことをChatGPTなどに聞きながらやっていくと結局丸一日かかりました。でも、これはやって良かったです。先に生のReactをやったほうが良かったかなと思う瞬間はありましたが、なんとかやっていけそうな気がします。
ここで解説でも書こうかと思ったのですが、そこそこなボリュームなので諦めました。
あとで見つけましたが、こちらに日本語で解説されている記事を見つけたので、気になる方は参考にされると良いかと思います。

https://zenn.dev/jun0723/articles/184c9cef3f46db

# CSSをなんとかしたい
チュートリアルで作成したものは、Remixのデザイナーさんが作ったようで、見栄えが良いのですが、チュートリアル専用のCSSみたいなので、Bootstrapに差し替えようと考えました。
ただ、色々とチャレンジしたのですが、なかなかうまくいきません。
そもそもBootstrapの知識もあまりないので、諦めて他のものを探したところ、以下の記事で、[Mantine](https://mantine.dev/)というものが紹介されていたので、試してみることにしました。

https://zenn.dev/wancup/articles/20220808_introduce_shiorimo

引用
> Mantineはデザインが好みというのもありますが、かなり使い勝手が良くてカスタマイズ性も高かったのが最終的に落ち着いた理由です。用意されているコンポーネント数も多く、痒いところに手が届く感じがあります。例えばオートコンプリートやStepperなども用意されているのが嬉しいです。

# RemixでMantineを使う
チュートリアルで作成したものに、Mantineを組み込もうと思ったのですが、やり方がわかりません。
こちらの記事を見ると、[Mantineの公式ページ](https://mantine.dev/)にRemixなどのフレームワーク用のガイドがあるということで、探してみました。

https://qiita.com/ot_RikuOta/items/0ddf5926270c1f019d20

ありました！

https://mantine.dev/guides/remix/

ただ、Remix素人なので、読んでもよく理解できません。　なので、ひとまずチュートリアルを改造するのは諦め、用意されている公式のテンプレートから新規でプロジェクトを作成することにしました。

https://github.com/mantinedev/remix-template

ただ、指定の仕方が良くわからないので[Remixの公式ドキュメント](https://remix.run/docs/en/main/guides/templates)を見てみます。

どうもgithubに置かれていれば、URLから`https://github.com/`の部分を削除したものを入れるといけるみたいなのでやってみます。

```
npx create-remix@latest --template mantinedev/remix-template
```
いけました！早速実行してみます。
```
% cd mantine-app
% npm run dev

> dev
> remix vite:dev

 warn  Fetcher persistence behavior is changing in React Router v7
┃ You can use the `v3_fetcherPersist` future flag to opt-in early.
┃ -> https://remix.run/docs/en/2.13.1/start/future-flags#v3_fetcherPersist
┗
 warn  Route discovery/manifest behavior is changing in React Router v7
┃ You can use the `v3_lazyRouteDiscovery` future flag to opt-in early.
┃ -> https://remix.run/docs/en/2.13.1/start/future-flags#v3_lazyRouteDiscovery
┗
<略>
  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
```
警告は出ますが、とりあえず無視して、http://localhost:5173/ をブラウザで開いてみます。

![mantine.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160271/d357b580-17b3-e738-5386-8a9e50a55c7a.jpeg)

無事画面が開きました！CSSもちゃんと適用されているようで良い感じです。

チュートリアルと同様に、`app/root.tsx` と `app/routes/_index.tsx` があるのでやりたいことはこれで出来そうです。必要なものはチュートリアルから持ってくることにします。

```
% tree --gitignore
.
├── README.md
├── app
│   ├── components
│   │   ├── ColorSchemeToggle
│   │   │   └── ColorSchemeToggle.tsx
│   │   └── Welcome
│   │       ├── Welcome.module.css
│   │       ├── Welcome.story.tsx
│   │       └── Welcome.tsx
│   ├── entry.client.tsx
│   ├── entry.server.tsx
│   ├── root.tsx
│   └── routes
│       └── _index.tsx
├── package-lock.json
├── package.json
├── postcss.config.cjs
├── public
│   └── favicon.ico
├── tsconfig.json
├── vite.config.ts
└── yarn.lock

7 directories, 16 files
```
`components/`というディレクトリが目新しいですが、ここにUIの部品をおいてあるようです。`Welcome/`がタイトルロゴと説明、`ColorSchemeToggle/`が、ボタンでカラースキーム（背景色など）を切り替えるUIの部品です。これで色んなページから、部品を使い回せるのですね。
切り替え自体は、`setColorScheme`で簡単に変更できるようです。

```tsx:ColorSchemeToggle.tsx
'use client';

import { Button, Group, useMantineColorScheme } from '@mantine/core';

export function ColorSchemeToggle() {
  const { setColorScheme } = useMantineColorScheme();

  return (
    <Group justify="center" mt="xl">
      <Button onClick={() => setColorScheme('light')}>Light</Button>
      <Button onClick={() => setColorScheme('dark')}>Dark</Button>
      <Button onClick={() => setColorScheme('auto')}>Auto</Button>
    </Group>
  );
}
```
今度は`root.tsx`を見てみます。

```tsx:root.tsx
import "@mantine/core/styles.css";

import { MantineProvider, ColorSchemeScript } from "@mantine/core";
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
        <ColorSchemeScript />
      </head>
      <body>
        <MantineProvider>{children}</MantineProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}

```
この２つが、Mantineを適用しているところのようです。（多分）
```tsx
import "@mantine/core/styles.css";

import { MantineProvider, ColorSchemeScript } from "@mantine/core";
```
チュートリアルと違うのが、`Layout`でHTMLを返しているところです。`<Outlet />`だけ、Appで返しています。
```tsx
export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
...略
   </html>
  );
}

export default function App() {
  return <Outlet />;
}
```
ChatGPTなどに色々聞いてみたものの、あまりよく理解できません。
メンテナンス性の問題のような回答が帰ってきますが、`root.tsx`に限ればあまり関係なさそうな気もしますが、とりあえず、今のところは深く考えないことにします。

長くなったので、次回に続きます。

# 参考

https://zenn.dev/jun0723/articles/184c9cef3f46db

https://qiita.com/ot_RikuOta/items/0ddf5926270c1f019d20