Dropdown
Form-like element that allows users to select an option from a list of choices.
| Class name | Type | |
|---|---|---|
.prs-dropdown |
Component | Container |
.prs-menu |
Component | For <ul> |
.prs-menu-item |
Component | For <button> within menu |
.prs-menu-item-label |
Component | For <span> within button |
.prs-menu_open |
State | Opened menu |
-
Default
This is an Alpine.js example. In the code block you can see the reactive data and the attributes that need to be toggled based on that data.
<div x-data="{ items: ['Assign', 'Edit', 'Delete'], selected: null, open: false, toggle() { if (this.open) { return this.close() } this.$refs.button.focus() this.open = true }, close(focusAfter) { if (!this.open) return this.open = false focusAfter && focusAfter.focus() } }" x-id="['dropdown']" @focusin.window="!$refs.panel.contains($event.target) && close()" @keydown.escape.prevent.stop="close($refs.button)" @keydown.up.prevent.stop="$focus.prev()" @keydown.down.prevent.stop="$focus.next()" class="prs-dropdown" > <button x-ref="button" @click="toggle()" class="prs-btn prs-btn-secondary" :aria-expanded="open" :aria-controls="$id('dropdown')" aria-label="Some dropdown" > <span x-text="selected !== null ? items[selected] : 'Dropdown'"></span> </button> <ul x-ref="panel" x-show="open" x-anchor.bottom-start.offset.4="$refs.button" x-transition @click.outside="close($refs.button)" :id="$id('dropdown')" :class="open && 'prs-menu_open'" class="prs-menu" > <template x-for="(item, index) in items" hidden> <!-- <- this is just for demo purposes --> <li> <button @click.prevent="selected === index ? selected = null : selected = index; close()" class="prs-menu-item" > <span class="prs-menu-item-label" x-text="item"></span> <span class="icon" x-show="selected === index" aria-label="Selected"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" role="img"><use href="/_assets/prs-icons.svg#status-check" /></svg> </span> </button> </li> </template> </ul> </div> -
<ul class="prs-menu"> <li><button class="prs-menu-item"><span>Menu item</span></button></li> <li> <button class="prs-menu-item"> <span class="prs-menu-item-label" aria-label="Truncate very long menu item labels" title="Truncate very long menu item labels">Truncate very long menu item labels</span> <!-- active/selected icon --> <span class="icon" aria-label="Selected"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" role="img"><use href="/_assets/prs-icons.svg#status-check" /></svg> </span> </button> </li> <li><button class="prs-menu-item"><span>Menu item</span></button></li> </ul>
CSS
Full Library
Component Only
.prs-dropdown {
position: relative;
> .prs-btn {
padding-right: 2rem;
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
linear-gradient(135deg, currentColor 50%, transparent 50%);
background-size: 4px 4px, 4px 4px;
background-position: calc(100% - 20px) calc(1px + 50%), calc(100% - 16.1px) calc(1px + 50%);
background-repeat: no-repeat;
}
}
.prs-menu {
border: 1px solid var(--prs-c-gray-300);
border-bottom: 0 none;
width: 100%;
min-width: 12.5rem;
max-width: 16rem;
background-color: var(--prs-c-white);
display: flex;
flex-direction: column;
box-shadow: 1px 3px 4px rgb(0 0 0 / 0.2);
> li {
border-bottom: 1px solid var(--prs-c-gray-300);
}
}
.prs-menu_open {
z-index: 1;
}
.prs-menu-item {
padding: 0.5rem 1rem;
width: 100%;
max-width: 18.75rem;
color: var(--prs-c-aqua);
font-size: 1rem;
line-height: 1.5rem;
text-align: start;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
border-radius: 0;
cursor: pointer;
transition-property: var(--prs-transition-property);
transition-timing-function: var(--prs-transition-timing);
transition-duration: var(--prs-transition-duration);
&:hover {
background-color: var(--prs-c-aqua);
color: var(--prs-c-white);
}
&:focus-visible {
outline: none;
box-shadow: inset 0 0 0 2px currentColor;
}
.icon {
flex-shrink: 0;
}
}
.prs-menu-item-label {
flex-grow: 1;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
Figma
Coming soon...