EduFlow
إتقان CSS الحديثة
⁦0%⁩

الوحدة 2 · تخطيط يصمد

الـ Subgrid لبطاقات متحاذية

17 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

استخدم الـ subgrid لتصطفّ أجزاء كل بطاقة جلسة (الوقت والعنوان والملخّص والقاعة) عبر صف البطاقات كله، أيًّا كان طول كل عنوان.

  1. 1لماذا لا تصطفّ الشبكات المتداخلة
  2. 2استعارة صفوف الأب
  3. 3الفجوات والخطوط والأعمدة
  4. 4دورك الآن

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

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

الإصلاحات القديمة كانت ارتفاعات ثابتة (تنكسر مع المحتوى الحقيقي)، أو JavaScript يقيس كل عنوان. أما الـ subgrid (الشبكة الفرعية) فيجعل البطاقات تتشارك مجموعة صفوف واحدة.

لماذا لا تصطفّ الشبكات المتداخلة

حين تصبح البطاقة display: grid، تنشئ tracks خاصة بها ومستقلة. صف العنوان فيها بارتفاع عنوانها هي؛ وصف العنوان في البطاقة المجاورة بارتفاع ذلك العنوان. لا شيء يربط بينهما، لأن كل بطاقة لا تعرف إلا محتواها.

ما تريده هو أن يكون «صف العنوان» track واحدًا تتشاركه كل البطاقات في الصف البصري نفسه، ويتحدّد حجمه بأطول عنوان. وهذا الـ track المشترك يجب أن يعيش في الأب، أي شبكة .sessions، وعلى البطاقات أن تستعيره.

استعارة صفوف الأب

sessions.css
.sessions {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  gap: 24px;
}

.session-card {
  display: grid;
  grid-row: span 4;              /* time, title, abstract, room */
  grid-template-rows: subgrid;   /* use the parent's four rows */
  row-gap: 8px;                  /* tighter than the parent's 24px */
}

تصريحان يؤدّيان العمل. grid-row: span 4 تجعل كل بطاقة تشغل أربعة صفوف من الشبكة الأب. وgrid-template-rows: subgrid تخبر البطاقة ألّا تنشئ صفوفها الخاصة بل تستخدم الصفوف الأربعة التي تمتدّ عليها. ويوضع أبناء البطاقة الأربعة (الوقت والعنوان والملخّص والقاعة) في صفوف الأب تلك بالترتيب.

الآن يحدّد المتصفّح حجم كل صف في الأب بأطول محتوى وُضع فيه من كل البطاقات في ذلك الصف. يكبر track العنوان ليتّسع للعنوان ذي الأسطر الثلاثة، وتحصل العناوين الأقصر على الـ track نفسه، ويبدأ كل ملخّص على الخط نفسه. وتصطفّ تسميات القاعات في الأسفل.

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

لاحظ أن الأب لا يعلن grid-template-rows أبدًا. لا يحتاج إلى ذلك: امتداد البطاقات ينشئ صفوفًا ضمنية، وتتحدّد أحجام هذه الصفوف من المحتوى الذي تضعه فيها الـ subgrids. وكل صف جديد من البطاقات يحصل على tracks أربعة خاصة به، فتصطفّ البطاقات في الصف البصري الثاني فيما بينها لا مع الصف الأول، وهذا ما تريده.

الفجوات والخطوط والأعمدة

يرث الـ subgrid الـ gap من الأب. الأب هنا يستخدم 24px بين البطاقات، وهذا أكثر بكثير مما ينبغي بين العنوان وملخّصه، لذا تتجاوز البطاقة row-gap في الـ tracks الخاصة بها. أما المسافة بين البطاقات فلا تتأثّر.

ويعمل الـ subgrid على الأعمدة أيضًا. حالة كلاسيكية في Waypoint هي نموذج الملف الشخصي للمتحدّث: التسميات في عمود، والحقول في العمود التالي، وعمود التسميات بالعرض نفسه عبر عدّة fieldsets. جعل كل fieldset grid-column: span 2; grid-template-columns: subgrid يعطي كل fieldset أعمدة النموذج الخارجي، فتصطفّ التسميات حتى الأسفل. وخطوط الشبكة المسمّاة في الأب مرئية أيضًا داخل الـ subgrid، لذا تظل grid-column: content-start / content-end تعمل بمستوى أدنى.

دورك الآن

يعطيك التمرين ثلاث بطاقات جلسات بعناوين مختلفة الطول. حوّل كل بطاقة إلى subgrid للصفوف لتصطفّ الملخّصات وتسميات القاعات عبر الصف. تقارن الفحوصات المواضع الرأسية لتلك الأجزاء عبر البطاقات. ويبقى الدرس التالي مع الأحجام: min() وmax() وclamp() والكلمات المفتاحية التي تترك للمحتوى أن يقرّر حجم الأشياء.

أهم النقاط

  • الشبكة المتداخلة التي لها grid-template-rows: subgrid تستخدم صفوف الـ tracks الخاصة بالأب بدل أن تنشئ صفوفها، فيتشارك المحتوى في البطاقات الشقيقة الصفوف نفسها.
  • يجب أن يمتدّ العنصر الذي يستخدم الـ subgrid على عدد من tracks الأب يساوي عدد أجزائه، مثل grid-row: span 4 لبطاقة من أربعة أجزاء.
  • يرث الـ subgrid الفجوة (gap) من الأب افتراضيًا، ويستطيع تجاوزها بـ row-gap أو column-gap خاصة به.
  • الـ subgrid ضمن Baseline منذ سبتمبر 2023 ومتاح على نطاق واسع منذ مارس 2026، لذا لم تعد تخطيطات البطاقات المتحاذية تحتاج إلى ارتفاعات ثابتة أو قياسات بـ JavaScript.

قراءات إضافية

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

لكل بطاقة أربعة أبناء (الوقت والعنوان والملخّص والقاعة). لماذا لا تصطفّ القاعات؟

CSS
.sessions { display: grid; grid-template-columns: repeat(3, 1fr); }
.session-card { display: grid; grid-template-rows: subgrid; }
السؤال 2 من 3

ما الطريقة الأمتن لجعل تسمية القاعة في كل بطاقة تصطفّ عبر الصف حين تلتفّ العناوين على أعداد مختلفة من الأسطر؟

السؤال 3 من 3

للشبكة الأب gap: 24px، والبطاقات التي تستخدم الـ subgrid تبدو متباعدة أكثر من اللازم من الداخل. ماذا تغيّر؟

تدريب

بيئة تجريب الكود

اجعل بطاقات الجلسات تصطفّ

البطاقات الثلاث موضوعة في شبكة من ثلاثة أعمدة، لكن عناوينها تلتفّ على أعداد مختلفة من الأسطر، فتقع الملخّصات وتسميات القاعات على ارتفاعات مختلفة. اجعل كل .session-card subgrid للصفوف يمتدّ على الأجزاء الأربعة (الوقت والعنوان والملخّص والقاعة)، بحيث يبدأ كل ملخّص على الارتفاع نفسه وتصطفّ كل تسمية قاعة. أبقِ البطاقات جنبًا إلى جنب ولا تستخدم ارتفاعات ثابتة.

<section class="sessions">
  <article class="session-card">
    <p class="time">09:30</p>
    <h3>Subgrid</h3>
    <p class="abstract">Aligning card content across a row without fixed heights.</p>
    <p class="room">Room A</p>
  </article>
  <article class="session-card">
    <p class="time">10:15</p>
    <h3>Container Queries in a Design System With Forty Components</h3>
    <p class="abstract">What changed when every component started measuring itself.</p>
    <p class="room">Room B</p>
  </article>
  <article class="session-card">
    <p class="time">11:00</p>
    <h3>Color Spaces for Humans Who Ship Interfaces Every Day of the Week</h3>
    <p class="abstract">oklch(), color-mix() and palettes that stay accessible.</p>
    <p class="room">Main hall</p>
  </article>
</section>جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • البطاقات ما زالت جنبًا إلى جنب
  • كل بطاقة تستخدم grid-template-rows: subgrid
  • كل الملخّصات تبدأ على الارتفاع نفسه
  • كل تسميات القاعات تصطفّ
  • لا شيء يتراكب داخل أيّ بطاقة