بلاگِ متنی
پیشنمایش تمامصفحهبلاگِ ساده بدونِ عکس: ستونِ تاریخ کنارِ هر نوشته، برچسب، زمانِ خواندن و جستوجو؛ برای وبلاگِ شخصی و فنی.
۱. نصب کامپوننتها
این بلاک از ۴ آیتم دیگ استفاده میکند: control-sizeiconspersiansearch-field
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/search-field.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/search-field.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/search-field.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/search-field.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
blog-list.tsx
"use client"
import * as React from "react"
import { ChevronLeftIcon } from "@/components/icons"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { SearchField } from "@/components/ui/search-field"
import { formatJalali, toFaDigits, toSearchKey } from "@/lib/persian"
const POSTS = [
{ title: "قانونِ ۵۰/۳۰/۲۰ برای بودجهبندی ماهانه", excerpt: "روشی ساده که بدونِ اکسل هم کار میکند.", date: "2026-10-02", minutes: 5, tag: "مالی شخصی" },
{ title: "پشتِ صحنهٔ گزارشِ ماهانهٔ خودکار", excerpt: "از ایده تا انتشار در شش هفته.", date: "2026-09-27", minutes: 8, tag: "محصول" },
{ title: "فاکتورِ رسمی بدونِ دردسر", excerpt: "چه اطلاعاتی لازم است و کجا اشتباه میکنیم.", date: "2026-09-25", minutes: 5, tag: "کسبوکار" },
{ title: "کارمزدِ جدیدِ کارتبهکارت از مهر", excerpt: "جدولِ کارمزدها و راههایی که کمتر بپردازید.", date: "2026-09-12", minutes: 3, tag: "اخبار" },
{ title: "پساندازِ اضطراری: چقدر کافی است؟", excerpt: "عددِ درست به سه چیز بستگی دارد.", date: "2026-08-30", minutes: 6, tag: "مالی شخصی" },
]
/**
* بلاگِ ساده و متنی: بدونِ عکس، با ستونِ تاریخ کنارِ هر نوشته، برچسب، زمانِ خواندن و
* جستوجو؛ برای وبلاگهای شخصی، تغییراتِ فنی و نوشتههایی که متن مهمتر از تصویر است.
*/
export default function BlogListBlock() {
const [query, setQuery] = React.useState("")
const visible = POSTS.filter((post) => toSearchKey(post.title + post.excerpt).includes(toSearchKey(query)))
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-3xl flex-col gap-10 px-4 py-16 sm:px-6">
<header className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<h1 className="text-4xl font-black">نوشتهها</h1>
<p className="text-muted-foreground">دربارهٔ پول، کار و ساختنِ آوند.</p>
</div>
<SearchField aria-label="جستوجو در نوشتهها" placeholder="جستوجو" value={query} onValueChange={setQuery} />
</header>
<ol className="flex flex-col divide-y divide-border">
{visible.map((post) => (
<li key={post.title}>
<a href="#" className="group grid grid-cols-1 gap-2 py-6 sm:grid-cols-[8rem_minmax(0,1fr)_auto] sm:items-baseline sm:gap-6">
<span className="text-sm text-muted-foreground tabular-nums">{formatJalali(new Date(post.date), "short")}</span>
<span className="flex flex-col gap-1.5">
<span className="text-lg font-bold group-hover:underline">{post.title}</span>
<span className="leading-7 text-muted-foreground">{post.excerpt}</span>
<span className="text-xs text-muted-foreground">
{post.tag} · {toFaDigits(post.minutes)} دقیقه
</span>
</span>
<ChevronLeftIcon aria-hidden className="hidden size-5 text-muted-foreground transition-transform group-hover:-translate-x-1 sm:block ltr:rotate-180" />
</a>
</li>
))}
{visible.length === 0 && <li className="py-10 text-center text-muted-foreground">نوشتهای با این واژه پیدا نشد.</li>}
</ol>
</main>
</ControlSizeProvider>
)
}