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

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

التحجيم الذاتي: min() وmax() وclamp()

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

الدرس في لمحة

حدّد أحجام العناصر من محتواها باستخدام min-content وmax-content وfit-content، واستبدل أزواج الـ breakpoints بـ min() وmax() وclamp().

  1. 1دع المحتوى يقرّر: الكلمات المفتاحية الذاتية
  2. 2min() وmax() وclamp()
  3. 3وحدات تجعل الدوال الرياضية مفيدة
  4. 4دورك الآن

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

كان لملخّصات الجلسات في Waypoint الزوج الكلاسيكي من القواعد: width: 100% مع max-width: 40ch، ثم media query لإضافة حشوة جانبية على الهواتف، وأخرى للشاشات الكبيرة. ثلاث قواعد ونقطتا تحوّل لقول شيء واحد: «كن بالعرض المريح للقراءة، لكن لا تتجاوز المساحة المتاحة لك أبدًا».

تستطيع CSS أن تقول ذلك مباشرةً الآن. يغطّي هذا الدرس أداتين: الكلمات المفتاحية للحجم الذاتي (intrinsic)، التي تترك للمحتوى أن يقرّر حجم العنصر، والدوال الرياضية min() وmax() وclamp()، التي تتيح لك وصف مدى بدل اختيار الـ breakpoints.

دع المحتوى يقرّر: الكلمات المفتاحية الذاتية

معظم الأحجام التي تكتبها خارجية (extrinsic): 300px و50% و1fr. لكن المتصفّح يستطيع أيضًا أن يحدّد حجم العنصر من محتواه، وثلاث كلمات مفتاحية تكشف ذلك:

  • min-content: أضيق ما يمكن أن يكونه العنصر دون أن يفيض، وهو عادةً عرض أطول كلمة فيه.
  • max-content: العرض الذي سيأخذه المحتوى لو لم يلتفّ أبدًا.
  • fit-content: هي max-content، لكن لا تتجاوز المساحة المتاحة أبدًا. وحين تنفد المساحة تلتفّ كأيّ كتلة عادية.

fit-content هي التي ستستخدمها أكثر. الشارة أو الزر من نوع block يتمدّد عادةً ليملأ حاويته؛ أما width: fit-content فتجعله يحتضن تسميته كأنه عنصر inline مع بقائه block، فتظل الـ margins والتراصّ تعمل كما يجب. أضف margin-inline: auto فيتوسّط.

وفي tracks الشبكة تعطيك هذه الكلمات أعمدةً يقودها المحتوى. جدول أعمال Waypoint يستخدم grid-template-columns: max-content 1fr: عمود الوقت بعرض أطول وقت بالضبط («10:15–11:00»)، وعنوان الجلسة يأخذ الباقي.

min() وmax() وclamp()

تأخذ الدوال الرياضية قائمة من القيم، بأيّ خليط من الوحدات، وتُحسم إلى طول واحد وقت التخطيط:

sizing.css
.abstract {
  width: min(100%, 40ch);            /* ceiling: never wider than 40ch */
}

.page {
  padding-inline: max(1rem, 4vw);    /* floor: never less than 1rem */
}

.session-card {
  padding: clamp(12px, 4%, 32px);    /* between 12px and 32px, 4% in the middle */
}

.content {
  width: min(100% - 2rem, 60rem);    /* arithmetic works without calc() */
  margin-inline: auto;
}

min() تُرجع أصغر قيمة، وهذا يعني أنها تضع حدًّا أعلى. min(100%, 40ch) تساوي 40ch في البطاقة العريضة و100% في الضيّقة؛ فهي تستبدل زوج width وmax-width بتصريح واحد. و**max() تُرجع أكبر قيمة**، فتضع حدًّا أدنى: الهامش الجانبي للصفحة 4vw على الشاشات العريضة، لكنه لا يتقلّص أبدًا تحت 1rem على الهاتف.

clamp(MIN, PREFERRED, MAX) تجمع الاثنتين: تُرجع القيمة المفضّلة، لكن لا أقل من MIN ولا أكثر من MAX. وهي بالضبط max(MIN, min(PREFERRED, MAX)).

رسم بياني للـ padding مقابل عرض الحاوية. الخط مستوٍ عند 12 بكسل للحاويات الضيّقة، ويرتفع مع 4 بالمئة من العرض في المنتصف، ويستوي عند 32 بكسل للحاويات العريضة. عرض الحاوية padding 12px (MIN) 4% (PREFERRED) 32px (MAX) 300px 800px
تتبع clamp() القيمة المفضّلة بين حدّين

مع clamp(12px, 4%, 32px) تحصل البطاقة في عمود عرضه 300px على 12px من الـ padding، وفي عمود عرضه 500px على 20px، وكل ما هو أعرض من 800px يتوقّف عند 32px. النسب المئوية في الـ padding نسبةً إلى الحجم الأفقي للكتلة الحاوية، لذا تتبع مسافات البطاقة حاويتها لا الـ viewport.

وحدات تجعل الدوال الرياضية مفيدة

يجب أن تكون القيمة المفضّلة في clamp() شيئًا يتدرّج: نسبة مئوية، أو vw، أو وحدات الحاوية التي ستقابلها في درس الـ Container Queries. أما الحدود فيجب أن تكون وحدات مستقرّة مثل px أو rem. واستخدام rem للحدود له ميزة إضافية: إن رفع المستخدم حجم الخط الافتراضي لديه، تتدرّج الحدود معه.

وهذه أيضًا الحيلة وراء الحماية التي رأيتها في درس grid: minmax(min(180px, 100%), 1fr). داخل حاوية أضيق من 180px تختار min() القيمة 100%، فلا يفرض الـ track فيضانًا أبدًا.

دورك الآن

في التمرين بطاقة جلسة داخل غلاف تغيّر الفحوصات عرضه. حدّ الملخّص بـ 40ch دون أن يفيض في البطاقات الضيّقة، واجعل الشارة تحتضن نصّها، وأعطِ البطاقة padding يتدرّج بين 12px و32px. وبعدها ستجعل components كاملة تغيّر تخطيطها بحسب عرض حاويتها، باستخدام الـ container queries.

أهم النقاط

  • min-content أضيق ما يمكن أن يكونه العنصر دون أن يفيض، وmax-content عرضه دون أيّ التفاف، وfit-content هي max-content محدودةً بالمساحة المتاحة.
  • min() تختار أصغر قيمة، فتعمل كسقف: width: min(100%, 40ch) لا تتجاوز 40ch أبدًا ولا تفيض خارج حاويتها أبدًا.
  • max() تعمل كأرضية، وclamp(MIN, PREFERRED, MAX) تُبقي القيمة المرنة بين حدّين.
  • الدوال الرياضية تقبل وحدات مختلطة وعمليات حسابية، لذا تعمل min(100% - 2rem, 60rem) دون calc() متداخلة.

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

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

لملخّص جلسة width: min(100%, 40ch)، وعرض بطاقته 900px. ما عرض الملخّص؟

السؤال 2 من 4

ما الذي تعطيه padding: clamp(12px, 4%, 32px) لبطاقة في حاوية عرضها 500px؟

السؤال 3 من 4

شارة «Sold out» لها display: block داخل بطاقة، وهي تتمدّد على عرض البطاقة كله. أيّ تصريح يجعلها تحتضن نصّها لكن تلتفّ إن كان النص أطول من البطاقة؟

السؤال 4 من 4

لماذا تصلح max(1rem, 4vw) هامشًا جانبيًا جيدًا للصفحة؟

تدريب

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

حدّد حجم البطاقة من محتواها

تغيّر الفحوصات عرض .wrap بين 220px و1000px. دون media queries:

  1. اجعل .abstract بعرض 40ch على الأكثر، لكن لا يتجاوز أبدًا صندوق المحتوى في البطاقة.
  2. اجعل .badge (وهي من نوع block) تحتضن نصّها بدل أن تتمدّد (أبقِ display: block).
  3. أعطِ .session-card قيمة padding تساوي 4% من عرض الغلاف، لا تقل أبدًا عن 12px ولا تزيد أبدًا على 32px.
<div class="wrap">
  <article class="session-card">
    <span class="badge">Sold out</span>
    <h3>Intrinsic Sizing in Practice</h3>
    <p class="abstract">Let content decide how big things are: min-content, max-content, fit-content and the math functions that replace half your media queries.</p>
  </article>
</div>جارٍ تحميل المحرّر…

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

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • في البطاقة العريضة عرض الملخّص 40ch
  • في البطاقة الضيّقة لا يفيض الملخّص
  • الشارة تحتضن نصّها وتبقى block
  • الـ padding لا ينزل أبدًا تحت 12px
  • الـ padding يساوي 4% من العرض فيما بين الحدّين
  • الـ padding لا يتجاوز أبدًا 32px