آکادمی بنو/آموزش CSS

Media Query در CSS

بهنام نوبخت · ۱۴۰۵/۶/۲۸

در مقاله قبل با مفهوم Responsive Design آشنا شدیم و دیدیم که یک وب‌سایت باید بتواند خودش را با اندازه‌های مختلف صفحه تطبیق دهد. برای رسیدن به این هدف، CSS ابزارهای مختلفی مانند Flexbox، Grid، واحدهای نسبی، clamp() و minmax() را در اختیار ما قرار می‌دهد.

اما گاهی Layout به تغییرات مشخصی نیاز دارد؛ مثلاً در دسکتاپ منوی کامل نمایش داده شود، در موبایل فقط یک دکمه منو دیده شود، یا یک بخش که در دسکتاپ دو ستون دارد در موبایل به یک ستون تبدیل شود.

اینجاست که Media Query وارد می‌شود.

Media Query به CSS اجازه می‌دهد بر اساس شرایط محیط نمایش، مجموعه خاصی از قوانین CSS را اعمال کند. این شرایط می‌توانند شامل عرض و ارتفاع Viewport، جهت نمایش، قابلیت Hover، نوع ورودی کاربر، ترجیح کاربر برای کاهش حرکت و موارد دیگر باشند.

در این مقاله یاد می‌گیریم:

  • Media Query چیست؟

  • ساختار @media چگونه است؟

  • min-width و max-width چه تفاوتی دارند؟

  • چگونه Breakpoint انتخاب کنیم؟

  • Mobile First چیست؟

  • چگونه چند شرط را با هم ترکیب کنیم؟

  • orientation چیست؟

  • hover و pointer چه کاربردی دارند؟

  • چگونه Media Query را برای Accessibility استفاده کنیم؟

  • Syntax جدید Range Query چیست؟

  • چگونه Media Query را در یک پروژه واقعی به کار ببریم؟

Media Query چیست؟

Media Query روشی برای اعمال شرطی قوانین CSS است.

به زبان ساده می‌توانیم به مرورگر بگوییم:

اگر صفحه از یک اندازه کوچک‌تر بود، این CSS را اجرا کن.

یا:

اگر صفحه از یک اندازه بزرگ‌تر بود، Layout را تغییر بده.

ساختار پایه:

@media (condition) {

    /* CSS */

}

مثلاً:

@media (max-width: 768px) {

    body {
        background: #f5f5f5;
    }

}

یعنی اگر عرض Viewport حداکثر 768px باشد، رنگ پس‌زمینه تغییر کند.

Media Query یکی از ابزارهای اصلی Responsive Design است، اما Responsive Design فقط به Media Query محدود نمی‌شود؛ Layoutهای مدرن مانند Grid و Flexbox و واحدهای انعطاف‌پذیر نیز می‌توانند بدون Media Query بخش زیادی از رفتار Responsive را ایجاد کنند.

ساختار media@

ساختار کلاسیک Media Query به صورت زیر است:

@media media-type and (media-feature) {

    selector {
        property: value;
    }

}

مثلاً:

@media screen and (max-width: 768px) {

    .container {
        padding-inline: 16px;
    }

}

در اینجا سه بخش داریم:

@media
   ↓
media type
   ↓
media feature
   ↓
CSS rules

Media Type چیست؟

Media Query می‌تواند نوع Media را نیز مشخص کند.

رایج‌ترین انواع:

all
screen
print

مثلاً:

@media screen {

    body {
        font-family: sans-serif;
    }

}

یا برای چاپ:

@media print {

    .navigation {
        display: none;
    }

}

در حالت چاپ، منوی سایت دیگر کاربردی ندارد، بنابراین می‌توانیم آن را مخفی کنیم.

Media Type اختیاری است؛ اگر آن را مشخص نکنیم، Media Query به صورت پیش‌فرض برای همه Mediaهای مناسب اعمال می‌شود.

مهم‌ترین Media Feature: width

یکی از پرکاربردترین Media Featureها width است.

این ویژگی عرض Viewport را بررسی می‌کند.

مثلاً:

@media (width: 600px) {

    body {
        background: red;
    }

}

یعنی فقط وقتی عرض Viewport دقیقاً 600px باشد.

اما در طراحی Responsive معمولاً بررسی یک بازه کاربردی‌تر است.

max-width

با max-width می‌توانیم بگوییم:

اگر عرض Viewport کمتر یا مساوی مقدار مشخصی بود، این CSS را اعمال کن.

مثلاً:

@media (max-width: 768px) {

    .navigation {
        display: none;
    }

}

در این مثال وقتی عرض صفحه 768px یا کمتر باشد، Navigation مخفی می‌شود.

min-width

min-width برعکس max-width عمل می‌کند.

یعنی:

اگر عرض Viewport حداقل مقدار مشخصی داشت، این CSS را اعمال کن.

مثلاً:

@media (min-width: 768px) {

    .navigation {
        display: flex;
    }

}

یعنی از 768px به بالا Navigation به صورت Flex نمایش داده شود.

تفاوت min-width و max-width

به این دو مثال دقت کنید.

max-width

@media (max-width: 768px) {

    .card {
        width: 100%;
    }

}

یعنی:

0px ─────────────── 768px
        فعال

min-width

@media (min-width: 768px) {

    .card {
        width: 50%;
    }

}

یعنی:

768px ───────────────────→
          فعال

Mobile First با Media Query

یکی از بهترین روش‌ها برای استفاده از Media Query، رویکرد Mobile First است.

در این روش ابتدا CSS پایه را برای صفحات کوچک می‌نویسیم و سپس برای Viewportهای بزرگ‌تر، قابلیت‌های بیشتری اضافه می‌کنیم.

مثلاً:

.services {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
}

این حالت پایه برای موبایل است.

سپس:

@media (min-width: 768px) {

    .services {
        grid-template-columns: repeat(2, 1fr);
    }

}

و در صفحه‌های بزرگ‌تر:

@media (min-width: 1100px) {

    .services {
        grid-template-columns: repeat(4, 1fr);
    }

}

نتیجه:

Mobile

┌─────────┐
│ Card 1  │
├─────────┤
│ Card 2  │
├─────────┤
│ Card 3  │
├─────────┤
│ Card 4  │
└─────────┘


Tablet

┌───────┬───────┐
│ Card1 │ Card2 │
├───────┼───────┤
│ Card3 │ Card4 │
└───────┴───────┘


Desktop

┌──────┬──────┬──────┬──────┐
│  1   │  2   │  3   │  4   │
└──────┴──────┴──────┴──────┘

این الگوی Mobile First با توصیه‌های فعلی MDN نیز هم‌راستا است: ابتدا سبک پایه برای صفحه‌های باریک و سپس Media Queryهای min-width برای عرض‌های بزرگ‌تر.

Breakpoint چیست؟

Breakpoint نقطه‌ای است که Layout در آن تغییر می‌کند.

مثلاً:

Mobile
0 ───────── 767px

Tablet
768 ─────── 1099px

Desktop
1100px ───────────→

اما یک نکته بسیار مهم:

Breakpoint را نباید فقط بر اساس مدل موبایل، تبلت یا لپ‌تاپ انتخاب کنیم.

بهتر است ابتدا Layout را بسازیم و ببینیم در چه عرضی محتوا شروع به خراب شدن می‌کند.

مثلاً فرض کنید Navigation در این حالت مشکل پیدا می‌کند:

900px
↓
Logo + 6 menu items
↓
فضای کافی وجود ندارد

پس می‌توانیم Breakpoint را نزدیک همان محدوده قرار دهیم.

MDN نیز توصیه می‌کند Breakpointها بر اساس نیاز Layout و محتوا انتخاب شوند، نه بر اساس دستگاه‌های خاص.

Breakpointهای رایج

در پروژه‌ها ممکن است مقادیری مانند این ببینید:

@media (min-width: 640px) { }

@media (min-width: 768px) { }

@media (min-width: 1024px) { }

@media (min-width: 1280px) { }

اما این اعداد قانون جهانی نیستند.

نباید تصور کنید:

640 = موبایل
768 = تبلت
1024 = لپ‌تاپ
1280 = دسکتاپ

این دسته‌بندی همیشه درست نیست.

ممکن است پروژه شما در 850px به Breakpoint نیاز داشته باشد یا اصلاً به Breakpoint در آن محدوده نیاز نداشته باشد.

استفاده از واحدهای نسبی در Breakpoint

در پروژه‌های حرفه‌ای می‌توان از واحدهایی مانند rem برای Breakpointها نیز استفاده کرد.

مثلاً:

@media (min-width: 48rem) {

    .services {
        grid-template-columns: repeat(2, 1fr);
    }

}

و:

@media (min-width: 64rem) {

    .services {
        grid-template-columns: repeat(4, 1fr);
    }

}

استفاده از واحدهای نسبی برای Breakpointها می‌تواند از وابستگی به یک اندازه مطلق خاص جلوگیری کند. MDN نیز استفاده از واحدهای نسبی را در تعریف Breakpointها توصیه می‌کند.

چند Media Query پشت سر هم

می‌توانیم چند Media Query در یک فایل داشته باشیم:

body {
    font-size: 16px;
}

@media (min-width: 768px) {

    body {
        font-size: 17px;
    }

}

@media (min-width: 1100px) {

    body {
        font-size: 18px;
    }

}

در اینجا:

Mobile
16px

Tablet
17px

Desktop
18px

این روش در Mobile First بسیار کاربردی است.

Media Query با چند شرط

می‌توانیم چند شرط را با and ترکیب کنیم.

مثلاً:

@media (min-width: 768px) and (max-width: 1200px) {

    .container {
        max-width: 960px;
    }

}

یعنی CSS زمانی اعمال شود که عرض بین این دو مقدار قرار داشته باشد.

Syntax مدرن Range

CSS مدرن Syntax خواناتری برای Range Query ارائه می‌کند.

به جای:

@media (min-width: 768px) and (max-width: 1200px) {

    /* CSS */

}

می‌توان نوشت:

@media (768px <= width <= 1200px) {

    /* CSS */

}

هر دو روش مفهوم یکسانی دارند. Syntax جدید Range کوتاه‌تر و در بعضی موارد خواناتر است.

مثال:

@media (width >= 768px) {

    .services {
        grid-template-columns: repeat(2, 1fr);
    }

}

و:

@media (width < 768px) {

    .services {
        grid-template-columns: 1fr;
    }

}

آیا باید همیشه از Syntax جدید استفاده کنیم؟

خیر.

هر دو Syntax در پروژه‌های مدرن کاربرد دارند:

@media (min-width: 768px) {
}

و:

@media (width >= 768px) {
}

اگر در پروژه‌ای Syntax قدیمی‌تر استفاده شده، لازم نیست صرفاً برای مدرن‌سازی همه فایل‌ها را تغییر دهید.

مهم‌تر از Syntax، ساختار صحیح Breakpointها و خوانایی کد است.

ترکیب Media Query با and

مثلاً:

@media screen and (width >= 768px) {

    .hero {
        display: grid;
    }

}

یعنی:

  1. Media باید screen باشد.

  2. عرض Viewport حداقل 768px باشد.

هر دو شرط باید برقرار باشند.

استفاده از or

می‌توانیم چند حالت جایگزین داشته باشیم.

در Syntax کلاسیک معمولاً از کاما استفاده می‌شود:

@media (max-width: 600px), (orientation: portrait) {

    .hero {
        padding-block: 48px;
    }

}

یعنی اگر یکی از شرایط برقرار باشد، CSS اعمال می‌شود.

Orientation چیست؟

Media Feature دیگری که می‌تواند مفید باشد orientation است.

دو مقدار اصلی:

portrait
landscape

portrait یعنی Viewport ارتفاع بیشتری نسبت به عرض دارد و landscape یعنی عرض بیشتر از ارتفاع است.

مثلاً:

@media (orientation: landscape) {

    .hero {
        grid-template-columns: 1fr 1fr;
    }

}

و:

@media (orientation: portrait) {

    .hero {
        grid-template-columns: 1fr;
    }

}

یک نکته مهم

orientation الزاماً به معنای وضعیت فیزیکی دستگاه نیست؛ این Feature بر اساس نسبت عرض و ارتفاع Viewport ارزیابی می‌شود. مثلاً باز شدن Keyboard موبایل می‌تواند روی ابعاد Viewport تأثیر بگذارد.

استفاده از hover

همه دستگاه‌ها قابلیت Hover واقعی ندارند.

مثلاً ماوس معمولاً می‌تواند روی یک عنصر قرار بگیرد، اما صفحه لمسی رفتار متفاوتی دارد.

می‌توانیم این موضوع را بررسی کنیم:

@media (hover: hover) {

    .service-card:hover {
        transform: translateY(-8px);
    }

}

یعنی افکت Hover را فقط در محیطی فعال کنیم که Hover به شکل مناسب پشتیبانی می‌شود.

این موضوع برای طراحی سایت‌های لمسی بسیار مهم است.


pointer چیست؟

Media Feature دیگری به نام pointer وجود دارد که درباره دقت ابزار اشاره‌گر اصلی کاربر اطلاعات می‌دهد.

مقادیر مهم:

none
coarse
fine

مثلاً coarse می‌تواند به یک ورودی کم‌دقت مانند لمس انگشت روی صفحه اشاره کند.

مثلاً:

@media (pointer: coarse) {

    .button {
        min-height: 48px;
        padding-inline: 24px;
    }

}

می‌توانیم برای محیط‌های لمسی فضای بیشتری برای کنترل‌ها در نظر بگیریم.

hover و pointer را با هم استفاده کنیم

مثلاً:

@media (hover: hover) and (pointer: fine) {

    .service-card:hover {
        transform: translateY(-6px);
    }

}

این الگو می‌تواند برای افکت‌های Hover مناسب باشد؛ زیرا به جای فرض اینکه همه کاربران ماوس دارند، شرایط ورودی را بررسی می‌کنیم.

Media Query برای Print

Media Query فقط برای Responsive Design نیست.

مثلاً:

@media print {

    .header,
    .footer,
    .navigation,
    .print-button {
        display: none;
    }

}

در هنگام چاپ، بخش‌های غیرضروری حذف می‌شوند.

این قابلیت برای:

  • فاکتورها

  • گزارش‌ها

  • صفحات آموزشی

  • رزومه

  • مقالات

می‌تواند کاربردی باشد.

Media Query و Accessibility

Media Query می‌تواند در دسترس‌پذیری نیز نقش داشته باشد.

یکی از نمونه‌های مهم:

prefers-reduced-motion

این Feature بررسی می‌کند آیا کاربر در سیستم خود ترجیح داده است حرکت‌ها و انیمیشن‌های غیرضروری کاهش پیدا کنند یا خیر.

مثلاً:

.card {
    transition: transform 0.3s ease;
}

.card:hover {
    transform: translateY(-8px);
}

@media (prefers-reduced-motion: reduce) {

    .card {
        transition: none;
    }

}

این کار باعث می‌شود در صورت درخواست کاربر، حرکت‌های غیرضروری کاهش پیدا کنند.

ساختار حرفه‌ای برای پروژه

فرض کنید پروژه ما چنین ساختاری دارد:

styles/
│
├── base.css
├── layout.css
├── components.css
└── responsive.css

می‌توانیم Media Queryهای مربوط به Layout را در فایل مربوط به همان Component یا Layout نگه داریم.

مثلاً:

/* Header */

.header {
    display: flex;
}

/* Mobile */
@media (width < 768px) {

    .header {
        flex-direction: column;
    }

}

لازم نیست تمام Media Queryها را حتماً در یک فایل جداگانه قرار دهیم.

در پروژه‌های بزرگ، محل قرارگیری Media Query باید بر اساس معماری CSS پروژه انتخاب شود.

پروژه عملی: Responsive Header

یک Header برای سایت بنو سرویس ایجاد کنیم.

HTML

<header class="site-header">

    <div class="container">

        <nav class="navbar">

            <a href="#" class="logo">
                بنو سرویس
            </a>

            <div class="nav-links">

                <a href="#">خانه</a>
                <a href="#">خدمات</a>
                <a href="#">محصولات</a>
                <a href="#">آموزش</a>
                <a href="#">درباره ما</a>
                <a href="#">تماس با ما</a>

            </div>

            <button class="menu-button">
                ☰
            </button>

        </nav>

    </div>

</header>

CSS پایه

.site-header {
    width: 100%;
}

.container {
    width: min(100% - 32px, 1200px);
    margin-inline: auto;
}

.navbar {
    min-height: 72px;

    display: flex;
    align-items: center;
    justify-content: space-between;

    gap: 24px;
}

.nav-links {
    display: flex;
    align-items: center;
    gap: 24px;
}

.menu-button {
    display: none;
}

در موبایل:

@media (width < 768px) {

    .nav-links {
        display: none;
    }

    .menu-button {
        display: inline-flex;
    }

}

حالا ساختار در دسکتاپ:

Logo

خانه
خدمات
محصولات
آموزش
درباره
تماس

و در موبایل:

Logo                         ☰

پروژه عملی: کارت‌های خدمات

HTML:

<section class="services">

    <article class="service-card">
        <h3>طراحی سایت</h3>
        <p>
            طراحی وب‌سایت‌های مدرن و حرفه‌ای.
        </p>
    </article>

    <article class="service-card">
        <h3>فروشگاه ساز</h3>
        <p>
            ساخت فروشگاه اینترنتی حرفه‌ای.
        </p>
    </article>

    <article class="service-card">
        <h3>سئو</h3>
        <p>
            بهینه‌سازی سایت برای موتورهای جستجو.
        </p>
    </article>

    <article class="service-card">
        <h3>ربات تلگرامی</h3>
        <p>
            طراحی ربات‌های اختصاصی تلگرام.
        </p>
    </article>

</section>

CSS پایه:

.services {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
}

.service-card {
    padding: 24px;
    border-radius: 20px;
}

Tablet:

@media (width >= 768px) {

    .services {
        grid-template-columns: repeat(2, 1fr);
    }

}

Desktop:

@media (width >= 1100px) {

    .services {
        grid-template-columns: repeat(4, 1fr);
    }

}

این مثال یک ساختار واضح Mobile First ایجاد می‌کند.

یک نکته مهم: آیا همیشه به Media Query نیاز داریم؟

خیر.

فرض کنید می‌خواهیم چهار کارت Responsive داشته باشیم.

ممکن است بنویسیم:

.services {
    display: grid;
    grid-template-columns:
        repeat(auto-fit, minmax(240px, 1fr));

    gap: 24px;
}

در بسیاری از موارد دیگر نیازی نیست بنویسیم:

@media (...) { ... }

زیرا Grid خودش بر اساس فضای موجود Layout را تنظیم می‌کند.

MDN نیز تأکید می‌کند که Media Query برای Responsive Design مفید است، اما الزاماً برای هر بخش Responsive لازم نیست.

اشتباه رایج: Media Query برای هر تغییر کوچک

فرض کنید:

@media (width < 1200px) { ... }

@media (width < 1100px) { ... }

@media (width < 1000px) { ... }

@media (width < 900px) { ... }

@media (width < 800px) { ... }

@media (width < 700px) { ... }

@media (width < 600px) { ... }

@media (width < 500px) { ... }

این ساختار می‌تواند CSS را بسیار پیچیده کند.

به جای آن ابتدا بررسی کنید آیا می‌توانید مشکل را با موارد زیر حل کنید:

Grid
Flexbox
minmax()
clamp()
min()
max()
درصدها
واحدهای نسبی

و تنها زمانی Media Query اضافه کنید که واقعاً نیاز دارید Layout یا رفتار Component تغییر کند.

اشتباه رایج: وابستگی به نام دستگاه

این کد از نظر مفهومی مناسب نیست:

/* iPhone */
@media (...) { }

/* Samsung */
@media (...) { }

/* iPad */
@media (...) { }

چون هدف ما طراحی برای دستگاه خاص نیست.

هدف:

عرض کم
    ↓
Layout A

عرض متوسط
    ↓
Layout B

عرض زیاد
    ↓
Layout C

است.

اشتباه رایج: نادیده گرفتن ترتیب CSS

به ترتیب زیر دقت کنید:

.title {
    font-size: 32px;
}

@media (width >= 768px) {

    .title {
        font-size: 48px;
    }

}

در شرایط مناسب، قانون داخل Media Query مقدار قبلی را Override می‌کند.

اما اگر ترتیب یا Specificity را اشتباه مدیریت کنیم، ممکن است نتیجه مورد انتظار را نبینیم.

پس هنگام Debug کردن Media Query همیشه موارد زیر را بررسی کنید:

  1. آیا Query واقعاً Match شده است؟

  2. آیا Selector درست است؟

  3. آیا قانون دیگری Specificity بیشتری دارد؟

  4. آیا قانون دیگری بعد از آن آمده است؟

  5. آیا CSS در فایل درست Load شده است؟

تست Media Query با DevTools

یکی از بهترین روش‌های یادگیری Responsive Design، استفاده از DevTools مرورگر است.

در Chrome یا Edge:

F12
↓
Toggle Device Toolbar

سپس می‌توانید عرض Viewport را تغییر دهید:

390px
480px
768px
1024px
1440px

و ببینید Layout چگونه تغییر می‌کند.

اما بهتر است فقط چند اندازه ثابت را تست نکنید.

عرض را به صورت پیوسته تغییر دهید و ببینید در چه نقطه‌ای Layout دچار مشکل می‌شود.

این کار به شما کمک می‌کند Breakpoint واقعی موردنیاز پروژه را پیدا کنید.

تمرین‌های عملی

تمرین ۱ — تغییر رنگ

یک صفحه بسازید که:

  • در موبایل پس‌زمینه سفید باشد.

  • در Tablet خاکستری باشد.

  • در Desktop مشکی باشد.

از سه Media Query استفاده کنید.

تمرین ۲ — Responsive Typography

یک عنوان ایجاد کنید:

<h1>بنو سرویس</h1>

سپس:

Mobile → 32px
Tablet → 44px
Desktop → 64px

را با Media Query پیاده‌سازی کنید.

سپس همان تمرین را یک بار با clamp() انجام دهید و تفاوت را بررسی کنید.

تمرین ۳ — Navigation

یک Navigation بسازید که:

Mobile

Logo                    ☰

Tablet

Logo          خانه | خدمات | ☰

Desktop

Logo | خانه | خدمات | محصولات | آموزش | تماس

برای هر حالت Layout مناسب ایجاد کنید.

تمرین ۴ — کارت خدمات

چهار کارت خدمات بسازید:

Mobile
1
2
3
4
Tablet
1 | 2
3 | 4
Desktop
1 | 2 | 3 | 4

سپس دو نسخه بسازید:

نسخه اول: با Media Query

نسخه دوم: با auto-fit و minmax()

و بررسی کنید کدام نسخه CSS کمتری نیاز دارد.

جمع‌بندی

Media Query یکی از مهم‌ترین ابزارهای CSS برای کنترل Responsive Layout است.

با آن می‌توانیم CSS را بر اساس شرایط محیط نمایش تغییر دهیم؛ از عرض Viewport گرفته تا Orientation، قابلیت Hover، نوع Pointer و ترجیحات کاربر مانند کاهش حرکت.

مهم‌ترین مفاهیمی که در این مقاله یاد گرفتیم عبارت‌اند از:

@media
min-width
max-width
orientation
hover
pointer
prefers-reduced-motion

و همچنین Syntax مدرن:

@media (768px <= width <= 1200px) {
}

اما یک نکته مهم را همیشه به خاطر داشته باشید:

Media Query ابزار Responsive Design است، نه خود Responsive Design.

اگر بتوانید بخشی از Layout را با Grid، Flexbox، minmax()، clamp() و واحدهای انعطاف‌پذیر حل کنید، معمولاً نیازی نیست برای هر تغییر کوچک Media Query اضافه کنید.

هدف نهایی این نیست که تعداد زیادی Breakpoint بنویسیم؛ هدف این است که با کمترین پیچیدگی، Layoutی انعطاف‌پذیر و قابل نگهداری ایجاد کنیم.

۱۰

نظرات

کد در دسترس نیست

هنوز نظری ثبت نشده است.