<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Design on kenji.blog</title><link>http://kenji.blog/ar/categories/design/</link><description>Recent content in Design on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ar</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 03:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ar/categories/design/index.xml" rel="self" type="application/rss+xml"/><item><title>تخصيص سمة Hugo: كيفية إدراج Tailwind CSS</title><link>http://kenji.blog/ar/p/hugo-tailwind-css-customization-guide/</link><pubDate>Sun, 13 Sep 2026 03:00:00 +0900</pubDate><guid>http://kenji.blog/ar/p/hugo-tailwind-css-customization-guide/</guid><description>&lt;img src="http://kenji.blog/p/hugo-tailwind-css-customization-guide/img/eyecatch.jpg" alt="Featured image of post تخصيص سمة Hugo: كيفية إدراج Tailwind CSS" />&lt;h1 id="مقدمة-التآزر-القوي-بين-مولد-المواقع-الثابتة-hugo-و-tailwind-css">مقدمة: التآزر القوي بين مولد المواقع الثابتة Hugo و Tailwind CSS
&lt;/h1>&lt;p>في تطوير الواجهات الأمامية للويب الحديث، يعد تحقيق التوازن بين الأداء وتجربة المطور (DX: Developer Experience) أحد أهم التحديات في كل مشروع. الجمع بين &lt;strong>Hugo&lt;/strong>، الذي يتميز بسرعات بناء من بين الأسرع عالمياً في مولدات المواقع الثابتة (SSG)، و &lt;strong>Tailwind CSS&lt;/strong>، الذي جلب نموذجاً مبتكراً يعطي الأولوية للخدمات (Utility-first)، يمكن اعتباره حلاً جذرياً لهذا التحدي.&lt;/p>
&lt;p>تمت كتابة Hugo بلغة Go، ولديه أداء مذهل يتيح له إكمال بناء موقع يحتوي على آلاف الصفحات في ثوانٍ معدودة، أو حتى في أجزاء من الثانية. من ناحية أخرى، من خلال كتابة عدد لا يحصى من فئات الخدمات المعرفة مسبقًا (مثل &lt;code>flex&lt;/code>, &lt;code>text-center&lt;/code>, &lt;code>mt-4&lt;/code>) مباشرة في HTML، يقضي Tailwind CSS على الحاجة للتبديل بين ملفات CSS و HTML، مما يسرع عملية تكرار التصميم.&lt;/p>
&lt;p>في هذا المقال، سنشرح بالتفصيل الدقيق ومن وجهة نظر شاملة - بدءًا من أساسيات البنية المعمارية وصولاً إلى منظور التحسين الرياضي للأداء - خطوات إدراج Tailwind CSS في سمة Hugo، وبناء مسار موارد متقدم (Hugo Pipes) باستخدام PostCSS.&lt;/p>
&lt;hr>
&lt;h2 id="1-فئات-الخدمات-لـ-css-وتطور-التوجه-القائم-على-المكونات">1. فئات الخدمات لـ CSS وتطور التوجه القائم على المكونات
&lt;/h2>&lt;p>قبل الدخول في خطوات إدراج Tailwind CSS، من المفيد جداً أن نفهم بعمق تاريخ وتطور فلسفة تصميم CSS التي تكمن وراء سبب وجوب استخدامنا لـ Tailwind CSS.&lt;/p>
&lt;h3 id="حدود-تصميم-css-التقليدي-bem-و-oocss">حدود تصميم CSS التقليدي (BEM و OOCSS)
&lt;/h3>&lt;p>في تطوير الويب سابقاً، كان من أفضل الممارسات إعطاء أسماء فئات ذات دلالات (Semantic). على سبيل المثال، عند إنشاء مكون بطاقة (Card)، كنا نفصل بين HTML و CSS كالتالي:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__image&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;thumbnail&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__content&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__title&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>العنوان&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__description&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>النص التوضيحي يكتب هنا.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">border-radius&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">box-shadow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="nb">rgba&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mf">0.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">overflow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">hidden&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card__title&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">1.5&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-weight&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">bold&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* تستمر الأنماط التفصيلية لاحقاً */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>مثل هذا التصميم القائم على BEM (Block Element Modifier) يعمل جيداً عندما يكون حجم المشروع صغيراً، ولكنه يميل إلى التسبب في المشاكل التالية:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>استنفاد وإرهاق في التسمية&lt;/strong>: في كل مرة تنشئ فيها مكوناً مشابهاً، عليك التفكير في أسماء فئات جديدة (مثل: &lt;code>card-news&lt;/code>، &lt;code>card-featured&lt;/code>).&lt;/li>
&lt;li>&lt;strong>تضخم CSS&lt;/strong>: مع إضافة كل ميزة جديدة، يزداد عدد أسطر CSS. وبمجرد كتابة CSS، نادراً ما يتم حذفه بسبب الخوف من &amp;ldquo;عدم معرفة أين يتم استخدامه&amp;rdquo;، مما يؤدي إلى تراكم الأكواد الميتة.&lt;/li>
&lt;li>&lt;strong>تبديل السياق&lt;/strong>: نظرًا لأنه يتم إدارة بنية HTML وأنماط CSS في ملفات منفصلة، فإن عدد مرات التبديل بين علامات التبويب في المحرر يزداد بشكل كبير.&lt;/li>
&lt;/ol>
&lt;h3 id="التحول-النموذجي-بواسطة-tailwind-css">التحول النموذجي بواسطة Tailwind CSS
&lt;/h3>&lt;p>يحل Tailwind CSS هذه المشاكل من خلال نهج &amp;ldquo;دمج فئات الخدمات&amp;rdquo;. يبدو مكون البطاقة المذكور أعلاه كالتالي عند استخدام Tailwind CSS:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;rounded-lg shadow-md bg-white overflow-hidden&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;w-full h-48 object-cover&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;thumbnail&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;p-6&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold text-gray-800&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>العنوان&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2 text-gray-600&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>النص التوضيحي يكتب هنا.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>نظراً لأن اسم الفئة نفسه يمثل القيمة الدقيقة للنمط (مثل &lt;code>p-6&lt;/code> تعني &lt;code>padding: 1.5rem;&lt;/code>)، يمكنك التنبؤ بنتيجة العرض النهائية بمجرد النظر إلى HTML. علاوة على ذلك، من خلال مترجم JIT (Just-In-Time) الخاص بـ Tailwind، يتم استخراج الفئات المستخدمة بالفعل فقط إلى ملف CSS المخصص للإنتاج، مما يقلل حجم ملف CSS إلى أدنى حد ممكن.&lt;/p>
&lt;hr>
&lt;h2 id="2-بنية-hugo-pipes-و-postcss">2. بنية Hugo Pipes و PostCSS
&lt;/h2>&lt;p>لدمج Tailwind CSS في Hugo، تحتاج إلى فهم مسار معالجة الموارد الذي يُسمى &lt;strong>Hugo Pipes&lt;/strong>. تعد Hugo Pipes ميزة قوية تنجز جميع العمليات المتعلقة بالموارد داخلياً في Hugo، مثل تجميع Sass/SCSS، وحزم ودمج (Minify) JavaScript، وتنفيذ &lt;strong>PostCSS&lt;/strong> الذي سنستخدمه في هذا المقال.&lt;/p>
&lt;p>PostCSS هي أداة لتحويل CSS باستخدام إضافات JavaScript. في الواقع، يعمل Tailwind CSS نفسه كإضافة لـ PostCSS.&lt;/p>
&lt;h3 id="آلية-تحويل-ast-شجرة-بناء-الجملة-المجردة-عبر-postcss">آلية تحويل AST (شجرة بناء الجملة المجردة) عبر PostCSS
&lt;/h3>&lt;p>إن فهم كيفية معالجة PostCSS لملفات CSS مفيد للغاية عند استكشاف الأخطاء وإصلاحها. يوضح مخطط Mermaid التالي المسار من قراءة PostCSS لملف CSS، وتحويله عبر الإضافات، حتى إخراج ملف CSS النهائي.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Raw CSS (styles.css)&amp;#34;] --&amp;gt;|Parser| B[&amp;#34;AST (شجرة بناء الجملة المجردة)&amp;#34;]
B --&amp;gt; C[&amp;#34;إضافة 1: Tailwind CSS&amp;#34;]
C --&amp;gt; D[&amp;#34;إضافة 2: Autoprefixer&amp;#34;]
D --&amp;gt; E[&amp;#34;إضافة N: cssnano&amp;#34;]
E --&amp;gt;|Stringifier| F[&amp;#34;Compiled &amp;amp; Optimized CSS&amp;#34;]
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>Parser (المحلل)&lt;/strong>: يقوم بتحليل سلسلة CSS الخام المدخلة ويحولها إلى شجرة بناء الجملة المجردة (AST)، وهي بنية بيانات يمكن معالجتها برمجيًا.&lt;/li>
&lt;li>&lt;strong>Plugins (مجموعة الإضافات)&lt;/strong>:
&lt;ul>
&lt;li>&lt;strong>Tailwind CSS&lt;/strong>: يمسح ملفات القالب (HTML أو Markdown) ويستخرج فئات الخدمات المستخدمة كعقد إضافية على شجرة AST. كما أنه يوسع التوجيهات مثل &lt;code>@tailwind&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Autoprefixer&lt;/strong>: يراجع قاعدة بيانات &lt;code>Can I Use&lt;/code> ويضيف البادئات الخاصة بالمتصفحات (مثل &lt;code>-webkit-&lt;/code>, &lt;code>-moz-&lt;/code>) إلى خصائص AST عند الحاجة.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Stringifier (محول السلاسل)&lt;/strong>: بعد اكتمال التحويل، يقوم بتحويل شجرة AST مرة أخرى إلى سلاسل CSS يمكن للمتصفح فهمها لإخراجها.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-إعداد-البيئة-والمتطلبات-الأساسية">3. إعداد البيئة والمتطلبات الأساسية
&lt;/h2>&lt;p>دعونا نبدأ بخطوات الإدراج الفعلية. أولاً، تحقق مما إذا كانت البرامج الضرورية مثبتة.&lt;/p>
&lt;h3 id="المتطلبات-الأساسية">المتطلبات الأساسية
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Hugo Extended Version (النسخة الموسعة من Hugo)&lt;/strong>:
بدلاً من الإصدار العادي من Hugo، تعد &lt;strong>النسخة الموسعة&lt;/strong> التي تتضمن وظيفة معالجة Sass/SCSS وميزات دعم PostCSS مدمجة ضرورية. قم بتشغيل الأمر التالي في الطرفية (Terminal) وتأكد من أن السلسلة &lt;code>extended&lt;/code> متضمنة في معلومات الإصدار.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># مثال للإخراج المتوقع:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># hugo v0.121.2-4146... windows/amd64 BuildDate=... VendorInfo=gohugoio +extended&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Node.js و npm&lt;/strong>:
تعمل حزم التبعيات مثل Tailwind CSS و PostCSS على بيئة Node.js. تأكد من تثبيت Node.js (يُفضل إصدار الدعم طويل الأمد LTS).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">node -v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="تثبيت-حزم-npm">تثبيت حزم npm
&lt;/h3>&lt;p>قم بتهيئة npm في دليل الجذر للمشروع (الدليل الذي يحتوي على ملف إعداد Hugo &lt;code>hugo.toml&lt;/code>)، وقم بتثبيت الحزم المطلوبة.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># إنشاء ملف package.json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm init -y
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># تثبيت Tailwind CSS، و PostCSS، و Autoprefixer كتبعيات تطوير&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install -D tailwindcss postcss postcss-cli autoprefixer
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>[!IMPORTANT]
إذا لم يتم تثبيت &lt;code>postcss-cli&lt;/code>، فقد تحدث أخطاء عند استدعاء PostCSS من داخل Hugo. تستخدم Hugo Pipes برنامج &lt;code>postcss-cli&lt;/code> داخلياً، لذا تأكد من تثبيته.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="4-بناء-ملفات-التكوين-postcss-و-tailwind-css">4. بناء ملفات التكوين (PostCSS و Tailwind CSS)
&lt;/h2>&lt;p>بعد اكتمال تثبيت الحزم، سننشئ ملفي تكوين مهمين يتحكمان في سلوك المشروع. ضعهما في دليل الجذر للمشروع.&lt;/p>
&lt;h3 id="إنشاء-ملف-tailwindconfigjs">إنشاء ملف tailwind.config.js
&lt;/h3>&lt;p>قم بتنفيذ الأمر التالي في الطرفية لإنشاء ملف التكوين الافتراضي.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npx tailwindcss init
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>افتح ملف &lt;code>tailwind.config.js&lt;/code> الذي تم إنشاؤه في المحرر، وقم بإعداد خاصية &lt;code>content&lt;/code>. هذا الجزء مهم جداً. بناءً على المسارات المحددة هنا، يقوم Tailwind بتحليل الملفات واستخراج الفئات المستخدمة. وفقاً لهيكلية مشروع Hugo، حدد ملفات التخطيط والمحتوى بدقة.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/** @type {import(&amp;#39;tailwindcss&amp;#39;).Config} */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// تحديد الملفات المراد مسحها وفقًا لهيكل دليل Hugo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">content&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./content/**/*.md&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./content/**/*.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./layouts/**/*.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./assets/**/*.js&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// إذا كنت تستخدم سمة مخصصة، يجب تضمين دليل السمة أيضًا
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// &amp;#34;./themes/my-theme/layouts/**/*.html&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// قم بإضافة الألوان والخطوط المخصصة هنا
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;brand-primary&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#3490dc&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;brand-secondary&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#ffed4a&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fontFamily&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;sans&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Helvetica Neue&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Arial&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Hiragino Kaku Gothic ProN&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Meiryo&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sans-serif&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// أضف الإضافات الرسمية حسب الحاجة (مثل: Typography plugin)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// require(&amp;#39;@tailwindcss/typography&amp;#39;),
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="إنشاء-ملف-postcssconfigjs">إنشاء ملف postcss.config.js
&lt;/h3>&lt;p>بعد ذلك، قم بإنشاء ملف &lt;code>postcss.config.js&lt;/code> في جذر المشروع لتعريف أي الإضافات سينفذها PostCSS وبأي ترتيب.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tailwindcss&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoprefixer&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>باستخدام هذا الإعداد، عندما يقوم Hugo باستدعاء PostCSS، ستتم معالجة Tailwind CSS أولاً، تليها إضافة Autoprefixer للبادئات الخاصة بالمتصفحات.&lt;/p>
&lt;hr>
&lt;h2 id="5-بناء-مسار-موارد-css-في-hugo">5. بناء مسار موارد CSS في Hugo
&lt;/h2>&lt;p>بعد إتمام الإعدادات، سنقوم بدمج Tailwind CSS في جانب سمة Hugo.&lt;/p>
&lt;h3 id="5-1-إنشاء-ملف-css-كنقطة-إدخال">5-1. إنشاء ملف CSS كنقطة إدخال
&lt;/h3>&lt;p>في الدليل &lt;code>assets/css/&lt;/code> (قم بإنشائه إن لم يكن موجوداً)، قم بإنشاء ملف CSS ليكون نقطة الإدخال. هنا سنسميه &lt;code>main.css&lt;/code>.&lt;/p>
&lt;p>&lt;strong>مسار الملف: &lt;code>assets/css/main.css&lt;/code>&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* استيراد الأنماط الأساسية لـ Tailwind (مثل إعادة تعيين CSS) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">base&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* استيراد فئات المكونات */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">components&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* استيراد فئات الخدمات */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">utilities&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* إذا كنت بحاجة إلى كتابة CSS مخصص، يمكنك إضافته هنا،
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> ولكن يفضل استخدام extend في tailwind.config.js قدر الإمكان */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">layer&lt;/span> &lt;span class="nt">components&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">btn-primary&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">@apply&lt;/span> &lt;span class="err">bg-blue-500&lt;/span> &lt;span class="n">hover&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">bg-blue-700&lt;/span> &lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">white&lt;/span> &lt;span class="k">font&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">bold&lt;/span> &lt;span class="n">py-2&lt;/span> &lt;span class="n">px-4&lt;/span> &lt;span class="n">rounded&lt;/span> &lt;span class="k">transition&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">colors&lt;/span> &lt;span class="n">duration-300&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="5-2-تعديل-ملف-التخطيط-headhtml">5-2. تعديل ملف التخطيط (head.html)
&lt;/h3>&lt;p>بعد ذلك، ستقوم بقراءة ملف CSS أعلاه من قوالب Hugo، وكتابة المسار لمعالجته بواسطة PostCSS. عادةً يتم تعديل القالب الجزئي الذي يُعرّف داخل الوسم &lt;code>&amp;lt;head&amp;gt;&lt;/code> (مثل: &lt;code>layouts/partials/head.html&lt;/code>).&lt;/p>
&lt;p>&lt;strong>مسار الملف: &lt;code>layouts/partials/head.html&lt;/code>&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go-html-template" data-lang="go-html-template">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;viewport&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;width=device-width, initial-scale=1&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Title&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span> | &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Site.Title&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- جلب assets/css/main.css --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">:=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">resources&lt;/span>&lt;span class="na">.Get&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;css/main.css&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- تعريف خيارات PostCSS --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$options&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">:=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">dict&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;inlineImports&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">true&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">resources&lt;/span>&lt;span class="na">.PostCSS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$options&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- مسار تحسين الموارد لبيئة الإنتاج (Production) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">hugo&lt;/span>&lt;span class="na">.IsProduction&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 1. الضغط (Minify) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">minify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 2. البصمة (إضافة تجزئة لكسر ذاكرة التخزين المؤقت - Fingerprint) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">fingerprint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;sha512&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 3. إخراج الوسم مع تكامل الموارد الفرعية (SRI) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.RelPermalink&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span> &lt;span class="na">integrity&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.Data.Integrity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span> &lt;span class="na">crossorigin&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;anonymous&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- في بيئة التطوير (Development)، أخرجه كما هو بدون ضغط (لأولوية سرعة البناء) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.RelPermalink&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="شرح-المسار-ورسمه-التوضيحي-باستخدام-mermaid">شرح المسار ورسمه التوضيحي باستخدام Mermaid
&lt;/h4>&lt;p>يوضح الرسم التالي كيف يقوم كود قوالب Go المذكور أعلاه بمعالجة ملف CSS في سلسلة من عمليات المسار.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;assets/css/main.css&amp;#34;] --&amp;gt;|resources.Get| B[&amp;#34;Hugo Memory Space&amp;#34;]
B --&amp;gt;|resources.PostCSS| C[&amp;#34;PostCSS + Tailwind JIT Process&amp;#34;]
C --&amp;gt; D{&amp;#34;hugo.IsProduction ?&amp;#34;}
D -- نعم (بيئة الإنتاج) --&amp;gt; E[&amp;#34;minify (حذف المسافات والأسطر الفارغة)&amp;#34;]
E --&amp;gt; F[&amp;#34;fingerprint (حساب التجزئة)&amp;#34;]
F --&amp;gt; G[&amp;#34;public/css/main.[hash].css (مع SRI)&amp;#34;]
D -- لا (بيئة التطوير) --&amp;gt; H[&amp;#34;public/css/main.css&amp;#34;]
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>&lt;code>resources.Get&lt;/code>&lt;/strong>: يبحث عن الملف المحدد داخل دليل &lt;code>assets&lt;/code>، ويقوم بتحميله ككائن موارد في الذاكرة.&lt;/li>
&lt;li>&lt;strong>&lt;code>resources.PostCSS&lt;/code>&lt;/strong>: يشير إلى ملف &lt;code>postcss.config.js&lt;/code> في جذر المشروع ويُطبق عمليات Tailwind CSS و Autoprefixer على الكود المصدري لـ CSS. في بيئة التطوير (&lt;code>hugo server&lt;/code>)، يعمل وضع JIT، الذي يقوم بإنشاء الفئات الضرورية بسرعة فائقة فقط عند تعديل الملفات.&lt;/li>
&lt;li>&lt;strong>&lt;code>minify&lt;/code>&lt;/strong>: عند بناء نسخة بيئة الإنتاج (مثلاً عبر &lt;code>hugo --environment production&lt;/code>)، يتم حذف المسافات والتعليقات غير الضرورية، وتقليل حجم الملف إلى أدنى حد ممكن.&lt;/li>
&lt;li>&lt;strong>&lt;code>fingerprint&lt;/code>&lt;/strong>: يقوم بحساب تجزئة SHA بناءً على محتوى الملف ويضيفها إلى اسم الملف (مثل: &lt;code>main.ab12cd...css&lt;/code>). هذا يحقق &amp;ldquo;كسر ذاكرة التخزين المؤقت&amp;rdquo; حيث يتم التأكد من أن المتصفح يحمل ملفاً جديداً عند تحديث CSS، مع الاستفادة من ميزة التخزين المؤقت القوية للمتصفح.&lt;/li>
&lt;li>&lt;strong>&lt;code>integrity&lt;/code>&lt;/strong>: يستخدم قيمة التجزئة المحسوبة عبر البصمة لإخراج خاصية SRI لمنع التلاعب من قبل شبكات توصيل المحتوى (CDN) وغيرها.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="6-التحليل-الرياضي-للأداء-في-تحسين-css">6. التحليل الرياضي للأداء في تحسين CSS
&lt;/h2>&lt;p>تعتبر إحدى أكبر ميزات إدراج Tailwind CSS هي تقليل حجم ملف CSS المرسل إلى أدنى حد. دعونا نحلل كمياً كيف يؤثر ذلك على أداء الويب (وخاصة على First Contentful Paint: FCP) باستخدام نماذج رياضية.&lt;/p>
&lt;h3 id="نموذج-تقليل-حجم-ملف-css">نموذج تقليل حجم ملف CSS
&lt;/h3>&lt;p>في أطر عمل CSS التقليدية (مثل Bootstrap)، يميل حجم الملف $S_{original}$ إلى أن يكون كبيراً (حوالي 150 كيلوبايت - 200 كيلوبايت) لأنه يتم تحميل جميع الأنماط، بما في ذلك تلك غير المستخدمة.
إذا رمزنا لحجم الملف بعد إزالة الفئات غير المستخدمة بواسطة مترجم JIT الخاص بـ Tailwind كـ $S_{purged}$، وباستخدام نسبة التقليل $R_{purge}$، يمكن التعبير عنها كما يلي:&lt;/p>
$$
S_{purged} = S_{original} \times (1 - R_{purge})
$$&lt;p>في المشاريع النموذجية، تصل نسبة $R_{purge}$ إلى ما يقرب من $0.9$ (تخفيض بنسبة 90%)، وينحصر حجم $S_{purged}$ في حوالي 10 كيلوبايت - 20 كيلوبايت فقط.&lt;/p>
&lt;p>بالإضافة إلى ذلك، يتم ضغط الملف عبر Brotli أو Gzip على جانب الخادم أثناء النقل. إذا كانت نسبة الضغط $R_{compress}$ (عادة ما تكون حوالي 0.7 إلى 0.8)، يتم حساب حجم الحمولة النهائية عبر الشبكة $S_{final}$ باستخدام المعادلة التالية:&lt;/p>
$$
S_{final} = S_{purged} \times (1 - R_{compress})
$$&lt;h3 id="مسار-العرض-الحرج-وتأخير-الشبكة">مسار العرض الحرج وتأخير الشبكة
&lt;/h3>&lt;p>يمكن تقريب الوقت المستغرق حتى يرسم المتصفح المحتوى الأولي على الشاشة (FCP) عن طريق مجموع وقت تنزيل HTML، ووقت تنزيل CSS، ووقت العرض.&lt;/p>
$$
T_{FCP} \approx RTT + \frac{S_{HTML}}{BW} + RTT + \frac{S_{final}}{BW} + T_{render}
$$&lt;p>حيث أن:&lt;/p>
&lt;ul>
&lt;li>$RTT$: زمن الرحلة ذهاباً وإياباً (وقت تأخير الاتصال بالخادم)&lt;/li>
&lt;li>$BW$: عرض النطاق الترددي للشبكة (Bandwidth)&lt;/li>
&lt;/ul>
&lt;p>في بيئات مثل شبكات الهاتف المحمول حيث يكون $BW$ ضيقًا و $RTT$ كبيرًا (تأخير كبير)، فإن نهج Tailwind CSS في تقليل $S_{final}$ إلى بضعة كيلوبايتات يؤدي إلى دفع قيمة $\frac{S_{final}}{BW}$ قريباً من الصفر، مما يصبح محركاً أساسياً لتحقيق درجات مذهلة في أدوات الأداء (مثل Google PageSpeed Insights).&lt;/p>
&lt;hr>
&lt;h2 id="7-تشغيل-خادم-التطوير-والتحقق-من-التحديث-التلقائي-hot-reload">7. تشغيل خادم التطوير والتحقق من التحديث التلقائي (Hot Reload)
&lt;/h2>&lt;p>بعد اكتمال جميع الإعدادات، قم بتشغيل خادم التطوير الخاص بـ Hugo وتأكد من أن Tailwind CSS يعمل بشكل صحيح.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo server -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>انتقل إلى &lt;code>http://localhost:1313/&lt;/code> عبر المتصفح وتحقق من عرض الموقع.
افتح ملف محتوى Markdown أو أحد قوالب Hugo (الملفات الموجودة تحت &lt;code>layouts/&lt;/code>)، وحاول إضافة فئات جديدة.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- مثال تطبيقي لفئات Tailwind بغرض الاختبار --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-gradient-to-r from-blue-500 to-purple-600 text-white p-8 rounded-xl shadow-2xl text-center transform transition duration-500 hover:scale-105&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-4xl font-extrabold tracking-tight&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Tailwind CSS + Hugo is Awesome!&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-4 text-lg font-medium&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>يرجى التأكد من أن التحديث التلقائي ينعكس في لمح البصر.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>بمجرد حفظ الملف، ستختبر المتعة حيث يعمل متتبع الملفات القوي في Hugo بالتعاون مع مترجم JIT في Tailwind معاً لإعادة بناء CSS في أجزاء من الثانية، مما يؤدي إلى إعادة تحميل المتصفح تلقائياً (التحديث التلقائي - Hot Reload).&lt;/p>
&lt;h3 id="استكشاف-الأخطاء-وإصلاحها-ماذا-لو-لم-تنعكس-الأنماط">استكشاف الأخطاء وإصلاحها: ماذا لو لم تنعكس الأنماط؟
&lt;/h3>&lt;p>إذا لم تنعكس التغييرات، فتحقق من النقاط التالية:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>إعداد مسار &lt;code>content&lt;/code> في &lt;code>tailwind.config.js&lt;/code>&lt;/strong>
إذا كانت مسارات الملفات المستهدفة خاطئة، فلن يتمكن Tailwind من اكتشاف الفئات المستخدمة داخلها ولن يضيفها إلى CSS. إذا كنت تستخدم سمة مخصصة خاصة، فتأكد من أن مسار دليل السمة لم يتم إسقاطه.&lt;/li>
&lt;li>&lt;strong>أخطاء PostCSS&lt;/strong>
إذا رأيت خطأً في سجلات خادم Hugo في الطرفية مثل &lt;code>Error: failed to transform resource: PostCSS not found&lt;/code>، فقد يعني ذلك أن الأمر &lt;code>npm install&lt;/code> لم ينفذ بشكل صحيح، أو أن &lt;code>postcss-cli&lt;/code> مفقود.&lt;/li>
&lt;li>&lt;strong>مسح ذاكرة التخزين المؤقت لـ Hugo&lt;/strong>
في حالات نادرة، قد يتسبب التخزين المؤقت في Hugo في الإبقاء على كود CSS القديم. أوقف الخادم وابدأه باستخدام الأمر &lt;code>hugo server --ignoreCache&lt;/code>، أو حاول حذف الدليل المؤقت في نظام التشغيل (مثل &lt;code>/tmp/hugo_cache/&lt;/code>).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-البناء-لبيئة-الإنتاج-ومزيد-من-التحسينات">8. البناء لبيئة الإنتاج ومزيد من التحسينات
&lt;/h2>&lt;p>عند نشر موقعك على خادم الإنتاج (مثل Netlify، Vercel، GitHub Pages، Cloudflare Pages، إلخ)، يجب عليك تعيين متغيرات البيئة وتشغيل مسار التحسين الخاص ببيئة الإنتاج.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># مثال لأمر البناء للإنتاج&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">NODE_ENV&lt;/span>&lt;span class="o">=&lt;/span>production hugo --minify --environment production
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>عن طريق إضافة علم &lt;code>--environment production&lt;/code>، يتم تنفيذ كتلة &lt;code>{{ if hugo.IsProduction }}&lt;/code> داخل ملف &lt;code>head.html&lt;/code>، وتطبيق تصغير لحجم CSS (Minify) وإضافة البصمة (Fingerprint).&lt;/p>
&lt;h3 id="تنسيق-markdown-باستخدام-إضافة-typography">تنسيق Markdown باستخدام إضافة Typography
&lt;/h3>&lt;p>في المواقع المبنية على المدونات أو الوثائق مثل Hugo، لا يمكنك إضافة فئات مباشرة إلى عناصر HTML النقية التي يتم إنشاؤها من Markdown (مثل &lt;code>&amp;lt;h1&amp;gt;&lt;/code>، &lt;code>&amp;lt;p&amp;gt;&lt;/code>، &lt;code>&amp;lt;ul&amp;gt;&lt;/code> وما إلى ذلك). في مثل هذه الحالات، من المفيد جدًا استخدام إضافة &lt;strong>Typography&lt;/strong> الرسمية من Tailwind.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>تثبيت الإضافة&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install -D @tailwindcss/typography
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>إضافتها إلى &lt;code>tailwind.config.js&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;@tailwindcss/typography&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>تطبيقها في القوالب
من خلال إضافة فئة &lt;code>prose&lt;/code> (ويمكنك إضافة تغيرات الحجم واللون حسب الرغبة) فقط إلى حاوية العنصر الذي يخرج محتوى المقالة، سيتم تطبيق أنماط افتراضية جميلة.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go-html-template" data-lang="go-html-template">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">article&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;prose prose-lg prose-blue mx-auto mt-10&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Content&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">article&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>بفضل ذلك، لم تعد هناك أي حاجة لكتابة محددات CSS معقدة يدويًا (مثل &lt;code>.article-content h2 { ... }&lt;/code>)، مما يضمن الحفاظ التام على نموذجية المكونات.&lt;/p>
&lt;hr>
&lt;h2 id="9-الخلاصة-اكتمال-نظام-واجهة-أمامية-عالي-القابلية-للصيانة">9. الخلاصة: اكتمال نظام واجهة أمامية عالي القابلية للصيانة
&lt;/h2>&lt;p>أحسنت العمل! مع هذا، اكتمل لديك مسار موارد الويب المثالي الذي يجمع بين محرك توليد المواقع الثابتة فائق السرعة لـ Hugo، وقدرات التنسيق الحديثة لـ Tailwind CSS، وقابلية التوسع لـ PostCSS.&lt;/p>
&lt;p>الشيء الرائع في هذه البنية هو أنك &lt;strong>&amp;ldquo;تحتاج إلى ضبط الإعدادات مرة واحدة فقط في البداية&amp;rdquo;&lt;/strong>. بمجرد بناء المسار، يمكن للمطورين البدء في تجميع واجهات المستخدم المعقدة بسرعة مذهلة وببساطة عبر كتابة فئات الخدمات البديهية في قوالب HTML أو Markdown، دون الحاجة لفتح ملف CSS.&lt;/p>
&lt;p>علاوة على ذلك، نظراً لأن حجم CSS الناتج يكون دائماً في حده الأدنى، فإن ذلك ينعكس بشكل مباشر على تحسين درجات Core Web Vitals، مما يمنحك ميزة كبيرة من منظور تحسين محركات البحث (SEO).&lt;/p>
&lt;p>يظل الجمع بين Hugo و Tailwind CSS أحد &amp;ldquo;أفضل الخيارات&amp;rdquo; لأي مشروع، من المدونات التقنية الشخصية إلى مواقع الشركات الضخمة. تأكد من الاستفادة من سلسلة الأدوات القوية هذه واستمتع بتجربة تطوير ويب مريحة!&lt;/p></description></item><item><title>أساسيات هندسة البرمجيات: ما هي البنية النظيفة (Clean Architecture)؟</title><link>http://kenji.blog/ar/p/clean-architecture-basics-guide/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ar/p/clean-architecture-basics-guide/</guid><description>&lt;img src="http://kenji.blog/p/clean-architecture-basics-guide/img/eyecatch.jpg" alt="Featured image of post أساسيات هندسة البرمجيات: ما هي البنية النظيفة (Clean Architecture)؟" />&lt;p>في تطوير البرمجيات الحديثة، يعد بناء &amp;ldquo;نظام مقاوم للتغيير&amp;rdquo; تحديًا دائمًا. التغييرات في متطلبات العمل، ظهور أطر عمل جديدة، تجديد واجهة المستخدم، وترحيل قواعد البيانات. استجابةً لكل هذه التغييرات، هناك حاجة إلى بنية يمكنها التكيف بمرونة دون إعادة بناء النظام بأكمله. كأحد الإجابات على ذلك، تم اقتراح &lt;strong>البنية النظيفة (Clean Architecture)&lt;/strong> بواسطة Robert C. Martin (المعروف باسم العم بوب، Uncle Bob).&lt;/p>
&lt;p>في هذه المقالة، سنتعمق في جوهر البنية النظيفة من خلال تاريخها، الغرض منها، تفاصيل الطبقات الأربع، قاعدة التبعية، وأمثلة تنفيذية محددة. سنقدم شرحًا تقنيًا عميقًا ومفصلاً للغاية.&lt;/p>
&lt;h2 id="1-مشاكل-البنى-التقليدية-وتاريخ-البنية-النظيفة">1. مشاكل البنى التقليدية وتاريخ البنية النظيفة
&lt;/h2>&lt;p>من الناحية التاريخية، شهدت هندسة البرمجيات تحولات نموذجية مختلفة. في الأنظمة المبكرة، كانت تعليمات منطق العمل، واجهة المستخدم، ووصول البيانات متشابكة بشدة (ما يسمى بشفرة السباغيتي). بعد ذلك، بهدف فصل الاهتمامات (Separation of Concerns)، انتشرت البنية ثلاثية الطبقات (طبقة العرض، طبقة منطق العمل، وطبقة الوصول إلى البيانات).&lt;/p>
&lt;p>ومع ذلك، كان هناك مشكلة رئيسية في البنية التقليدية ثلاثية الطبقات. وهي أن &amp;ldquo;&lt;strong>المجال (منطق العمل) يعتمد على قاعدة البيانات وأطر العمل&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;p>على سبيل المثال، إذا استدعت طبقة منطق العمل طبقة الوصول إلى البيانات (مثل ORM) مباشرةً، فإن التغييرات في مخطط قاعدة البيانات أو التغييرات في ORM ستمتد إلى منطق العمل. بعبارة أخرى، نشأ تناقض حيث أصبحت &amp;ldquo;قواعد العمل&amp;rdquo;، وهي الأكثر أهمية والتي لا ينبغي تغييرها، تعتمد على &amp;ldquo;البنية التحتية&amp;rdquo;، وهي الأكثر عرضة للتغييرات التقنية.&lt;/p>
&lt;p>كحلول لهذه المشكلة، تم ابتكار البنى التالية:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>البنية السداسية (Ports and Adapters)&lt;/strong> - Alistair Cockburn&lt;/li>
&lt;li>&lt;strong>بنية البصلة (Onion Architecture)&lt;/strong> - Jeffrey Palermo&lt;/li>
&lt;li>&lt;strong>DCI (Data, Context and Interaction)&lt;/strong> - James Coplien, Trygve Reenskaug&lt;/li>
&lt;li>&lt;strong>BCE (Boundary-Control-Entity)&lt;/strong> - Ivar Jacobson&lt;/li>
&lt;/ul>
&lt;p>كل هذه البنى لها نفس الغرض. ألا وهو &amp;ldquo;&lt;strong>فصل الاهتمامات&lt;/strong>&amp;rdquo;. يتمثل ذلك في تقسيم البرنامج إلى طبقات، بحيث يمكن اختبار كل منها بشكل مستقل، وتكون مستقلة عن الوكلاء الخارجيين (واجهة المستخدم، قاعدة البيانات، أطر العمل).&lt;/p>
&lt;p>قام Robert C. Martin بدمج مفاهيم هذه البنى الممتازة وتلخيصها في قاعدة عملية واحدة أسماها &amp;ldquo;&lt;strong>البنية النظيفة (Clean Architecture)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;h2 id="2-الغرض-من-البنية-النظيفة-وخصائصها">2. الغرض من البنية النظيفة وخصائصها
&lt;/h2>&lt;p>النظام الذي يعتمد البنية النظيفة يتمتع بالخصائص التالية:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>الاستقلالية عن أطر العمل (Independent of Frameworks)&lt;/strong>: لا تعتمد البنية على وجود مكتبات برمجية غنية بالميزات. يتيح ذلك استخدام أطر العمل كـ &amp;ldquo;أدوات&amp;rdquo;، دون الحاجة إلى إجبار النظام على التوافق مع قيود إطار العمل.&lt;/li>
&lt;li>&lt;strong>قابلية الاختبار (Testable)&lt;/strong>: يمكن اختبار قواعد العمل دون واجهة المستخدم، قاعدة البيانات، خادم الويب، أو أي عنصر خارجي آخر.&lt;/li>
&lt;li>&lt;strong>الاستقلالية عن واجهة المستخدم (Independent of UI)&lt;/strong>: يمكن تغيير واجهة المستخدم بسهولة دون تغيير بقية النظام. على سبيل المثال، يمكن استبدال واجهة مستخدم الويب بواجهة مستخدم تعتمد على وحدة التحكم (Console UI) دون تغيير قواعد العمل.&lt;/li>
&lt;li>&lt;strong>الاستقلالية عن قاعدة البيانات (Independent of Database)&lt;/strong>: يمكنك تغيير Oracle أو SQL Server إلى Mongo أو BigTable أو CouchDB وما إلى ذلك. قواعد العمل ليست مقيدة بقاعدة البيانات.&lt;/li>
&lt;li>&lt;strong>الاستقلالية عن أي وكيل خارجي (Independent of any external agency)&lt;/strong>: في الواقع، قواعد العمل لا تعرف شيئًا عن العالم الخارجي.&lt;/li>
&lt;/ol>
&lt;h2 id="3-الطبقات-الأربع-للبنية-النظيفة-layers">3. الطبقات الأربع للبنية النظيفة (Layers)
&lt;/h2>&lt;p>تُمثل البنية النظيفة عمومًا برسم بياني لدوائر متحدة المركز. كلما اقتربت من المركز، أصبحت البرمجيات سياسات عالية المستوى (قواعد عمل عالية التجريد). وكلما اتجهت نحو الخارج، أصبحت آليات (تفاصيل محددة).&lt;/p>
&lt;pre class="mermaid">
graph TD
subgraph &amp;#34;External Interfaces (Frameworks &amp;amp; Drivers)&amp;#34;
A[&amp;#34;الويب&amp;#34;]
B[&amp;#34;واجهة المستخدم&amp;#34;]
C[&amp;#34;قاعدة البيانات&amp;#34;]
D[&amp;#34;واجهات برمجة التطبيقات الخارجية&amp;#34;]
end
subgraph &amp;#34;Interface Adapters&amp;#34;
E[&amp;#34;وحدات التحكم&amp;#34;]
F[&amp;#34;البوابات&amp;#34;]
G[&amp;#34;مقدمو العرض&amp;#34;]
end
subgraph &amp;#34;Application Business Rules (Use Cases)&amp;#34;
H[&amp;#34;متفاعلات حالات الاستخدام&amp;#34;]
end
subgraph &amp;#34;Enterprise Business Rules (Entities)&amp;#34;
I[&amp;#34;الكيانات&amp;#34;]
end
A --&amp;gt; E
B --&amp;gt; E
C --&amp;gt; F
D --&amp;gt; F
E --&amp;gt; H
F --&amp;gt; H
G --&amp;gt; H
H --&amp;gt; I
&lt;/pre>
&lt;h3 id="31-الكيانات-entities">3.1. الكيانات (Entities)
&lt;/h3>&lt;p>تغلف الكيانات قواعد العمل على مستوى المؤسسة (Enterprise Business Rules). يمكن أن يكون الكيان كائنًا يحتوي على طرق، أو يمكن أن يكون مجموعة من هياكل البيانات والدوال. إنها القواعد الأكثر عمومية وعالية المستوى والتي يمكن إعادة استخدامها في تطبيقات مختلفة متعددة داخل الشركة.
حتى لو كنت تنشئ تطبيقًا واحدًا فقط، فإن الكيانات هي كائنات العمل لهذا التطبيق. الكيانات لا تتأثر أبدًا بالتغييرات الخارجية (مثل التغييرات في تنقل الصفحات أو التغييرات في الأمان).&lt;/p>
&lt;h3 id="32-حالات-الاستخدام-use-cases">3.2. حالات الاستخدام (Use Cases)
&lt;/h3>&lt;p>تحتوي طبقة حالات الاستخدام على قواعد العمل الخاصة بالتطبيق (Application Business Rules). هنا يتم تغليف وتنفيذ جميع حالات الاستخدام الخاصة بالنظام. تنسق حالات الاستخدام تدفق البيانات من وإلى الكيانات وتوجه الكيانات لتحقيق أهداف النظام.
يجب ألا تؤثر التغييرات في هذه الطبقة على الكيانات. كما أن التغييرات الخارجية مثل قاعدة البيانات، واجهة المستخدم، وأطر العمل لا تؤثر على هذه الطبقة. حالات الاستخدام معزولة تمامًا عن هذه الاهتمامات.&lt;/p>
&lt;h3 id="33-محولات-الواجهات-interface-adapters">3.3. محولات الواجهات (Interface Adapters)
&lt;/h3>&lt;p>طبقة محولات الواجهات عبارة عن مجموعة من المحولات التي تقوم بتحويل البيانات من التنسيق المناسب لحالات الاستخدام والكيانات، إلى التنسيق المناسب للوكلاء الخارجيين مثل قاعدة البيانات والويب.
على سبيل المثال، تنتمي عناصر بنية MVC (نموذج-عرض-تحكم) الخاصة بـ GUI في عالم الويب إلى هنا. تتلقى وحدة التحكم إدخال المستخدم، وتمرره إلى حالة الاستخدام، ويتلقى مقدم العرض المخرجات من حالة الاستخدام ويقوم بتنسيقها للعرض (واجهة المستخدم).
كما أن دور هذه الطبقة هو تحويل البيانات إلى التنسيق الذي يمكن لقاعدة البيانات (مثل SQL) فهمه. يجب ألا يعرف الكود الموجود داخل هذه الطبقة أي شيء عن قاعدة البيانات.&lt;/p>
&lt;h3 id="34-أطر-العمل-والمحركات-frameworks--drivers">3.4. أطر العمل والمحركات (Frameworks &amp;amp; Drivers)
&lt;/h3>&lt;p>تتكون الطبقة الخارجية من أدوات مثل قواعد البيانات وأطر عمل الويب. هنا عادةً لا تكتب الكثير من الكود بخلاف &amp;ldquo;كود الربط&amp;rdquo; للتواصل مع الدوائر الداخلية.
تحتفظ هذه الطبقة بجميع التفاصيل. الويب هو تفصيل. قاعدة البيانات هي تفصيل. نضع هذه التفاصيل في الخارج لتقليل الضرر إلى أدنى حد.&lt;/p>
&lt;h2 id="4-قاعدة-التبعية-the-dependency-rule">4. قاعدة التبعية (The Dependency Rule)
&lt;/h2>&lt;p>لجعل البنية النظيفة تعمل، هناك قاعدة واحدة بالغة الأهمية يجب عدم كسرها أبدًا. وهي &amp;ldquo;&lt;strong>قاعدة التبعية (The Dependency Rule)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;blockquote>
&lt;p>يجب أن تتجه تبعيات الكود المصدري دائمًا نحو الداخل (نحو السياسات عالية المستوى).&lt;/p>
&lt;/blockquote>
&lt;p>يجب ألا يعرف الكود الذي ينتمي إلى الدوائر الداخلية أي شيء عن الكود الذي ينتمي إلى الدوائر الخارجية. يجب عدم الإشارة إلى الأسماء (الدوال، الفئات، المتغيرات، إلخ) المعلنة في الدوائر الخارجية داخل الدوائر الداخلية.
وبالمثل، يجب عدم استخدام تنسيقات البيانات المستخدمة في الدوائر الخارجية داخل الدوائر الداخلية. وينطبق هذا بشكل خاص إذا كان هذا التنسيق ناتجًا عن إطار عمل في الدائرة الخارجية.&lt;/p>
&lt;pre class="mermaid">
graph LR
A[&amp;#34;أطر العمل والمحركات&amp;#34;] --&amp;gt;| اعتماد | B[&amp;#34;محولات الواجهات&amp;#34;]
B --&amp;gt;| اعتماد | C[&amp;#34;حالات الاستخدام&amp;#34;]
C --&amp;gt;| اعتماد | D[&amp;#34;الكيانات&amp;#34;]
style A fill:#f9f9f9,stroke:#333,stroke-width:2px
style B fill:#e6f7ff,stroke:#333,stroke-width:2px
style C fill:#fff0f6,stroke:#333,stroke-width:2px
style D fill:#f6ffed,stroke:#333,stroke-width:2px
&lt;/pre>
&lt;p>للتعبير عن ذلك رياضيًا، إذا كان مؤشر الطبقة هو $L_i$، وحددنا $i=0$ للكيانات (الطبقة الأعمق) و $i=3$ لأطر العمل (الطبقة الأبعد)، ففي حال وجود تبعية من طبقة $L_m$ إلى $L_n$، يجب أن تتحقق المتباينة التالية دائمًا:&lt;/p>
$$ m > n $$&lt;p>بمعنى آخر، يتجه متجه التبعية $\vec{D}$ دائمًا نحو المركز.&lt;/p>
&lt;h2 id="5-عبور-الحدود-مبدأ-انعكاس-التبعية-dip">5. عبور الحدود: مبدأ انعكاس التبعية (DIP)
&lt;/h2>&lt;p>عند محاولة الالتزام بقاعدة التبعية، ستواجه سريعًا مشكلة كبيرة واحدة. &amp;ldquo;&lt;strong>ماذا تفعل عندما تحتاج حالة الاستخدام إلى جلب بيانات من قاعدة البيانات؟&lt;/strong>&amp;rdquo;&lt;/p>
&lt;p>إذا استدعت طبقة حالات الاستخدام (الداخلية) طبقة محولات الواجهات (تنفيذ المستودع الخارجي) بشكل مباشر، فإن التبعية ستتجه نحو الخارج، مما يشكل انتهاكًا لقاعدة التبعية.&lt;/p>
&lt;p>ما يحل هذه المشكلة هو الحرف &amp;ldquo;D&amp;rdquo; في مبادئ SOLID (مبدأ انعكاس التبعية: Dependency Inversion Principle).&lt;/p>
&lt;h3 id="تعريف-مبدأ-انعكاس-التبعية-dip">تعريف مبدأ انعكاس التبعية (DIP)
&lt;/h3>&lt;ol>
&lt;li>يجب ألا تعتمد الوحدات عالية المستوى على الوحدات منخفضة المستوى. يجب أن يعتمد كلاهما على التجريدات.&lt;/li>
&lt;li>يجب ألا تعتمد التجريدات على التفاصيل. يجب أن تعتمد التفاصيل على التجريدات.&lt;/li>
&lt;/ol>
&lt;p>لتحقيق ذلك، نُعرّف &lt;strong>واجهة (تجريد)&lt;/strong> في طبقة حالات الاستخدام، ونقوم بـ &lt;strong>تنفيذ&lt;/strong> تلك الواجهة في الطبقة الخارجية (محولات الواجهات). تعتمد طبقة حالات الاستخدام فقط على الواجهة التي حددتها، ولا تعتمد على التنفيذ الملموس في الخارج.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class UseCaseInteractor {
- UserRepository repository
+ execute()
}
class UserRepository {
&amp;lt;&amp;lt;Interface&amp;gt;&amp;gt;
+ findById(id)
+ save(user)
}
class UserRepositoryImpl {
+ findById(id)
+ save(user)
}
UseCaseInteractor --&amp;gt; UserRepository : &amp;#34;Depends on (يعتمد على)&amp;#34;
UserRepositoryImpl ..|&amp;gt; UserRepository : &amp;#34;Implements (ينفذ)&amp;#34;
note for UseCaseInteractor &amp;#34;الطبقة الداخلية (حالة الاستخدام)&amp;#34;
note for UserRepository &amp;#34;الطبقة الداخلية (حالة الاستخدام)&amp;#34;
note for UserRepositoryImpl &amp;#34;الطبقة الخارجية (محولات الواجهات)&amp;#34;
&lt;/pre>
&lt;p>في الرسم البياني أعلاه، يكون تدفق التحكم (Control Flow) أثناء وقت التشغيل هو &lt;code>UseCaseInteractor&lt;/code> $\rightarrow$ &lt;code>UserRepositoryImpl&lt;/code>. ومع ذلك، فإن تبعية الكود المصدري (Source Code Dependency) هي &lt;code>UserRepositoryImpl&lt;/code> $\rightarrow$ &lt;code>UserRepository&lt;/code> (نحو الداخل). باستخدام تعدد الأشكال (Polymorphism)، تمكنا من توجيه تبعية الكود المصدري في الاتجاه المعاكس لتدفق التحكم. هذا هو سبب تسميته بـ &amp;ldquo;&lt;strong>انعكاس&lt;/strong> التبعية&amp;rdquo;.&lt;/p>
&lt;h2 id="6-مثال-تنفيذي-محدد-باستخدام-typescript">6. مثال تنفيذي محدد باستخدام TypeScript
&lt;/h2>&lt;p>هنا، نوضح مثالًا بسيطًا لتنفيذ البنية النظيفة (ميزة تسجيل المستخدم) باستخدام TypeScript.&lt;/p>
&lt;h3 id="61-الكيانات-entities">6.1. الكيانات (Entities)
&lt;/h3>&lt;p>قواعد العمل الأكثر مركزية.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/domain/entities/User.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">Date&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// قواعد العمل الخاصة بالكيان (مثل التحقق من طول الاسم)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">public&lt;/span> &lt;span class="nx">isValid&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;@&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-حالات-الاستخدام-use-cases">6.2. حالات الاستخدام (Use Cases)
&lt;/h3>&lt;p>في طبقة حالات الاستخدام، نحدد هياكل بيانات الإدخال والإخراج (DTO)، وواجهة المستودع لعكس التبعية.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/repositories/UserRepository.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// الواجهة المحددة بواسطة طبقة حالات الاستخدام
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>: &lt;span class="kt">User&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/usecases/RegisterUser/RegisterUserDTO.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">RegisterUserInputDTO&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">RegisterUserOutputDTO&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">Date&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/usecases/RegisterUser/RegisterUserUseCase.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../repositories/UserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserInputDTO&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">RegisterUserOutputDTO&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./RegisterUserDTO&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// يعتمد على التجريد (الواجهة). لا يعتمد على التنفيذ الملموس.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">userRepository&lt;/span>: &lt;span class="kt">UserRepository&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>: &lt;span class="kt">RegisterUserInputDTO&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">RegisterUserOutputDTO&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">existingUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">existingUser&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;User already exists&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">newUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">crypto&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">randomUUID&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">newUser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isValid&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Invalid user data&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// يستدعي عملية الحفظ في قاعدة البيانات الخارجية، لكن التبعية تتجه للداخل (للواجهة)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">newUser&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">newUser.id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">newUser.name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">newUser.email&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">newUser.createdAt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-محولات-الواجهات-interface-adapters">6.3. محولات الواجهات (Interface Adapters)
&lt;/h3>&lt;p>نقوم بإنشاء عملية الوصول المحددة لقاعدة البيانات (تنفيذ المستودع) ووحدة التحكم التي تعالج طلبات HTTP.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/adapters/repositories/PostgresUserRepository.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/repositories/UserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// نفترض عميل قاعدة البيانات كطبقة خارجية (Driver)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../infrastructure/database/DatabaseClient&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span> &lt;span class="kr">implements&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">dbClient&lt;/span>: &lt;span class="kt">DatabaseClient&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">record&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;SELECT * FROM users WHERE email = $1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">created_at&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>: &lt;span class="kt">User&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;INSERT INTO users (id, name, email, created_at) VALUES ($1, $2, $3, $4)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createdAt&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/adapters/controllers/UserController.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/usecases/RegisterUser/RegisterUserUseCase&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">UserController&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">registerUserUseCase&lt;/span>: &lt;span class="kt">RegisterUserUseCase&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">input&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">req.body.name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">req.body.email&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">registerUserUseCase&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">201&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">output&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">400&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">message&lt;/span>: &lt;span class="kt">error.message&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="64-المكون-الرئيسي-حقن-التبعية-di">6.4. المكون الرئيسي (حقن التبعية: DI)
&lt;/h3>&lt;p>عند بدء تشغيل التطبيق، نقوم ببناء جميع التبعيات (الربط). يُطلق على هذا اسم Composition Root.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/infrastructure/web/server.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="nx">express&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;express&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../database/DatabaseClient&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../adapters/repositories/PostgresUserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/usecases/RegisterUser/RegisterUserUseCase&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserController&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../adapters/controllers/UserController&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">express&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 1. تهيئة المحرك
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">dbClient&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="cm">/* معلومات الاتصال */&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 2. تهيئة المحول (إنشاء كائن من الفئة الملموسة)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">userRepository&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 3. تهيئة حالة الاستخدام (حقن الفئة الملموسة في الواجهة = DI)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">registerUserUseCase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 4. تهيئة وحدة التحكم
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">userController&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">UserController&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">registerUserUseCase&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// التوجيه
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/users&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">userController&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">listen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Server is running on port 3000&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>بهذه الطريقة، يتولى &amp;ldquo;نص التشغيل&amp;rdquo; الأبعد التعامل مع التفاصيل القذرة (إنشاء كائنات من الفئات الملموسة)، ويمرر واجهات نظيفة فقط للطبقات الداخلية، مما يؤدي إلى بنية تعزل منطق العمل تمامًا عن العالم الخارجي.&lt;/p>
&lt;h2 id="7-اعتبارات-رياضية-حول-الاقتران-والتماسك">7. اعتبارات رياضية حول الاقتران والتماسك
&lt;/h2>&lt;p>في هندسة البرمجيات، هناك مقاييس لتقييم جودة البنية وهي &lt;strong>الاقتران (Coupling)&lt;/strong> و &lt;strong>التماسك (Cohesion)&lt;/strong>.&lt;/p>
&lt;p>يمثل الاقتران $C$ قوة التبعية بين الوحدات (Modules). إذا كانت الوحدة $A$ تعتمد على الوحدة $B$، وافترضنا أن إجمالي عدد التبعيات في النظام هو $N_{dep}$، وعدد الوحدات هو $N_{mod}$، فإن أحد المقاييس التي تشير إلى التعقيد يمكن التعبير عنه على النحو التالي:&lt;/p>
$$ Complexity \propto \frac{N_{dep}}{N_{mod}} $$&lt;p>في البنية النظيفة، من خلال تطبيق DIP، نوجه سهم التبعية المادية نحو التجريد. تم تصميم وتيرة تغيير التجريد (الواجهة) (عدم الاستقرار: Instability $I$) لتكون منخفضة للغاية.&lt;/p>
&lt;p>يُحسب عدم الاستقرار $I$ بالصيغة التالية (كما عرفه Robert C. Martin):&lt;/p>
&lt;ul>
&lt;li>$C_e$ (Efferent Coupling): الاقتران الخارجي (عدد التبعيات الصادرة)&lt;/li>
&lt;li>$C_a$ (Afferent Coupling): الاقتران الداخلي (عدد التبعيات الواردة)&lt;/li>
&lt;/ul>
$$ I = \frac{C_e}{C_e + C_a} $$&lt;ul>
&lt;li>عندما يكون $I = 0$، يكون المكون مستقرًا تمامًا (لا يعتمد على أحد، ويعتمد عليه الآخرون).&lt;/li>
&lt;li>عندما يكون $I = 1$، يكون المكون غير مستقر تمامًا (لا يعتمد عليه أحد، ويعتمد هو على الآخرين).&lt;/li>
&lt;/ul>
&lt;p>نظرًا لأن &amp;ldquo;طبقة الكيانات&amp;rdquo; في البنية النظيفة لها $C_e = 0$ (لا تعتمد على الخارج)، فإنها تصبح $I = 0$. بعبارة أخرى، إنها الطبقة الأكثر استقرارًا.
على العكس من ذلك، &amp;ldquo;طبقة واجهة المستخدم&amp;rdquo; أو &amp;ldquo;طبقة قاعدة البيانات&amp;rdquo; يكون لها $C_a \approx 0$ و $C_e > 0$، لذلك تصبح $I \approx 1$، مما يجعلها طبقات سهلة التغيير (طبقات غير مستقرة).&lt;/p>
&lt;p>ينص المبدأ المهم للبنية SDP (Stable Dependencies Principle: مبدأ التبعيات المستقرة) على أن &amp;ldquo;&lt;strong>التبعية يجب أن تتجه نحو مكون أكثر استقرارًا (مكون ذو قيمة $I$ أصغر)&lt;/strong>&amp;rdquo;. الدوائر متحدة المركز للبنية النظيفة هي تصور دقيق لـ SDP، وهي مصممة بحيث تتجه التبعيات من الخارج ($I=1$) إلى الداخل ($I=0$).&lt;/p>
&lt;h2 id="8-استراتيجية-الاختبار-والبنية-النظيفة">8. استراتيجية الاختبار والبنية النظيفة
&lt;/h2>&lt;p>إحدى أكبر مزايا البنية النظيفة هي &lt;strong>سهولة الاختبار&lt;/strong>. نظرًا لفصل الطبقات، يمكنك كتابة اختبارات لكل طبقة بشكل مستقل.&lt;/p>
&lt;h3 id="81-اختبار-الكيانات-unit-test">8.1. اختبار الكيانات (Unit Test)
&lt;/h3>&lt;p>نظرًا لأنه منطق نقي خالٍ من أي تبعيات خارجية، فليس هناك حاجة لقاعدة بيانات أو كائنات وهمية (mocks). إنه أسرع اختبار موثوق يمكنك تشغيله.&lt;/p>
&lt;h3 id="82-اختبار-حالات-الاستخدام-unit-test-with-mocks">8.2. اختبار حالات الاستخدام (Unit Test with Mocks)
&lt;/h3>&lt;p>نظرًا لأن التبعيات الخارجية مثل المستودعات يتم تعريفها جميعًا كواجهات، فإن كل ما عليك فعله أثناء الاختبار هو حقن (DI) &lt;strong>كائنات وهمية أو تنفيذ في الذاكرة (Fake)&lt;/strong> مخصص للاختبار. لا داعي لتشغيل قاعدة بيانات فعلية. يتيح لك ذلك اختبار التفرعات المعقدة لمنطق العمل ومعالجة الاستثناءات بسرعات عالية.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// مثال لاختبار حالة الاستخدام (بافتراض Jest)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;محاولة التسجيل ببريد إلكتروني موجود مسبقًا يجب أن ينتج عنها خطأ&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// إنشاء مستودع مزيف (Fake)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">mockRepo&lt;/span>: &lt;span class="kt">UserRepository&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">findByEmail&lt;/span>: &lt;span class="kt">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">()),&lt;/span> &lt;span class="c1">// إرجاع مستخدم موجود
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">save&lt;/span>: &lt;span class="kt">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">useCase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mockRepo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// تنفيذ حالة الاستخدام والتأكد من الخطأ
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">useCase&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">execute&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Bob&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test@example.com&amp;#39;&lt;/span> &lt;span class="p">}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">rejects&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">toThrow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;User already exists&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="83-اختبار-المحولات-integration-test">8.3. اختبار المحولات (Integration Test)
&lt;/h3>&lt;p>تختبر فئات تنفيذ المستودع فعليًا الاتصال بقاعدة البيانات وما إذا كان SQL صحيحًا. يختبر اختبار وحدة التحكم تلقي طلب HTTP وإرجاع JSON. هنا لا نقوم بالتحقق المفصل من منطق العمل، بل نكتفي بتأكيد صحة &amp;ldquo;التحويل&amp;rdquo; و&amp;quot;الاتصال&amp;quot;.&lt;/p>
&lt;h2 id="9-عيوب-البنية-النظيفة-ومتى-يجب-اعتمادها">9. عيوب البنية النظيفة ومتى يجب اعتمادها
&lt;/h2>&lt;p>البنية النظيفة التي تبدو قادرة على كل شيء ليست رصاصة فضية. هناك عيوب (مفاضلات) مثل ما يلي:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>زيادة تكلفة التعلم الأولية وتكلفة التطوير&lt;/strong>: يزداد عدد الملفات والواجهات (التجريدات) بشكل كبير. سيكون هناك الكثير من &amp;ldquo;الأكواد النمطية (boilerplate code)&amp;rdquo; مثل إعادة حزم DTOs.&lt;/li>
&lt;li>&lt;strong>مبالغة في المشاريع الصغيرة&lt;/strong>: في النماذج الأولية التي يتم إنشاؤها في أيام قليلة أو الأدوات التي تُستخدم لمرة واحدة والتي نادرًا ما تتغير، غالبًا ما تكون هذه البنية تكلفة مهدرة. إنها أيضًا غير مناسبة لواجهات برمجة التطبيقات البسيطة التي تقوم فقط بعمليات CRUD.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>متى يجب اعتمادها&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>المنتجات التي يُتوقع الحفاظ عليها وتشغيلها لفترة طويلة (عدة سنوات أو أكثر).&lt;/li>
&lt;li>الأنظمة ذات قواعد العمل المعقدة حيث تتغير المتطلبات بشكل متكرر.&lt;/li>
&lt;li>عندما ترغب في تقسيم العمل في فرق تطوير كبيرة (الواجهة الأمامية، الخلفية، البنية التحتية، إلخ).&lt;/li>
&lt;li>عندما تريد تصميم مجال عمل معقد بالتزامن مع التصميم المدفوع بالمجال (DDD: Domain-Driven Design).&lt;/li>
&lt;/ul>
&lt;h2 id="10-الخلاصة">10. الخلاصة
&lt;/h2>&lt;p>البنية النظيفة هي فلسفة تصميم لحماية جوهر النظام المسمى &amp;ldquo;قواعد العمل&amp;rdquo; من &amp;ldquo;التفاصيل&amp;rdquo; مثل واجهة المستخدم وقاعدة البيانات وأطر العمل.&lt;/p>
&lt;p>يكمن جوهر هذا في &lt;strong>قاعدة التبعية&lt;/strong> و &lt;strong>مبدأ انعكاس التبعية (DIP)&lt;/strong>. من خلال تطبيقها بشكل صحيح، تصبح البرمجيات مرنة للتغيير، وأسهل للاختبار، ويمكنها الحفاظ على قيمتها على مدى فترات طويلة.&lt;/p>
&lt;p>الأمر المهم ليس التقليد الأعمى لهيكل أدلة البنية النظيفة، بل فهم جوهر أسئلة مثل &amp;ldquo;&lt;strong>لماذا نقسمها بهذه الطريقة&lt;/strong>&amp;rdquo; و&amp;quot;&lt;strong>أين تشير أسهم التبعية&lt;/strong>&amp;quot;، وتطبيقها بشكل مناسب وفقًا لحجم مشروعك ومدى تعقيده.&lt;/p>
&lt;hr>
&lt;p>&lt;em>مرجع: &amp;ldquo;Clean Architecture: A Craftsman&amp;rsquo;s Guide to Software Structure and Design&amp;rdquo; بقلم Robert C. Martin&lt;/em>&lt;/p></description></item><item><title>نقاط تصميم CSS لدعم الوضع الداكن في المدونات</title><link>http://kenji.blog/ar/p/dark-mode-css-design-patterns/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ar/p/dark-mode-css-design-patterns/</guid><description>&lt;img src="http://kenji.blog/p/dark-mode-css-design-patterns/img/eyecatch.jpg" alt="Featured image of post نقاط تصميم CSS لدعم الوضع الداكن في المدونات" />&lt;p>في تطوير الويب الحديث، تحول دعم الوضع الداكن (Dark Mode) من مجرد &amp;ldquo;ميزة إضافية جيدة&amp;rdquo; (Nice to have) إلى &amp;ldquo;متطلب أساسي&amp;rdquo; (Must have) لتحسين تجربة المستخدم (UX). خاصة في الوسائط التي تفترض قراءة النصوص لفترات طويلة مثل المدونات ومواقع الوثائق، حيث يقلل من إجهاد عين المستخدم ويحافظ على استهلاك بطارية الجهاز، لذا فإن أهمية دعم الوضع الداكن عالية جدًا.&lt;/p>
&lt;p>في هذه المقالة، سنشرح بتعمق شديد من منظور مهندس الواجهة الأمامية التحديات التقنية التي لا مفر منها لدعم الوضع الداكن في المدونات، ونقاط تصميم CSS ذات القابلية العالية للصيانة. سنغطي كل شيء بدءًا من استخدام المتغيرات المخصصة (CSS Custom Properties)، والتحكم المتقدم عبر JavaScript والتكامل مع SSR لمنع FOUC (Flash of Unstyled Content)، وتصميم الألوان لضمان إمكانية الوصول (WCAG 2.1 AAA) (بما في ذلك RGB و HSL وأحدث OKLCH)، وحتى أمثلة عملية للأكواد باستخدام Tailwind CSS.&lt;/p>
&lt;hr>
&lt;h2 id="1-أساسيات-تصميم-السمات-باستخدام-متغيرات-css-css-custom-properties">1. أساسيات تصميم السمات باستخدام متغيرات CSS (CSS Custom Properties)
&lt;/h2>&lt;p>النهج الأكثر قياسية وقوة حاليًا لتنفيذ الوضع الداكن هو استخدام &lt;strong>متغيرات CSS المخصصة (CSS Custom Properties)&lt;/strong>. على عكس متغيرات معالجات CSS مثل Sass (مثلاً &lt;code>$color&lt;/code>) التي يتم حلها بشكل ثابت أثناء الترجمة (compile)، يتم حل متغيرات CSS والكتابة فوقها ديناميكيًا في وقت التشغيل (runtime) للمتصفح. يتيح ذلك تغيير ألوان الصفحة بالكامل فورًا بمجرد تبديل الفئة (class) عبر JavaScript.&lt;/p>
&lt;h3 id="11-تعريف-سمة-الألوان-الأساسية">1.1 تعريف سمة الألوان الأساسية
&lt;/h3>&lt;p>أولاً، نستخدم الفئة الزائفة &lt;code>:root&lt;/code> لتعريف لوحة ألوان الوضع الفاتح (الافتراضي). ثم النمط التصميمي الشائع هو الكتابة فوق تلك المتغيرات عند إضافة سمة مثل &lt;code>[data-theme='dark']&lt;/code> (أو فئة &lt;code>.dark&lt;/code>).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* تعريف متغيرات الوضع الفاتح (الافتراضي) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f3f4f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#4b5563&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#3b82f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#e5e7eb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* الكتابة فوق المتغيرات في الوضع الداكن */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* التطبيق الفعلي */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bg&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">background-color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">accent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>بهذه الطريقة، من خلال الفصل التام بين التخطيط والطباعة من جهة وتحديد الألوان (السمة) من جهة أخرى، تتحسن قابلية صيانة CSS بشكل كبير.&lt;/p>
&lt;h3 id="12-الاستفادة-من-media-prefers-color-scheme-dark">1.2 الاستفادة من @media (prefers-color-scheme: dark)
&lt;/h3>&lt;p>إذا تم تعيين الوضع الداكن على مستوى نظام التشغيل، فمن المستحسن من منظور تجربة المستخدم تطبيق السمة الداكنة تلقائيًا منذ الزيارة الأولى للموقع. الاستعلام الإعلامي (media query) &lt;code>@media (prefers-color-scheme: dark)&lt;/code> يحقق ذلك.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* حل بديل عندما يكون إعداد بيئة نظام التشغيل هو الوضع الداكن */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">media&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="nt">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nt">dark&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>في هذا التدوين، طالما أن المستخدم لم يختر الوضع الفاتح صراحةً (&lt;code>data-theme='light'&lt;/code>)، فسيتم احترام إعداد الوضع الداكن لنظام التشغيل والكتابة فوق المتغيرات.&lt;/p>
&lt;hr>
&lt;h2 id="2-فهم-مساحات-الألوان-وإمكانية-الوصول-wcag-21-aaa">2. فهم مساحات الألوان وإمكانية الوصول (WCAG 2.1 AAA)
&lt;/h2>&lt;p>في تصميم ألوان الوضع الداكن، لا يكفي مجرد &amp;ldquo;جعل الخلفية سوداء والنص أبيض&amp;rdquo;. إذا كان التباين قويًا جدًا، فسيؤدي ذلك إلى توهج (halation) ويجعل القراءة أصعب، وإذا كان منخفضًا جدًا، فستتأثر الرؤية. تحدد إرشادات إمكانية الوصول لمحتوى الويب (WCAG) بدقة نسبة التباين المطلوبة لضمان الرؤية الواضحة.&lt;/p>
&lt;h3 id="21-معادلة-حساب-نسبة-التباين-في-wcag">2.1 معادلة حساب نسبة التباين في WCAG
&lt;/h3>&lt;p>تُعرف نسبة التباين (Contrast Ratio) $CR$ في WCAG باستخدام الإضاءة النسبية (Relative Luminance) للون الخلفية ولون المقدمة على النحو التالي:&lt;/p>
$$CR = \frac{L_{lighter} + 0.05}{L_{darker} + 0.05}$$&lt;p>حيث أن $L_{lighter}$ هو الإضاءة النسبية للون الأفتح، و $L_{darker}$ هو الإضاءة النسبية للون الأغمق (نطاق القيم من 0.0 إلى 1.0). لتحقيق مستوى AAA في WCAG 2.1، يلزم نسبة تباين تبلغ &lt;strong>7:1 أو أكثر&lt;/strong> للنصوص العادية، و &lt;strong>4.5:1 أو أكثر&lt;/strong> للنصوص الكبيرة.&lt;/p>
&lt;p>تُحسب الإضاءة النسبية $L$ من قيم RGB في مساحة ألوان sRGB باستخدام المعادلة المعقدة التالية:&lt;/p>
$$L = 0.2126 \times R + 0.7152 \times G + 0.0722 \times B$$&lt;p>كل مكون ($R, G, B$) يستخدم القيمة المُطبعة (normalized value) عن طريق قسمة القيمة الأصلية ذات الـ 8 بت ($R_{sRGB}$) على 255، ويقوم بالتحويل التالي لفك تصحيح جاما:&lt;/p>
$$
R, G, B =
\begin{cases}
\frac{C_{sRGB}}{12.92} &amp; \text{if } C_{sRGB} \le 0.03928 \\
\left( \frac{C_{sRGB} + 0.055}{1.055} \right)^{2.4} &amp; \text{otherwise}
\end{cases}
$$&lt;p>من الصعب إجراء هذه الحسابات يدويًا، ولكن باستخدام أدوات تصميم الألوان، يمكنك تلقائيًا تحديد الألوان التي تلبي نسبة تباين 7:1 ($CR \ge 7.0$).&lt;/p>
&lt;h3 id="22-hsl-مقابل-rgb-مقابل-oklch">2.2 HSL مقابل RGB مقابل OKLCH
&lt;/h3>&lt;p>عند إنشاء لوحات الألوان، كانت RGB و HSL هي السائدة في الماضي. ولكن هذه النماذج بها عيوب كبيرة فيما يتعلق بـ &amp;ldquo;التوحيد الإدراكي&amp;rdquo; (perceptual uniformity).&lt;/p>
&lt;ul>
&lt;li>&lt;strong>RGB&lt;/strong>: هي ألوان الضوء الأساسية الآلية، ومن الصعب على البشر إجراء تعديلات بديهية مثل &amp;ldquo;جعله أفتح&amp;rdquo; أو &amp;ldquo;جعله أغمق&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>HSL&lt;/strong>: تستخدم تدرج اللون (Hue)، والتشبع (Saturation)، والإضاءة (Lightness)، لكن &amp;ldquo;الإضاءة (L)&amp;rdquo; في HSL لا تتطابق مع السطوع الإدراكي للعين البشرية. على سبيل المثال، الأصفر النقي والأزرق النقي بإضاءة 50% في HSL لهما نفس السطوع عدديًا، ولكن الأصفر يبدو أكثر سطوعًا للعين البشرية بشكل هائل.&lt;/li>
&lt;li>&lt;strong>OKLCH&lt;/strong>: هي أحدث مساحة ألوان تم تقديمها في CSS Color Module Level 4. وتتكون من الإضاءة الإدراكية (Lightness)، والتشبع (Chroma)، وتدرج اللون (Hue)، وهي &lt;strong>تتطابق تمامًا مع الخصائص البصرية البشرية (موحدة إدراكيًا)&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>باستخدام OKLCH، يمكنك الحفاظ على نفس الإضاءة الإدراكية (Lightness) حتى لو قمت بتغيير تدرج اللون (Hue)، مما يجعل إنشاء لوحات ألوان الوضع الداكن قابلة للتنبؤ وآمنة للغاية.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* مثال على تعريف متغيرات CSS باستخدام OKLCH */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* في الوضع الفاتح، تكون الإضاءة الأساسية عالية والتشبع منخفض */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.98&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.25&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.65&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;dark&amp;#34;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* في الوضع الداكن، بمجرد عكس الإضاءة يسهل الحفاظ على التباين الإدراكي */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.20&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.95&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.75&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c">/* جعله أفتح قليلاً للوضع الداكن لضمان الرؤية */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>من خلال تبني OKLCH بهذه الطريقة، يمكنك ببساطة بناء منطق يضمن نسبة تباين متسقة (بمستوى WCAG AAA) عبر سمات متعددة.&lt;/p>
&lt;hr>
&lt;h2 id="3-منع-fouc-وميض-المحتوى-غير-المنسق-و-ssr-hydration">3. منع FOUC (وميض المحتوى غير المنسق) و SSR Hydration
&lt;/h2>&lt;p>المشكلة الأكبر التي تزعج المطورين عند دعم الوضع الداكن هي مشكلة وميض الشاشة المعروفة باسم &lt;strong>FOUC (Flash of Unstyled Content)&lt;/strong>.&lt;/p>
&lt;h3 id="31-فخ-تبديل-السمات-عبر-client-side-js">3.1 فخ تبديل السمات عبر Client-Side JS
&lt;/h3>&lt;p>في SPA مثل React أو Vue (أو المواقع الثابتة عبر SSG)، من الشائع حفظ إعدادات المستخدم في &lt;code>localStorage&lt;/code> وقراءتها بواسطة JavaScript لتبديل السمة. ومع ذلك، إذا تم هذا الإجراء في &lt;code>useEffect&lt;/code> في React، فستحدث المشكلات التالية:&lt;/p>
&lt;ol>
&lt;li>يقوم المتصفح بعرض HTML/CSS في الوضع الفاتح.&lt;/li>
&lt;li>يتم تحميل حزمة JS وتنفيذها.&lt;/li>
&lt;li>يقرأ إعداد &lt;code>dark&lt;/code> من &lt;code>localStorage&lt;/code>.&lt;/li>
&lt;li>تتم إضافة فئة &lt;code>dark&lt;/code> إلى HTML، وتصبح الشاشة داكنة فجأة (وميض).&lt;/li>
&lt;/ol>
&lt;h3 id="32-الحل-المثالي-لمنع-fouc-استخدام-ملفات-تعريف-الارتباط-cookies-و-ssr">3.2 الحل المثالي لمنع FOUC: استخدام ملفات تعريف الارتباط (Cookies) و SSR
&lt;/h3>&lt;p>أفضل ممارسة لمنع FOUC تمامًا ومنع أخطاء الـ hydration هي &lt;strong>حفظ إعدادات سمة المستخدم في &lt;code>document.cookie&lt;/code>، وإرجاع HTML مع الفئة المناسبة المُضافة بالفعل أثناء مرحلة العرض من جانب الخادم (SSR)&lt;/strong>.&lt;/p>
&lt;p>يوضح مخطط التسلسل التالي التدفق المثالي لتهيئة السمة باستخدام ملفات تعريف الارتباط.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant User as &amp;#34;المستخدم&amp;#34;
participant Browser as &amp;#34;متصفح نظام التشغيل&amp;#34;
participant Server as &amp;#34;خادم SSR&amp;#34;
participant DOM as &amp;#34;عميل DOM&amp;#34;
User-&amp;gt;&amp;gt;Browser: &amp;#34;طلب الصفحة&amp;#34;
Browser-&amp;gt;&amp;gt;Server: &amp;#34;طلب GET مع ملف تعريف الارتباط (theme=dark)&amp;#34;
Server--&amp;gt;&amp;gt;Browser: &amp;#34;إرجاع HTML مع &amp;lt;html data-theme=&amp;#39;dark&amp;#39;&amp;gt;&amp;#34;
Browser-&amp;gt;&amp;gt;DOM: &amp;#34;عرض HTML الأولي (بدون FOUC)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;تنفيذ نص الـ Hydration&amp;#34;
alt &amp;#34;لا يوجد ملف تعريف ارتباط (الزيارة الأولى)&amp;#34;
DOM-&amp;gt;&amp;gt;Browser: &amp;#34;التحقق من prefers-color-scheme لنظام التشغيل&amp;#34;
Browser--&amp;gt;&amp;gt;DOM: &amp;#34;نتيجة matchMedia (مثلاً dark)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;تعيين data-theme=&amp;#39;dark&amp;#39; عبر نص برمجي حاجز مضمن&amp;#34;
end
&lt;/pre>
&lt;h3 id="33-خط-الدفاع-عبر-النصوص-البرمجية-المضمنة-للمواقع-الثابتة-التي-لا-تستخدم-cookies">3.3 خط الدفاع عبر النصوص البرمجية المضمنة (للمواقع الثابتة التي لا تستخدم Cookies)
&lt;/h3>&lt;p>في حالة المدونات التي تستخدم SSG (توليد المواقع الثابتة) فقط حيث يكون SSR غير ممكن (مثل Hugo أو Gatsby أو التصدير الثابت لـ Astro)، من الضروري وضع نص JavaScript مضمن يُنفذ بشكل يحظر العرض (blocking) داخل وسم &lt;code>&amp;lt;head&amp;gt;&lt;/code> لإضافة الفئة قبل رسم DOM مباشرةً.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- يوضع في نهاية &amp;lt;head&amp;gt; --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;theme&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">osTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matchMedia&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">matches&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">theme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">osTheme&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">documentElement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data-theme&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">theme&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>هذا النص الصغير يحظر عرض المتصفح ويُنفذ على الفور، لذلك بحلول وقت رسم الشاشة، تكون سمة &lt;code>data-theme&lt;/code> قد تم تعيينها بالفعل، مما يمنع وميض الشاشة (FOUC) بشكل كامل.&lt;/p>
&lt;hr>
&lt;h2 id="4-أساليب-التنفيذ-باستخدام-tailwind-css-و-raw-scsscss">4. أساليب التنفيذ باستخدام Tailwind CSS و Raw SCSS/CSS
&lt;/h2>&lt;p>عند دمج الوضع الداكن في مشاريع فعلية، يجب فهم أسلوب كل أداة.&lt;/p>
&lt;h3 id="41-الوضع-الداكن-في-tailwind-css">4.1 الوضع الداكن في Tailwind CSS
&lt;/h3>&lt;p>يوفر Tailwind CSS بشكل افتراضي متغير &lt;code>dark:&lt;/code>، مما يجعل تنفيذ الوضع الداكن أمرًا سهلاً للغاية. يمكنك تعيين خاصية &lt;code>darkMode&lt;/code> في ملف الإعدادات (&lt;code>tailwind.config.js&lt;/code>).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// tailwind.config.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// &amp;#39;media&amp;#39; (يعتمد على إعداد نظام التشغيل) أو &amp;#39;class&amp;#39; (يمكن تبديله يدويًا)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">darkMode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;class&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* توسيع لوحة ألوان Tailwind باستخدام متغيرات CSS */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">primary&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-primary) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">background&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-background) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>في جانب HTML، ما عليك سوى إضافة الفئات كما يلي.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>مرحبا بالعالم&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>يجعل Tailwind الوضع الداكن سهلاً بشكل لا يصدق.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ومع ذلك، كتابة &lt;code>dark:bg-xxx&lt;/code> على كل عنصر يمكن أن يتسبب في تضخم المكونات. بالنسبة للمدونات والتطبيقات واسعة النطاق، يُوصى بتصميم هجين (تصميم الألوان الدلالية) &lt;strong>يعتمد على متغيرات CSS، ويشير Tailwind إلى تلك المتغيرات&lt;/strong>.&lt;/p>
&lt;p>فيما يلي مخطط فئة يوضح وراثة متغيرات CSS وطبقة التطبيق.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class GlobalCSSVariables {
&amp;#34;--color-brand-500&amp;#34;
&amp;#34;--color-gray-900&amp;#34;
}
class SemanticVariables {
&amp;#34;--bg-primary&amp;#34;
&amp;#34;--text-base&amp;#34;
&amp;#34;--accent&amp;#34;
}
class TailwindConfig {
&amp;#34;theme.colors.background&amp;#34;
&amp;#34;theme.colors.primary&amp;#34;
}
class UIComponents {
&amp;#34;class=&amp;#39;bg-background text-primary&amp;#39;&amp;#34;
}
GlobalCSSVariables &amp;lt;|-- SemanticVariables : &amp;#34;:root &amp;amp; .dark&amp;#34;
SemanticVariables &amp;lt;|-- TailwindConfig : &amp;#34;tailwind.config.js&amp;#34;
TailwindConfig &amp;lt;.. UIComponents : &amp;#34;تطبيق فئات المنفعة&amp;#34;
&lt;/pre>
&lt;h3 id="42-التنفيذ-باستخدام-raw-scsscss-الاستفادة-من-mixin">4.2 التنفيذ باستخدام Raw SCSS/CSS (الاستفادة من Mixin)
&lt;/h3>&lt;p>في المشاريع التي لا تستخدم Tailwind وتكتب SCSS بشكل مخصص، نستخدم &lt;code>@mixin&lt;/code> لتغليف أنماط الوضع الداكن.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* تعريف SCSS Mixin */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">@mixin&lt;/span>&lt;span class="nf"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* يدعم كلاً من سمة [data-theme=&amp;#39;dark&amp;#39;] أو إعدادات نظام التشغيل */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@media&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">dark&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">:root:not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* مثال الاستخدام */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="ni">solid&lt;/span> &lt;span class="mh">#eeeeee&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@include&lt;/span>&lt;span class="nd"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#1a202c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#e2e8f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#2d3748&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>هذه الطريقة بديهية، ولكن نظرًا لأن حجم ملف CSS النهائي يميل إلى التضخم (يتم تكرار media query لكل محدد)، فإن الاتجاه الحالي هو الانتقال إلى تصميم يتمحور حول متغيرات CSS (Custom Properties).&lt;/p>
&lt;hr>
&lt;h2 id="5-تحسين-الوضع-الداكن-للصور-image-و-svg">5. تحسين الوضع الداكن للصور (Image) و SVG
&lt;/h2>&lt;p>حتى لو اكتمل تصميم ألوان النصوص والخلفية، إذا ظلت الصور والأيقونات (SVG) الموضوعة كمحتوى في وضعها الفاتح، فستبدو ساطعة للغاية وبارزة بشكل مزعج في الوضع الداكن. يعد تحسين هذه العناصر أيضًا أمرًا بالغ الأهمية.&lt;/p>
&lt;h3 id="51-فلتر-css-لتقليل-إضاءة-الصور">5.1 فلتر CSS لتقليل إضاءة الصور
&lt;/h3>&lt;p>الصور النقطية مثل الصور الفوتوغرافية يمكن أن تكون ساطعة جدًا إذا تم عرضها كما هي في الوضع الداكن. باستخدام خاصية &lt;code>filter&lt;/code> في CSS، يمكنك تقليل الإضاءة (brightness) والتباين (contrast) قليلاً، مما يسمح لها بالاندماج بشكل طبيعي في واجهة المستخدم للسمة الداكنة.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">src&lt;/span>&lt;span class="o">*=&lt;/span>&lt;span class="s2">&amp;#34;.svg&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* تقليل الإضاءة وزيادة التباين قليلاً */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">filter&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">hover&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* استعادة السطوع الأصلي عند التمرير (إذا كان المستخدم يريد رؤية التفاصيل) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-عرض-الصور-باستخدام-وسم-picture">5.2 عرض الصور باستخدام وسم &lt;code>&amp;lt;picture&amp;gt;&lt;/code>
&lt;/h3>&lt;p>لا يمكن التعامل مع صور الشعارات أو الرسوم التوضيحية (مثل صور JPEG ذات الخلفية البيضاء الثابتة) باستخدام الفلاتر فقط. في هذه الحالة، الحل الصحيح هو استخدام عنصر &lt;code>&amp;lt;picture&amp;gt;&lt;/code> في HTML مع استعلامات الوسائط (media queries) لتبديل وعرض ملف صورة مختلف للوضع الداكن.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- عرض هذا للمستخدمين ذوي إعداد نظام التشغيل للوضع الداكن --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">source&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-dark.png&amp;#34;&lt;/span> &lt;span class="na">media&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;(prefers-color-scheme: dark)&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- الافتراضي (الوضع الفاتح) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-light.png&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;شعار الموقع&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>※ ومع ذلك، لا ترتبط هذه الطريقة بالتبديل اليدوي من خلال أشياء مثل &lt;code>localStorage&lt;/code> (إنها تعتمد فقط على إعداد نظام التشغيل). إذا قمت بتنفيذ التبديل اليدوي، فستحتاج إما إلى إعادة كتابة الـ &lt;code>src&lt;/code> للصور ديناميكيًا باستخدام JS، أو تبديل &lt;code>display: none&lt;/code> باستخدام فئات CSS.&lt;/p>
&lt;h3 id="53-دعم-currentcolor-في-أيقونات-svg">5.3 دعم &lt;code>currentColor&lt;/code> في أيقونات SVG
&lt;/h3>&lt;p>بالنسبة لرسومات SVG المضمنة المستخدمة كأيقونات وغيرها، فإن أذكى طريقة هي ربط لون التعبئة بلون النص الخاص بالعنصر الأصل. حدد &lt;code>currentColor&lt;/code> في سمة &lt;code>fill&lt;/code> أو &lt;code>stroke&lt;/code> الخاصة بـ SVG.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- يتم تطبيق قيمة خاصية color في CSS (مثل var(--text-primary)) تلقائيًا --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">svg&lt;/span> &lt;span class="na">viewBox&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;0 0 24 24&amp;#34;&lt;/span> &lt;span class="na">fill&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;currentColor&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">path&lt;/span> &lt;span class="na">d&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;M12 2L2 22h20L12 2z&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>من خلال هذا، عند التبديل إلى الوضع الداكن ويصبح لون نص العنصر الأصل أبيض، ستتغير أيقونة SVG تلقائيًا أيضًا إلى اللون الأبيض.&lt;/p>
&lt;hr>
&lt;h2 id="6-الخلاصة-نحو-تصميم-مستدام-للوضع-الداكن">6. الخلاصة: نحو تصميم مستدام للوضع الداكن
&lt;/h2>&lt;p>لتنفيذ وضع داكن عالي الجودة في المدونات وتطبيقات الويب، لا غنى عن تصميم CSS يغطي النقاط التالية:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>استخدام CSS Custom Properties&lt;/strong>: تجنب الترميز الثابت (hardcoding) للألوان، وقم بتجريدها إلى أسماء متغيرات دلالية (مثل: &lt;code>--bg-primary&lt;/code>).&lt;/li>
&lt;li>&lt;strong>اعتماد مساحة الألوان OKLCH&lt;/strong>: صمم منطقيًا نسبة تباين عالية الوصول (7:1 أو أكثر) تلبي معايير WCAG 2.1 AAA ضمن مساحة ألوان موحدة إدراكيًا.&lt;/li>
&lt;li>&lt;strong>تنفيذ تدابير FOUC بصرامة&lt;/strong>: تخلص تمامًا من وميض الشاشة عند التحميل الأولي من خلال التنسيق بين SSR وملفات تعريف الارتباط (Cookies)، أو باستخدام نصوص برمجية حاجبة مضمنة داخل &lt;code>&amp;lt;head&amp;gt;&lt;/code>.&lt;/li>
&lt;li>&lt;strong>تحسين الوسائط والأصول&lt;/strong>: استخدم &lt;code>filter: brightness()&lt;/code> و &lt;code>currentColor&lt;/code> وعلامات &lt;code>&amp;lt;picture&amp;gt;&lt;/code> لمزامنة العناصر بخلاف النصوص مع السمة الداكنة.&lt;/li>
&lt;/ol>
&lt;p>هذا الاهتمام الدقيق بالتفاصيل الذي يتجاوز مجرد &amp;ldquo;عكس الألوان&amp;rdquo; هو ما يصنع الفارق في تجربة المستخدم، وهو شرط أساسي لمدونة حديثة توفر تجربة قراءة ممتازة ومريحة للعين وتحظى بتقدير طويل الأمد. نأمل أن يستخدم المطورون الذين يخططون لإضافة الوضع الداكن أنماط التصميم المذكورة في هذه المقالة كمرجع.&lt;/p></description></item></channel></rss>