طراحی وب با HTML و CSS فهرست مطالب
فصل ۱۲

طراحی واکنش‌گرا

امروز بیشتر بازدیدکنندگان هر سایتی با گوشی می‌آیند. در این فصل یاد می‌گیریم یک صفحه بسازیم که روی هر اندازه‌ای درست و زیبا دیده شود.

در این فصل با این مباحث آشنا می‌شوید
  1. واکنش‌گرا یعنی چه
  2. واحدهای نسبی
  3. media query
  4. پنهان کردن در موبایل
  5. تغییر جهت فلکس
  6. تغییر ستون‌های گرید
  7. تصویر واکنش‌گرا
  8. نمونه نهایی فصل

۱. واکنش‌گرا یعنی چه

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

قدم اول این کار را از فصل اول انجام داده‌ایم، بدون اینکه بدانیم چرا. همان خط viewport در قسمت head:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

خطی که بدون آن هیچ‌کدام از تکنیک‌های این فصل کار نمی‌کنند

توضیح

گوشی‌ها مشکلی تاریخی دارند: وقتی ساخته شدند، هیچ سایتی برای صفحه کوچک طراحی نشده بود. پس تصمیم گرفتند وانمود کنند صفحه‌شان حدود ۹۸۰ پیکسل عرض دارد و بعد کل تصویر را کوچک کنند تا جا شود. نتیجه‌اش سایتی است که کاملاً درست اما به شکل ریز و ناخوانا دیده می‌شود.

width=device-width به گوشی می‌گوید «وانمود نکن، عرض واقعی خودت را استفاده کن». initial-scale=1.0 هم یعنی «هیچ بزرگ‌نمایی اولیه‌ای اعمال نکن».

بدون این خط هیچ چیز کار نمی‌کند

اگر این خط را ننویسید، می‌توانید ده‌ها media query بنویسید و هیچ‌کدام روی گوشی اجرا نمی‌شوند. چون گوشی فکر می‌کند عرضش ۹۸۰ پیکسل است و شرط‌های شما برای عرض کمتر از ۷۶۸ هرگز درست نمی‌شوند.

۲. واحدهای نسبی

تا این بخش تقریباً همه اندازه‌ها را با پیکسل مشخص کرده‌ایم. پیکسل واحد ثابت است و در طراحی واکنش‌گرا محدودمان می‌کند. چند واحد نسبی هم داریم.

.fixed {
    width: 300px;              /* ثابت */
}

.percent {
    width: 80%;                /* نسبت به عرض والد */
}

.viewport-width {
    width: 50vw;               /* نصف عرض پنجره */
}

.viewport-height {
    height: 100vh;             /* تمام ارتفاع پنجره */
}

html {
    font-size: 16px;
}

.rem-size {
    font-size: 1.5rem;         /* ۱.۵ برابر قلم ریشه = ۲۴ پیکسل */
    padding: 2rem;             /* ۳۲ پیکسل */
}

.em-size {
    font-size: 1.2em;          /* ۱.۲ برابر قلم والد */
}

واحدها: نمونه‌های 01-units و 01-partition

شرح مثال

واحدنسبت به چهکاربرد
px هیچ چیز، ثابت است حاشیه، گردی گوشه، سایه
% عنصر والد عرض جعبه‌ها
vw عرض پنجره مرورگر بخش‌های تمام‌عرض
vh ارتفاع پنجره مرورگر هدر بزرگ تمام‌صفحه
rem قلم ریشه صفحه اندازه قلم و فاصله‌ها
em قلم عنصر والد فاصله داخلی دکمه‌ها

rem مهم‌ترینشان است. قلم ریشه به طور پیش‌فرض شانزده پیکسل است، پس 1rem برابر شانزده پیکسل و 2rem برابر سی و دو پیکسل. مزیتش این است که اگر روزی قلم ریشه را عوض کنید، کل سایت به شکل هماهنگ بزرگ یا کوچک می‌شود.

rem یا em

rem همیشه به ریشه نگاه می‌کند، پس قابل پیش‌بینی است. em به والد نگاه می‌کند و در عنصرهای تودرتو ضرب می‌شود و از کنترل خارج می‌شود. برای شروع، همیشه rem را انتخاب کنید.

۳. media query

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

/* استایل پایه: برای همه اندازه‌ها */
.container {
    width: 90%;
    margin: 0 auto;
    font-size: 1rem;
}

/* فقط وقتی عرض صفحه ۷۶۸ پیکسل یا کمتر است */
@media (max-width: 768px) {
    .container {
        width: 100%;
        font-size: 0.95rem;
    }
}

/* فقط وقتی عرض صفحه ۴۸۰ پیکسل یا کمتر است */
@media (max-width: 480px) {
    .container {
        padding: 10px;
    }
}

media query: نمونه 02-media-query-basics

شرح مثال

نقطه‌هایی که در آنها چیدمان عوض می‌شود را نقطه شکست می‌نامیم. اعداد رایج:

عرضمعمولاً یعنی
۴۸۰ پیکسلگوشی
۷۶۸ پیکسلتبلت
۹۹۲ پیکسللپ‌تاپ کوچک
۱۲۰۰ پیکسلمانیتور بزرگ
این اعداد قانون ثابت و تغییرناپذیری نیستند

روش بهتر این است که عرض پنجره مرورگر را به‌تدریج کاهش دهید و هر جا طرحتان زشت شد، همان‌جا یک نقطه شکست بگذارید. نقطه شکست را طرح شما تعیین می‌کند، نه فهرست مدل‌های گوشی.

۴. پنهان کردن در موبایل

.sidebar {
    display: block;
}

.mobile-only {
    display: none;             /* در حالت عادی پنهان */
}

@media (max-width: 768px) {
    .sidebar {
        display: none;         /* در موبایل پنهان شود */
    }

    .mobile-only {
        display: block;        /* فقط در موبایل دیده شود */
    }
}

پنهان کردن: نمونه 03-hide-in-mobile

شرح مثال

display: none عنصر را کاملاً از صفحه حذف می‌کند. نه دیده می‌شود و نه جایی اشغال می‌کند، انگار اصلاً در HTML نیست.

با ترکیب این ویژگی و media query می‌شود دو نسخه از یک قسمت داشت: یکی برای صفحه بزرگ و یکی برای موبایل. مثلاً یک منوی کامل برای دسکتاپ و یک منوی فشرده برای گوشی.

با احتیاط پنهان کنید

کاربر موبایل، کاربر درجه دو نیست. محتوای مهم را از موبایل حذف نکنید؛ تنها چیدمانش را عوض کنید. display: none را برای تزیینات و ستون‌های فرعی نگه دارید، نه برای محتوای اصلی.

۵. تغییر جهت فلکس

این پرکاربردترین تکنیک واکنش‌گرا کردن یک چیدمان فلکس است.

.layout {
    display: flex;
    flex-direction: row;       /* کنار هم در دسکتاپ */
    gap: 20px;
}

.sidebar {
    flex-basis: 250px;
    flex-shrink: 0;
}

.content {
    flex-grow: 1;
}

@media (max-width: 768px) {
    .layout {
        flex-direction: column;    /* زیر هم در موبایل */
    }

    .sidebar {
        flex-basis: auto;          /* دیگر عرض ثابت لازم نیست */
    }
}

تغییر جهت: نمونه 03-responsive-flex-direction

شرح مثال

تنها با عوض کردن flex-direction از row به column، کل چیدمان از افقی به عمودی تبدیل می‌شود. نوار کناری که در دسکتاپ کنار محتوا بود، در موبایل بالای آن قرار می‌گیرد.

نکته‌ای که بسیار‌ها فراموش می‌کنند: باید flex-basis را هم به auto برگردانید. وگرنه در حالت عمودی، آن مقدار به ارتفاع تبدیل می‌شود و نوار کناری بی‌دلیل دویست و پنجاه پیکسل بلند می‌شود.

یادآوری از فصل هفتم

اگر تنها چند کارت کنار هم دارید، حتی به media query هم نیاز ندارید. flex-wrap: wrap به تنهایی کار را انجام می‌دهد و کارت‌ها خودشان در صفحه باریک زیر هم می‌روند. ساده‌ترین راه‌حل را همیشه اول امتحان کنید.

۶. تغییر ستون‌های گرید

.gallery {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 20px;
}

@media (max-width: 992px) {
    .gallery {
        grid-template-columns: repeat(3, 1fr);
    }
}

@media (max-width: 768px) {
    .gallery {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 480px) {
    .gallery {
        grid-template-columns: 1fr;
    }
}

ستون‌های واکنش‌گرا: نمونه 04-responsive-grid-columns

شرح مثال

یکی از دلایلی که گرید را دوست داریم همین است: برای عوض کردن کل چیدمان یک گالری، تنها یک خط عوض می‌شود. چهار ستون در مانیتور بزرگ، سه ستون در لپ‌تاپ، دو ستون در تبلت و یک ستون در گوشی.

همین کار را با grid-template-areas هم می‌شود کرد. کافی است نقشه را در media query بار دیگر و به شکل تک‌ستونی بنویسید:

@media (max-width: 768px) {
    .layout {
        grid-template-columns: 1fr;
        grid-template-areas:
            "header"
            "content"
            "sidebar"
            "footer";
    }
}

عوض کردن کل نقشه صفحه با چند خط

دقت کنید که در این نقشه، content بالای sidebar آمده است، در حالی که در دسکتاپ کناری اول بود. یعنی حتی ترتیب بخش‌ها را هم بدون دست زدن به HTML عوض کردیم.

۷. تصویر واکنش‌گرا

img {
    max-width: 100%;
    height: auto;
    display: block;
}

تصویر واکنش‌گرا: نمونه 05-responsive-images

شرح مثال

این سه خط کوچک، مشکل بزرگی را حل می‌کنند. یک تصویر با عرض ۱۲۰۰ پیکسل روی گوشی ۳۷۵ پیکسلی، صفحه را می‌شکند و نوار اسکرول افقی می‌سازد.

این را در هر پروژه بنویسید

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

۸. نمونه نهایی فصل

<body>

    <header class="site-header">
        <div class="logo">کادوس</div>
        <nav class="desktop-menu">
            <a href="#">خانه</a>
            <a href="#">دوره‌ها</a>
            <a href="#">درباره ما</a>
            <a href="#">تماس</a>
        </nav>
    </header>

    <div class="layout">
        <aside class="sidebar">
            <h3>دسته‌بندی</h3>
            <p>طراحی وب</p>
            <p>شبکه</p>
        </aside>

        <main class="content">
            <h2>دوره‌های ما</h2>
            <div class="cards">
                <div class="card"><h3>HTML</h3></div>
                <div class="card"><h3>CSS</h3></div>
                <div class="card"><h3>فلکس</h3></div>
                <div class="card"><h3>گرید</h3></div>
            </div>
        </main>
    </div>

</body>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: Tahoma, sans-serif;
}

img {
    max-width: 100%;
    height: auto;
    display: block;
}

body {
    background-color: #f5f5f5;
    line-height: 1.9;
    font-size: 1rem;
}

/* ---------- سربرگ ---------- */
.site-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background-color: #2f4f4f;
    color: white;
    padding: 1rem 2rem;
}

.desktop-menu a {
    color: white;
    text-decoration: none;
    margin-right: 1.2rem;
}

/* ---------- چیدمان اصلی ---------- */
.layout {
    display: flex;
    gap: 1.5rem;
    padding: 1.5rem;
}

.sidebar {
    flex-basis: 240px;
    flex-shrink: 0;
    background-color: white;
    padding: 1.2rem;
    border-radius: 10px;
}

.content {
    flex-grow: 1;
}

.content h2 {
    color: #2f4f4f;
    margin-bottom: 1rem;
}

/* ---------- کارت‌ها ---------- */
.cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}

.card {
    background-color: white;
    padding: 1.5rem;
    border-radius: 10px;
    border-top: 4px solid #ff6347;
}

.card h3 {
    color: #2f4f4f;
}

/* ================= تبلت ================= */
@media (max-width: 768px) {
    .layout {
        flex-direction: column;
    }

    .sidebar {
        flex-basis: auto;
    }

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

/* ================= موبایل ================= */
@media (max-width: 480px) {
    body {
        font-size: 0.95rem;
    }

    .site-header {
        padding: 1rem;
    }

    .desktop-menu {
        display: none;
    }

    .layout {
        padding: 1rem;
        gap: 1rem;
    }

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

نمونه نهایی فصل: نمونه 06-final-example

شرح مثال

این یک صفحه واقعاً واکنش‌گراست و در سه حالت مختلف رفتار می‌کند. روش نوشتنش هم روش درست است: اول استایل عادی و کامل را برای صفحه بزرگ می‌نویسیم، بعد در media query ها تنها چیزهایی را که باید عوض شوند بازنویسی می‌کنیم.

روی دسکتاپ:

روی تبلت با عرض ۷۶۸ یا کمتر:

روی گوشی با عرض ۴۸۰ یا کمتر:

یک کار ناتمام

وقتی منوی دسکتاپ را پنهان می‌کنیم، کاربر موبایل هیچ منویی ندارد. راه‌حل درست، یک دکمه سه‌خطی است که با کلیک منو را باز کند. اما CSS نمی‌تواند «کلیک» را به خاطر بسپارد. این تنها کاری است که در این دوره با CSS تنها نمی‌توانیم انجام دهیم، و در فصل پانزدهم با سه خط جاوااسکریپت حلش می‌کنیم.

فایل‌های تمرینی این فصل

پوشه 12-responsive-design شامل نمونه‌های 01-units و 01-partition تا 06-final-example.

خلاصه فصل

ویژگی یا مفهومکارش
meta viewportپیش‌نیاز همه چیز. بدون آن هیچ media query روی گوشی کار نمی‌کند.
%نسبت به عنصر والد
vw و vhنسبت به عرض و ارتفاع پنجره
remنسبت به قلم ریشه. انتخاب اول برای قلم و فاصله.
emنسبت به قلم والد. در تودرتویی ضرب می‌شود.
@media (max-width: ...)قانون‌هایی برای صفحه‌های کوچک‌تر
display: noneحذف کامل عنصر از صفحه
flex-direction: columnعمودی کردن چیدمان در موبایل
grid-template-columns در media queryکم کردن تعداد ستون‌ها
max-width: 100% روی تصویرجلوگیری از بیرون زدن تصویر

تمرین‌های فصل دوازده

  1. صفحه‌ای بسازید که در دسکتاپ سه ستون، در تبلت دو ستون و در موبایل یک ستون داشته باشد.
  2. خط viewport را حذف کنید، صفحه را روی گوشی باز کنید و تفاوت را ببینید.
  3. همه اندازه‌های یک صفحه را از px به rem تبدیل کنید و بعد تنها قلم body را عوض کنید.
  4. یک نوار کناری بسازید که در موبایل به بالای محتوا منتقل شود.
  5. تصویری بزرگ‌تر از عرض صفحه بگذارید و ببینید max-width: 100% چگونه نجاتش می‌دهد.
  6. پنجره مرورگر را آرام‌آرام باریک کنید و هر جا طرحتان زشت شد، یک نقطه شکست تازه اضافه کنید.
فصل قبل: گرید فصل بعد: تکنیک‌های تکمیلی