طراحی منوی دایره ای با CSS
منوی دایرهای (radial / circular menu) یک الگوی تعاملی جذاب برای نمایش گزینهها اطراف یک نقطه مرکزی است. این نوع منو مناسب اپلیکیشنهای موبایل، داشبوردها، و المانهای کنترلی است که نیاز به دسترسی سریع به چند عمل دارند. در این مقاله به اصول فنی، مثالهای عملی، نکات دسترسپذیری و راهکارهای بهینهسازی برای طراحی منوی دایرهای با CSS میپردازیم.
اصول فنی و مفاهیم کلیدی
- transform-origin: تعیین نقطه مرجع چرخش و جابجایی.
- rotate و translate: قرار دادن آیتمها در شعاع دایره با ترکیب rotate و translate.
- :nth-child و CSS variables: برای تقسیمبندی زاویهها و کاهش تکرار کد.
- transition و animation: برای جلوههای باز/بسته شدن نرم.
- دسترسی: عناصر قابل فوکوس، ARIA و پشتیبانی از کیبورد.
مثال پایه (HTML + CSS)
<div class="radial-menu">
<button class="center-btn" aria-expanded="false">Menu</button>
<ul class="items">
<li><a href="#">A</a></li>
<li><a href="#">B</a></li>
<li><a href="#">C</a></li>
<li><a href="#">D</a></li>
<li><a href="#">E</a></li>
</ul>
</div>
/* CSS */.radial-menu { position: relative; width: 200px; height: 200px; }
.center-btn {
position: absolute; left: 50%; top: 50%;
transform: translate(-50%,-50%);
z-index: 2;
}
.items { list-style: none; padding: 0; margin: 0; position: absolute; inset: 0; }
.items li {
position: absolute; left: 50%; top: 50%;
transform-origin: 0 0;
transition: transform 0.3s, opacity 0.3s;
opacity: 0;
}
.radial-menu.open .items li {
opacity: 1;
}
/* simple static positions (example for 5 items) */.items li:nth-child(1) { transform: rotate(-72deg) translate(80px) rotate(72deg); }
.items li:nth-child(2) { transform: rotate(-36deg) translate(80px) rotate(36deg); }
.items li:nth-child(3) { transform: rotate(0deg) translate(80px) rotate(0deg); }
.items li:nth-child(4) { transform: rotate(36deg) translate(80px) rotate(-36deg); }
.items li:nth-child(5) { transform: rotate(72deg) translate(80px) rotate(-72deg); }توضیح: این مثال یک ساختار ساده HTML برای منوی دایرهای نشان میدهد. هر آیتم با ترکیب rotate و translate جابجا شده تا در دایره قرار گیرد. از دو بار rotate استفاده شده است تا آیتمها همیشه به صورت افقی (غیر چرخیده) نمایش داده شوند: اولین rotate زاویه محل را تنظیم میکند، translate آیتم را به شعاع مورد نظر منتقل میکند و rotate دوم آیتم را برمیگرداند تا متن یا آیکون تراز باشد.
نسخه بهبود یافته با CSS variables و index
<div class="radial-menu" style="--radius:80px">
<button class="center-btn" aria-expanded="false">Menu</button>
<ul class="items">
<li style="--i:0"><a href="#">A</a></li>
<li style="--i:1"><a href="#">B</a></li>
<li style="--i:2"><a href="#">C</a></li>
<li style="--i:3"><a href="#">D</a></li>
<li style="--i:4"><a href="#">E</a></li>
</ul>
</div>
/* CSS */.items li {
--count: 5; /* number of items */ --angle: calc(180deg / (var(--count) - 1)); /* spread across 180deg */ position: absolute; left:50%; top:50%;
transform-origin: 0 0;
transform: rotate(calc(var(--i) * var(--angle) - 90deg))
translate(var(--radius))
rotate(calc(-1 * (var(--i) * var(--angle) - 90deg)));
transition: transform 0.35s ease, opacity 0.25s;
opacity: 0;
}
.radial-menu.open .items li { opacity: 1; }توضیح: در این نسخه از متغیرهای CSS استفاده شده تا فرمول زاویه برای هر آیتم محاسبه شود. با قرار دادن –i به عنوان ایندکس هر li میتوان به سادگی تعداد آیتمها و زاویه انتشار را کنترل کرد. این روش کد را قابل نگهداریتر و قابل تنظیمتر میکند.
افزودن تعامل و دسترسپذیری — نمونه JS ساده
// JS: toggle open and handle Escape key
const root = document.querySelector('.radial-menu');
const btn = root.querySelector('.center-btn');
btn.addEventListener('click', () => {
const open = root.classList.toggle('open');
btn.setAttribute('aria-expanded', open);
if (open) {
// focus first item for keyboard users
const first = root.querySelector('.items a');
first && first.focus();
} else {
btn.focus();
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
root.classList.remove('open');
btn.setAttribute('aria-expanded', 'false');
btn.focus();
}
});توضیح: این اسکریپت ساده با کلیک روی دکمه منو را باز/بسته میکند و مقدار aria-expanded را بهروزرسانی مینماید. همچنین با فشردن Escape منو بسته شده و فوکوس به دکمه مرکزی بازمیگردد — این به تجربه کاربری و دسترسپذیری کمک میکند.
نکات عملی و بهینهسازی
- اندازهٔ هدف لمسی را حداقل 44×44 پیکسل در موبایل نگه دارید.
- محدودهٔ انتشار (spread) آیتمها را طوری تنظیم کنید که روی هم نیفتند.
- از transitionهای GPU-friendly مثل transform و opacity استفاده کنید تا انیمیشن روان باشد.
- برای کاربران صفحهخوان، از aria-label روی دکمه و aria-expanded استفاده کنید.
- برای سازگاری با مرورگرهای قدیمی، میتوانید fallback شامل لیست عمودی ساده فراهم کنید.
دسترسی و کیبورد
- اجازه دهید آیتمها با Tab قابل فوکوس باشند.
- پشتیبانی از Enter/Space برای انتخاب و Escape برای بستن را اضافه کنید.
- در صورت نیاز از role=”menu” و role=”menuitem” استفاده کنید اما دقت کنید رفتارهای ARIA کاملاً پیادهسازی شوند.
مزایا و معایب (خلاصه)
| مزایا | معایب |
|---|---|
| فضای بصری جذاب و جمعوجور | پیچیدگی در پیادهسازی و دسترسپذیری |
| مناسب برای موبایل و کنترولهای شناور | ممکن است برای کاربران تازهکار قابل فهم نباشد |
چند نکتهٔ پایانی برای طراحان و توسعهدهندگان
- ابتدا UX را در نظر بگیرید: آیا منوی دایرهای واقعاً بهترین انتخاب است؟
- اطمینان حاصل کنید که اندازه و فاصلهٔ آیتمها برای کلیک/لمس مناسب است.
- از یک fallback ساده برای مرورگرهای قدیمی یا کاربرانی که CSS/JS غیرفعال دارند استفاده کنید.
- با تست دستی و با ابزارهای دسترسپذیری، تجربهٔ کیبورد و صفحهخوان را بررسی کنید.
با ترکیب درست CSS (transforms، variables، nth-child) و چند خط JS برای تعامل و دسترسپذیری میتوان منوی دایرهای زیبا، روان و قابل دسترس ساخت. این الگو وقتی به درستی پیادهسازی شود، هم تجربهٔ کاربری را بهبود میدهد و هم جلوهٔ بصری مدرن به رابط میبخشد.
آیا این مطلب برای شما مفید بود ؟




