🚀
Zenn を Astro で雑クローン
Astro の Markdown の機能を使ってみたかったのでローカルの Zenn 的なものを作ることにした。
セットアップ
Astro と Tailwind CSS のテンプレートリポジトリを作っておいたのでこれをクローンして使います。
$ git clone https://github.com/ogty/astro-tailwindcss-template
$ cd astro-tailwindcss-template
$ pnpm install
記事作成
記事作成を簡略化したいので Makefile を作ります。
記事は./src/pages/posts
に配置することにします。
name ?= $(shell uuidgen)
today := $(shell date '+%Y/%m/%d')
POSTS_PATH := ./src/pages/posts
LAYOUT_PATH := ../../layouts/ArticleLayout.astro
TEMPLATE := $(shell echo '---\\nlayout: $(LAYOUT_PATH)\\ntitle:\\nemoji:\\ntype:\\ndate: $(today)\\n---')
article:
@echo $(TEMPLATE) > $(POSTS_PATH)/$(name).md
新しい記事は ↓ で作成できます。name
を指定しない場合はuuid
がファイル名になります。
$ make article
$ make article name=article1
実行すると ↓ のような内容になっているので、
---
layout: ../../layouts/ArticleLayout.astro
title:
emoji:
type:
date: 2022/09/15
---
適当に書き足します。
---
layout: ../../layouts/ArticleLayout.astro
title: 記事1
emoji: 🥺
type: tech
date: 2022/09/15
---
処理
作成された./src/pages/posts/*.md
の記事を Zenn 風にしていきます。
./src/index.astro
---
import Layout from '@layouts/Layout.astro';
import Card from '../components/Card.astro';
export type Type = 'TECH' | 'IDEA';
interface Post {
title: string;
emoji: string;
type: Type;
date: string;
url: string;
}
const posts = await Astro.glob('../pages/posts/*.md');
const postsList = posts.map((post): Post => {
const { title, date, emoji } = post.frontmatter;
const type = post.frontmatter.type.toUpperCase();
const url = post.url as string;
return {
title,
emoji,
type,
date,
url
};
});
---
<Layout title="Memo" backgroundColor="#F1F5F9">
<main class="mt-0 mx-auto pt-16 px-4 sm:px-6 md:px-10 lg:px-10" style="max-width: 960px;">
<div
class="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4 sm:gap-7 md:gap-7 lg:gap-7"
>
{
postsList.map((post: Post) => {
return <Card {...post} />;
})
}
</div>
</main>
</Layout>
↓ で記事の---
で囲った情報が取れるので、これをPost
型に合うようにいい感じに処理します。
---
const posts = await Astro.glob('../pages/posts/*.md');
---
./src/components/Card.astro
---
export type Type = 'TECH' | 'IDEA';
export interface Props {
title: string;
emoji: string;
type: Type;
date: string;
url: string;
}
const { title, emoji, type, date, url } = Astro.props;
---
<article
class="flex flex-col relative max-w-sm bg-white rounded-xl border border-gray-200 shadow-lg"
>
<a class="absolute top-0 left-0 h-full w-full flex-1" href={url}> </a>
<div class="absolute top-3 left-3 flex space-x-2 justify-center">
<span
style={`background-color: ${type === 'TECH' ? '#3EA8FF' : '#807AFF'}; font-size: 10px;`}
class="text-xs inline-block py-1 px-1.5 leading-none text-center whitespace-nowrap align-baseline font-semibold text-white rounded-full"
>
{type.toUpperCase()}
</span>
</div>
<div
class="py-7 text-center text-5xl rounded-t-lg"
style={`background-color: ${type === 'TECH' ? '#CFE5FF' : '#DBDFFF'};`}
>
{emoji}
</div>
<div class="pt-4 px-4 flex-1">
<h5 class="text-md font-semibold tracking-tigh">
{title}
</h5>
</div>
<div class="pb-3 px-4 pt-1">
<span class="text-xs" style="color: #93A5B1;">
{date}
</span>
</div>
</article>
index.astro
から受け取ったデータを展開します。
記事用のレイアウト作成
次に記事用のレイアウトを作成します。
./layouts/ArticleLayout.astro
---
import Layout from './Layout.astro';
const { frontmatter } = Astro.props;
const { title, emoji } = frontmatter;
---
<Layout title={title} backgroundColor="#edf2f7">
<header class="text-center p-14 max-w-4xl my-0 mx-auto">
<span class="text-7xl">{emoji}</span>
<h1 class="mt-6 text-3xl font-bold">{title}</h1>
</header>
<div class="mt-0 mx-auto bg-white rounded-xl p-8" style="max-width: 960px;">
<slot />
</div>
</Layout>
<style is:global>
...
</style>
記事のlayout
にArticleLayout.astro
を指定すれば、<slot />
に記事の内容が展開されます。
ただ、Tailwind CSS で元々のタグのデザインがなくなっているので、<style is:global>
に記事用の CSS を書く必要があります。
Markdown 内でも問題なく Tailwind CSS は適応されるので、記事ごとにデザインを変えられますが、面倒なのでグローバルに書きます。
結構良さそうです。
Discussion