A macOS pop-up button (NSPopUpButton). The two AppKit modes ship as separate components: MacPopUpButton chooses and displays one value, MacPullDownButton runs a command and does not retain a selection. Both share the trigger scale and the menu material; only their selection and command semantics differ.
Preview Code
Copy
vue < script setup lang = "ts" >
import {
MacPopUpButton,
MacPopUpButtonItem,
MacPullDownButton,
MacPullDownButtonItem,
} from 'macvue'
import { ref } from 'vue'
const color = ref ( 'red' )
</ script >
< template >
< div class = "pop-up-basic" >
< div class = "pop-up-basic__row" >
< span >pop up</ span >
< MacPopUpButton
v-model = " color "
aria-label = "Color"
: teleport-to = " false "
>
< MacPopUpButtonItem value = "red" >
Red
</ MacPopUpButtonItem >
< MacPopUpButtonItem value = "green" >
Green
</ MacPopUpButtonItem >
< MacPopUpButtonItem value = "blue" >
Blue
</ MacPopUpButtonItem >
</ MacPopUpButton >
</ div >
< div class = "pop-up-basic__row" >
< span >pull down</ span >
< MacPullDownButton
aria-label = "Actions"
label = "Actions"
: teleport-to = " false "
>
< MacPullDownButtonItem >New</ MacPullDownButtonItem >
< MacPullDownButtonItem >Open…</ MacPullDownButtonItem >
< MacPullDownButtonItem >Save</ MacPullDownButtonItem >
</ MacPullDownButton >
</ div >
</ div >
</ template >
< style scoped >
.pop-up-basic {
display : grid ;
gap : 8 px ;
}
.pop-up-basic__row {
display : grid ;
grid-template-columns : 72 px 84 px ;
align-items : center ;
gap : 8 px ;
}
.pop-up-basic__row > span {
color : var ( --macvue-label-secondary );
font : 500 12 px / 1 var ( --macvue-font-family );
text-align : right ;
}
</ style > Usage vue < script setup lang = "ts" >
import {
MacPopUpButton,
MacPopUpButtonItem,
MacPullDownButton,
MacPullDownButtonItem,
} from 'macvue'
import { ref } from 'vue'
const color = ref ( 'red' )
</ script >
< template >
< MacPopUpButton
v-model = " color "
aria-label = "Color"
>
< MacPopUpButtonItem value = "red" >
Red
</ MacPopUpButtonItem >
< MacPopUpButtonItem value = "green" >
Green
</ MacPopUpButtonItem >
</ MacPopUpButton >
</ template > Use the command-oriented companion without modelling a selected value:
vue < MacPullDownButton aria-label = "Actions" >
<MacPullDownButtonItem @select="createDocument">
New
</MacPullDownButtonItem>
<MacPullDownButtonItem @select="openDocument">
Open…
</MacPullDownButtonItem>
</ MacPullDownButton > Sizes All five macOS control sizes; regular is the default. The matrix keeps Pop-Up and Pull-Down triggers side by side so their native menu registration can be compared at every size.
Preview Code
mini
small
regular
large
extraLarge
Copy
vue < script setup lang = "ts" >
import type { MacControlSize } from 'macvue'
import {
MacPopUpButton,
MacPopUpButtonItem,
MacPullDownButton,
MacPullDownButtonItem,
} from 'macvue'
const sizes : MacControlSize [] = [
'mini' ,
'small' ,
'regular' ,
'large' ,
'extra-large' ,
]
const sizeLabels : Record < MacControlSize , string > = {
'mini' : 'mini' ,
'small' : 'small' ,
'regular' : 'regular' ,
'large' : 'large' ,
'extra-large' : 'extraLarge' ,
}
</ script >
< template >
< div class = "pop-up-size-grid" >
< template
v-for = " size in sizes "
: key = " size "
>
< span class = "pop-up-size-label" >{{ sizeLabels[size] }}</ span >
< MacPopUpButton
default-value = "red"
: size = " size "
: aria-label = "`${ size } color`"
: teleport-to = " false "
>
< MacPopUpButtonItem value = "red" >
Red
</ MacPopUpButtonItem >
< MacPopUpButtonItem value = "green" >
Green
</ MacPopUpButtonItem >
< MacPopUpButtonItem value = "blue" >
Blue
</ MacPopUpButtonItem >
</ MacPopUpButton >
< MacPullDownButton
label = "Actions"
: size = " size "
: aria-label = "`${ size } actions`"
: teleport-to = " false "
>
< MacPullDownButtonItem >New</ MacPullDownButtonItem >
< MacPullDownButtonItem >Open…</ MacPullDownButtonItem >
< MacPullDownButtonItem >Save</ MacPullDownButtonItem >
</ MacPullDownButton >
</ template >
</ div >
</ template >
< style scoped >
.pop-up-size-grid {
display : grid ;
grid-template-columns : max-content max-content max-content ;
align-items : center ;
justify-content : center ;
gap : 8 px 12 px ;
}
.pop-up-size-label {
color : var ( --macvue-label-secondary );
font-size : 12 px ;
text-align : end ;
}
</ style > Liquid Glass Liquid Glass is experimental and disabled by default. The open menu uses the stable CSS material until an ancestor explicitly sets data-macvue-glass="on"; data-macvue-glass="off" and reduced transparency keep the fallback.
Because the menu teleports to body by default, keep it inside the glass boundary with :teleport-to="false", or provide a portal target inside that boundary. See the Liquid Glass guide for the opt-in boundary, fallbacks and prior art.
Preview Code
Light Dark
Detailed backdrop Copy
vue < script setup lang = "ts" >
import { MacPopUpButton, MacPopUpButtonItem } from 'macvue'
import { ref } from 'vue'
const color = ref ( 'red' )
</ script >
< template >
< div data-macvue-glass = "on" >
< MacPopUpButton
v-model = " color "
aria-label = "Color"
: teleport-to = " false "
>
< MacPopUpButtonItem value = "red" >
Red
</ MacPopUpButtonItem >
< MacPopUpButtonItem value = "green" >
Green
</ MacPopUpButtonItem >
</ MacPopUpButton >
</ div >
</ template > Disabled Disable the whole control or individual items.
Preview Code
Copy
vue < script setup lang = "ts" >
import { MacPopUpButton, MacPopUpButtonItem } from 'macvue'
</ script >
< template >
< div style = " display : flex ; align-items : center ; gap : 12 px " >
< MacPopUpButton
default-value = "green"
aria-label = "Enabled color"
: teleport-to = " false "
>
< MacPopUpButtonItem value = "red" >
Red
</ MacPopUpButtonItem >
< MacPopUpButtonItem value = "green" >
Green
</ MacPopUpButtonItem >
< MacPopUpButtonItem
value = "blue"
disabled
>
Blue
</ MacPopUpButtonItem >
</ MacPopUpButton >
< MacPopUpButton
default-value = "red"
aria-label = "Disabled color"
disabled
>
< MacPopUpButtonItem value = "red" >
Red
</ MacPopUpButtonItem >
</ MacPopUpButton >
</ div >
</ template > Scoped themes and portals The menu teleports to body by default. When the control lives inside a locally scoped data-macvue-appearance, accent, or glass boundary, set :teleport-to="false" to render the menu beside the trigger and preserve that inherited context. A selector can be supplied as a custom portal target.
API Prop Type Default modelValue (v-model)generic acceptable value — defaultValuegeneric acceptable value — open (v-model:open)boolean— defaultOpenboolean— size'extra-large' | 'large' | 'regular' | 'small' | 'mini''regular'disabledbooleanfalserequiredbooleanfalsenamestring— autocompletestring— bystring | ((a, b) => boolean)— dir'ltr' | 'rtl'inherited placeholderstring''teleportTostring | falsebody
With name set, the button participates in native form submission.
Event Payload update:modelValueselected value update:openboolean
Slot Description defaultMacPopUpButtonItem children.valueCustom trigger label; receives selectedLabel and modelValue.
Name Type Description elRef<HTMLButtonElement | null>The semantic trigger button. focus() => voidFocuses the trigger. blur() => voidRemoves focus from the trigger.
Prop Type Default valuegeneric acceptable value — (required) disabledbooleanfalsetextValuestringitem text
Set textValue when the item content is not plain text. An empty-string value is reserved for clearing the selection and is not a valid item value.
Slot Description defaultItem label; may contain custom inline content.
Name Type Description elRef<HTMLElement | null>The underlying menu item element. focus() => voidFocuses the item. blur() => voidRemoves focus from the item.
Prop Type Default open (v-model:open)boolean— defaultOpenboolean— size'extra-large' | 'large' | 'regular' | 'small' | 'mini''regular'disabledbooleanfalsedir'ltr' | 'rtl'inherited labelstring''teleportTostring | falsebody
Event Payload update:openboolean
Slot Description defaultMacPullDownButtonItem commands.triggerReplaces label; without either, the trigger is the compact chevron-only form.
Name Type Description elRef<HTMLButtonElement | null>The semantic trigger button. focus() => voidFocuses the trigger. blur() => voidRemoves focus from the trigger.
Prop Type Default disabledbooleanfalsetextValuestringitem text
Event Payload selectEvent — cancellable
Slot Description defaultCommand label.
Name Type Description elRef<HTMLElement | null>The underlying menu item element. focus() => voidFocuses the item. blur() => voidRemoves focus from the item.