ブログ/

非対称レイアウトブログ

Preview

フィーチャー記事を左カラムに大きく、関連記事を右カラムにリスト表示する非対称レイアウトのブログセクション

Source Code
tsx
186 lines
1export function BlogAsymmetric001() {
2 const featured = {
3 title: "デザインの余白が生む静かな説得力",
4 excerpt:
5 "情報を詰め込むのではなく、引き算の美学でユーザーの視線を導く。ホワイトスペースの戦略的な活用が、プロダクトの品格をいかに高めるかを考察します。",
6 date: "2024.03.12",
7 category: "デザイン",
8 readTime: "10 min read",
9 };
10
11 const posts = [
12 {
13 title: "コンポーネント設計の境界線",
14 excerpt:
15 "再利用性と柔軟性のバランスを取る、実践的なコンポーネント分割の指針。",
16 date: "2024.03.05",
17 category: "エンジニアリング",
18 readTime: "7 min read",
19 },
20 {
21 title: "色彩心理とブランド認知",
22 excerpt:
23 "色が人の判断に与える無意識的な影響と、ブランドカラー選定の科学的アプローチ。",
24 date: "2024.02.28",
25 category: "デザイン",
26 readTime: "6 min read",
27 },
28 {
29 title: "レスポンシブを超えた適応型UI",
30 excerpt:
31 "画面サイズだけでなく、コンテキストに応じて振る舞いを変えるインターフェースの設計論。",
32 date: "2024.02.20",
33 category: "デザイン",
34 readTime: "8 min read",
35 },
36 {
37 title: "静的型付けが変えるチーム開発",
38 excerpt:
39 "TypeScriptの導入がコードレビューの質と開発速度に与える定量的な効果。",
40 date: "2024.02.14",
41 category: "エンジニアリング",
42 readTime: "5 min read",
43 },
44 ];
45
46 return (
47 <section className="bg-background py-28 border-t border-border">
48 <div className="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
49 {/* ヘッダー */}
50 <div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
51 <div>
52 <p className="text-[10px] uppercase tracking-[0.3em] text-muted-foreground">
53 Insights
54 </p>
55 <h2 className="mt-3 text-2xl font-medium tracking-wide text-foreground sm:text-3xl">
56 ブログ
57 </h2>
58 </div>
59 <a
60 href="#"
61 className="inline-flex items-center gap-2 text-xs uppercase tracking-[0.2em] text-muted-foreground transition-colors duration-200 hover:text-foreground"
62 >
63 すべての記事
64 <svg
65 className="h-3.5 w-3.5"
66 fill="none"
67 stroke="currentColor"
68 viewBox="0 0 24 24"
69 >
70 <path
71 strokeLinecap="round"
72 strokeLinejoin="round"
73 strokeWidth={1.5}
74 d="M17 8l4 4m0 0l-4 4m4-4H3"
75 />
76 </svg>
77 </a>
78 </div>
79
80 <div className="mt-4 h-px bg-border/40" />
81
82 {/* 非対称レイアウト */}
83 <div className="mt-12 grid grid-cols-1 gap-12 lg:grid-cols-5">
84 {/* 左カラム — フィーチャー記事 */}
85 <div className="lg:col-span-3">
86 <a href="#" className="group block">
87 <div className="relative aspect-[16/10] overflow-hidden rounded-lg border border-border bg-muted">
88 <div className="absolute inset-0 bg-gradient-to-br from-foreground/5 to-foreground/10 transition-all duration-500 group-hover:from-foreground/10 group-hover:to-foreground/15" />
89 <div className="absolute left-3 top-3 h-1.5 w-1.5 rounded-full bg-foreground/20" />
90 <div className="absolute right-3 top-3 h-1.5 w-1.5 rounded-full bg-foreground/20" />
91 <div className="absolute bottom-3 left-3 h-1.5 w-1.5 rounded-full bg-foreground/20" />
92 <div className="absolute bottom-3 right-3 h-1.5 w-1.5 rounded-full bg-foreground/20" />
93 </div>
94
95 <div className="mt-6">
96 <div className="flex items-center gap-3">
97 <span className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground/70">
98 {featured.category}
99 </span>
100 <span className="h-px w-4 bg-border" />
101 <span className="text-[10px] tracking-[0.2em] text-muted-foreground/50">
102 {featured.date}
103 </span>
104 </div>
105 <h3 className="mt-3 text-xl font-medium tracking-wide text-foreground transition-colors duration-200 group-hover:text-muted-foreground sm:text-2xl">
106 {featured.title}
107 </h3>
108 <p className="mt-3 text-sm font-light leading-relaxed text-muted-foreground">
109 {featured.excerpt}
110 </p>
111 <div className="mt-4 flex items-center gap-2 text-[10px] tracking-[0.2em] text-muted-foreground/50">
112 <svg
113 className="h-3 w-3"
114 fill="none"
115 stroke="currentColor"
116 viewBox="0 0 24 24"
117 >
118 <path
119 strokeLinecap="round"
120 strokeLinejoin="round"
121 strokeWidth={1.5}
122 d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
123 />
124 </svg>
125 {featured.readTime}
126 </div>
127 </div>
128 </a>
129 </div>
130
131 {/* 右カラム — 記事リスト */}
132 <div className="lg:col-span-2">
133 <div className="divide-y divide-border/40">
134 {posts.map((post, i) => (
135 <a
136 key={post.title}
137 href="#"
138 className="group/item block py-6 first:pt-0 last:pb-0"
139 >
140 <div className="flex items-start gap-4">
141 <span className="mt-0.5 text-xs font-light tabular-nums text-muted-foreground/30">
142 {String(i + 1).padStart(2, "0")}
143 </span>
144 <div className="flex-1">
145 <div className="flex items-center gap-3">
146 <span className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground/70">
147 {post.category}
148 </span>
149 <span className="h-px w-3 bg-border" />
150 <span className="text-[10px] tracking-[0.2em] text-muted-foreground/50">
151 {post.date}
152 </span>
153 </div>
154 <h3 className="mt-2 text-sm font-medium tracking-wide text-foreground transition-colors duration-200 group-hover/item:text-muted-foreground">
155 {post.title}
156 </h3>
157 <p className="mt-1.5 text-xs font-light leading-relaxed text-muted-foreground">
158 {post.excerpt}
159 </p>
160 <span className="mt-2 inline-flex items-center gap-1.5 text-[10px] tracking-[0.2em] text-muted-foreground/50">
161 <svg
162 className="h-3 w-3"
163 fill="none"
164 stroke="currentColor"
165 viewBox="0 0 24 24"
166 >
167 <path
168 strokeLinecap="round"
169 strokeLinejoin="round"
170 strokeWidth={1.5}
171 d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
172 />
173 </svg>
174 {post.readTime}
175 </span>
176 </div>
177 </div>
178 </a>
179 ))}
180 </div>
181 </div>
182 </div>
183 </div>
184 </section>
185 );
186}