Reactでコーポレートサイトを作る

投稿日:2024年09月16日

更新日:2024年09月17日

投稿者:HIROKI CHIYODA

はじめに

題名通りに、Reactを使って、コーポレートサイトを作ります。
今回使う題材は、こちらのデザインを参考にします。
ちなみに他にも、練習用サイトが難易度を分けて用意されています。
また、コードも提供されていて、HTMLとCSSを勉強した後に、自分で試すのに適しています。
ただ、HTMLとCSSで作成しても面白くないので、React+Tailwind CSSを使って実装します。
デザインを参考にしますが、完コピを目的にしていません。

デモサイト

デモサイトは以下のリンクから見ることができます。
レスポンシブ対応で、1024pxで切り替えるようになっています。
また、トップページのみ実装しています。

https://react-corporate-website.pages.dev

プロジェクト作成

mkdir react-corporate-website
cd react-corporate-website
bun create cloudflare@latest ./

What would you like to start with?
category Framework Starter

Which development framework do you want to use?
framework React

Select a variant:
variant TypeScript

Do you want to use git for version control?
yes git

Do you want to deploy your application?
no deploy via `bun run deploy`

動作確認

bun run dev

Tailwind CSSをインストール

bun add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

作成されたtailwind.config.jsに設定を追加

  /** @type {import('tailwindcss').Config} */
  export default {
-   content: [],
+   content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
    theme: {
      extend: {},
    },
    plugins: [],
  };

index.cssを以下のコードに変更
App.cssを削除

@tailwind base;
@tailwind components;
@tailwind utilities;

App.tsxのコードを整理

function App() {
  return <h1 className="flex justify-center">Vite + React</h1>;
}

export default App;

ファビコンを設定

publicディレクトリに、favicon.icoを追加
index.htmlにファビコンを設定

  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8" />
- 		<link rel="icon" type="image/svg+xml" href="/vite.svg" />
+     <link rel="icon" href="/favicon.ico" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Vite + React + TS</title>
    </head>
    <body>
      <div id="root"></div>
      <script type="module" src="/src/main.tsx"></script>
    </body>
  </html>

画像ファイルを追加

publicディレクトリに、logo.svgを追加して、vite.svgを削除
また、assetディレクトリに以下の画像ファイルを追加

タイトル・言語を変更

index.htmlのタイトル・言語を変更

  <!DOCTYPE html>
- <html lang="en">
+ <html lang="ja">
    <head>
      <meta charset="UTF-8" />
      <link rel="icon" href="/favicon.ico" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-     <title>Vite + React + TS</title>
+ 		<title>React Website</title>
    </head>
    <body>
      <div id="root"></div>
      <script type="module" src="/src/main.tsx"></script>
    </body>
  </html>

CSSを設定

index.csshtmlタグにスクロールをスムーズにする設定を追加
また、bodyタグに背景色の設定を追加

  @tailwind base;
  @tailwind components;
  @tailwind utilities;
+ 
+ html {
+ 	scroll-behavior: smooth;
+ }
+ 
+ body {
+ 	background-color: #F5F5F5;
+ }

共通コンポーネントを作成

ヘッダー・フッター・ヒーローセクションの追加

export default function Header() {
  return <header>Header</header>;
}
export default function Hero() {
  return <div>Hero</div>;
}
export default function Footer() {
  return <footer className="mt-auto">Footer</footer>;
}

ヘッダー・フッター・ヒーローセクションの仮設置

+ import Footer from "./components/footer";
+ import Header from "./components/header";
+ import Hero from "./components/hero";
  
  function App() {
- 	return <h1 className="flex justify-center">Vite + React</h1>;
+   return (
+     <main className="flex flex-col min-h-dvh">
+       <Header />
+       <Hero />
+       <main className="container mx-auto min-h-dvh">Main</main>
+       <Footer />
+     </main>
+   );
  }
  
  export default App;

ヘッダーを作成

header.tsxを編集

"use client";

import { useState } from "react";
import MenuLink from "./ui/menu-link";

export default function Header() {
  const [isMenuOpen, setIsMenuOpen] = useState(false);

  return (
    <header className="h-20 flex justify-between items-center fixed top-0 left-0 right-0 bg-gray-100/80 z-10">
      <h1 className="w-48 lg:flex justify-end items-end pl-4">
        <a href="#">
          <img src="/logo.svg" alt="logo" width="100" height="100" />
        </a>
      </h1>
      <button
        className="lg:hidden z-10 mr-8 text-3xl flex justify-center items-center h-full"
        onClick={() => setIsMenuOpen(!isMenuOpen)}
      >
        {isMenuOpen ? "" : ""}
      </button>
      <ul
        className={`lg:flex ${
          isMenuOpen ? "flex text-center text-xl" : "hidden"
        } flex-col lg:flex-row absolute lg:relative top-20 lg:top-0 left-0 w-full lg:w-auto bg-gray-100/80 lg:bg-transparent`}
      >
        <li className="p-4">
          <MenuLink href="#new">NEWS</MenuLink>
        </li>
        <li className="p-4">
          <MenuLink href="#about">ABOUT</MenuLink>
        </li>
        <li className="p-4">
          <MenuLink href="#business">BUSINESS</MenuLink>
        </li>
        <li className="p-4">
          <MenuLink href="#company">COMPANY</MenuLink>
        </li>
      </ul>
      <a
        href="#"
        className="hidden lg:flex justify-center items-center bg-black text-white w-52 h-full hover:bg-gray-700"
      >
        お問い合わせ
      </a>
    </header>
  );
}

componentsディレクトリに、uiディレクトリを作成
その中に、menu-link.tsxを作成して、メニューの共通部分をコンポーネント化

export default function MenuLink({
  children,
  href,
}: {
  children: React.ReactNode;
  href: string;
}) {
  return (
    <a href={href} className="hover:underline underline-offset-8">
      {children}
    </a>
  );
}

ヘッダーを固定したことで、ヒーロー部分がかぶるので、margin-topでヘッダーの高さを追加

  export default function Hero() {
-   return <div>Hero</div>;
+   return <div className="mt-20">Hero</div>;
  }

ヒーローを作成

hero.tsxを編集

+ import mainVisual from "../assets/mainvisual.jpg";

  export default function Hero() {
- 	return <div className="mt-20">Hero</div>;
+  return (
+    <div className="h-screen w-full overflow-hidden">
+      <img
+        src={mainVisual}
+        alt="ヒーロー画像"
+        className="w-full h-full object-cover"
+      />
+    </div>
+  );
  }

ヘッダーが透けてしまうので、背景色を追加
<header>
CSSに、bg-gray-100/80を追加

-	<header className="h-20 flex justify-between items-center fixed top-0 left-0 right-0 z-10">
+ <header className="h-20 flex justify-between items-center fixed top-0 left-0 right-0 z-10 bg-gray-100/80">

レスポンシブでメニューを開いた時も透けてしまうので、メニューの背景色も追加
<ul>CSSにも、bg-gray-100/80を追加

  <ul
    className={`lg:flex ${
-   	isMenuOpen ? "flex text-center text-xl" : "hidden"
+   	isMenuOpen ? "flex text-center text-xl bg-gray-100/80" : "hidden"
    } flex-col lg:flex-row absolute lg:relative top-20 lg:top-0 left-0 w-full lg:w-auto bg-white lg:bg-transparent`}
  >

フッターを作成

footer.tsxを編集

  export default function Footer() {
- 	return <footer className="mt-auto">Footer</footer>;
+   return (
+     <footer className="lg:h-56 h-64 bg-white">
+ 			<main className="max-w-5xl mx-auto px-4">
+         <div className="lg:flex justify-between items-center pt-10">
+           <a href="#">
+             <img src="/logo.svg" alt="logo" width="100" height="100" />
+           </a>
+           <section className="text-sm pt-2.5 lg:pt-0">
+             <p>Web Entertainment Design Inc.</p>
+             <p>West Building 3F</p>
+             <p>9-99 Sakuragaokacho Shibuya-ku</p>
+             <p>Tokyo, Japan 150-0031</p>
+             <p>T/99-9999-9999</p>
+           </section>
+         </div>
+         <p className="text-xs pt-10 lg:pt-12">
+           © Web Entertainment Design Inc.
+         </p>
+       </main>
+     </footer>
+   );
  }

セクションタイトルコンポーネントを作成

uiディレクトリにsection-title.tsxを作成

export default function SectionTitle({
  title,
  subTitle,
}: {
  title: string;
  subTitle: string;
}) {
  return (
    <div>
      <h2 className="text-4xl font-light uppercase tracking-[0.25em]">
        {title}
      </h2>
      <h3 className="text-sm font-light h3t-2.5">{subTitle}</h3>
      <hr className="w-10 border-black mt-9" />
    </div>
  );
}

ニュースを作成

componentsディレクトリにnews.tsxを作成

import NewsContent from "./ui/news-content";
import SectionTitle from "./ui/section-title";

export default function News() {
  return (
    <div id="news" className="max-w-5xl mx-auto pt-28 px-4">
      <SectionTitle title="news" subTitle="ニュース" />
      <div className="lg:flex lg:space-x-5 mt-16 space-y-10 lg:space-y-0">
        <NewsContent
          date="2022.01.01"
          tag="news"
          title="タイトルタイトルタイトルタイトル"
        />
        <NewsContent
          date="2022.01.01"
          tag="press"
          title="タイトルタイトルタイトルタイトル"
        />
        <NewsContent
          date="2022.01.01"
          tag="news"
          title="タイトルタイトルタイトルタイトル"
        />
      </div>
    </div>
  );
}

また、uiディレクトリにnews-content.tsxを作成

export default function NewsContent({
  date,
  tag,
  title,
}: {
  date: string;
  tag: string;
  title: string;
}) {
  return (
    <div className="flex flex-col justify-center first:pl-0 space-y-4 lg:border-r-2 border-black last:border-r-0 lg:w-1/3 h-20">
      <div className="flex space-x-2.5">
        <time className="text-sm font-light">{date}</time>
        <p className="flex justify-center items-center text-white text-xs font-light bg-black uppercase w-12">
          {tag}
        </p>
      </div>
      <p className="font-light">{title}</p>
    </div>
  );
}

最後に、app.tsxの<main>を削除して、代わりにNewsコンポーネントを設置

  import Footer from "./components/footer";
  import Header from "./components/header";
  import Hero from "./components/hero";
+ import News from "./components/news";
	
  function App() {
    return (
      <>
        <Header />
        <Hero />
-       <main className="container mx-auto min-h-dvh">Main</main>
+ 			<News />
        <Footer />
      </>
    );
  }
  
  export default App;

アバウトを作成

componentsディレクトリにabout.tsxを作成

import about from "../assets/about.jpg";
import SectionTitle from "./ui/section-title";

export default function About() {
  return (
    <div id="about" className="lg:flex lg:mt-32 pt-24">
      <img src={about} alt="" className="lg:h-[400px] lg:w-7/12 object-cover" />
      <div className="lg:ml-16 lg:mt-44 mt-8 px-4">
        <SectionTitle title="about" subTitle="私たちについて" />
        <div className="text-sm lg:mt-12 mt-8 space-y-2">
          <p>
            テキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキスト
          </p>
          <p>
            テキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキスト
          </p>
          <p>
            テキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキスト
          </p>
        </div>
      </div>
    </div>
  );
}

app.tsxAboutコンポーネントを設置

+ import About from "./components/about";
  import Footer from "./components/footer";
  import Header from "./components/header";
  import Hero from "./components/hero";
  import News from "./components/news";
	
  function App() {
    return (
      <>
        <Header />
        <Hero />
  			<News />
+       <About />
        <Footer />
      </>
    );
  }
  
  export default App;

ビジネスを作成

componentsディレクトリにbusiness.tsxを作成

import business1 from "../assets/business1.jpg";
import business2 from "../assets/business2.jpg";
import business3 from "../assets/business3.jpg";
import business4 from "../assets/business4.jpg";
import BusinessContent from "./ui/business-content";
import SectionTitle from "./ui/section-title";

export default function Business() {
  return (
    <div id="business" className="max-w-5xl mx-auto px-4 pt-32">
      <SectionTitle title="business" subTitle="事業内容" />
      <div className="lg:grid grid-cols-2 gap-16 lg:mt-12 mt-8 space-y-8">
        <div className="lg:mt-32 space-y-8">
          <BusinessContent
            title="Web制作・マーケティング"
            imageUrl={business1}
          />
          <BusinessContent
            title="インターネットメディア事業"
            imageUrl={business2}
          />
        </div>
        <div className="space-y-8">
          <BusinessContent
            title="プロモーション企画・制作"
            imageUrl={business3}
          />
          <BusinessContent title="ソーシャル企画・運営" imageUrl={business4} />
        </div>
      </div>
    </div>
  );
}

また、uiディレクトリにbusiness-content.tsxを作成

export default function NewsContent({
  date,
  tag,
  title,
}: {
  date: string;
  tag: string;
  title: string;
}) {
  return (
    <div className="flex flex-col justify-center first:pl-0 space-y-4 lg:border-r-2 border-black last:border-r-0 lg:w-1/3 h-20">
      <div className="flex space-x-2.5">
        <time className="text-sm font-light">{date}</time>
        <p className="flex justify-center items-center text-white text-xs font-light bg-black uppercase w-12">
          {tag}
        </p>
      </div>
      <p className="font-light">{title}</p>
    </div>
  );
}

app.tsxBusinessコンポーネントを設置

  import About from "./components/about";
+ import Business from "./components/business";
  import Footer from "./components/footer";
  import Header from "./components/header";
  import Hero from "./components/hero";
  import News from "./components/news";
	
  function App() {
    return (
      <>
        <Header />
        <Hero />
  			<News />
        <About />
+       <Business />
        <Footer />
      </>
    );
  }
  
  export default App;

カンパニーを作成

componentsディレクトリにcompany.tsxを作成

import company from "../assets/company.jpg";
import SectionTitle from "./ui/section-title";

export default function Company() {
  return (
    <div id="company" className="lg:relative max-w-5xl mx-auto px-4 lg:mb-32">
      <div className="bg-white lg:max-w-xl lg:py-24 py-10 lg:px-16 px-4 lg:mt-32 mt-20 h-full">
        <SectionTitle title="company" subTitle="会社情報" />
        <table className="lg:mt-14 mt-8 text-sm font-light mb-10">
          <tr className="flex flex-col lg:block">
            <td width="80px" className="lg:h-10">
              会社名
            </td>
            <td className="mb-5">ウェブエンターテイメントデザイン株式会社</td>
          </tr>
          <tr className="flex flex-col lg:block">
            <td width="80px" className="lg:h-10">
              所在地
            </td>
            <td className="mb-5">東京都渋谷区桜丘町99-9 West Building 3F</td>
          </tr>
          <tr className="flex flex-col lg:block">
            <td width="80px" className="lg:h-10">
              代表
            </td>
            <td className="mb-5">XXXXXX</td>
          </tr>
          <tr className="flex flex-col lg:block">
            <td width="80px" className="lg:h-10">
              設立
            </td>
            <td className="mb-5">2021年1月1日</td>
          </tr>
          <tr className="flex flex-col lg:block">
            <td width="80px" className="lg:h-10">
              資本金
            </td>
            <td className="mb-5">3,000,000円</td>
          </tr>
          <tr className="flex flex-col lg:block">
            <td width="80px" valign="top">
              事業内容
            </td>
            <td height="40px" className="lg:space-y-2.5">
              <p>Web制作・マーケティング</p>
              <p>インターネットメディア事業</p>
              <p>プロモーション企画・制作</p>
              <p>ソーシャル企画・運営</p>
            </td>
          </tr>
        </table>
      </div>
      <img
        src={company}
        alt="会社画像"
        className="lg:absolute lg:top-28 right-0 lg:w-1/2 lg:h-2/3 lg:object-cover pt-5 static"
      />
    </div>
  );
}

app.tsxCompanyコンポーネントを設置

  import About from "./components/about";
  import Business from "./components/business";
+ import Company from "./components/company";
  import Footer from "./components/footer";
  import Header from "./components/header";
  import Hero from "./components/hero";
  import News from "./components/news";
	
  function App() {
    return (
      <>
        <Header />
        <Hero />
  			<News />
        <About />
        <Business />
+       <Company />
        <Footer />
      </>
    );
  }
  
  export default App;