🦔

ReactとTailwindCSSでモックを作るのは楽しいぞ

2022/03/17に公開

(2022年11月25日追記)
私の本が株式会社インプレス R&Dさんより出版されました。この記事の内容も含まれています。イラストは鍋料理さんの作品です。猫のモデルはなんとうちのコです!

https://www.amazon.co.jp/dp/B0BMPZW444/

感想を書いていただけるととても嬉しいです!

(2022年8月3日追記)この記事の内容はこちらの本でも読めます。無料公開している「3章 モックアップを作ろう」に相当します。本の方がより詳しく説明しています。

https://zenn.dev/sikkim/books/how_to_create_api_sales_service

はじめに

現在、とあるAPIを販売するWEBサービスをReactで作成中です。先週は環境構築を行いましたが、今週はモックの作成を進めました。TailwindCSSのUIコンポーネントを公開しているページがたくさんあるので、気に入ったデザインを選んでコードをコピペすると画面モックがサクサク作れてとても楽しいです。

コピペする前にこれを読もう

コードをコピペすればきれいなデザインは再現できますが、セマンティクスを意識したコードまでは書けません。セマンティクスとはコードの持つ「意味」のことです。HTMLにはセマンティックタグと呼ばれる特別なタグ群が定義されていて、これらを適切に設定するとコンピューターが理解しやすいコードになります。たとえばメインコンテンツを<main>タグで囲んでおけば、視覚障害者がスクリーンリーダーを通じてメインコンテンツを探しやすくなります。詳しくは以下のMDNドキュメントをお読みください。

https://developer.mozilla.org/ja/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure

個人的には<header><footer><main><nav><section>あたりを適切に使えていれば大丈夫だと思います。

TailwindCSSでUIコンポーネントを作る手順

TailwindCSSのUIコンポーネントをMITライセンスで公開しているページの中から、使い勝手の良さそうなものをピックアップしました。

https://flowrift.com/

https://tailblocks.cc/

https://kitwind.io/products/kometa/components

https://www.tailwind-kit.com/components

https://merakiui.com/components/

Footerの実装(作業例)

たとえばこのFooterを実装してみましょう。

https://flowrift.com/c/footer/Zqs6g?view=preview

コードを表示して、

コピーします。

次にcomponents/Footer.tsxを新規作成します。/* ここにペースト */と書いてあるところに先ほどコピーしたコードをペーストしてください。

components/Footer.tsx
import { VFC } from 'react';

const Footer: VFC = () => (
  /* ここにペースト */
);
export default Footer;

HTML形式をJSX形式に変更するため、class=className=に全置換しましょう。また<!-- -->形式のコメントは削除します。もし残しておきたいコメントがあったらJavaScriptのコメント形式に修正しておきましょう。

まだエラーは残っていますが、ここまで修正すれば表示を確認できます。App.tsxを以下のように修正してみましょう。

App.tsx
import { VFC } from 'react';
import Footer from './components/Footer';

const App: VFC = () => (
  <footer>
    <Footer />
  </footer>
);

export default App;

npm run devで開発サーバーを立ち上げてlocalhost:3000にアクセスすると下図のように表示されるはずです。

これでFooterを表示させることができました。

この手順を繰り返していけば、ランディングページくらいならすぐに作れます。私のApp.tsxは最終的にこうなりました。

App.tsx
import { VFC } from 'react';
import Header1 from './components/Header1';
import Footer from './components/Footer';
import Overview from './components/Overview';
import Feature from './components/Feature';
import Price from './components/Price';
import Contact from './components/Contact';

// ランディングページ
const App: VFC = () => (
  <>
    <header>
      <Header1 />
    </header>
    <main>
      <Overview />
      <Feature />
      <Price />
      <Contact />
    </main>
    <footer>
      <Footer />
    </footer>
  </>
);

export default App;

<header><main><footer>というセマンティックタグが設定されていることに注目してください。これらのタグを取り除いてもページの見た目はまったく変わりませんが、コンピューターがコンテンツの構造を理解できなくなってしまうので、必ず設定しましょう。

開発中なので変更になる可能性はありますが、現時点の見た目はこんな感じです。

LP1
LP2
LP3

React Router v6とReact Router Hash Linkの設定

コピペして整形しただけの状態だと<a>タグで軒並み以下のanchor-is-validというエラーが発生しているはずです。(この環境構築をしている場合)

The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value. If you cannot provide a valid href, but still need the element to resemble a link, use a button and change it with appropriate styles.

外部の特定ページへのリンクであればhrefを適切に設定することでこのエラーは解消しますが、Reactアプリ内部の画面遷移を実現するにはルーティングライブラリを導入する必要があります。ReactのルーティングライブラリはReact Routerが圧倒的に高いシェアを持っています。React Routerの最新版はバージョン6ですが、このライブラリは過去に2回破壊的変更を行っています。1回目はバージョン3から4へ上がったときで、2回目はバージョン5から6へ上がったときです。いずれの場合も設定方法がガラッと変わったので、今後のバージョンアップには気をつけましょう。

また、画面内遷移を可能にするReact Router Hash Linkも導入します。それではまとめてインストールしましょう。

npm install react-router-dom@6 react-router-hash-link

Viteで構築した場合、ルーティングの設定はmain.tsxに記述します。現時点ではこんな感じです。

main.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import App from './App';
import Doc from './routes/Doc';
import Login from './routes/Login';
import MyPage from './routes/MyPage';
import NoMatch from './routes/NoMatch';
import PrivacyPolicy from './routes/PrivacyPolicy';
import Signup from './routes/Signup';
import Thanks from './routes/Thanks';
import Terms from './routes/Terms';
import Tokusyouhou from './routes/Tokusyouhou';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />} />
        <Route path="doc" element={<Doc />} />
        <Route path="login" element={<Login />} />
        <Route path="mypage" element={<MyPage />} />
        <Route path="privacy_policy" element={<PrivacyPolicy />} />
        <Route path="signup" element={<Signup />} />
        <Route path="terms" element={<Terms />} />
        <Route path="thanks" element={<Thanks />} />
        <Route path="tokusyouhou" element={<Tokusyouhou />} />
        <Route path="*" element={<NoMatch />} />
      </Routes>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root'),
);

まだ複雑なことをしていないというのもありますが、直感的でわかりやすい印象です。<Route>はネストさせることもできます。

呼び出し元は次のように記述します。まずは普通のリンクから。

<Link to="/signup" >
  新規登録
</Link>

画面内リンクはこんな感じです。

<HashLink
  smooth
  to="/#Price"
>
  料金プラン
</HashLink>

smoothを設定するとスクロール表現が入るので、画面内遷移だとわかりやすくなります。好みに応じて外しましょう。

利用規約やプライバシーポリシーはマークダウンで書きたい

有料のWEBサービスを公開する場合、利用規約とプライバイシーポリシー、特定商取引法に基づく表記が必要です。実際に読む人は少ないですが、いざという時にサービス提供者やユーザーを守る盾になるものなのでとても重要です。必ず作成しましょう。

いずれも長い文章になりがちで、なおかつ利用規約は頻繁に改定されます。いちいちマークアップして修飾していたら効率が悪いですし保守性も下がります。そこでマークダウンで記述できるようにしましょう。Reactでマークダウンを使うにはreact-markdownを導入して、マークダウンを<ReactMarkdown>で囲みます。これで該当の箇所がブラウザではHTMLに変換されます。それではインストールしてみましょう。

npm install react-markdown

react-markdownはデザインについてはノータッチなので、別途設定する必要があります。しかしタグごとに設定するのは面倒なので、マークダウンを良い感じに自動で修飾してくれる@tailwindcss/typographyを導入します。まずはインストールから。

npm install -D @tailwindcss/typography

次にtailwind.config.jspluginsに設定を追加します。

tailwind.config.js
module.exports = {
  content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [require('@tailwindcss/typography')],
};

それではプラバシーポリシーを作成してみましょう。文章はこちらの雛形をベースにしています。

components/PrivacyPolicyContent.tsx
import ReactMarkdown from 'react-markdown';

const body = `
# プライバシーポリシー
_____(以下,「当社」といいます。)は,本ウェブサイト上で提供するサービス(以下,「本サービス」といいます。)における,ユーザーの個人情報の取扱いについて,以下のとおりプライバシーポリシー(以下,「本ポリシー」といいます。)を定めます。

## 第1条(個人情報)
「個人情報」とは,個人情報保護法にいう「個人情報」を指すものとし,生存する個人に関する情報であって,当該情報に含まれる氏名,生年月日,住所,電話番号,連絡先その他の記述等により特定の個人を識別できる情報及び容貌,指紋,声紋にかかるデータ,及び健康保険証の保険者番号などの当該情報単体から特定の個人を識別できる情報(個人識別情報)を指します。

## 第2条(個人情報の収集方法)
当社は,ユーザーが利用登録をする際に氏名,生年月日,住所,電話番号,メールアドレス,銀行口座番号,クレジットカード番号,運転免許証番号などの個人情報をお尋ねすることがあります。また,ユーザーと提携先などとの間でなされたユーザーの個人情報を含む取引記録や決済に関する情報を,当社の提携先(情報提供元,広告主,広告配信先などを含みます。以下,「提携先」といいます。)などから収集することがあります。

## 第3条(個人情報を収集・利用する目的)
当社が個人情報を収集・利用する目的は,以下のとおりです。

1. 当社サービスの提供・運営のため
1. ユーザーからのお問い合わせに回答するため(本人確認を行うことを含む)
1. ユーザーが利用中のサービスの新機能,更新情報,キャンペーン等及び当社が提供する他のサービスの案内のメールを送付するため
1. メンテナンス,重要なお知らせなど必要に応じたご連絡のため
1. 利用規約に違反したユーザーや,不正・不当な目的でサービスを利用しようとするユーザーの特定をし,ご利用をお断りするため
1. ユーザーにご自身の登録情報の閲覧や変更,削除,ご利用状況の閲覧を行っていただくため
1. 有料サービスにおいて,ユーザーに利用料金を請求するため
1. 上記の利用目的に付随する目的

## 第4条(利用目的の変更)
当社は,利用目的が変更前と関連性を有すると合理的に認められる場合に限り,個人情報の利用目的を変更するものとします。
利用目的の変更を行った場合には,変更後の目的について,当社所定の方法により,ユーザーに通知し,または本ウェブサイト上に公表するものとします。

## 第5条(個人情報の第三者提供)
1. 当社は,次に掲げる場合を除いて,あらかじめユーザーの同意を得ることなく,第三者に個人情報を提供することはありません。ただし,個人情報保護法その他の法令で認められる場合を除きます。
    1. 人の生命,身体または財産の保護のために必要がある場合であって,本人の同意を得ることが困難であるとき
    1. 公衆衛生の向上または児童の健全な育成の推進のために特に必要がある場合であって,本人の同意を得ることが困難であるとき
    1. 国の機関もしくは地方公共団体またはその委託を受けた者が法令の定める事務を遂行することに対して協力する必要がある場合であって,本人の同意を得ることにより当該事務の遂行に支障を及ぼすおそれがあるとき
    1. 予め次の事項を告知あるいは公表し,かつ当社が個人情報保護委員会に届出をしたとき
        1. 利用目的に第三者への提供を含むこと
        1. 第三者に提供されるデータの項目
        1. 第三者への提供の手段または方法
        1. 本人の求めに応じて個人情報の第三者への提供を停止すること
        1. 本人の求めを受け付ける方法

1. 前項の定めにかかわらず,次に掲げる場合には,当該情報の提供先は第三者に該当しないものとします。
    1. 当社が利用目的の達成に必要な範囲内において個人情報の取扱いの全部または一部を委託する場合
    1. 合併その他の事由による事業の承継に伴って個人情報が提供される場合
    1. 個人情報を特定の者との間で共同して利用する場合であって,その旨並びに共同して利用される個人情報の項目,共同して利用する者の範囲,利用する者の利用目的および当該個人情報の管理について責任を有する者の氏名または名称について,あらかじめ本人に通知し,または本人が容易に知り得る状態に置いた場合

## 第6条(個人情報の開示)
1. 当社は,本人から個人情報の開示を求められたときは,本人に対し,遅滞なくこれを開示します。ただし,開示することにより次のいずれかに該当する場合は,その全部または一部を開示しないこともあり,開示しない決定をした場合には,その旨を遅滞なく通知します。なお,個人情報の開示に際しては,1件あたり1,000円の手数料を申し受けます。
    1. 本人または第三者の生命,身体,財産その他の権利利益を害するおそれがある場合
    1. 当社の業務の適正な実施に著しい支障を及ぼすおそれがある場合
    1. その他法令に違反することとなる場合
1. 前項の定めにかかわらず,履歴情報および特性情報などの個人情報以外の情報については,原則として開示いたしません。

## 第7条(個人情報の訂正および削除)
1. ユーザーは,当社の保有する自己の個人情報が誤った情報である場合には,当社が定める手続きにより,当社に対して個人情報の訂正,追加または削除(以下,「訂正等」といいます。)を請求することができます。
1. 当社は,ユーザーから前項の請求を受けてその請求に応じる必要があると判断した場合には,遅滞なく,当該個人情報の訂正等を行うものとします。
1. 当社は,前項の規定に基づき訂正等を行った場合,または訂正等を行わない旨の決定をしたときは遅滞なく,これをユーザーに通知します。

## 第8条(個人情報の利用停止等)
1. 当社は,本人から,個人情報が,利用目的の範囲を超えて取り扱われているという理由,または不正の手段により取得されたものであるという理由により,その利用の停止または消去(以下,「利用停止等」といいます。)を求められた場合には,遅滞なく必要な調査を行います。
1. 前項の調査結果に基づき,その請求に応じる必要があると判断した場合には,遅滞なく,当該個人情報の利用停止等を行います。
1. 当社は,前項の規定に基づき利用停止等を行った場合,または利用停止等を行わない旨の決定をしたときは,遅滞なく,これをユーザーに通知します。
1. 前2項にかかわらず,利用停止等に多額の費用を有する場合その他利用停止等を行うことが困難な場合であって,ユーザーの権利利益を保護するために必要なこれに代わるべき措置をとれる場合は,この代替策を講じるものとします。

## 第9条(プライバシーポリシーの変更)
1. 本ポリシーの内容は,法令その他本ポリシーに別段の定めのある事項を除いて,ユーザーに通知することなく,変更することができるものとします。
1. 当社が別途定める場合を除いて,変更後のプライバシーポリシーは,本ウェブサイトに掲載したときから効力を生じるものとします。

## 第10条(お問い合わせ窓口)
本ポリシーに関するお問い合わせは,下記の窓口までお願いいたします。

https://www.example.com/contact

以上
`;

const PrivacyPolicyContent = () => (
  <section className="bg-white py-6 sm:py-8 lg:py-12">
    <div className="prose mx-auto max-w-screen-md justify-center px-4 md:px-8">
      <ReactMarkdown>{body}</ReactMarkdown>
    </div>
  </section>
);
export default PrivacyPolicyContent;

<div>タグのclassNameに設定されたproseという項目が重要です。このクラス名が指定された範囲で@tailwindcss/typographyが有効になります。マークダウンはとりあえず変数にベタ書きしていますが、別ファイルに分離した方が良いかもしれません。

プライバシーポリシーページ全体は以下のようにしました。

routes/PrivacyPolicy.tsx
import { VFC } from 'react';
import Header1 from '../components/Header1';
import PrivacyPolicyContent from '../components/PrivacyPolicyContent';
import Footer from '../components/Footer';

// プライバシーポリシー
const PrivacyPolicy: VFC = () => (
  <>
    <header>
      <Header1 />
    </header>
    <main>
      <PrivacyPolicyContent />
    </main>
    <footer>
      <Footer />
    </footer>
  </>
);

export default PrivacyPolicy;

実際の表示はこのようになります。

プライバシーポリシーポリシー

何もしないでここまできれいに表示してくれれば、個人的には十分かなと思います。

なお、react-markdownは単体ではテーブル表記に対応していないので、マークダウンでテーブル表記を使いたい場合はremark-gfmというプラグインを追加する必要があります。

まとめ

以下の内容についてご説明しました。

  • セマンティクスの重要性
  • コピペでTailwindCSSのUIを実装する手順
  • ルーティングの実装方法
  • マークダウンの実装方法

静的なサイトならここまでの情報でひととおり作れてしまいます。UIを適当に組み合わせるだけで今風のページが簡単に作れるのは楽しかったです。

来週は実装が終わっていれば認証周りの記事を書く予定です。

Discussion