Skip to content

Commit 8980326

Browse files
authored
feat: add blog section (#30)
1 parent a3db4c0 commit 8980326

3 files changed

Lines changed: 279 additions & 0 deletions

File tree

src/app/_meta.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@ export default {
66
type: 'page',
77
title: 'Documentation'
88
},
9+
blog: {
10+
type: 'page',
11+
title: 'Blog'
12+
},
913
}

src/app/blog/page.jsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { FiArrowUpRight } from 'react-icons/fi'
2+
import styles from './page.module.css'
3+
4+
export const metadata = {
5+
title: 'Blog',
6+
description: 'Articles about Oxia and distributed metadata systems.',
7+
}
8+
9+
const posts = [
10+
{
11+
title: 'Designing a Distributed Metadata System - Foundation',
12+
date: '2026-07-05',
13+
displayDate: 'Jul 5, 2026',
14+
author: 'Qiang Zhao',
15+
href: 'https://www.linkedin.com/pulse/designing-distributed-metadata-system-foundation-qiang-zhao-qlvlc/',
16+
summary:
17+
'A walkthrough of the standalone server semantics that distributed metadata systems need before adding replication, sharding, and cluster management.',
18+
tags: ['Metadata systems', 'Distributed systems', 'Design'],
19+
},
20+
{
21+
title: 'From Metadata Store to Metadata Plane',
22+
date: '2026-06-20',
23+
displayDate: 'Jun 20, 2026',
24+
author: 'Qiang Zhao',
25+
href: 'https://www.linkedin.com/pulse/from-metadata-store-plane-qiang-zhao-gysuc/',
26+
summary:
27+
'How coordination metadata grows into a runtime metadata plane with storage, notification, sharding, and federation requirements.',
28+
tags: ['Metadata plane', 'Coordination', 'Scalability'],
29+
},
30+
]
31+
32+
export default function BlogPage() {
33+
return (
34+
<main className={styles.page}>
35+
<section className={styles.header}>
36+
<p className={styles.kicker}>Oxia Blog</p>
37+
<h1>Articles on distributed metadata systems</h1>
38+
<p className={styles.intro}>
39+
Technical posts about Oxia, metadata stores, coordination, and the
40+
architecture patterns behind scalable infrastructure.
41+
</p>
42+
</section>
43+
44+
<section className={styles.posts} aria-label="Blog posts">
45+
{posts.map((post) => (
46+
<article className={styles.post} key={post.href}>
47+
<div className={styles.meta}>
48+
<time dateTime={post.date}>{post.displayDate}</time>
49+
<span>{post.author}</span>
50+
</div>
51+
<h2>
52+
<a href={post.href} target="_blank" rel="noreferrer">
53+
{post.title}
54+
</a>
55+
</h2>
56+
<p>{post.summary}</p>
57+
<div className={styles.footer}>
58+
<ul className={styles.tags} aria-label={`${post.title} topics`}>
59+
{post.tags.map((tag) => (
60+
<li key={tag}>{tag}</li>
61+
))}
62+
</ul>
63+
<a className={styles.readLink} href={post.href} target="_blank" rel="noreferrer">
64+
Read on LinkedIn
65+
<FiArrowUpRight aria-hidden="true" />
66+
</a>
67+
</div>
68+
</article>
69+
))}
70+
</section>
71+
</main>
72+
)
73+
}

src/app/blog/page.module.css

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
.page {
2+
width: min(100%, 980px);
3+
margin: 0 auto;
4+
padding: 72px 24px 96px;
5+
color: #101827;
6+
}
7+
8+
.header {
9+
max-width: 760px;
10+
margin-bottom: 40px;
11+
}
12+
13+
.kicker {
14+
margin: 0 0 12px;
15+
color: #0f766e;
16+
font-size: 0.82rem;
17+
font-weight: 700;
18+
letter-spacing: 0;
19+
text-transform: uppercase;
20+
}
21+
22+
.header h1 {
23+
margin: 0;
24+
color: #0b1220;
25+
font-size: 2.35rem;
26+
line-height: 1.12;
27+
font-weight: 760;
28+
}
29+
30+
.intro {
31+
max-width: 680px;
32+
margin: 18px 0 0;
33+
color: #4b5563;
34+
font-size: 1.05rem;
35+
line-height: 1.7;
36+
}
37+
38+
.posts {
39+
display: grid;
40+
gap: 18px;
41+
}
42+
43+
.post {
44+
border: 1px solid #d7dee8;
45+
border-radius: 8px;
46+
background: #ffffff;
47+
padding: 28px;
48+
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06);
49+
}
50+
51+
.meta {
52+
display: flex;
53+
flex-wrap: wrap;
54+
gap: 8px 14px;
55+
margin-bottom: 12px;
56+
color: #64748b;
57+
font-size: 0.88rem;
58+
}
59+
60+
.meta span::before {
61+
content: "/";
62+
margin-right: 14px;
63+
color: #cbd5e1;
64+
}
65+
66+
.post h2 {
67+
margin: 0;
68+
color: #0b1220;
69+
font-size: 1.45rem;
70+
line-height: 1.25;
71+
font-weight: 720;
72+
}
73+
74+
.post h2 a {
75+
color: inherit;
76+
text-decoration: none;
77+
}
78+
79+
.post h2 a:hover,
80+
.post h2 a:focus-visible {
81+
color: #0f766e;
82+
}
83+
84+
.post p {
85+
margin: 14px 0 0;
86+
color: #4b5563;
87+
font-size: 1rem;
88+
line-height: 1.65;
89+
}
90+
91+
.footer {
92+
display: flex;
93+
align-items: center;
94+
justify-content: space-between;
95+
gap: 20px;
96+
margin-top: 24px;
97+
}
98+
99+
.tags {
100+
display: flex;
101+
flex-wrap: wrap;
102+
gap: 8px;
103+
padding: 0;
104+
margin: 0;
105+
list-style: none;
106+
}
107+
108+
.tags li {
109+
border: 1px solid #cbd5e1;
110+
border-radius: 999px;
111+
padding: 4px 10px;
112+
color: #475569;
113+
background: #f8fafc;
114+
font-size: 0.78rem;
115+
line-height: 1.4;
116+
}
117+
118+
.readLink {
119+
display: inline-flex;
120+
align-items: center;
121+
gap: 6px;
122+
flex: 0 0 auto;
123+
color: #0f766e;
124+
font-size: 0.95rem;
125+
font-weight: 650;
126+
text-decoration: none;
127+
}
128+
129+
.readLink:hover,
130+
.readLink:focus-visible {
131+
color: #115e59;
132+
text-decoration: underline;
133+
text-underline-offset: 3px;
134+
}
135+
136+
.readLink svg {
137+
width: 1rem;
138+
height: 1rem;
139+
}
140+
141+
:global(html.dark) .page {
142+
color: #e5e7eb;
143+
}
144+
145+
:global(html.dark) .kicker,
146+
:global(html.dark) .post h2 a:hover,
147+
:global(html.dark) .post h2 a:focus-visible,
148+
:global(html.dark) .readLink {
149+
color: #2dd4bf;
150+
}
151+
152+
:global(html.dark) .header h1,
153+
:global(html.dark) .post h2 {
154+
color: #f8fafc;
155+
}
156+
157+
:global(html.dark) .intro,
158+
:global(html.dark) .post p,
159+
:global(html.dark) .meta {
160+
color: #a7b0c0;
161+
}
162+
163+
:global(html.dark) .post {
164+
border-color: #273346;
165+
background: #111827;
166+
box-shadow: none;
167+
}
168+
169+
:global(html.dark) .tags li {
170+
border-color: #334155;
171+
color: #cbd5e1;
172+
background: #182233;
173+
}
174+
175+
@media (max-width: 700px) {
176+
.page {
177+
padding: 48px 18px 72px;
178+
}
179+
180+
.header h1 {
181+
font-size: 1.9rem;
182+
}
183+
184+
.post {
185+
padding: 22px;
186+
}
187+
188+
.footer {
189+
align-items: flex-start;
190+
flex-direction: column;
191+
}
192+
193+
.meta {
194+
flex-direction: column;
195+
gap: 4px;
196+
}
197+
198+
.meta span::before {
199+
content: none;
200+
margin: 0;
201+
}
202+
}

0 commit comments

Comments
 (0)