ویژگی تصویر

طراحی منوی دایره‌ای با CSS — راهنمای جامع

  /  CSS   /  طراحی منوی دایره ای با 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 برای تعامل و دسترس‌پذیری می‌توان منوی دایره‌ای زیبا، روان و قابل دسترس ساخت. این الگو وقتی به درستی پیاده‌سازی شود، هم تجربهٔ کاربری را بهبود می‌دهد و هم جلوهٔ بصری مدرن به رابط می‌بخشد.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: