احتفظ برموز التصميم المشتركة في CSS الصفحة، وأتح متغيرات خاصة بالمكوّن بوصفها واجهة التنسيق، واربط بينها بتعيين صغير تملكه الصفحة. يشرح هذا الدليل إنشاء عرض توضيحي محلي على Mac يفصل هذه المسؤوليات.

ما الذي يخص الصفحة، وما الذي يخص المكوّن؟

لمن يحرر صفحة ويب مكتوبة على Mac، تتمثل المهمة العملية في تنسيق مكوّن قابل لإعادة الاستخدام من دون جعل تنفيذه يعتمد على أسماء الرموز المميزة الخاصة بالصفحة. يستخدم المثال أدناه مكوّنًا اصطناعيًا orbit-tile يحتوي على شكل تجريدي وتسمية قصيرة.

يمكن أن يكون كل من رمز التصميم المميز ومتغير المكوّن العام خاصية مخصصة في CSS. وينبع الفرق بينهما من العقد الذي توثقه. تشارك الخصائص المخصصة العادية غير المسجلة في التتالي وتُورَّث؛ ويتيح تعريفها على :root الوصول إليها عبر التوريث في المستند. لا توجد كلمة مفتاحية خاصة في CSS تجعل الخاصية رمز تصميم مميزًا. استخدام الخصائص المخصصة في CSS (المتغيرات)

استخدم جدول الملكية هذا لتحديد موضع كل تعريف. الأسماء توضيحية ومخصصة لهذا المثال.

الطبقة مثال المسؤولية
رموز تصميم الصفحة --scene-accent، --scene-rounding خيارات بصرية مشتركة للصفحة المكتوبة
قواعد تكامل الصفحة قاعدة تستهدف orbit-tile ترجمة خيارات الصفحة إلى واجهة المكوّن البرمجية
متغيرات المكوّن العامة --orbit-tile-fill، --orbit-tile-radius عناصر تحكم موثقة متاحة للمستهلكين
تنفيذ المكوّن .shape، قواعد التخطيط، القيم الافتراضية الحرفية عرض المكوّن دون اشتراط رموز الصفحة

الاختبار المفيد هو قابلية النقل: هل يمكن لصفحة أخرى توفير أسماء مختلفة للرموز المميزة مع استخدام المكوّن نفسه من دون تغيير؟ باستخدام تعيين تملكه الصفحة، يمكن ذلك. ستعيّن الصفحة الجديدة رموزها الخاصة إلى متغيرات المكوّن العامة نفسها.

كيف تعبر واجهة التنسيق حدود Shadow DOM؟

يفصل Shadow DOM بين مطابقة المحددات والتوريث. لا يستطيع محدد الصفحة عادةً استهداف شكل داخلي عبر حد الظل. لكن يمكن أن تصل قيم الخصائص الموروثة إليه عبر العنصر المضيف. وتصف مواصفة CSS Shadow شجرة العناصر المسطحة المستخدمة في هذه العلاقة؛ إذ لا تلغي أشجار المحددات المنفصلة التوريث. CSS Shadow Module Level 1

اترك المتغيرات العامة دون تعيين داخل المكوّن، ووفّر القيم الافتراضية في مواضع استخدامها. على سبيل المثال، يقبل background-color: var(--orbit-tile-fill, #667085) قيمة المستهلك الموروثة، مع توفير قيمة افتراضية مستقلة. أما تعريف --orbit-tile-fill: #667085 على :host فسيمنح العنصر المضيف قيمة خاصة به ويحجب قيمة موروثة من سلف. ويمكن للمستهلك مع ذلك استهداف العنصر المضيف مباشرةً. تنسيق Web Components

في هذا المثال، وثّق عنصرَي تحكم عامين فقط:

المتغير العام القيمة المقبولة في هذا المثال القيمة الافتراضية عند غيابه التأثير
--orbit-tile-fill لون CSS صالح #667085 لون خلفية الشكل
--orbit-tile-radius طول أو نسبة مئوية غير سالبة واحدة 1rem نصف قطر زوايا الشكل

هذه القيم المقبولة جزء من اتفاق الواجهة البرمجية. لا يسجلها التنفيذ الأساسي ولا يتحقق من صلاحيتها تلقائيًا. أسماء الأصناف الداخلية تفاصيل تنفيذية، لذا ينبغي لمؤلفي الصفحات تخصيص المتغيرات العامة بدلًا من الاعتماد على .shape.

أنشئ مثالًا محليًا متكاملًا على Mac

استخدم محرر نصوص عاديًا ومتصفحًا يدعم الخصائص المخصصة في CSS وShadow DOM والعناصر المخصصة المستقلة. يستخدم هذا المثال عنصرًا مستقلًا يمتد من HTMLElement؛ ويميّز MDN هذا الأسلوب عن العناصر المضمنة المخصصة، التي لا تخطط Safari لدعمها. استخدام العناصر المخصصة

في TextEdit، اختر File > New، ثم Format > Make Plain Text. الصق المستند الكامل أدناه واحفظه باسم token-demo.html. إذا ظهرت مطالبة، فاختر Use .html. عند فتحه مجددًا للتحرير، اختر Ignore rich text commands في مربع حوار الفتح، أو فعّل Display HTML files as HTML code instead of formatted text في الإعدادات. العمل مع مستندات HTML في TextEdit على Mac

تتولى ورقة الأنماط الأولى في المستند الرموز المميزة وقواعد التكامل وتخطيط الصفحة. وينشئ المكوّن ورقة أنماط منفصلة داخل جذر الظل. يعرّف HTML العنصر style وسيلةً لتضمين CSS؛ وإبقاء المصدر كله في ملف واحد يجعل إعداد الموارد في المثال واضحًا. معيار HTML: عنصر style

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Orbit tile token demo</title>
  <style>
    /* Page-owned shared tokens */
    :root {
      --scene-accent: #4058b8;
      --scene-rounding: 12px;
    }

    /* Page-owned integration mapping */
    orbit-tile {
      --orbit-tile-fill: var(--scene-accent);
      --orbit-tile-radius: var(--scene-rounding);
    }

    /* A local page theme */
    .alternate {
      --scene-accent: #276f64;
      --scene-rounding: 24px;
    }

    /* An intentional override for one component instance */
    orbit-tile.special {
      --orbit-tile-fill: #a65b20;
      --orbit-tile-radius: 50%;
    }

    body {
      max-width: 36rem;
      margin: 3rem auto;
      padding: 0 1rem;
      font: 1rem/1.5 system-ui, sans-serif;
    }
    main { display: grid; gap: 1rem; }
  </style>
</head>
<body>
  <main>
    <orbit-tile>Sample A</orbit-tile>
    <section class="alternate" aria-label="Alternate samples">
      <orbit-tile>Sample B</orbit-tile>
    </section>
    <orbit-tile class="special">Sample C</orbit-tile>
  </main>

  <script>
    class OrbitTile extends HTMLElement {
      constructor() {
        super();
        const root = this.attachShadow({ mode: 'open' });
        const style = document.createElement('style');
        style.textContent = `
          :host { display: block; }
          .tile {
            display: flex;
            align-items: center;
            gap: 1rem;
            padding: 1rem;
            background: #f2f4f6;
            color: #202630;
          }
          .shape {
            display: block;
            width: 4rem;
            height: 4rem;
            flex: 0 0 4rem;
            background-color: var(--orbit-tile-fill, #667085);
            border-radius: var(--orbit-tile-radius, 1rem);
          }
        `;
        const tile = document.createElement('div');
        tile.className = 'tile';
        const shape = document.createElement('span');
        shape.className = 'shape';
        shape.setAttribute('aria-hidden', 'true');
        tile.append(shape, document.createElement('slot'));
        root.append(style, tile);
      }
    }
    customElements.define('orbit-tile', OrbitTile);
  </script>
</body>
</html>

افتح الملف المحفوظ في Safari أو Chrome أو Firefox. لا يستخدم المثال أي نصوص برمجية أو خطوط أو صور أو أوراق أنماط خارجية. اجتاز JavaScript فحص الصياغة؛ والنتائج المرئية التالية متوقعة استنادًا إلى التعريفات، وليست تقريرًا عن تشغيله في متصفح.

النسخة الشكل المتوقع مسار القيمة
Sample A أزرق، بزوايا مقدارها 12px رموز الجذر المميزة ← تعيين المضيف ← المكوّن
Sample B أخضر، بزوايا مقدارها 24px رموز القسم المميزة ← تعيين المضيف ← المكوّن
Sample C دائرة برتقالية تجاوز النسخة ← المكوّن

لاحظ أن JavaScript الخاص بالمكوّن لا يحتوي على أي مراجع إلى --scene-*. لا تستهلك ورقة أنماطه إلا الأسماء العامة --orbit-tile-*. يمكن للصفحة إعادة تنظيم مفردات السمة دون تغيير المكوّن.

أين ينبغي أن يحدث تعيين الرموز المميزة إلى المكوّن؟

عيّن القيم في الموضع الذي تتوفر فيه السمة المقصودة. في هذا العرض التوضيحي، تُطبّق قاعدة orbit-tile على كل عنصر مضيف. وهذا يتيح للمضيف الثاني حل --scene-accent بعد أن يرث القيمة الخضراء من القسم.

تجنب نقل هذا التعيين إلى :root من دون مراعاة توقيت الحل. إذا حُسب --orbit-tile-fill: var(--scene-accent) هناك، فستَرث العناصر التابعة قيمته المحسوبة. وتغيير --scene-accent وحده في قسم لاحق لا يعيد إنشاء الاسم المستعار الموروث. تُحل مراجع var() في الخصائص المخصصة قبل توريث قيمها المحسوبة. وحدة الخصائص المخصصة في CSS للمتغيرات المتتالية، المستوى 1

القرار العملي متعلق بالنطاق: استخدم تعيينًا على مستوى المضيف عندما ينبغي لكل نسخة اتباع رموز الصفحة المحيطة بها. وإذا كان المقصود أن تشترك منطقة كاملة في قيمة عامة واحدة للمكوّن، فقد يكون تعريف ذلك المتغير العام على المنطقة مناسبًا؛ لكن أزل تعيين المضيف لهذا المتغير كيلا يستبدل القيمة الموروثة.

للاستخدامات الداخلية المتكررة، يمكن لاسم مستعار خاص بالتنفيذ أن يجمع تعريفات القيم الاحتياطية. قد يشير اسم مثل --_orbit-tile-fill إلى اصطلاح داخلي، لكن الشرطة السفلية لا تنشئ خصوصية مفروضة. يكون الاستهلاك المباشر، كما في المثال الكامل، أسهل في المتابعة عندما تُستخدم كل قيمة مرة واحدة. تنسيق Web Components

كيف تشخّص تجاوزًا لا يعمل؟

استخدم النسخ الثلاثة بوصفها اختبارًا صغيرًا للعقد. غيّر تعريفًا واحدًا في كل مرة، ثم احفظ وأعد التحميل. تساعد هذه الفحوص على التمييز بين مورد مفقود ومشكلة في التوريث وقيمة غير صالحة.

  1. غيّر اللون البارز الأساسي إلى #7251a1. يُتوقع أن تتبناه Sample A فقط؛ فللقسم والنسخة خياراهما الخاصان.
  2. غيّر اللون البارز للقسم إلى #32758a. يُتوقع أن تتغير Sample B من دون تعديل شيفرة المكوّن.
  3. أزل تعيين التكامل بالكامل. يُتوقع أن تستخدم Sample A وSample B التعبئة الرمادية ونصف القطر 1rem الخاصين بالمكوّن؛ بينما تحتفظ Sample C بتجاوزاتها العامة الصريحة.
  4. أعد التعيين قبل متابعة التجارب الأخرى.

إذا اختفت الأشكال كلها، فتحقق أولًا من حفظ المستند بصيغة HTML ومن سلامة النص البرمجي. وإذا ظهرت التسميات دون تنسيق الأشكال، فافحص ورقة أنماط المكوّن. لا تنسق ورقة أنماط المستند وحدها العناصر التابعة للظل باستخدام المحددات العادية؛ بل يجب أن يرفق التنفيذ أنماطه الداخلية بشجرة الظل. يوثّق MDN الأنماط المضمنة وأوراق الأنماط المُنشأة لهذا الغرض. استخدام Shadow DOM

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

إذا أصبح الشكل شفافًا بعد تعيين --orbit-tile-fill: 12px، فلن تصلح القيمة الاحتياطية المشكلة. المتغير موجود، لكن قيمته بعد الاستبدال غير صالحة لـ background-color. يصبح ذلك التعريف غير صالح وقت حساب القيمة؛ ولا يعود إلى تعريف خلفية سابق. أزل التعيين أو قدّم لونًا صالحًا. وحدة الخصائص المخصصة في CSS للمتغيرات المتتالية، المستوى 1

هل ينبغي استخدام @property للمتغيرات العامة؟

التسجيل اختياري لهذا العقد. تضيف @property قيودًا على الصياغة وقيمة أولية وسلوك توريث صريحًا. ويصنفها MDN ضمن Baseline 2024، ومتاحة عبر المتصفحات الحالية منذ يوليو 2024، مع التحذير من احتمال غياب الدعم في المتصفحات الأقدم. يتجنب المثال الكامل جعل التسجيل شرطًا مسبقًا. قاعدة CSS ‏@property

إذا أضفت التسجيل، فنسّقه على مستوى المستند. تفيد وثائق تنفيذ Chrome بأن التسجيلات داخل جذور الظل يجري تجاهلها؛ ويعود تقريرها إلى أغسطس 2024، لذا تعامل معه بوصفه حدًا موثقًا للتشغيل البيني، لا دليلًا على اختبار حديث للمتصفح. المسار المدعوم الموصوف هناك هو التسجيل على مستوى المستند، مع استهلاك المكوّن للخاصية عبر الحد. أسماء CSS المعرفة من المؤلف وShadow DOM: في المواصفة والتطبيق العملي

يغيّر التسجيل السلوك أيضًا. إن تعيين inherits: false سيعطّل التوريث من المضيف إلى الشكل في المثال. واللون الأولي المسجل يعني أن للخاصية لونًا عندما لا تُعيّن، لذا لن يعود العنصر المستهلك var() قيمة افتراضية عند غياب القيمة. احتفظ بـ inherits: true لهذا التصميم، واختر عمدًا الآلية المسؤولة عن القيمة الافتراضية. قاعدة CSS ‏@property

ينبغي أن يتيح العقد المكتمل لمؤلف الصفحة شرح مسار كل قيمة: خيار صفحة مشترك، وتعيين إلى عنصر تحكم موثق في المكوّن، وقيمة احتياطية يملكها المكوّن. أبقِ التعيين قريبًا من نطاق السمة المقصود، واترك المدخلات العامة الموروثة غير معيّنة داخليًا، واحتفظ بمثال النسخ الثلاث كلما راجعت الواجهة البرمجية.

المصادر