Skip to content

TicFrame v5.7.0-alpha.1 is available - read the changelog.

TicFrame

Components

Dropdown

.tf-dropdown is the click/hover disclosure primitive: a trigger and a positioned panel. It doesn't require the panel to contain a menu — when it does, pair it with Menu for roving-focus keyboard navigation among the items.

How It Works

A .tf-dropdown wraps a [data-tf-dropdown-trigger] button and a .tf-dropdown-menu panel. Clicking the trigger toggles the panel; Escape and an outside click close it.

Click vs. hover

Default is click-to-open. Add data-tf-dropdown-hover to the .tf-dropdown wrapper to also open on pointer hover and focus — click still works alongside it.

Alignment

The panel aligns to the trigger's start edge by default. Add data-align="end" on .tf-dropdown-menu to align it to the end edge instead — useful near the right side of a header.

Not just menus

The panel can hold anything — a small form, a color swatch grid, plain text. Only add role="menu" and .tf-menu when the content really is a list of commands.

Keyboard model

Dropdown itself only handles opening, closing, and Escape. Arrow-key navigation between items comes from nesting a Menu inside the panel — a dropdown holding a form uses ordinary Tab order instead.

Live Examples

Click-triggered menu dropdown

The default mode, holding a .tf-menu for keyboard navigation.

HTML
<div class="tf-dropdown">
  <button type="button" class="tf-btn" data-tf-dropdown-trigger>Actions <i class="ti ti-005063" aria-hidden="true"></i></button>
  <div class="tf-dropdown-menu tf-menu" role="menu">
    <button type="button" class="tf-dropdown-item tf-menu-item" role="menuitem">Rename</button>
    <button type="button" class="tf-dropdown-item tf-menu-item" role="menuitem">Duplicate</button>
    <button type="button" class="tf-dropdown-item tf-menu-item" role="menuitem" disabled>Archive</button>
  </div>
</div>

Hover-triggered, end-aligned

data-tf-dropdown-hover plus data-align="end" — the pattern this site's own header "More" menu uses.

HTML
<div class="tf-dropdown" data-tf-dropdown-hover>
  <button type="button" class="tf-btn tf-btn-ghost" data-tf-dropdown-trigger>Settings</button>
  <div class="tf-dropdown-menu tf-menu" role="menu" data-align="end">
    <button type="button" class="tf-dropdown-item tf-menu-item" role="menuitem">Profile</button>
    <button type="button" class="tf-dropdown-item tf-menu-item" role="menuitem">Preferences</button>
  </div>
</div>

Non-menu content (a form)

No .tf-menu/role="menu" — ordinary Tab order applies inside, since this isn't a list of commands.

HTML
<div class="tf-dropdown">
  <button type="button" class="tf-btn" data-tf-dropdown-trigger>Filter</button>
  <div class="tf-dropdown-menu tf-w-64">
    <label class="tf-label" for="dropdown-filter-input">Search term</label>
    <input id="dropdown-filter-input" class="tf-input" type="text" placeholder="Filter by name">
    <button type="button" class="tf-btn tf-btn-primary tf-btn-sm tf-w-full">Apply filter</button>
  </div>
</div>

States

StateHow it's represented
Closedhidden attribute and data-state="closed" on the panel; aria-expanded="false" on the trigger.
Openhidden removed, data-state="open", aria-expanded="true".
Disabled menu itemNative disabled attribute — skipped by Menu's roving focus if the panel uses .tf-menu, as shown in the first example.
Hover mode, pointer leavesCloses after a short delay rather than instantly, so moving the pointer from trigger to panel doesn't close it mid-transit.

Accessibility

RequirementTicFrame guidance
RelationshipTicFrame sets aria-haspopup="menu" and tracks aria-expanded on the trigger automatically.
Menu contentAdd role="menu" and .tf-menu when the panel is a command list — see Menu for item roles and keyboard behavior.
Non-menu contentLeave off role="menu"; form controls inside use their own native semantics and ordinary Tab order.
KeyboardEscape closes the panel and returns focus is left where it was (unlike Modal/Offcanvas, Dropdown doesn't force focus into the panel on open).

API Reference

Classes

ClassPurpose
tf-dropdownRequired wrapper around the trigger and panel.
tf-dropdown-menuRequired panel.
tf-dropdown-itemReset styling for a <button> or <a> row inside the panel.

Attributes

AttributeWhereDescription
data-tf-dropdown-triggerTrigger buttonMarks the click target.
data-tf-dropdown-hover.tf-dropdown wrapperAlso opens on hover/focus, in addition to click.
data-align="end".tf-dropdown-menuAligns the panel to the trigger's end edge instead of its start edge.

Events

EventWhen it firesTarget
tf:dropdown:openAfter the panel opens..tf-dropdown wrapper
tf:dropdown:closeAfter the panel closes..tf-dropdown wrapper

Troubleshooting

SymptomFix
Panel is clipped inside a card or scroll containerDon't nest a dropdown inside an element with overflow: hidden unless that element specifically rounds child corners — see the Menu page's note on .tf-doc-example for the equivalent site-wide fix.
Arrow keys don't move between itemsThe panel needs .tf-menu and item elements need .tf-menu-item — plain .tf-dropdown-item alone only gets click/Enter activation, not roving focus.
Hover mode closes while moving the mouse into the panelThis shouldn't happen — the close is deliberately delayed. If it does, check for another script calling TicFrame.init() a second time on the same element, which can double-register listeners.
Panel opens off the edge of the screenAdd data-align="end" near the right edge of the viewport (or left edge in RTL).