드롭다운 메뉴 컴포넌트
absolute 위치 지정, 그림자, 둥근 모서리를 사용해 트리거 버튼 아래에 나타나는 드롭다운 메뉴를 만듭니다.
드롭다운 메뉴 컴포넌트은(는) CoddyKit의 무료 Tailwind CSS Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Tailwind CSS Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Tailwind CSS Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
What Is a Dropdown Menu
A dropdown menu is a panel of options that appears below (or beside) a trigger element when activated. Unlike a native <select>, a custom dropdown can contain icons, descriptions, badges, dividers, and any other HTML content.
The key challenge with dropdowns is positioning: the panel must appear directly below the trigger and stay within the viewport. Tailwind's positioning utilities combined with a small amount of JavaScript make this achievable without any external library.
Trigger Button Structure
The dropdown trigger is usually a button that shows the currently selected option or an action label. Wrap the trigger and the dropdown panel together in a relative container so the panel positions itself relative to the trigger.
The trigger button uses inline-flex items-center gap-2 to align a label and a chevron icon. The chevron can rotate with rotate-180 when the dropdown is open to indicate state.
<div class="relative inline-block">
<button
id="dd-trigger"
onclick="toggleDropdown()"
class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium
text-gray-700 bg-white border border-gray-300 rounded-lg
hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500">
Options
<svg id="dd-chevron" class="w-4 h-4 transition-transform duration-200"
fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
</div>Dropdown Panel Positioning
The dropdown panel uses absolute top-full left-0 mt-1 to position it directly below the trigger button. top-full places the top of the panel at the bottom edge of the relative parent, and mt-1 adds a small 4px gap between the trigger and the panel.
Add min-w-48 w-56 to give the panel a comfortable minimum width regardless of the trigger's size. If the dropdown could be cut off on the right edge, use right-0 instead of left-0 to align it to the right edge of the trigger.
<div
id="dropdown"
class="absolute top-full left-0 mt-1 z-50
w-56 bg-white border border-gray-200 rounded-xl shadow-lg
hidden">
<!-- menu items -->
</div>Dropdown Menu Items
Each menu item is a full-width anchor or button with flex items-center gap-3 w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 for a comfortable hover target. The first item uses rounded-t-xl and the last uses rounded-b-xl to clip hover highlights within the panel's rounded corners.
Alternatively, add rounded-xl to each item individually with a 4px inset margin from the panel padding to create a floating highlight effect.
<div class="py-1">
<a href="/profile"
class="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<svg class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
</svg>
View Profile
</a>
<a href="/settings"
class="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<svg class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
Settings
</a>
</div>Dividers and Section Groups
Dividers separate groups of related items within a dropdown. Use an <hr class='border-t border-gray-100 my-1'> element between groups. Alternatively, add a small section heading above each group using px-4 py-1 text-xs font-semibold uppercase tracking-wider text-gray-400.
Group dangerous actions (like logout or delete) at the bottom, below a divider, and color them red to make it immediately clear they are destructive.
<div class="py-1">
<!-- Main section -->
<a href="/profile" class="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">View Profile</a>
<a href="/settings" class="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a>
<!-- Divider -->
<hr class="border-t border-gray-100 my-1" />
<!-- Danger section -->
<button class="flex items-center gap-3 w-full px-4 py-2 text-sm text-red-600 hover:bg-red-50">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
</svg>
Sign Out
</button>
</div>Toggle Logic and Close on Outside Click
The dropdown's toggle logic shows and hides the panel. More importantly, it must close when the user clicks outside the dropdown. Add a click listener to the document that checks if the click target is not inside the dropdown container.
Use contains() to check if the click happened within the dropdown wrapper element. If not, hide the dropdown and rotate the chevron back.
<script>
const wrapper = document.getElementById('dd-wrapper');
const panel = document.getElementById('dropdown');
const chevron = document.getElementById('dd-chevron');
function toggleDropdown() {
panel.classList.toggle('hidden');
chevron.classList.toggle('rotate-180');
}
// Close when clicking outside
document.addEventListener('click', (e) => {
if (!wrapper.contains(e.target)) {
panel.classList.add('hidden');
chevron.classList.remove('rotate-180');
}
});
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
panel.classList.add('hidden');
chevron.classList.remove('rotate-180');
}
});
</script>Dropdown With Fade Animation
Add a fade and scale entrance animation to the dropdown panel by using the same technique as the modal: transition from scale-95 opacity-0 to scale-100 opacity-100 with transition-all duration-150 origin-top.
The origin-top utility sets the transform origin to the top of the panel so it scales outward from the trigger, making the animation feel connected to the button that opened it.
<div
id="dropdown"
class="absolute top-full left-0 mt-1 z-50
w-56 bg-white border border-gray-200 rounded-xl shadow-lg
transition-all duration-150 origin-top
scale-95 opacity-0 pointer-events-none">
<!-- items -->
</div>
<script>
function openDropdown() {
const d = document.getElementById('dropdown');
d.classList.remove('scale-95', 'opacity-0', 'pointer-events-none');
d.classList.add('scale-100', 'opacity-100', 'pointer-events-auto');
}
function closeDropdown() {
const d = document.getElementById('dropdown');
d.classList.remove('scale-100', 'opacity-100', 'pointer-events-auto');
d.classList.add('scale-95', 'opacity-0', 'pointer-events-none');
}
</script>Right-Aligned Dropdown
When a dropdown trigger is near the right edge of the screen (like a user avatar in the top-right of a navbar), the panel should align to the right edge of the trigger to avoid overflowing off-screen. Use right-0 instead of left-0 on the panel.
This is a critical detail that beginners often miss — a left-aligned dropdown on a right-side trigger will appear partially or fully off-screen on mobile devices.
<div class="relative inline-block">
<!-- Right-side trigger (e.g., user avatar) -->
<button onclick="toggleDropdown()" class="w-8 h-8 rounded-full bg-blue-600 text-white text-sm font-bold">JD</button>
<!-- Panel aligned to right edge of trigger -->
<div
id="dropdown"
class="hidden absolute top-full right-0 mt-1 z-50
w-56 bg-white border border-gray-200 rounded-xl shadow-lg py-1">
<div class="px-4 py-2 border-b border-gray-100">
<p class="text-sm font-semibold text-gray-900">Jane Doe</p>
<p class="text-xs text-gray-500">jane@example.com</p>
</div>
<a href="/profile" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
<a href="/settings" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a>
<hr class="border-t border-gray-100 my-1" />
<button class="block w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-50">Sign Out</button>
</div>
</div>Dropdown With Checkmarks for Active Items
A selection dropdown shows a checkmark next to the currently selected option. Use flex items-center justify-between on each item to place the checkmark on the right, or flex items-center gap-3 to put it on the left before the label.
Color the active item with text-blue-600 font-semibold and show a colored checkmark icon. Inactive items use the standard text-gray-700.
<div class="py-1">
<!-- Active item -->
<button class="flex items-center justify-between w-full px-4 py-2 text-sm font-semibold text-blue-600 hover:bg-blue-50">
English
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Inactive items -->
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">French</button>
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">German</button>
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Japanese</button>
</div>Context Menu Dropdown
A context menu appears on right-click or at a three-dot button's location to offer contextual actions for a specific item (like a file, row, or card). Position it at the clicked coordinates using absolute positioning and JavaScript.
For the three-dot pattern (the most common approach), place a relative container on the item, and a three-dot button at top-2 right-2. The dropdown panel appears below or beside the button.
<div class="relative">
<!-- Item row -->
<div class="flex items-center justify-between p-4 bg-white rounded-lg border border-gray-200">
<span class="text-sm font-medium text-gray-900">Project Alpha.zip</span>
<!-- Three-dot button -->
<div class="relative">
<button onclick="toggleCtx()" class="p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-md">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"/>
</svg>
</button>
<div id="ctx-menu" class="hidden absolute top-full right-0 mt-1 z-50 w-40 bg-white border border-gray-200 rounded-xl shadow-lg py-1">
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Rename</button>
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Download</button>
<hr class="border-t border-gray-100 my-1" />
<button class="block w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-50">Delete</button>
</div>
</div>
</div>
</div>Nested (Sub-Menu) Dropdown
A nested submenu appears to the right of a parent menu item when it is hovered or clicked. Use group relative on the parent item and group-hover:block absolute left-full top-0 on the submenu panel to position it beside the parent item.
Add a right-pointing chevron icon on the parent item to signal that it has a submenu. Nested menus should be limited to one level — two-level nesting is already complex; three levels is rarely justified.
<div class="py-1 relative">
<a href="/" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
<!-- Parent with submenu -->
<div class="group relative">
<button class="flex items-center justify-between w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
Export
<svg class="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</button>
<!-- Submenu -->
<div class="hidden group-hover:block absolute left-full top-0 ml-1 w-40 bg-white border border-gray-200 rounded-xl shadow-lg py-1 z-50">
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">As PDF</button>
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">As CSV</button>
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">As Excel</button>
</div>
</div>
</div>Quick Check
Test your understanding of Tailwind CSS Mastery concepts from this lesson.
Lesson Recap
In this lesson you learned: dropdown positioning uses relative on the wrapper and absolute top-full left-0 mt-1 on the panel; right-side alignment uses right-0 instead of left-0 to prevent overflow on right-side triggers; and outside-click dismissal requires a document click listener that checks contains() to detect clicks outside the wrapper element. Next up we make modals and dropdowns fully accessible.
자주 묻는 질문
“드롭다운 메뉴 컴포넌트” 강의는 무료인가요?
네 — “드롭다운 메뉴 컴포넌트” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Tailwind CSS Academy 강의 전체를 잠금 해제할 수 있습니다. Tailwind CSS Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“드롭다운 메뉴 컴포넌트”에서 뭘 배우나요?
absolute 위치 지정, 그림자, 둥근 모서리를 사용해 트리거 버튼 아래에 나타나는 드롭다운 메뉴를 만듭니다. 브라우저에서 직접 실행하는 실습 코드로 Tailwind CSS Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Tailwind CSS Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Tailwind CSS Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“드롭다운 메뉴 컴포넌트” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Tailwind CSS Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Tailwind CSS Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 모달 대화상자 구조
- 모달 열기 및 닫기 애니메이션
- 드롭다운 메뉴 컴포넌트
- 접근성을 고려한 모달과 드롭다운