見出し画像

AstroとMarkdownで作るポートフォリオ

この記事は「株式会社メンバーズ Jamstack研究会主催 Advent Calendar2023」の5日目の記事です。
4日目の「JamstackでWebサイトを脱炭素化しよう」に続いて、5日目はAstroとMarkdownが題材です。
6日目の記事は「記事をスクレイピングしてmicroCMSに移行してみた」 です。
他の記事もぜひご覧ください!


はじめに

AstroはpagesディレクトリにあるMarkdownファイルをページとして扱うことができます。

会社に提出用のポートフォリオを作成することになり、Astro + microCMSで作れないか考えていたところ、microCMSを使う前段階としてMarkdownで簡易的なポートフォリオが作れそうと思い立ち、今回私自身の初めてのポートフォリオをAstroとMarkdownで制作していきます。

Astroのプロジェクトを作成

▼環境
Node.js v20.3.1
npm v9.6.7
Astro v3.6.0

▼導入
Astroのプロジェクトを作成を作成します。

npm create astro@latest

Astroプロジェクトはこのように設定しました。

ローカルの開発サーバーを起動し、ウェブサイトが実際に動作していることを確認します。

npm run dev

http://localhost:4321/ 

Markdownの動作確認

前述の通りAstroはpagesディレクトリにあるMarkdownファイルをページとして扱うことができます。
公式サイトのチュートリアルを参考にしてMarkdownの動作確認をします。
pagesディレクトリにpage-1.mdを追加します。

page-1.md

---
title: Hello, World
---

# Hi there!

This Markdown file creates a page at `your-domain.com/page-1/`

It probably isn't styled much, but Markdown does support:
- **bold** and _italics._
- lists
- [links](<https://astro.build>)
- and more!

http://localhost:4321/page-1 を開くと、ブラウザにMarkdownの内容が表示されました。

ポートフォリオを作成

トップページの作成

では本題のポートフォリオを作成してきます。
今回作るポートフォリオは、トップページに表示するプロフィールページと、これまでに携わったプロジェクトの詳細ページを作成します。
そして、プロジェクト詳細のデータをMarkdownファイルで管理します。

まずはポートフォリオのトップページから作成します。既存のLayout.astroとindex.astroの中身をそれぞれ書き換えます。

Layout.astro

---
interface Props {
  title: string
}

const { title } = Astro.props
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="description" content="Astro description" />
    <meta name="viewport" content="width=device-width" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="generator" content={Astro.generator} />
    <title>{title}</title>
  </head>
  <body>
    <header><a href="/"><span>PORTFOLIO</span></a></header>
    <main>
      <slot />
    </main>
  </body>
  <style is:global>
    :root {
      --accent: 136, 58, 234;
      --accent-light: 224, 204, 250;
      --accent-dark: 49, 10, 101;
      --accent-gradient: linear-gradient(45deg, rgb(var(--accent)), rgb(var(--accent-light)) 30%, white 60%);
    }
    html {
      font-family: system-ui, sans-serif;
      background: #f5f5f7;
      background-size: 224px;
    }
    code {
      font-family:
        Menlo,
        Monaco,
        Lucida Console,
        Liberation Mono,
        DejaVu Sans Mono,
        Bitstream Vera Sans Mono,
        Courier New,
        monospace;
    }
    body {
      margin: 0;
    }
    header {
      background-color: #fff;
      height: 48px;
    }
    main {
      margin: 0 auto;
      max-width: 768px;
      padding: 24px 32px;
      display: flex;
      justify-content: center;
      flex-direction: column;
    }
    span {
      font-size: 32px;
    }
    p,
    ul {
      background-color: #dddddd;
      padding: 16px;
      border-radius: 5px;
      margin-bottom: 16px;
    }
    ul {
      padding-left: 32px;
    }
    a {
      text-decoration: none;
      color: #000000;
      cursor: pointer;
    }
  </style>
</html>

index.astro

---
import Layout from '../layouts/Layout.astro'
---

<Layout title="ポートフォリオ">
  <h1>PROFILE</h1>
  <img src="/src/image/ifn0822.svg" width="200px" />
  <h2><span>名前</span> / なまえ</h2>
  <h3>職種</h3>
  <p>フロントエンドエンジニア</p>
  <h3>拠点</h3>
  <p>大阪府</p>
  <h3>キャリア</h3>
  <ul>
    <li>〇〇年 大学卒業</li>
    <li>〇〇年 入社</li>
  </ul>
  <h3>資格</h3>
  <ul>
    <li>資格1</li>
    <li>資格2</li>
    <li>資格3</li>
  </ul>
  <h3>その他</h3>
  <p>文章</p>
  <h3>プロジェクト一覧</h3>
  <ul>
    <li><a href="/projects/project1">プロジェクト1</a></li>
  </ul>
</Layout>

それっぽくなるように簡単なスタイルをつけて、プロフィール画像をつけました。
http://localhost:4321/ をブラウザで開くとこのような表示になります。

プロジェクトの詳細ページの作成

続いて、プロジェクトの詳細ページを作成します。
layoutsディレクトリにProjectLayout.astroを追加し、プロジェクト詳細のレイアウトを作成します。
プロジェクト詳細ページに入れる必須項目として「プロジェクト名・プロジェクト概要・期間・ポジション・チーム構成・担当作業内容・プロジェクト画像」としてみました。

<slot>を入れていますが、ここにはプロジェクトごとに記載したい項目を追加できるようにしました。

ProjectLayout.astro

---
import Layout from './Layout.astro'

const { frontmatter } = Astro.props
---

<Layout title={`${frontmatter.project} | ポートフォリオ`}>
  <h1>PROJECT</h1>
  <div class="title">
    <img src={frontmatter.image.url} alt={frontmatter.image.alt} />
    <h2>{frontmatter.project}</h2>
  </div>
  <h3>プロジェクト概要</h3>
  <p>{frontmatter.description}</p>
  <h3>期間</h3>
  <p>{frontmatter.date}</p>
  <h3>ポジション</h3>
  <p>{frontmatter.position}</p>
  <h3>チーム構成</h3>
  <p>{frontmatter.team}</p>
  <h3>担当作業内容</h3>
  <p>{frontmatter.job}</p>
  <slot />
</Layout>

<style>
  .title {
    display: flex;
    align-items: center;
  }
  .title > img {
    width: 10%;
    aspect-ratio: 1 / 1;
    margin-right: 16px;
  }
</style>

const { frontmatter } = Astro.propsを通して、この後作成するMarkdownファイルのフロントマターのプロパティにアクセスできます。

プロジェクト詳細データのファイルとしてpagesディレクトリにproject/project1.mdを追加します。
フロントマターは---で囲んだ中にYAML 形式で記載することができます。
layoutプロパティには使用するLayoutコンポーネントのパスを設定し、その他プロパティにもデータを設定します。

---
layout: /src/layouts/ProjectLayout.astro
id: 1
project: プロジェクト1
description: プロジェクト概要
team: 'フロントエンド:1人 バックエンド:1人 インフラ:1人'
date: 20XX年X月〜X月
position: フロントエンド、リーダー
job: 担当業務
image:
  url: /src/image/ifn1146.svg
  alt: 'プロジェクト1'
---

## 追加項目

テキスト

http://localhost:4321/projects/project1 をブラウザで開くとMarkdownファイル内のデータにレイアウトがあたった状態で表示しています。

frontmatterの中身はこのようになっています。

Astro.globによる複数ファイルのインポート

トップページにて静的に表示しているプロジェクト一覧を、project1.md、project2.md、project3.md・・・とプロジェクトファイルが増えるごとにプロジェクト一覧に連動して表示するようにします。

まずはpages/projectsディレクトリに先ほど作成したproject1.mdのコピーを2つ作成し、プロジェクトファイルを増やします。
それぞれファイル名をproject2.md、project3.mdとし、propsのprojectをプロジェクト2、プロジェクト3と修正しておきます。

次に、Astro.globによる複数ファイルのインポートをindex.astroに追加します。
プロジェクト一覧にあてるスタイルも追加しました。

---
import Layout from '../layouts/Layout.astro'

// 追加1 ここから
const projects = await Astro.glob('/src/pages/projects/*.md')
// 追加1 ここまで
---

<Layout title="ポートフォリオ">
  <h1>PROFILE</h1>
  <img src="/src/image/ifn0822.svg" width="200px" />
  <h2><span>名前</span> / なまえ</h2>
  <h3>職種</h3>
  <p>フロントエンドエンジニア</p>
  <h3>拠点</h3>
  <p>大阪府</p>
  <h3>キャリア</h3>
  <ul>
    <li>〇〇年 大学卒業</li>
    <li>〇〇年 入社</li>
  </ul>
  <h3>資格</h3>
  <ul>
    <li>資格1</li>
    <li>資格2</li>
    <li>資格3</li>
  </ul>
  <h3>その他</h3>
  <p>文章</p>
  <h3>プロジェクト一覧</h3>

  <!-- 追加2 ここから -->
  <ul class="project-list">
    {
      projects.map((project) => {
        return (
          <li class="project-item">
            <a class="project-link" href={`projects/project${project.frontmatter.id}`}>
              <img src={project.frontmatter.image.url} alt={project.frontmatter.image.alt} />
              <h4> {project.frontmatter.project}</h4>
            </a>
          </li>
        )
      })
    }
  </ul>
  <!-- 追加2 ここまで -->

  <!-- 追加3 ここから -->
  <style>
    .project-list {
      list-style-type: none;
      padding-left: 0px;
      padding-left: 0px;
      background-color: #f5f5f7;
    }
    .project-item {
      list-style: none;
      background-color: #fff;
      border-radius: 5px;
      background-color: #dddddd;
      padding: 16px;
      border-radius: 5px;
      margin-bottom: 16px;
    }
    .project-item:hover {
      opacity: 60%;
      transition: opacity 0.2s;
    }
    .project-link {
      display: flex;
      align-items: center;
    }
    .project-link > img {
      width: 10%;
      aspect-ratio: 1 / 1;
      margin-right: 16px;
    }
  </style>
  <!-- 追加3 ここまで -->
</Layout>

http://localhost:4321/ をブラウザで表示すると、プロジェクト1、プロジェクト2、プロジェクト3と全てのファイルが表示されることが確認できました。
ページリンクを追加したので、各プロジェクトの詳細ページに飛ぶことができます。

動的ページルーティング

最後に、プロジェクト詳細ページを動的ルーティングします。

AstroではMarkdownやMDXのファイルをsrc/contentフォルダで管理できます。

動的ルーティング実装の前に、ここまでに作成したMarkdownファイルをsrc/content/フォルダに移行します。

ファイル名を角括弧で囲み、getStaticPathsを使って実装することで動的ルーティングが実装できます。
プロジェクト詳細のデータがidを持っているので、pages/projects/[id].astroとしました。

[id].astro

---
export async function getStaticPaths() {
  const projects = await Astro.glob('/src/content/projects/*.md')

  return projects.map((project) => ({
    params: {
      id: `project${project.frontmatter.id}`,
    },
    props: {
      project,
    },
  }))
}

const { Content } = Astro.props.project
---

<Content />

AstroではContentをインポートしてMarkdownをレンダリングしたコンテンツをコンポーネントとして使用することができます。

Markdownファイルの管理場所の移行に伴って、トップページのプロジェクト一覧に使用している複数ファイルのインポートのパスを修正します。

index.astro

const projects = await Astro.glob('/src/content/projects/*.md')

トップページhttp://localhost:4321を確認します。

トップページにプロジェクト一覧が表示され、各プロジェクト詳細のページに移動することが確認できました。
これでポートフォリオは完成です。

おわりに

今回はAstroとMarkdownを使って簡易的なポートフォリオを作りました。
通常のAstroファイルと同じようにpagesディレクトリにあるMarkdownをページとして扱うことができます。
Markdownファイル専用のcontentsディレクトリでのデータ管理方法や、Markdownファイルの複数インポートをつかった動的ルーティングの作成方法も理解することができました。

#メンバーズ #Jamstack #Astro #Markdown

参考

この記事が気に入ったらサポートをしてみませんか?