Skip to content

Commit 3443de7

Browse files
authored
Blog makeover (#18)
* Make it feel more like a proper article * Tweaks
1 parent 5d371c2 commit 3443de7

5 files changed

Lines changed: 174 additions & 124 deletions

File tree

app/tag-data.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
2-
"react": 2,
3-
"global-state": 2,
4-
"valtio": 1,
5-
"zustand": 1,
62
"react-three-fiber": 1,
73
"glsl": 1,
84
"shaders": 1,
95
"vfx": 1,
10-
"pmndrs": 1,
6+
"react": 2,
7+
"global-state": 2,
8+
"zustand": 1,
9+
"valtio": 1,
1110
"xr": 1,
12-
"react-three": 1
11+
"react-three": 1,
12+
"pmndrs": 1
1313
}

contentlayer.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export const Authors = defineDocumentType(() => ({
135135
fields: {
136136
name: { type: 'string', required: true },
137137
avatar: { type: 'string' },
138+
description: { type: 'string' },
138139
occupation: { type: 'string' },
139140
company: { type: 'string' },
140141
email: { type: 'string' },

data/authors/christian-oritz.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
---
22
name: Christian Oritz
33
avatar: /static/images/cortiz.webp
4+
description: Nostalgic | Creative/Game Dev | I have a YouTube channel where I share my projects/experiments, sometimes with the free source code
45
twitter: https://x.com/cortiz2894
56
youtube: https://www.youtube.com/@cortizdev
6-
github:
7+
github: https://github.com/cortiz2894
8+
linkedin: https://www.linkedin.com/in/christian-daniel-ortiz-ororbia-95b14210b/
79
layout:
810
---

data/authors/daishi.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ company:
66
email:
77
twitter: https://twitter.com/dai_shi
88
github: https://github.com/dai-shi
9+
linkedin: https://jp.linkedin.com/in/daishikato
10+
bluesky: https://bsky.app/profile/daishikato.com
911
---

layouts/PostLayout.tsx

Lines changed: 162 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { ReactNode } from 'react'
2+
import { slug as slugify } from 'github-slugger'
23
import { CoreContent } from 'pliny/utils/contentlayer'
34
import type { Blog, Authors } from 'contentlayer/generated'
45
import Comments from '@/components/Comments'
56
import Link from '@/components/Link'
6-
import PageTitle from '@/components/PageTitle'
77
import SectionContainer from '@/components/SectionContainer'
88
import Image from '@/components/Image'
9-
import Tag from '@/components/Tag'
9+
import SocialIcon from '@/components/social-icons'
1010
import siteMetadata from '@/data/siteMetadata'
1111
import ScrollTopAndComment from '@/components/ScrollTopAndComment'
1212

@@ -29,150 +29,195 @@ interface LayoutProps {
2929
children: ReactNode
3030
}
3131

32+
const formatAuthorMeta = (author: CoreContent<Authors>) => {
33+
if (author.occupation && author.company) {
34+
return `${author.occupation} @ ${author.company}`
35+
}
36+
37+
return author.occupation || author.company || null
38+
}
39+
40+
const formatAuthorNames = (authorDetails: CoreContent<Authors>[]) => {
41+
const names = authorDetails.map((author) => author.name).filter(Boolean)
42+
43+
if (names.length <= 1) {
44+
return names[0] || null
45+
}
46+
47+
if (names.length === 2) {
48+
return `${names[0]} and ${names[1]}`
49+
}
50+
51+
return `${names.slice(0, -1).join(', ')}, and ${names.at(-1)}`
52+
}
53+
3254
export default function PostLayout({ content, authorDetails, next, prev, children }: LayoutProps) {
3355
const { filePath, path, slug, date, title, tags } = content
34-
const basePath = path.split('/')[0]
56+
const byline = formatAuthorNames(authorDetails)
3557

3658
return (
3759
<SectionContainer>
3860
<ScrollTopAndComment />
3961
<article>
40-
<div className="xl:divide-y xl:divide-gray-200 xl:dark:divide-gray-700">
41-
<header className="pt-6 xl:pb-6">
42-
<div className="space-y-1 text-center">
43-
<dl className="space-y-10">
62+
<div className="divide-y divide-gray-200 dark:divide-gray-700">
63+
<header className="pt-6">
64+
<div className="mx-auto max-w-3xl space-y-6 pb-10">
65+
<dl>
4466
<div>
4567
<dt className="sr-only">Published on</dt>
46-
<dd className="text-base leading-6 font-medium text-gray-500 dark:text-gray-400">
68+
<dd className="text-base text-gray-500 dark:text-gray-400">
69+
<span>on </span>
4770
<time dateTime={date}>
4871
{new Date(date).toLocaleDateString(siteMetadata.locale, postDateTemplate)}
4972
</time>
5073
</dd>
5174
</div>
5275
</dl>
53-
<div>
54-
<PageTitle>{title}</PageTitle>
55-
</div>
56-
</div>
57-
</header>
58-
<div className="grid-rows-[auto_1fr] divide-y divide-gray-200 pb-8 xl:grid xl:grid-cols-4 xl:gap-x-6 xl:divide-y-0 dark:divide-gray-700">
59-
<dl className="pt-6 pb-10 xl:border-b xl:border-gray-200 xl:pt-11 xl:dark:border-gray-700">
60-
<dt className="sr-only">Authors</dt>
61-
<dd>
62-
<ul className="flex flex-wrap justify-center gap-4 sm:space-x-12 xl:block xl:space-y-8 xl:space-x-0">
63-
{authorDetails.map((author) => (
64-
<li className="flex items-center space-x-2" key={author.name}>
65-
{author.avatar && (
66-
<Image
67-
src={author.avatar}
68-
width={38}
69-
height={38}
70-
alt="avatar"
71-
className="h-10 w-10 rounded-full"
72-
/>
73-
)}
74-
<dl className="text-sm leading-5 font-medium whitespace-nowrap">
75-
<dt className="sr-only">Name</dt>
76-
<dd className="text-gray-900 dark:text-gray-100">{author.name}</dd>
77-
<dt className="sr-only">Twitter</dt>
78-
<dd>
79-
{author.twitter && (
80-
<Link
81-
href={author.twitter}
82-
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
83-
>
84-
{author.twitter
85-
.replace('https://twitter.com/', '@')
86-
.replace('https://x.com/', '@')}
87-
</Link>
88-
)}
89-
</dd>
90-
<dt className="sr-only">YouTube</dt>
91-
<dd>
92-
{author.youtube && (
93-
<Link
94-
href={author.youtube}
95-
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
96-
>
97-
{author.youtube
98-
.replace('https://www.youtube.com/', '')
99-
.replace('https://youtube.com/', '')}
100-
</Link>
101-
)}
102-
</dd>
103-
</dl>
104-
</li>
76+
<h1 className="max-w-4xl text-4xl leading-[1] font-semibold tracking-tight text-gray-950 sm:text-5xl md:text-6xl dark:text-gray-50">
77+
{title}
78+
</h1>
79+
{byline && (
80+
<p className="text-base text-gray-600 dark:text-gray-400">
81+
By <span className="font-medium text-gray-900 dark:text-gray-100">{byline}</span>
82+
</p>
83+
)}
84+
{tags && tags.length > 0 && (
85+
<div className="flex flex-wrap gap-3">
86+
{tags.map((tag) => (
87+
<Link
88+
key={tag}
89+
href={`/tags/${slugify(tag)}`}
90+
className="rounded-full border border-gray-300 px-4 py-1.5 text-sm font-medium text-gray-700 transition hover:border-gray-400 hover:text-gray-950 dark:border-gray-700 dark:text-gray-300 dark:hover:border-gray-500 dark:hover:text-gray-100"
91+
>
92+
{tag}
93+
</Link>
10594
))}
106-
</ul>
107-
</dd>
108-
</dl>
109-
<div className="divide-y divide-gray-200 xl:col-span-3 xl:row-span-2 xl:pb-0 dark:divide-gray-700">
110-
<div className="prose dark:prose-invert max-w-none pt-10 pb-8">{children}</div>
111-
<div className="pt-6 pb-6 text-sm text-gray-700 dark:text-gray-300">
112-
<Link href={discussUrl(path)} rel="nofollow">
113-
Discuss on Twitter
114-
</Link>
115-
{` • `}
116-
<Link href={editUrl(filePath)}>View on GitHub</Link>
117-
</div>
118-
{siteMetadata.comments && (
119-
<div
120-
className="pt-6 pb-6 text-center text-gray-700 dark:text-gray-300"
121-
id="comment"
122-
>
123-
<Comments slug={slug} />
12495
</div>
12596
)}
12697
</div>
127-
<footer>
128-
<div className="divide-gray-200 text-sm leading-5 font-medium xl:col-start-1 xl:row-start-2 xl:divide-y dark:divide-gray-700">
129-
{tags && (
130-
<div className="py-4 xl:py-8">
131-
<h2 className="text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400">
132-
Tags
133-
</h2>
134-
<div className="flex flex-wrap">
135-
{tags.map((tag) => (
136-
<Tag key={tag} text={tag} />
137-
))}
138-
</div>
139-
</div>
140-
)}
98+
</header>
99+
<div className="mx-auto max-w-3xl pb-12">
100+
<div className="prose dark:prose-invert max-w-none py-10">{children}</div>
101+
<footer className="space-y-10 border-t border-gray-200 pt-10 dark:border-gray-700">
102+
<div className="space-y-6">
103+
<div className="flex flex-wrap gap-x-4 gap-y-2 text-sm text-gray-700 dark:text-gray-300">
104+
<Link href={discussUrl(path)} rel="nofollow">
105+
Discuss on Twitter
106+
</Link>
107+
<Link href={editUrl(filePath)}>View on GitHub</Link>
108+
</div>
109+
</div>
110+
{authorDetails.length > 0 && (
111+
<section className="space-y-6">
112+
<h2 className="text-2xl font-semibold tracking-tight text-gray-900 dark:text-gray-100">
113+
Author
114+
</h2>
115+
<ul className="space-y-6">
116+
{authorDetails.map((author) => {
117+
const authorMeta = formatAuthorMeta(author)
118+
const hasSocials = Boolean(
119+
author.email ||
120+
author.github ||
121+
author.linkedin ||
122+
author.twitter ||
123+
author.youtube ||
124+
author.bluesky
125+
)
126+
127+
return (
128+
<li
129+
className="rounded-3xl border border-gray-200 bg-gray-50/60 p-6 dark:border-gray-700 dark:bg-gray-900/40"
130+
key={author.slug || author.name}
131+
>
132+
<div className="flex flex-col gap-5 sm:flex-row sm:items-center">
133+
{author.avatar && (
134+
<Image
135+
src={author.avatar}
136+
width={96}
137+
height={96}
138+
alt={author.name}
139+
className="h-20 w-20 rounded-full object-cover"
140+
/>
141+
)}
142+
<div className="min-w-0 flex-1 space-y-3">
143+
<div className="space-y-1">
144+
<h3 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-100">
145+
{author.name}
146+
</h3>
147+
{authorMeta && (
148+
<p className="text-sm text-gray-600 dark:text-gray-400">
149+
{authorMeta}
150+
</p>
151+
)}
152+
</div>
153+
{author.description && (
154+
<p className="max-w-2xl text-sm leading-6 text-gray-600 dark:text-gray-300">
155+
{author.description}
156+
</p>
157+
)}
158+
{hasSocials && (
159+
<div className="flex flex-wrap items-center gap-3">
160+
<SocialIcon
161+
kind="mail"
162+
href={author.email ? `mailto:${author.email}` : undefined}
163+
size={6}
164+
/>
165+
<SocialIcon kind="github" href={author.github} size={6} />
166+
<SocialIcon kind="linkedin" href={author.linkedin} size={6} />
167+
<SocialIcon kind="x" href={author.twitter} size={6} />
168+
<SocialIcon kind="youtube" href={author.youtube} size={6} />
169+
<SocialIcon kind="bluesky" href={author.bluesky} size={6} />
170+
</div>
171+
)}
172+
</div>
173+
</div>
174+
</li>
175+
)
176+
})}
177+
</ul>
178+
</section>
179+
)}
180+
<section className="space-y-6">
141181
{(next || prev) && (
142-
<div className="flex justify-between py-4 xl:block xl:space-y-8 xl:py-8">
182+
<div className="grid gap-4 md:grid-cols-2">
143183
{prev && prev.path && (
144-
<div>
145-
<h2 className="text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400">
146-
Previous Article
147-
</h2>
148-
<div className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400">
149-
<Link href={`/${prev.path}`}>{prev.title}</Link>
150-
</div>
184+
<div className="rounded-3xl border border-gray-200 p-6 dark:border-gray-700">
185+
<p className="text-xs tracking-[0.2em] text-gray-500 uppercase dark:text-gray-400">
186+
Previous article
187+
</p>
188+
<Link
189+
href={`/${prev.path}`}
190+
className="hover:text-primary-500 dark:hover:text-primary-400 mt-3 block text-lg font-medium text-gray-900 transition dark:text-gray-100"
191+
>
192+
{prev.title}
193+
</Link>
151194
</div>
152195
)}
153196
{next && next.path && (
154-
<div>
155-
<h2 className="text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400">
156-
Next Article
157-
</h2>
158-
<div className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400">
159-
<Link href={`/${next.path}`}>{next.title}</Link>
160-
</div>
197+
<div className="rounded-3xl border border-gray-200 p-6 dark:border-gray-700">
198+
<p className="text-xs tracking-[0.2em] text-gray-500 uppercase dark:text-gray-400">
199+
Next article
200+
</p>
201+
<Link
202+
href={`/${next.path}`}
203+
className="hover:text-primary-500 dark:hover:text-primary-400 mt-3 block text-lg font-medium text-gray-900 transition dark:text-gray-100"
204+
>
205+
{next.title}
206+
</Link>
161207
</div>
162208
)}
163209
</div>
164210
)}
165-
</div>
166-
<div className="pt-4 xl:pt-8">
167-
<Link
168-
href={`/${basePath}`}
169-
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
170-
aria-label="Back to the blog"
171-
>
172-
&larr; Back to the blog
173-
</Link>
174-
</div>
211+
</section>
175212
</footer>
213+
{siteMetadata.comments && (
214+
<div
215+
className="pt-10 text-center text-gray-700 dark:text-gray-300"
216+
id="comment"
217+
>
218+
<Comments slug={slug} />
219+
</div>
220+
)}
176221
</div>
177222
</div>
178223
</article>

0 commit comments

Comments
 (0)