إعادة بناء لوحة تحكم SaaS في يوم واحد باستخدام Claude Code — Tailwind v4 وتوافق الأعمدة بـ CSS Grid

HeatMapX Engineering Team12 min read
  • engineering
  • tailwind
  • css-grid
  • dark-mode
  • claude-code

TL;DR

  • المشكلة: 11 من أصل 15 مستخدمًا جديدًا (73%) أتمّوا التسجيل لكنهم غادروا دون إضافة أي رابط
  • الفرضية: كل أداة خرائط حرارية في فئتنا تعتمد واجهة فاتحة افتراضيًا — ربما تُسبب لوحتنا الداكنة ارتباكًا وخروجًا مبكرًا
  • ما فعلناه: إعادة تصميم شاملة للوحة في يوم واحد — مع تبديل ثلاثي (فاتح / داكن / النظام) وقائمة جدولية بأسلوب GitHub
  • الدروس المستفادة: @custom-variant في Tailwind v4، فخ عمود auto في CSS Grid، دفاع ثلاثي الطبقات ضد النصوص الطويلة، ولماذا تُعدّ 10–16 دورة تصميم أمرًا طبيعيًا لا استثناءً
  • النتيجة: أثر التغيير على معدل الخروج قيد القياس؛ تقرير متابعة مخطط بعد أسبوعين من البيانات

الخلفية: هل كنّا نفقد المستخدمين بسبب ظلام لوحة التحكم؟

HeatMapX (heatmapx.com) أداة للخرائط الحرارية وتحسين معدل التحويل، يمكن استدعاؤها من Claude Code عبر واجهة سطر الأوامر. بعد انطلاق أول حملة ترويجية، لاحظنا نمطًا واضحًا: 11 من أصل 15 مستخدمًا جديدًا (73%) أكملوا التسجيل لكنهم غادروا دون إضافة أي رابط واحد.

الفرضية الأولى: كل أداة خرائط حرارية رئيسية في فئتنا تُشحن بـ واجهة فاتحة افتراضيًا. كانت HeatMapX الاستثناء الوحيد بواجهة إدارية داكنة. ربما يواجه المستخدمون الجدد لوحة داكنة وسط بحر من التطبيقات ذات الألوان الفاتحة فيغادرون بسبب الارتباك.

لهذا تعاوننا مع Claude Code وأجرينا إعادة تصميم كاملة في جلسة واحدة. يتناول هذا المقال النصف الأول من اختبار الفرضية — تشخيص مشكلة الخروج وتنفيذ التصميم الجديد. أما المتابعة بالأرقام الفعلية فستأتي بعد أسبوعين من بيانات ما بعد النشر.

إعداد التصميم النهائي

العنصر الوضع الفاتح الوضع الداكن
خلفية الصفحة bg-neutral-100 (#f5f5f5) dark:bg-neutral-950 (#0a0a0a)
سطح البطاقة bg-white + border-slate-200 dark:bg-slate-900 + dark:border-slate-800
النص الأساسي text-slate-900 dark:text-slate-100
اللون المحوري (CTA) bg-orange-600 (#ea580c) مشترك بين الوضعين
نصف قطر الحافة rounded-md (6px) · بلا ظلال · بأسلوب GitHub
مفتاح السمة تبديل ثلاثي ☀ / 💻 / 🌙 في رأس الصفحة

الوضع الداكن في Tailwind v4 — من الإعداد الصفري إلى التبديل اليدوي

البداية مع @media prefers-color-scheme

السلوك الافتراضي في Tailwind v4 يربط متغير dark: مباشرةً بـ @media (prefers-color-scheme: dark). بدون أي إعداد، يتابع الأداة تلقائيًا إعداد الوضع الداكن في نظام التشغيل.

{/* layout.tsx */}
<div className="bg-neutral-100 text-slate-900 dark:bg-neutral-950 dark:text-slate-100">
  ...
</div>

هذا يغطي حالة "اتباع النظام" (داكن ليلًا تلقائيًا وما شابه) دون أي إعداد إضافي. لكن المستخدمين أحيانًا يريدون تجاوز إعداد النظام — قد يكون "النظام" غير دقيق، أو هو مجرد تفضيل شخصي — لذا وسّعناه إلى تبديل ثلاثي الخيارات.

التحويل إلى متغير داكن قائم على الفئة

في Tailwind v4 يمكنك إعادة تعريف سلوك متغير dark: باستخدام @custom-variant. حوّلنا الإعداد بحيث لا يُفعَّل dark: إلا عند وجود .dark على عنصر <html>:

/* globals.css */
@import "tailwindcss";

@custom-variant dark (&:where(.dark, .dark *));

غلاف :where() يُبقي الخصوصية عند الصفر. بدونه، قد تتعارض أنماط المتغير الداكن مع قواعد الخصوصية الموجودة في ورقة الأنماط.

سكريبت المزامنة لمنع وميض غير المُنسَّق

إذا طبّقت السمة داخل useEffect بعد إعادة التحميل في React، ستحصل على وميض للمحتوى غير المُنسَّق (FOUC) — وميض أبيض قصير عند التصيير الأول. الحل هو سكريبت مزامنة مُدرج مباشرةً في <head> يعمل قبل تصيير جسم الصفحة:

{/* app/layout.tsx */}
<head>
  <script dangerouslySetInnerHTML={{ __html: `
    (function(){try{
      var t = localStorage.getItem('theme') || 'system';
      var d = t === 'dark' || (t === 'system' &&
        window.matchMedia('(prefers-color-scheme: dark)').matches);
      if (d) document.documentElement.classList.add('dark');
    }catch(e){}})();
  `}} />
</head>

يعمل هذا بشكل متزامن قبل تحميل React، لذا تكون .dark موجودة على عنصر <html> بحلول وقت بدء تصيير الجسم. أضف suppressHydrationWarning إلى <html> لإسكات تحذير عدم تطابق التحميل.

💡 الدرس الأول: dark: في Tailwind v4 يعمل بالإعداد الصفري. إذا كان تتبّع النظام كافيًا، فلا حاجة لأي إعداد. أضف @custom-variant والسكريبت فقط حين تحتاج إلى تبديل يدوي من المستخدم — نهج نظيف على مرحلتين.

توافق الأعمدة بـ CSS Grid — وأين يعضّك auto

القائمة الأحادية العمود الأولية لم تكن كافية

بعد ملاحظة أن بطاقات المواقع بعد التسجيل تبدو "غريبة"، جرّبنا الحل الواضح أولًا: استبدلنا شبكة بطاقات ثنائية الأعمدة بقائمة أحادية العمود. كل SiteCard استخدمت flexbox داخليًا لمحاذاة name · url · status · events مع سهم في اليمين.

المشكلة ظهرت فور وجود عناصر متعددة:

Short · example.com · ● Active · 5,577
Very Long Site Name That... · normal.com · ● Active · 5,577
ACME · acme.io · ● Active · 5,577

· Active و· 5,577 و› chevron تقع في إحداثيات x مختلفة في كل صف. العين لا تستطيع التثبيت على "عمود الحالة هنا". لا تبدو كقائمة — بل كنصوص متراكمة.

لم تكن هذه قائمة. كانت نصوصًا متراكمة رأسيًا.

التحويل إلى تخطيط جدولي بـ CSS Grid

استبدلنا الـ flexbox الداخلي بـ تعريف أعمدة ثابتة في CSS Grid. بما أن كل بطاقة تشترك في نفس قالب الشبكة، تتوافق حواف الأعمدة اليسرى بشكل مثالي عبر الصفحة:

// SiteCard.tsx
<Link className="grid grid-cols-[minmax(0,2fr)_minmax(0,2fr)_minmax(0,1.5fr)_minmax(0,3fr)_110px_72px_20px] items-center gap-x-4 ...">
  <h3 className="truncate">{name}</h3>
  <span className="truncate">{displayUrl}</span>
  <span className="truncate font-mono">{apiKey}</span>
  <span className="truncate font-mono">{trackerSnippet}</span>
  <span>● Active</span>
  <span>{count}</span>
  <span>›</span>
</Link>

صف الرأس يستخدم نفس قالب الشبكة تمامًا:

// page.tsx header row
<div className="grid grid-cols-[minmax(0,2fr)_minmax(0,2fr)_minmax(0,1.5fr)_minmax(0,3fr)_110px_72px_20px] gap-x-4 ...">
  <span>SITE</span>
  <span>URL</span>
  <span>API KEY</span>
  <span>TRACKER TAG</span>
  <span>STATUS</span>
  <span>TODAY</span>
  <span></span>
</div>

الفخ: أعمدة auto في شبكات منفصلة تُحدَّد أبعادها بشكل مستقل

في المحاولة الأولى استخدمنا grid-cols-[minmax(0,2fr)_minmax(0,2fr)_auto_auto_auto]. المنطق: "auto يتكيف مع المحتوى، وبما أن الرأس و SiteCard يشتركان في نفس نص القالب، سيتوافقان."

لم يتوافقا. إليك السبب:

  • عمود auto في الرأس: يُحدَّد بعرض نص "STATUS"
  • عمود auto في SiteCard: يُحدَّد بعرض نص "● Active"
  • كلاهما حاويات شبكة منفصلة — يُحسب حجم المسار بشكل مستقل لكل منهما

حلّان:

  1. عرض ثابت: استبدل auto بقيم بكسل صريحة مثل 110px، 72px (هذا ما أطلقناه)
  2. CSS Subgrid: ضع الرأس وكل SiteCards داخل شبكة أم واحدة، ثم اجعل كل صف يرث مسارات الأم عبر subgrid (أكثر تعقيدًا)

💡 الدرس الثاني: لا تستخدم أعمدة auto حين تحتاج إلى توافق جدولي عبر حاويات شبكة منفصلة. كل حاوية تحسب عروض مساراتها بناءً على محتواها الخاص. استخدم عروض البكسل الثابتة أو شارك المسارات عبر Subgrid.

الدفاع ضد النصوص الطويلة — 3 طبقات

كان مخطط قاعدة البيانات الأولي:

CREATE TABLE sites (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  name text NOT NULL,  -- no length limit
  url text NOT NULL,   -- no length limit
  ...
);

نوع text في Postgres غير محدود فعليًا. اسم موقع مكوّن من 10,000 حرف SQL صحيح تمامًا — وسيُدمر التخطيط الجدولي فورًا. انتقالنا إلى شبكة ذات أعمدة ثابتة جعل قيود طول النص غير قابلة للتفاوض.

الدفاع الثلاثي الذي توصّلنا إليه:

  1. حدّ HTML5 في الواجهة الأمامية: maxLength={60} / maxLength={512} على حقول الإدخال
  2. التحقق من جانب الخادم: Server Action يتحقق من name.length > 60 ويُعيد خطأ
  3. اقتطاع العرض: فئة truncate على كل خلية شبكة، مقترنة بـ minmax(0, ...) لتفعيل الفيض

الطبقة الثالثة هي التي يغفل عنها الناس. truncate تتوسع إلى overflow: hidden + text-overflow: ellipsis + white-space: nowrap، لكن في سياق flex أو grid تحتاج أيضًا إلى min-width: 0 على الخلية — وإلا تتمدد الخلية لتناسب محتواها ولن يُفعَّل الفيض أبدًا. minmax(0, ...) يوفّر هذا تحديدًا.

16 دورة تصميم

مرّت SiteCard وحدها بـ 16 دورة تصميم في هذه الجلسة. مختارات:

# التغيير السبب
1 تحويل CopyBlock إلى حبّة رمادية فاتحة خلفية سوداء + نص أخضر بدت غريبة في وضع GitHub الفاتح
3 مقارنة أربعة خيارات A/B/C/D الحاجة إلى إشارة نقر أقوى
4 التحويل إلى قائمة عمود واحد رُفضت بوصفها "مزخرفة أكثر مما ينبغي" — عودة إلى الأساسيات
7 إزالة CTA "View Heatmap →" "لا تبدو نظيفة"
9 التحويل إلى تخطيط جدولي بـ CSS Grid تخطيط النص القائم على الفواصل "لا يبدو كقائمة"
10 auto → عروض بكسل ثابتة أعمدة الرأس والبطاقة غير متوافقة
11 استعادة عمود API KEY "لحظة، أين مفتاح API؟"
12 إضافة عمود TRACKER TAG "ضعهما جنبًا إلى جنب"
14 إضافة maxLength والتحقق من الخادم حل صحيح لمتانة النصوص الطويلة
16 إضافة زر "+" بجانب العنوان (الوضع المدمج) إضافة سريعة دون التمرير للأسفل

لا تتوقع الوصول إليه من المحاولة الأولى. دورة التصميم تعني "تحقّق في خادم التطوير → تغذية راجعة فورية → المحاولة التالية"، تتكرر أكثر من 10 مرات. خطّط لهذه الحلقة. بيئة التطوير بمساعدة الذكاء الاصطناعي مثل Claude Code هي المكان الذي يتألق فيه هذا النمط حقًا.

الأنماط التي نجحت فعلًا مع التطوير بمساعدة الذكاء الاصطناعي

هذه ليست ملاحظات خاصة بأداة بعينها — بل أنماط عامة أثبتت فاعليتها عند الاقتران بذكاء اصطناعي تفاعلي:

  • لا تكلفة للبحث في الوثائق لصياغات API الجديدة: @custom-variant في Tailwind v4 حديثة نسبيًا. Claude Code أظهرها فورًا في شكل قابل للتطبيق، دون الحاجة للغوص في الوثائق.
  • حلقة "نفّذ ← تحقق ← أصلح" تعمل في ثوانٍ: إعادة إنتاج عدم توافق auto في CSS Grid، وعزل السبب، وإطلاق الإصلاح، استغرق حوالي 5 دقائق.
  • العمل الميكانيكي الضخم يتلاشى: استبدال النص التجريبي في 15 ملف ترجمة (in-golexample.com) في دورة واحدة.
  • تقارب مع HMR: تعديل → حفظ → تحديث فوري للمتصفح → تغذية راجعة → إعادة تعديل. الحلقة تعمل بسرعة كافية للبقاء في حالة التركيز.
  • من البداية إلى النهاية في جلسة واحدة: من إتمام إعادة التصميم مباشرةً إلى النشر في بيئة الإنتاج ومتابعة نشر Vercel — دون تبديل السياق.

الأنماط التي لم تعمل بالقدر نفسه موجودة في القسم التالي.

ما كان صعبًا / ما لا يزال قائمًا

  • ترجمة التغذية الراجعة المبهمة إلى قرارات هيكلية ضاعف عدد الدورات. "هذا يبدو غريبًا" كان لا بد أن يتحول إلى "جدولي مقابل قائمة مقابل بطاقات" قبل أن يحدث أي شيء مفيد. التوصل إلى هذا الاتفاق الهيكلي مسبقًا كان سيوفر 3–4 دورات.
  • حول الدورة العاشرة، استمرت المتطلبات في التوسع — "كل شيء مضمّن"، "كل شيء مقتطع"، "كل الأعمدة" — وبعض القيود كانت في تعارض حقيقي (كثافة المعلومات مقابل قابلية المسح). لم يكن ثمة اختصار نظيف للشكل النهائي.
  • الجوّال لم يُلمس بعد. شبكة من 7 أعمدة تُنتج تمريرًا أفقيًا على الشاشات الصغيرة. الحل إما استعلام وسائط sm: يُكوّم الأعمدة، أو مكوّن جوّال منفصل. هذا هو الشيء التالي للمعالجة.

الخلاصة

في جلسة واحدة، أعدنا بناء لوحة تحكم HeatMapX من شبكة بطاقات داكنة إلى تخطيط يدعم الفاتح/الداكن/النظام مع قائمة جدولية بأسلوب GitHub. النقاط التقنية الرئيسية:

  1. الوضع الداكن في Tailwind v4: ابدأ بالوضع الداكن القائم على @media — لا إعداد مطلوب. أضف @custom-variant والسكريبت فقط حين يحتاج المستخدمون إلى تجاوز يدوي.
  2. التوافق الجدولي بـ CSS Grid: أعمدة auto لا تتوافق عبر حاويات شبكة منفصلة. استخدم عروض البكسل الثابتة أو CSS Subgrid — هذان هما الخياران الموثوقان الوحيدان.
  3. متانة النصوص الطويلة: ثلاث طبقات، كلها ضرورية — maxLength في الواجهة، فحص الطول من جانب الخادم، وtruncate في العرض مع minmax(0, ...).
  4. ميزانية دورات التصميم: افترض 10–16 دورة لكل مكوّن وابنِ حلقة تغذية راجعة سريعة من البداية.

هل سيؤثر هذا فعلًا على معدل الخروج؟ لا يزال السؤال مفتوحًا. سننشر المتابعة بأسبوعين من البيانات الحقيقية.

خرائط حرارية تُشغّل من Claude Code — ابدأ مجانًا.

ألصق وسم تتبع واحد، واحصل على التحليل واقتراحات CRO من سطر الأوامر.