Next.jsで簡易コーポレートサイトを作成する #1 -Home/Contactコンポーネント編-

# 概要
webSiteをNext.jsで作成します。
Next.jsの基本構造を理解し、Topページ・お問合せページのコンポーネントを作成します。

# 完成イメージ
<img width="1495" alt="000001.jpg" src="https://dl.dropbox.com/s/6300ikkvohluhe4/000001.jpg">
<img width="1495" alt="000010.jpg" src="https://dl.dropbox.com/s/relacivuuslnbpr/000010.jpg">

# プロジェクト作成
create-next-appを使ってNext.jsのプロジェクトを作成します。
create-next-appは公式のNext.js シングルページアプリケーションを作る方法です。
ボイラープレートを生成してくれます。
npxはパッケージをインストールせずに実行できる便利コマンドなコマンドです。
今回は「next-website」というプロジェクト名で作成していきます。

```
npx create-next-app@latest next-website
```
Ok to proceed? (y) と出たらそのままエンターをおします。

```
Ok to proceed? (y) y
```
TypeScriptを使うか聞かれますが、今回はJavaScriptを使うのでNoを選択します。

```
? Would you like to use TypeScript with this project? › No / Yes
```
ESLintを使うか聞かれるので、Yesにしてすすめます。
> ESLintはJavaScriptの記述を統一するのに役立ち、コードを実行する前にバグを見つけることができるので便利です。

```
? Would you like to use ESLint with this project? › No / Yes
```


プロジェクト名がフォルダ名となり、フォルダが作成されます。
ガイドのサジェストにも書いてあるとおり、プロジェクトフォルダ内に移動して開発サーバをスタートさせて確認してみます。

```
cd next-website
npm run dev
```

> windowsでうまく起動しない（SWC Failed to Loadがでる）場合、`.babelrc`ファイルを作成して下記を記述して保存し、もう一度実行してみます。
next/babel プリセットは、React アプリケーションや、サーバーサイドのコードをコンパイルに必要なすべてが含まれます。

```env:.babelrc
{
    "presets": ["next/babel"]
}
```


ローディングが完了したらhttp://localhost:3000 にアクセスするとwebサイトが表示されます。
<img width="1495" alt="000020.jpg" src="https://dl.dropbox.com/s/s3vwxkmcqiy6en7/000020.jpg">


# 基本部分編集
VisualStudioCodeでプロジェクトフォルダを開きボイラープレートを編集して不要な部分を削除していきます。

Next.jsでは、ページはpagesディレクトリにある.js, .jsx, .ts, .tsxファイルから書き出されるReact Componentです。各ページは、そのファイル名に基づいてルートと関連づけられます。
index.jsの中身を消して下記のように編集して、http://localhost:3000 にアクセスしたときに表示される内容を変えてみます。

```pages/index.js
export default function Home() {
  return (
    <h1>Top</h1>
  )
}
```
変更を加えて保存するとすぐに反映されます（npm run devしたままの状態の場合）。
（デフォルトで設定されているglobal.cssのbackgroundが効いているため背景が変です。）

<img width="1495" alt="000030.jpg" src="https://dl.dropbox.com/s/mpivv7em48g8tjk/000030.jpg">


# Top画面を作成する
## index.js
index.jsを編集します。
Top画面に中身を追加します。
中身は[フロントエンド開発](https://qiita.com/minimumskills/private/0a41bb64480c88505c3b)で作成したwebsiteのindex.htmlの中からコピーして作成します。
コピーするのは`<main>〜</main>`です。
コピーして、index.jsのreturn ()内に貼り付けます。

```diff_javascript:index.js
  export default function Home() {
    return (
-     <h1>Top</h1>
+     <>
+       <header class="top">
+         <h1 class="top_msg">bluecode, Lifestyle developer.</h1>
+       </header>
+       <main>
+         <section class="content">
+           <h2>websiteの最新情報</h2>
+           <p>最新の情報をご案内します。</p>
+         </section>
+       </main>
+     </>
    )
  }
```
```javascript:コピペ用
<>
  <header class="top">
    <h1 class="top_msg">bluecode, Lifestyle developer.</h1>
  </header>
  <main>
    <section class="content">
      <h2>websiteの最新情報</h2>
      <p>最新の情報をご案内します。</p>
    </section>
  </main>
</>
```

保存して確認するとすでに反映されています。
（デフォルトで設定されているglobal.cssのbackgroundが効いているため背景が変です。）
<img width="1495" alt="000040.jpg" src="https://dl.dropbox.com/s/noc6ior0fwl3ycu/000040.jpg">


## スタイル調整
全体に適用するものに関しては、stylesフォルダのglobal.cssに記述し、Topページのみの場合はHome.module.cssに記述するようにします。
一旦global.cssとHome.modules.cssの中身をすべて削除して下記を貼り付けます。
next.jsで自動で生成されるdivに__nextがあり、これがフロントエンド開発で作成したfooterに位置に影響を及ぼすため、`height:100%;`を指定しておきます。

```styles/global.css
html {
  height: 100%;
}

#__next {
  height: 100%;
}

.App {
  height: 100%;
}

body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  height: 100%;
}
```
```styles/Home.module.css
.top {
  /* 背景に取得した画像を設定 */
  background-image: url("https://picsum.photos/1000/300");
  /* 画像をいっぱいに表示 */
  background-size: cover;
  /* 表示する高さを画面の40％に */
  height: 40vh;
  /* 表示幅を画面の90%に */
  width: 100%;
  /* フレックスボックスを指定*/
  display: flex;
  /* フレックスアイテムを使って両端から中央に配置*/
  justify-content: center;
  /* フレックスアイテムを使って上下から中央に配置 */
  align-items: center;
  /* テキストの配置を中央揃えに */
  text-align: center;
}

.top_msg {
  /* 文字色を白に */
  color: #fff;
}

.content {
  width: 90%;
  margin: 0 auto;
  text-align: center;
}
```

## index.js編集
global.cssは既に適用されていますが（_app.jsで読み込み済みのため）、Home.module.cssを適用するには設定が必要です。
まず、上部でHome.module.cssを読み込みます。
classはclassNameに変更し、Home.module.cssで設定されているものを読み込むので、styles.〇〇という形でclassNameを指定します。

保存したら、Home.jsでHome.cssを読み込みます。

```diff_javascript:index.js
+ import styles from '../styles/Home.module.css';

  export default function Home() {
    return (
      <>
        <header class="top">
          <h1 class="top_msg">bluecode, Lifestyle developer.</h1>
        </header>
        <main>
          <section class="content">
            <h2>websiteの最新情報</h2>
            <p>最新の情報をご案内します。</p>
          </section>
        </main>
      </>
    )
  }
```
classはclassNameと表現するので、index.js内のclassをclassNameに書き換えていきます。
すべてのclassを下記のように書き換えます。
importしたstylesのtop,title、contentを指すように指定しています。

```diff_javascript:例
+  <section className={styles.top}>
-  <section class="top">
```

ここまでの最終的なコードはこちらです。

```pages/index.js
import styles from '../styles/Home.module.css';

export default function Home() {
  return (
    <>
      <header className={styles.top}>
        <h1 className={styles.top_msg}>bluecode, Lifestyle developer.</h1>
      </header>
      <main>
        <section className={styles.content}>
          <h2>websiteの最新情報</h2>
          <p>最新の情報をご案内します。</p>
        </section>
      </main>
    </>
  )
}
```
<img width="1495" alt="000050.jpg" src="https://dl.dropbox.com/s/g5xz4g0ifqgh0e9/000050.jpg">


# Contact画面作成
同様にcontact.jsとContact.module.cssを作成して実装していきます。

```
touch pages/contact.js styles/Contact.module.css
```
## contact.js編集
編集して、基本構造の中に、contact.htmlの`<main>〜</main>`を記述します。

```pages/contact.js
import styles from '../styles/Contact.module.css';

export default function Contact() {
    return (
        <main class="contact_form">
            <h2>お問合せ</h2>
            <form>
                <div class="form_group">
                    <label>お名前</label>
                    <input type="text" id="name" />
                    <span class="form_error hidden" id="name_error">お名前は必須です。</span>
                </div>
                <div class="form_group">
                    <label>email</label>
                    <input type="email" id="email" />
                    <span class="form_error hidden" id="email_error">emailは必須かつemailの形式で入力してください。</span>
                </div>
                <div class="form_group">
                    <label>お問合せ内容</label>
                    <textarea id="body" rows={4}></textarea>
                    <span class="form_error hidden" id="body_error">お問合せ内容は必須かつ1文字以上10文字以下で入力してください。</span>
                </div>
                <button class="submit_btn" id="submit_btn">送信</button>
            </form>
        </main >
    )
}
```

## スタイル設定
Contact.module.cssにContactに関わるスタイルコピペで記述します。

```Contact.module.css
.contact_form {
    padding: 40px 20px;
    text-align: center;
}

.form_group {
    border-top: 1px solid #ddd;
    margin-top: 20px;
    padding: 20px 0px;
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

.form_group label {
    width: 30%;
    font-weight: bold;
    font-size: 18px;
}

.form_group input,
.form_group textarea {
    border: 1px solid #ddd;
    border-radius: 6px;
    width: 60%;
    font-size: 18px;
}

.form_group input {
    height: 40px;
}

.submit_btn {
    padding: 5px 15px;
    background-color: #049dbf;
    color: #fff;
    border-radius: 5px;
    border: 0;
    font-size: 16px;
}

.submit_btn:hover {
    background-color: #0183a0;
}

.form_error {
    padding-left: 30%;
    width: 60%;
    font-size: small;
    color: red;
    text-align: start;
    padding-top: 10px;
}

/*スマホ対応*/
@media screen and (max-width: 640px) {
    /* お問合せフォーム */
    .form_group label {
        width: 100%;
        padding-bottom: 10px;
    }

    .form_group input,
    .form_group textarea {
        width: 100%;
        margin: 0;
        padding: 0;
    }

    .submit_btn {
        width: 100%;
        height: 50px;
    }

    .form_error {
        width: 100%;
        text-align: left;
        padding: 5px 0;
    }
}
```

## 読み込み、className変更
Contact.module.cssの読み込みとクラス名の編集をして保存します。

エラーを表示する部分は、クラス名がform_errorとhiddenの2つ用意されていました。複数のクラス名を設定する場合下記のように記述できます。

```
className={`${styles.form_error} ${styles.hidden}`}
```

が、のちのちreact-hook-formでバリデーションをつけて不要になってしまうので、hiddenクラスを削除して対処しておきます。

```diff_javascript:contact.js
import styles from '../styles/Contact.module.css';

export default function Contact() {
    return (
        <main className={styles.contact_form}>
            <h2>お問合せ</h2>
            <form>
                <div className={styles.form_group}>
                    <label>お名前</label>
                    <input type="text" id="name" />
                    <span className={styles.form_error} id="name_error">お名前は必須です。</span>
                </div>
                <div className={styles.form_group}>
                    <label>email</label>
                    <input type="email" id="email" />
                    <span className={styles.form_error} id="email_error">emailは必須かつemailの形式で入力してください。</span>
                </div>
                <div className={styles.form_group}>
                    <label>お問合せ内容</label>
                    <textarea id="body" rows={4}></textarea>
                    <span className={styles.form_error} id="body_error">お問合せ内容は必須かつ1文字以上10文字以下で入力してください。</span>
                </div>
                <button className={styles.submit_btn} id="submit_btn">送信</button>
            </form>
        </main >
    )
}
```
保存して確認します。
Next.jsではファイル名がそのままURLになるので、確認するときはhttp://localhost:3000/contact となります。
<img width="1495" alt="000060.jpg" src="https://dl.dropbox.com/s/yleazskl6ixgd1h/000060.jpg">

Contact画面ができました。
# 最終的なコード
## index.js

```index.js
import styles from '../styles/Home.module.css';

export default function Home() {
  return (
    <>
      <header className={styles.top}>
        <h1 className={styles.top_msg}>bluecode, Lifestyle developer.</h1>
      </header>
      <main>
        <section className={styles.content}>
          <h2>websiteの最新情報</h2>
          <p>最新の情報をご案内します。</p>
        </section>
      </main>
    </>
  )
}
```

## contact.js

```contact.js
import styles from '../styles/Contact.module.css';

export default function Contact() {
    return (
        <main className={styles.contact_form}>
            <h2>お問合せ</h2>
            <form>
                <div className={styles.form_group}>
                    <label>お名前</label>
                    <input type="text" id="name" />
                    <span className={styles.form_error} id="name_error">お名前は必須です。</span>
                </div>
                <div className={styles.form_group}>
                    <label>email</label>
                    <input type="email" id="email" />
                    <span className={styles.form_error} id="email_error">emailは必須かつemailの形式で入力してください。</span>
                </div>
                <div className={styles.form_group}>
                    <label>お問合せ内容</label>
                    <textarea id="body" rows={4}></textarea>
                    <span className={styles.form_error} id="body_error">お問合せ内容は必須かつ1文字以上10文字以下で入力してください。</span>
                </div>
                <button className={styles.submit_btn} id="submit_btn">送信</button>
            </form>
        </main >
    )
}
```

## Contact.module.css

```Contact.module.css
.contact_form {
    padding: 40px 20px;
    text-align: center;
}

.form_group {
    border-top: 1px solid #ddd;
    margin-top: 20px;
    padding: 20px 0px;
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

.form_group label {
    width: 30%;
    font-weight: bold;
    font-size: 18px;
}

.form_group input,
.form_group textarea {
    border: 1px solid #ddd;
    border-radius: 6px;
    width: 60%;
    font-size: 18px;
}

.form_group input {
    height: 40px;
}

.submit_btn {
    padding: 5px 15px;
    background-color: #049dbf;
    color: #fff;
    border-radius: 5px;
    border: 0;
    font-size: 16px;
}

.submit_btn:hover {
    background-color: #0183a0;
}

.form_error {
    padding-left: 30%;
    width: 60%;
    font-size: small;
    color: red;
    text-align: start;
    padding-top: 10px;
}

/*スマホ対応*/
@media screen and (max-width: 640px) {

    /* お問合せフォーム */
    .form_group label {
        width: 100%;
        padding-bottom: 10px;
    }

    .form_group input,
    .form_group textarea {
        width: 100%;
        margin: 0;
        padding: 0;
    }

    .submit_btn {
        width: 100%;
        height: 50px;
    }

    .form_error {
        width: 100%;
        text-align: left;
        padding: 5px 0;
    }
}
```

## globals.css

```globals.css
html {
  height: 100%;
}

#__next {
  height: 100%;
}

.App {
  height: 100%;
}

body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  height: 100%;
}
```

## Home.module.css

```Home.module.css
.top {
  /* 背景に取得した画像を設定 */
  background-image: url("https://picsum.photos/1000/300");
  /* 画像をいっぱいに表示 */
  background-size: cover;
  /* 表示する高さを画面の40％に */
  height: 40vh;
  /* 表示幅を画面の90%に */
  width: 100%;
  /* フレックスボックスを指定*/
  display: flex;
  /* フレックスアイテムを使って両端から中央に配置*/
  justify-content: center;
  /* フレックスアイテムを使って上下から中央に配置 */
  align-items: center;
  /* テキストの配置を中央揃えに */
  text-align: center;
}

.top_msg {
  /* 文字色を白に */
  color: #fff;
}

.content {
  width: 90%;
  margin: 0 auto;
  text-align: center;
}
```

# 関連コンテンツ
- 次：[Next.jsで簡易コーポレートサイトを作成する #2 -ナビゲーションバーとフッター編-](https://qiita.com/minimumskills/private/d5c0e2d0ced5766b295b)