post.md
23/10/2026 · قراءة 3 دقيقة
كيف بُني هذا الموقع
جولة قصيرة في بنية cmkrw.com، من تخطيط الطرفية إلى Markdown والرسوم البيانية وتقارير الأخطاء.
- nextjs
- design
المحتويات
هذا الموقع صغير عن قصد: صفحة واحدة، ومدونة واحدة، وأقل قدر ممكن من JavaScript. إليك كيف تتكامل أجزاؤه، إن أردت بناء شيء مشابه.
الطرفية هي التخطيط
صُمّم الموقع كتطبيق طرفية. يمتد شريط علامات تبويب في الأعلى، ويقع سطر الحالة في الأسفل، وكل ما بينهما لوحات: صناديق بإطار يستقر عنوانها في حافتها العلوية. مكوّن صغير واحد هو Pane يرسمها جميعًا.
المدونة بنمط القائمة والتفاصيل: تبقى القائمة ثابتة بينما يتغير المقال.
ويتبع التنقل الفكرة نفسها. في المدونة تبقى قائمة المقالات على جانب بينما يُفتح المقال على الجانب الآخر، فلا يحتاج التنقل بين المقالات إلى رابط رجوع. وتعمل الاختصارات كما في الطرفية، ويمكنك فرز هذا الجدول بالنقر على رأس أي عمود:
| 1 | فتح الرئيسية | عام |
| 2 | فتح المدونة | عام |
| j | التمرير لأسفل | عام |
| k | التمرير لأعلى | عام |
| g g | الانتقال إلى البداية | عام |
| G | الانتقال إلى النهاية | عام |
| n | مقال أقدم | المدونة |
| p | مقال أحدث | المدونة |
| d | تبديل المظهر | عام |
| ? | عرض الاختصارات | عام |
الحركة في معظمها CSS. يُكتب الأمر في رأس الصفحة بتحريك steps()، وتظهر اللوحات تدريجيًا عند التمرير إليها، ويتبع الخط تحت شريط التبويب ونسبة التمرير في سطر الحالة موضع التمرير. وكل ذلك يتوقف عند تفعيل prefers-reduced-motion.
كتابة المقالات
المقالات ملفات Markdown عادية في content/blog/<slug>/<locale>.md تُعرض بواسطة TanStack Markdown. إذا غابت ترجمة يُستخدم الملف الإنجليزي، ويُبقي draft: true المقال خارج نسخة الإنتاج.
وتتولى ثلاث أدوات من TanStack بقية العمل:
- Markdown يحلل المقال ويعرضه كعناصر React دون سلسلة HTML وسيطة.
- Highlight يلوّن كتل الشيفرة. تُضمَّن فقط اللغات التي أسجلها، وتأتي الألوان من متغيرات CSS نفسها التي يستخدمها بقية المظهر.
- Charts وTable يُدرجان بتعليق من سطر واحد يسمّي ملف JSON بجوار المقال.
يمكن أن تحمل كتل الشيفرة عنوان ملف:
export function createEmbeds(slug: string, locale: string) {
return {
chart: ({ data = "", kind = "bar", x = "x", y = "y", title = "Chart" }) => (
<TuiChart
rows={getPostDataset(slug, data, locale)}
kind={kind}
x={x}
y={y}
label={title}
/>
),
}
}وتعمل جداول Markdown العادية أيضًا وتحصل على التنسيق نفسه:
| الطبقة | المجلد | المسؤولية |
|---|---|---|
| المسارات | app/ | الصفحات والبيانات الوصفية والأيقونات والصور |
| الواجهة | components/tui/ | اللوحات وشريط التنقل وسطر الحالة وربط المفاتيح |
| الميزات | features/ | أقسام الصفحة الرئيسية والمدونة |
| الأدوات | lib/, hooks/ | البيانات الوصفية والمراقبة وإدخال المفاتيح |
وهذا عدد ملفات المصدر في كل مجلد وقت الكتابة:
عرض البيانات
| folder | files |
|---|---|
| app | 17 |
| components | 16 |
| lib | 16 |
| features | 15 |
| i18n | 4 |
| hooks | 3 |
| types | 1 |
أيقونات وبطاقات مشاركة مولَّدة
يبني Next.js 16 صور البيانات الوصفية من ملفات في مجلد app/:
- يرسم
icon.tsxوapple-icon.tsxأيقونة الموقع وأيقونة iOS من ملف SVG واحد هوassets/logo-icon.svg. - يرسم
opengraph-image.tsxبطاقة المشاركة للصفحة الرئيسية، ويرسم ملف آخر بطاقة لكل مقال.
لا يوجد favicon.tsx. تنص الوثائق صراحةً على أن الأيقونة المفضلة لا يمكن توليدها، لذا يتولى icon.tsx هذه المهمة. وتشترك كلها في مجموعة صغيرة من العناصر الأساسية، فتغيير شعار العلامة يغيّر الأيقونة وجميع بطاقات المشاركة دفعة واحدة.
القياس والمراقبة
يتولى ملفان المراقبة. يعمل instrumentation.ts على الخادم ويبلّغ عن أخطاء الطلبات. ويعمل instrumentation-client.ts قبل الاستعادة (hydration) ويتتبع أخطاء العميل ومقاييس الويب وتغيّر المسارات. ويمرّر كلاهما الأحداث إلى مُبلِّغ يوزعها على عدة وجهات:
export const serverReporter = createReporter([
consoleSink,
...(endpoint ? [endpointSink(endpoint)] : []),
])تُرسل أحداث العميل إلى /api/observability الذي يسجلها. وعند ربط المُبلِّغ بجامع حقيقي لاحقًا يكفي إضافة وجهة واحدة دون لمس أي من ملفَّي الدخول.
الترجمات
يتوفر الموقع بعشر لغات، منها العربية والعبرية اللتان تُقرآن من اليمين إلى اليسار. تعيش كل العبارات في ملف رسائل لكل لغة؛ وإذا غاب مفتاح يومًا تُدمج رسائل اللغة الافتراضية من الأسفل، فلا تظهر صفحة فارغة. وتعمل المقالات بالطريقة نفسها: لكل لغة ملفها، وإذا غاب يُستخدم الإنجليزي.