درس 42 از دوره آموزش HTML و CSS ارومیا دیزاین
منوی سایت در مانیتور جا می شود، اما در گوشی لینک ها از صفحه بیرون می زنند یا آن قدر کوچک می شوند که انتخاب آن ها دشوار است. در این درس، ساخت منوی واکنش گرا با HTML و CSS را با یک پروژه فارسی تمرین می کنیم: نسخه باریک با یک کنترل بومی باز و بسته می شود و نسخه عریض، لینک ها را مستقیم کنار هم نشان می دهد. هدف، حفظ مسیر دسترسی به صفحات و شناخت محدودیت های راه حل بدون جاوااسکریپت است.
فایل پروژه درس 42 + پاسخ تمرین
پروژه و پاسخ تمرین: دانلود ZIP پروژه درس 42؛ شامل index.html، style.css، solution.css و راهنمای آزمایش. ZIP را استخراج کنید و index.html را باز کنید؛ پروژه بدون اینترنت و نصب اجرا می شود.
چه چیزی می سازیم و چه رفتاری انتظار داریم؟
در عرض کمتر از 800px، یک بخش details با عنوان «فهرست صفحات» دیده می شود. کاربر آن را باز می کند و لینک های عمودی در جریان عادی صفحه ظاهر می شوند؛ محتوا زیر منو جابه جا می شود و پنل شناور روی آن قرار نمی گیرد. از عرض 800px به بالا، فهرست عریض نمایش پیدا می کند و نسخه باریک پنهان می شود. آستانه این پروژه آموزشی است؛ در طراحی واقعی، طول لینک ها و فضای سربرگ تعیین کننده هستند.
پیش نیازها، چیدمان با Flexbox، رویکرد Mobile First و مدیا کوئری و نقاط شکست هستند. این بار به جای تغییر تعداد ستون کارت ها، مسیر راهبری را با فضای موجود هماهنگ می کنیم. عنوان و مقصد لینک ها در هر دو نسخه باید یکسان بمانند.
ساختار درست؛ nav، فهرست و لینک واقعی
<nav aria-label="راهبری اصلی">
<ul class="nav-list">
<li><a href="#home">صفحه اصلی</a></li>
<li><a href="#services">خدمات آموزشی</a></li>
<li><a href="#projects">پروژه ها</a></li>
<li><a href="#contact">تماس با ما</a></li>
</ul>
</nav>
nav نقش راهبری را مشخص می کند و فهرست، ارتباط مجموعه لینک ها را روشن نگه می دارد. در نمونه آموزشی مقصدها شناسه بخش های همان صفحه هستند؛ در سایت واقعی آدرس صفحات را قرار دهید. برای رفتن به مقصد از a و برای انجام عمل از کنترل مناسب استفاده کنید. یک div با متن «منو» به تنهایی رفتار قابل اتکا برای صفحه کلید یا نام و نقش درست ایجاد نمی کند.
به راهبری معمول سایت، صرفا به دلیل نام رایج «منو»، role=”menu” ندهید. این نقش، الگوی تعامل پیچیده تری از فهرست لینک های معمول دارد. در پروژه از HTML معنایی و ترتیب طبیعی Tab استفاده می کنیم؛ کلیدهای جهت و مدیریت فوکوس یک منوی اپلیکیشن را شبیه سازی نکرده ایم.
باز و بسته شدن بدون جاوااسکریپت با details و summary
<details class="mobile-nav">
<summary>فهرست صفحات</summary>
<nav aria-label="راهبری اصلی">
<ul class="nav-list">
<li><a href="#home">صفحه اصلی</a></li>
<li><a href="#services">خدمات آموزشی</a></li>
<li><a href="#projects">پروژه ها</a></li>
<li><a href="#contact">تماس با ما</a></li>
</ul>
</nav>
</details>
summary اولین فرزند details است و عنوان کنترل را فراهم می کند. مرورگر رفتار باز و بسته شدن را مدیریت می کند. در حالت اولیه، ویژگی open وجود ندارد؛ بنابراین محتوا بسته است. با فعال کردن summary، مرورگر حالت را تغییر می دهد. برای کنترل بومی، aria-expanded ثابت و دستی اضافه نمی کنیم؛ ممکن است با وضعیت واقعی ناسازگار شود.
open یک ویژگی بولی است: نوشتن open=”false” هم به معنی وجود ویژگی و در نتیجه باز بودن است. برای بسته بودن اولیه، آن را ننویسید. داخل summary لینک یا دکمه دیگری نگذارید تا کنترل های تو در تو نسازید. متن «فهرست صفحات» هم از آیکون سه خط بدون نام، روشن تر است؛ نشانگر بومی را در این پروژه حفظ کرده ایم.
نسخه پایه؛ لینک های عمودی و فضای کافی
.mobile-nav {
flex-basis: 100%;
min-width: 0;
}
.mobile-nav summary {
cursor: pointer;
padding: 12px 16px;
background: #d5eee7;
border-radius: 8px;
}
.nav-list {
list-style: none;
padding: 0;
margin: 12px 0 0;
}
.mobile-nav .nav-list {
display: flex;
flex-direction: column;
gap: 8px;
}
نسخه باریک، حالت پایه ماست. هر لینک در ردیف خودش قرار می گیرد و فضای اطراف آن انتخاب با لمس را راحت تر می کند. برای متن های بلند، ارتفاع ثابت نمی گذاریم و اجازه رشد محتوا را حفظ می کنیم. حذف bullet فقط ظاهر فهرست را تغییر می دهد؛ ساختار ul و li در HTML باقی می ماند.
.nav-list a {
display: block;
padding: 12px 16px;
border-radius: 8px;
text-decoration: none;
overflow-wrap: anywhere;
}
padding به ناحیه فعال لینک فضا می دهد، اما اندازه واقعی هدف به فونت و محتوا هم وابسته است؛ خروجی را روی دستگاه هدف آزمایش کنید. overflow-wrap برای رشته طولانی کمک می کند، ولی جای اندازه گذاری درست والد را نمی گیرد. درس مدیریت سرریز تفاوت شکستن متن با برش یا اسکرول را توضیح می دهد.
نمای عریض؛ فهرست افقی با امکان شکستن خط
.desktop-nav { display: none; }
@media (min-width: 800px) {
.mobile-nav { display: none; }
.desktop-nav {
display: block;
margin-inline-start: auto;
min-width: 0;
}
.desktop-nav .nav-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 0;
}
}
نسخه کامل پروژه دو فهرست هم محتوا دارد: یک nav برای نمای عریض و یک nav داخل details برای نمای باریک. در هر عرض فقط یکی نمایش پیدا می کند. display: none معمولاً نسخه غیر فعال را از چیدمان، پیمایش و درخت دسترس پذیری خارج می کند؛ صرفا شفاف یا بیرون صفحه نشده است. روی نسخه ها شناسه تکراری تعریف نکرده ایم و aria-hidden متغیر دستی هم لازم نیست.
این انتخاب آموزشی، یک هزینه نگهداری دارد: هنگام تغییر مقصد یا عنوان، باید هر دو فهرست را به روز کنید. در سایت قالب دار، هر دو را از یک منبع داده تولید کنید؛ برای یک DOM مشترک همراه رفتار سفارشی، جاوااسکریپت و مدیریت حالت گزینه دیگری است. وجود دو فهرست، راه حل بی هزینه یا مناسب تمام پروژه ها نیست، اما منطق CSS این جلسه را روشن نگه می دارد.
از flex-wrap استفاده می کنیم تا عنوان های بلندتر باعث خروج بی دلیل منو نشوند. در صفحه فارسی، dir=”rtl” و row معمولی، شروع ردیف را با جهت متن هماهنگ می کنند؛ row-reverse را فقط برای فارسی کردن اضافه نکنید. ترتیب دیداری و مسیر خواندن باید منطقی بمانند.

فوکوس، صفحه کلید و لینک پرش
a:focus-visible, summary:focus-visible {
outline: 3px solid #b45309;
outline-offset: 4px;
}
با Tab به summary برسید و باز و بسته شدن را با Enter یا Space بررسی کنید؛ سپس با Tab لینک های بازشده را دنبال کنید. هنگام بسته بودن، لینک های داخلی نباید در مسیر عادی Tab دیده شوند. نشانه فوکوس را حذف نکنید و فقط روی هاور تکیه نکنید. برای جزئیات، درس hover و focus-visible را مرور کنید.
در پروژه، اولین لینک «رفتن به محتوای اصلی» است که هنگام فوکوس نمایان می شود. این لینک به main اشاره می کند و به کاربر صفحه کلید اجازه عبور از سربرگ را می دهد. main در نمونه tabindex=”-1″ دارد؛ این مقدار آن را وارد ترتیب عادی Tab نمی کند. با مرورگر و فناوری کمکی هدف، رفتار رسیدن به مقصد را آزمایش کنید.
محدودیت های نمونه CSS و HTML را بشناسید
در این پروژه، انتخاب لینک داخل details آن را خودکار نمی بندد. کلیک بیرون یا فشردن Escape هم رفتار سفارشی تضمین شده ای ندارد؛ کنترل بومی را با فعال کردن خود summary می بندیم. این پنل در جریان صفحه باز می شود و پنجره مودال یا منوی شناور پیچیده نیست. برای بستن پس از انتخاب، بازگرداندن فوکوس یا کنترل کامل رفتار در تغییر عرض، باید لایه جاوااسکریپت مناسب اضافه کنید.
اگر منوی باریک را باز کنید، به عرض عریض بروید و دوباره باریک شوید، وضعیت open می تواند باقی بماند؛ CSS آن ویژگی را پاک نمی کند. همچنین اگر هنگام تغییر عرض، فوکوس روی عنصری باشد که پنهان می شود، رفتار مرورگر را بررسی کنید. نمونه آموزشی انتقال فوکوس را مدیریت نمی کند و نباید بدون آزمایش، برای تمام محیط ها آماده تحویل فرض شود.
چه زمانی منوی ساده چندخطی بهتر است؟
اگر فقط دو یا سه لینک کوتاه دارید، شاید نمایش مستقیم و چندخطی در همه عرض ها انتخاب ساده تری باشد. کنترل باز و بسته شدن را صرفا برای تقلید ظاهر گوشی اضافه نکنید. معیار، فضای واقعی، اهمیت لینک ها و سهولت دسترسی است. یک منوی کوچک که همیشه دیده می شود، گاهی از پنهان کردن راهبری پشت کنترل اضافی مفیدتر است.
در انتخاب اندازه، واحدهای CSS و در خوانایی نوشته، اصول تایپوگرافی را در نظر بگیرید. برای سربرگ، لوگو و منو را در والد فلکس با wrap قرار داده ایم؛ نه اینکه عرض ثابت هر کدام را بدون توجه به عنوان های فارسی جمع کنیم.
تمرین مستقل و پاسخ درس 42
در بخش practice، یک منوی ساده از ابتدا عمودی است. آن را از عرض حداقل 640px افقی و چندخطی کنید و حالت پایه را تغییر ندهید. هدف تمرین، تنظیم چیدمان همان فهرست است؛ جزئیات باز و بسته شدن در نمونه اصلی کامل وجود دارد. پیش از دیدن پاسخ، قانون را خودتان بنویسید.
@media (min-width: 640px) {
.practice-list {
flex-direction: row;
flex-wrap: wrap;
}
}
پاسخ در solution.css قرار دارد و فعال نیست. آن را با خط زیر پس از style.css در head اضافه کنید. در 639 و 640 پیکسل تفاوت را ببینید و بعد متن یکی از لینک ها را طولانی کنید. گروه افقی باید در صورت نیاز به خط بعد برود و از صفحه خارج نشود.
<link rel="stylesheet" href="solution.css">
برنامه آزمایش پروژه
در عرض های 320، 799، 800 و 1280 پیکسل آزمایش کنید. در 799، کنترل بومی باریک و در 800، نسخه مستقیم عریض باید دیده شود. هر دو مقصدهای یکسان دارند. منو را با لمس و صفحه کلید باز کنید، عنوان لینک ها را بلند کنید و بزرگ نمایی را افزایش دهید؛ لینک ها نباید با برش پنهان شوند یا برای دیده شدن به اسکرول افقی کل صفحه نیاز داشته باشند.
راهنمای ZIP محدودیت های تغییر عرض و وضعیت open را توضیح می دهد. نتیجه را در مرورگرهای هدف و در صورت نیاز با فناوری کمکی بررسی کنید. اگر قانون اجرا نمی شود، ابتدا شرط عرض و کلاس والد را ببینید؛ سپس قاعده غالب را در Styles پیدا کنید. مقدمات CSS روش اتصال فایل را مرور می کند و درس مدیا کوئری علت هم پوشانی شرط ها را توضیح می دهد.
پرسش های متداول
آیا منو برای باز شدن به جاوااسکریپت نیاز دارد؟
نمونه این درس با details و summary بومی باز و بسته می شود. رفتارهای سفارشی مثل بستن پس از انتخاب یا مدیریت انتقال فوکوس، کد تکمیلی می خواهند.
چرا open=”false” منو را نمی بندد؟
چون open بولی است؛ وجود آن به معنی حالت باز است. برای بسته بودن اولیه، ویژگی را ننویسید.
آیا باید role=”menu” اضافه کنیم؟
برای این راهبری معمول، خیر. nav، فهرست و لینک ها کافی اند و پروژه تعامل منوی اپلیکیشن را پیاده نمی کند.
چرا لینک ها دو بار در HTML هستند؟
این نمونه آموزشی برای نمای باریک و عریض دو فهرست دارد؛ فقط یکی در هر عرض نمایش پیدا می کند. هنگام تغییر، هر دو را همزمان به روز کنید.
آیا ZIP پاسخ تمرین هم دارد؟
بله؛ HTML، CSS اصلی، solution.css و راهنمای اجرای آفلاین همراه هم هستند.
فایل پروژه درس 42 + پاسخ تمرین
منابع تکمیلی: مستند details در MDN و راهنمای راهبری بازشونده W3C. نمونه W3C رفتار جاوااسکریپتی جداگانه دارد؛ از آن برای شناخت اصول راهبری و تفاوت با نقش menu استفاده کنید.
ادامه دوره: درس 43؛ ساخت هدر و بخش معرفی صفحه اصلی همراه پروژه درس.


بدون دیدگاه