Overlays & menus
Every overlay in this family is a Reka UI primitive whose content is portalled into <body>, or into the element set by SbProvider’s teleport-to. Nothing uses a browser-drawn popup, a native <dialog> or a title tooltip, so they all render in OPEN//77’s in-game browser.
Root components (SbDialog, SbPopover, SbDropdownMenu, …) forward every Reka root prop and emit, including v-model:open. Triggers, closes and anchors accept as-child, so you can use an SbButton as the trigger.
SbDialog
Section titled “SbDialog”A modal window for a focused task, wrapping Reka Dialog. SbDialogContent renders the portal, a blurred backdrop and the panel, with a close button in the top-end corner.
<script setup lang="ts">import { ref } from 'vue'import { SbButton, SbDialog, SbDialogBody, SbDialogClose, SbDialogContent, SbDialogDescription, SbDialogFooter, SbDialogHeader, SbDialogTitle, SbDialogTrigger, SbProvider,} from '@static-bloom/vue'
const open = ref(false)const answer = ref('No answer yet')</script>
<template> <SbProvider> <div class="flex flex-wrap items-center gap-4"> <SbDialog v-model:open="open"> <SbDialogTrigger as-child> <SbButton>View contract</SbButton> </SbDialogTrigger> <SbDialogContent> <SbDialogHeader> <SbDialogTitle>Courier run: Harbor district</SbDialogTitle> <SbDialogDescription >Deliver a sealed case to the dockside depot before dawn.</SbDialogDescription > </SbDialogHeader> <SbDialogBody> <p class="text-fg-muted"> Payout 4,800. The route crosses two checkpoints; a clean vehicle is advised. </p> </SbDialogBody> <SbDialogFooter> <SbDialogClose as-child> <SbButton variant="ghost" @click="answer = 'Declined'">Decline</SbButton> </SbDialogClose> <SbDialogClose as-child> <SbButton @click="answer = 'Accepted'">Accept contract</SbButton> </SbDialogClose> </SbDialogFooter> </SbDialogContent> </SbDialog> <output class="text-sm text-fg-muted">{{ answer }}</output> </div> </SbProvider></template>SbDialog├─ SbDialogTrigger└─ SbDialogContent ├─ SbDialogHeader │ ├─ SbDialogTitle │ └─ SbDialogDescription ├─ SbDialogBody └─ SbDialogFooter └─ SbDialogCloseSbDialogContent prop |
Type | Default | Effect |
|---|---|---|---|
size |
'sm' | 'md' | 'lg' | 'xl' | 'full' |
'md' |
Maximum width 24rem, 32rem, 48rem, 64rem, or the whole viewport |
showClose |
boolean |
true |
Renders the icon close button |
closeLabel |
string |
'Close' |
Accessible name of the close button |
class |
HTMLAttributes['class'] |
none | Merged onto the panel |
SbDialog takes Reka’s open, defaultOpen and modal. The other Reka DialogContent props and emits (@escape-key-down, @pointer-down-outside, …) pass through.
Keyboard and accessibility. The panel has role="dialog", labelled by the title and described by the description. Opening focuses the first focusable element; Tab and Shift+Tab stay inside; Escape or a click on the backdrop closes it and returns focus to the trigger.
SbAlertDialog
Section titled “SbAlertDialog”A confirmation that interrupts, wrapping Reka AlertDialog. It cannot be dismissed by clicking outside, and focus starts on the safe choice.
<script setup lang="ts">import { ref } from 'vue'import { SbAlertDialog, SbAlertDialogAction, SbAlertDialogCancel, SbAlertDialogContent, SbAlertDialogDescription, SbAlertDialogFooter, SbAlertDialogHeader, SbAlertDialogTitle, SbAlertDialogTrigger, SbButton, SbProvider,} from '@static-bloom/vue'
const vehicles = ref(3)</script>
<template> <SbProvider> <div class="flex flex-wrap items-center gap-4"> <SbAlertDialog> <SbAlertDialogTrigger as-child> <SbButton intent="danger" variant="outline" :disabled="vehicles === 0" >Scrap vehicle</SbButton > </SbAlertDialogTrigger> <SbAlertDialogContent intent="danger"> <SbAlertDialogHeader> <SbAlertDialogTitle>Scrap this vehicle?</SbAlertDialogTitle> <SbAlertDialogDescription >Its upgrades are lost and the garage slot is freed. This cannot be undone.</SbAlertDialogDescription > </SbAlertDialogHeader> <SbAlertDialogFooter> <SbAlertDialogCancel>Keep it</SbAlertDialogCancel> <SbAlertDialogAction intent="danger" @click="vehicles -= 1">Scrap</SbAlertDialogAction> </SbAlertDialogFooter> </SbAlertDialogContent> </SbAlertDialog> <output class="text-sm text-fg-muted">{{ vehicles }} vehicles in the garage</output> </div> </SbProvider></template>SbAlertDialog├─ SbAlertDialogTrigger└─ SbAlertDialogContent ├─ SbAlertDialogHeader │ ├─ SbAlertDialogTitle │ └─ SbAlertDialogDescription └─ SbAlertDialogFooter ├─ SbAlertDialogCancel └─ SbAlertDialogAction| Part | Prop | Type | Default | Effect |
|---|---|---|---|---|
SbAlertDialogContent |
size |
'sm' | 'md' | 'lg' |
'sm' |
Maximum width 24rem, 32rem or 48rem |
intent |
'primary' | 'secondary' | 'neutral' | 'info' | 'success' | 'warning' | 'danger' |
'primary' |
Colors the top hairline (primary is the bloom gradient) |
|
SbAlertDialogAction |
intent |
same as above | 'primary' |
Color of the solid confirming button |
SbAlertDialogCancel |
class |
HTMLAttributes['class'] |
none | A ghost button |
Keyboard and accessibility. The panel has role="alertdialog" with its description. SbAlertDialogCancel receives focus when it opens, so Enter never confirms by accident. Escape closes it and restores focus; a pointer down outside does not. The action runs its click handler, then closes the dialog.
SbSheet
Section titled “SbSheet”A dialog docked to an edge of the viewport, for settings and secondary panels. Only SbSheetContent differs from the dialog: SbSheet, SbSheetTrigger, SbSheetHeader, SbSheetBody, SbSheetFooter, SbSheetTitle, SbSheetDescription and SbSheetClose are the dialog parts under sheet names.
<script setup lang="ts">import { SbButton, SbProvider, SbSheet, SbSheetBody, SbSheetClose, SbSheetContent, SbSheetDescription, SbSheetFooter, SbSheetHeader, SbSheetTitle, SbSheetTrigger,} from '@static-bloom/vue'
const sides = ['left', 'right'] as const</script>
<template> <SbProvider> <div class="flex flex-wrap items-center gap-3"> <SbSheet v-for="side in sides" :key="side"> <SbSheetTrigger as-child> <SbButton variant="outline">Crew roster ({{ side }})</SbButton> </SbSheetTrigger> <SbSheetContent :side="side"> <SbSheetHeader> <SbSheetTitle>Crew roster</SbSheetTitle> <SbSheetDescription>Four members online, two on a job.</SbSheetDescription> </SbSheetHeader> <SbSheetBody> <ul class="grid gap-2 text-sm text-fg-muted"> <li>Driver: available</li> <li>Netrunner: on a job</li> <li>Medic: available</li> <li>Fixer: on a job</li> </ul> </SbSheetBody> <SbSheetFooter> <SbSheetClose as-child> <SbButton>Done</SbButton> </SbSheetClose> </SbSheetFooter> </SbSheetContent> </SbSheet> </div> </SbProvider></template>SbSheetContent prop |
Type | Default | Effect |
|---|---|---|---|
side |
'top' | 'right' | 'bottom' | 'left' |
'right' |
Edge it docks to; side panels are at most 26rem wide |
showClose |
boolean |
true |
Renders the icon close button |
closeLabel |
string |
'Close' |
Accessible name of the close button |
class |
HTMLAttributes['class'] |
none | Merged onto the panel |
Keyboard behavior is the dialog’s: focus is trapped, and Escape or the close button closes the sheet and returns focus to the trigger.
SbDrawer
Section titled “SbDrawer”A swipeable panel, wrapping Reka Drawer. It slides from the bottom by default and follows the pointer while it is dragged.
<script setup lang="ts">import { SbButton, SbDrawer, SbDrawerBody, SbDrawerClose, SbDrawerContent, SbDrawerDescription, SbDrawerFooter, SbDrawerHeader, SbDrawerTitle, SbDrawerTrigger, SbProvider,} from '@static-bloom/vue'</script>
<template> <SbProvider> <SbDrawer> <SbDrawerTrigger as-child> <SbButton variant="outline">Open stash</SbButton> </SbDrawerTrigger> <SbDrawerContent show-close> <SbDrawerHeader> <SbDrawerTitle>Stash</SbDrawerTitle> <SbDrawerDescription >12 of 40 slots used. Swipe down or press Escape to close.</SbDrawerDescription > </SbDrawerHeader> <SbDrawerBody> <p class="text-fg-muted">Medkits, spare ammunition and a sealed data shard.</p> </SbDrawerBody> <SbDrawerFooter> <SbDrawerClose as-child> <SbButton>Done</SbButton> </SbDrawerClose> </SbDrawerFooter> </SbDrawerContent> </SbDrawer> </SbProvider></template>SbDrawer├─ SbDrawerTrigger└─ SbDrawerContent ├─ SbDrawerHandle (rendered for you unless show-handle is false) ├─ SbDrawerHeader │ ├─ SbDrawerTitle │ └─ SbDrawerDescription ├─ SbDrawerBody └─ SbDrawerFooter └─ SbDrawerClose| Part | Prop | Type | Default | Effect |
|---|---|---|---|---|
SbDrawer |
swipeDirection |
'down' | 'up' | 'left' | 'right' |
'down' |
Edge and dismiss direction: bottom sheet, top sheet, side panels (Reka) |
modal |
boolean | 'trap-focus' |
true |
'trap-focus' keeps focus inside but lets the page take pointer input (Reka) |
|
snapPoints |
(number | string)[] |
none | Snap positions: fractions, pixels, or CSS lengths (Reka) | |
SbDrawerContent |
showHandle |
boolean |
true |
Renders the grab bar on the edge facing the page |
showClose |
boolean |
false |
Renders an icon close button, useful without touch input | |
closeLabel |
string |
'Close' |
Accessible name of the close button |
The other Reka DrawerRoot props (v-model:open, v-model:snapPoint, snapToSequentialPoints) and DrawerContent emits pass through. The handle is decorative (aria-hidden).
Keyboard and accessibility. The drawer opens from its trigger as a described dialog; Escape closes it and restores focus. Swiping is pointer-only, so keep a close control (show-close or an SbDrawerClose) for keyboard and controller users.
SbPopover
Section titled “SbPopover”A non-modal panel anchored to its trigger, wrapping Reka Popover. Use it for small forms and details that do not need a dialog.
<script setup lang="ts">import { SbButton, SbPopover, SbPopoverClose, SbPopoverContent, SbPopoverTrigger, SbProvider,} from '@static-bloom/vue'</script>
<template> <SbProvider> <SbPopover> <SbPopoverTrigger as-child> <SbButton variant="outline">Route details</SbButton> </SbPopoverTrigger> <SbPopoverContent aria-label="Route details" arrow show-close class="grid gap-2"> <p class="font-display font-semibold">Harbor loop</p> <p class="text-sm text-fg-muted">6.4 km, two checkpoints, est. 9 minutes.</p> <SbPopoverClose as-child> <SbButton size="sm">Set waypoint</SbButton> </SbPopoverClose> </SbPopoverContent> </SbPopover> </SbProvider></template>SbPopover├─ SbPopoverAnchor (optional: position against another element)├─ SbPopoverTrigger└─ SbPopoverContent └─ SbPopoverClose (optional)SbPopoverContent prop |
Type | Default | Effect |
|---|---|---|---|
size |
'sm' | 'md' | 'lg' |
'md' |
Minimum width 12rem, 16rem or 22rem |
variant |
'solid' | 'glass' |
'solid' |
glass blurs what is behind the panel, for HUD overlays |
arrow |
boolean |
false |
Draws an arrow pointing at the trigger |
showClose |
boolean |
false |
Renders an icon close button in the top-end corner |
closeLabel |
string |
'Close' |
Accessible name of the close button |
sideOffset |
number |
6 |
Distance from the trigger in px (Reka) |
arrowPadding |
number |
8 |
Keeps the arrow away from the corners (Reka) |
class |
HTMLAttributes['class'] |
none | Merged onto the panel |
side, align, collision options and the other Reka PopoverContent props and emits pass through.
Keyboard and accessibility. The trigger toggles aria-expanded; opening moves focus into the content, and Escape closes it and returns focus to the trigger. Give the content an aria-label or a heading.
SbTooltip
Section titled “SbTooltip”A short label shown on hover or keyboard focus, wrapping Reka Tooltip. It relies on the TooltipProvider inside SbProvider, whose tooltip-delay (300 ms) and tooltip-skip-delay (200 ms) set the timing; never add a second provider.
<script setup lang="ts">import { Radar } from '@lucide/vue'import { SbButton, SbProvider, SbTooltip, SbTooltipContent, SbTooltipTrigger,} from '@static-bloom/vue'</script>
<template> <SbProvider> <div class="flex flex-wrap items-center gap-3"> <SbTooltip> <SbTooltipTrigger as-child> <SbButton variant="outline" icon aria-label="Scan area"> <Radar aria-hidden="true" /> </SbButton> </SbTooltipTrigger> <SbTooltipContent>Scan area (Q)</SbTooltipContent> </SbTooltip> <SbTooltip> <SbTooltipTrigger as-child> <SbButton variant="ghost">Fast travel</SbButton> </SbTooltipTrigger> <SbTooltipContent side="bottom">Unlocks after the first job</SbTooltipContent> </SbTooltip> </div> </SbProvider></template>SbTooltip├─ SbTooltipTrigger└─ SbTooltipContentSbTooltipContent prop |
Type | Default | Effect |
|---|---|---|---|
arrow |
boolean |
true |
Draws an arrow pointing at the trigger |
sideOffset |
number |
6 |
Distance from the trigger in px (Reka) |
arrowPadding |
number |
6 |
Keeps the arrow off the corners (Reka) |
class |
HTMLAttributes['class'] |
none | Merged onto the bubble |
SbTooltip forwards Reka’s root props, such as delayDuration and disabled.
Keyboard and accessibility. Keyboard focus opens the tooltip, and the tooltip describes its trigger. Escape hides it while the trigger keeps focus. Put only a label in a tooltip; an icon-only trigger still needs its own aria-label.
SbHoverCard
Section titled “SbHoverCard”A preview card for sighted pointer users, wrapping Reka HoverCard: it opens after the pointer rests on the trigger (700 ms) and closes 300 ms after it leaves.
Equipped: Thermal katana
<script setup lang="ts">import { SbHoverCard, SbHoverCardContent, SbHoverCardTrigger, SbProvider } from '@static-bloom/vue'</script>
<template> <SbProvider> <p class="text-sm text-fg-muted"> Equipped: <SbHoverCard> <SbHoverCardTrigger href="#sbhovercard" class="text-primary underline underline-offset-4" >Thermal katana</SbHoverCardTrigger > <SbHoverCardContent arrow class="grid gap-1"> <p class="font-display font-semibold text-fg">Thermal katana</p> <p class="text-sm text-fg-muted"> Melee, tier 4. Burn damage on charged strikes. Value 2,600. </p> </SbHoverCardContent> </SbHoverCard> </p> </SbProvider></template>SbHoverCard├─ SbHoverCardTrigger (renders a link)└─ SbHoverCardContentSbHoverCardContent prop |
Type | Default | Effect |
|---|---|---|---|
variant |
'solid' | 'glass' |
'solid' |
glass blurs what is behind the card |
arrow |
boolean |
false |
Draws an arrow pointing at the trigger |
sideOffset |
number |
8 |
Distance from the trigger in px (Reka) |
arrowPadding |
number |
8 |
Keeps the arrow off the corners (Reka) |
class |
HTMLAttributes['class'] |
none | Merged onto the card |
SbHoverCard forwards Reka’s openDelay, closeDelay and the other root props.
Keyboard and accessibility. Keyboard focus on the trigger opens the card too. The card is supplementary: put nothing in it that is not reachable elsewhere.
SbDropdownMenu
Section titled “SbDropdownMenu”A menu of actions opened from a button, wrapping Reka DropdownMenu, with items, checkbox and radio items, labels, groups, separators, shortcuts and submenus.
<script setup lang="ts">import { ref } from 'vue'import { SbButton, SbDropdownMenu, SbDropdownMenuCheckboxItem, SbDropdownMenuContent, SbDropdownMenuGroup, SbDropdownMenuItem, SbDropdownMenuLabel, SbDropdownMenuRadioGroup, SbDropdownMenuRadioItem, SbDropdownMenuSeparator, SbDropdownMenuShortcut, SbDropdownMenuSub, SbDropdownMenuSubContent, SbDropdownMenuSubTrigger, SbDropdownMenuTrigger, SbProvider,} from '@static-bloom/vue'
const last = ref('none')const tracked = ref(true)const priority = ref('normal')</script>
<template> <SbProvider> <div class="flex flex-wrap items-center gap-4"> <SbDropdownMenu> <SbDropdownMenuTrigger as-child> <SbButton variant="outline">Job actions</SbButton> </SbDropdownMenuTrigger> <SbDropdownMenuContent> <SbDropdownMenuLabel>Courier run</SbDropdownMenuLabel> <SbDropdownMenuGroup> <SbDropdownMenuItem @select="last = 'accepted'"> Accept <SbDropdownMenuShortcut>Enter</SbDropdownMenuShortcut> </SbDropdownMenuItem> <SbDropdownMenuItem disabled>Hire backup</SbDropdownMenuItem> <SbDropdownMenuSub> <SbDropdownMenuSubTrigger>Share with</SbDropdownMenuSubTrigger> <SbDropdownMenuSubContent> <SbDropdownMenuItem @select="last = 'shared with crew'">Crew</SbDropdownMenuItem> <SbDropdownMenuItem @select="last = 'shared with fixer'">Fixer</SbDropdownMenuItem> </SbDropdownMenuSubContent> </SbDropdownMenuSub> </SbDropdownMenuGroup> <SbDropdownMenuSeparator /> <SbDropdownMenuCheckboxItem v-model="tracked">Track on map</SbDropdownMenuCheckboxItem> <SbDropdownMenuSeparator /> <SbDropdownMenuLabel>Priority</SbDropdownMenuLabel> <SbDropdownMenuRadioGroup v-model="priority"> <SbDropdownMenuRadioItem value="normal">Normal</SbDropdownMenuRadioItem> <SbDropdownMenuRadioItem value="urgent">Urgent</SbDropdownMenuRadioItem> </SbDropdownMenuRadioGroup> <SbDropdownMenuSeparator /> <SbDropdownMenuItem intent="danger" @select="last = 'abandoned'" >Abandon</SbDropdownMenuItem > </SbDropdownMenuContent> </SbDropdownMenu> <output class="text-sm text-fg-muted" >Last action: {{ last }} · tracked: {{ tracked }} · priority: {{ priority }}</output > </div> </SbProvider></template>SbDropdownMenu├─ SbDropdownMenuTrigger└─ SbDropdownMenuContent ├─ SbDropdownMenuLabel ├─ SbDropdownMenuGroup │ ├─ SbDropdownMenuItem │ │ └─ SbDropdownMenuShortcut │ └─ SbDropdownMenuSub │ ├─ SbDropdownMenuSubTrigger │ └─ SbDropdownMenuSubContent ├─ SbDropdownMenuSeparator ├─ SbDropdownMenuCheckboxItem └─ SbDropdownMenuRadioGroup └─ SbDropdownMenuRadioItem| Part | Prop | Type | Default | Effect |
|---|---|---|---|---|
SbDropdownMenu |
modal |
boolean |
false |
true blocks outside interaction and hides the page from assistive tech |
SbDropdownMenuContent, …SubContent |
variant |
'solid' | 'glass' |
'solid' |
glass blurs what is behind the menu |
sideOffset |
number |
4 |
Distance from the trigger (Reka) | |
SbDropdownMenuSubContent |
alignOffset |
number |
-5 |
Lines the first sub item up with its sub trigger (Reka) |
SbDropdownMenuItem |
intent |
'default' | 'danger' |
'default' |
danger paints a destructive item |
SbDropdownMenuItem, …Label, …SubTrigger |
inset |
boolean |
false |
Indents the label to line up with checkbox and radio items |
Items emit @select; SbDropdownMenuCheckboxItem binds v-model (boolean) and SbDropdownMenuRadioGroup binds v-model (the selected value). The other Reka props and emits pass through.
modal defaults to false here, unlike in Reka: a modal menu sets aria-hidden on the rest of the page while its focusable trigger stays inside that hidden tree.
Keyboard. Enter, Space or ArrowDown on the trigger opens the menu on its first item. Arrow keys move between items and skip disabled ones; Enter selects. ArrowRight opens a submenu on its first item and ArrowLeft returns to the sub trigger. Escape or a pointer down outside closes the menu.
SbContextMenu
Section titled “SbContextMenu”The same menu opened by a right-click (or a long press) on an area, wrapping Reka ContextMenu. Its parts mirror the dropdown’s under SbContextMenu* names, with SbContextMenuTrigger as the area.
<script setup lang="ts">import { ref } from 'vue'import { SbContextMenu, SbContextMenuCheckboxItem, SbContextMenuContent, SbContextMenuItem, SbContextMenuLabel, SbContextMenuSeparator, SbContextMenuShortcut, SbContextMenuSub, SbContextMenuSubContent, SbContextMenuSubTrigger, SbContextMenuTrigger, SbProvider,} from '@static-bloom/vue'
const last = ref('none')const favorite = ref(false)</script>
<template> <SbProvider> <div class="grid w-full max-w-md gap-3"> <SbContextMenu> <SbContextMenuTrigger class="flex h-28 items-center justify-center rounded-lg border border-dashed border-line-strong text-sm text-fg-muted" > Right-click this inventory slot </SbContextMenuTrigger> <SbContextMenuContent> <SbContextMenuLabel>Medkit</SbContextMenuLabel> <SbContextMenuItem @select="last = 'used'"> Use <SbContextMenuShortcut>E</SbContextMenuShortcut> </SbContextMenuItem> <SbContextMenuSub> <SbContextMenuSubTrigger>Move to</SbContextMenuSubTrigger> <SbContextMenuSubContent> <SbContextMenuItem @select="last = 'moved to stash'">Stash</SbContextMenuItem> <SbContextMenuItem @select="last = 'moved to vehicle'" >Vehicle trunk</SbContextMenuItem > </SbContextMenuSubContent> </SbContextMenuSub> <SbContextMenuSeparator /> <SbContextMenuCheckboxItem v-model="favorite">Favorite</SbContextMenuCheckboxItem> <SbContextMenuSeparator /> <SbContextMenuItem intent="danger" @select="last = 'dropped'">Drop</SbContextMenuItem> </SbContextMenuContent> </SbContextMenu> <output class="text-sm text-fg-muted" >Last action: {{ last }} · favorite: {{ favorite }}</output > </div> </SbProvider></template>SbContextMenu├─ SbContextMenuTrigger (the area that listens for right-click)└─ SbContextMenuContent └─ same items as SbDropdownMenuContent, prefixed SbContextMenuThe props match the dropdown menu’s: modal defaults to false; content and sub content take variant; items take intent and inset; labels and sub triggers take inset; sub content defaults to sideOffset 4 and alignOffset -5.
Keyboard. The menu opens next to the pointer. ArrowDown focuses the first item, Enter selects, ArrowRight and ArrowLeft open and close submenus, Escape closes. A right-click has no keyboard equivalent, so offer every context action somewhere else as well.
SbMenubar
Section titled “SbMenubar”A horizontal row of menus, as in a desktop application, wrapping Reka Menubar. Each SbMenubarMenu has a trigger and its content; v-model on SbMenubar holds the value of the open menu.
<script setup lang="ts">import { ref } from 'vue'import { SbMenubar, SbMenubarCheckboxItem, SbMenubarContent, SbMenubarItem, SbMenubarLabel, SbMenubarMenu, SbMenubarRadioGroup, SbMenubarRadioItem, SbMenubarSeparator, SbMenubarShortcut, SbMenubarSub, SbMenubarSubContent, SbMenubarSubTrigger, SbMenubarTrigger, SbProvider,} from '@static-bloom/vue'
const last = ref('none')const showMinimap = ref(true)const units = ref('metric')</script>
<template> <SbProvider> <div class="grid gap-3"> <SbMenubar> <SbMenubarMenu value="loadout"> <SbMenubarTrigger>Loadout</SbMenubarTrigger> <SbMenubarContent> <SbMenubarItem @select="last = 'new loadout'"> New <SbMenubarShortcut>Ctrl+N</SbMenubarShortcut> </SbMenubarItem> <SbMenubarSub> <SbMenubarSubTrigger>Recent</SbMenubarSubTrigger> <SbMenubarSubContent> <SbMenubarItem @select="last = 'stealth'">Stealth</SbMenubarItem> <SbMenubarItem @select="last = 'assault'">Assault</SbMenubarItem> </SbMenubarSubContent> </SbMenubarSub> <SbMenubarSeparator /> <SbMenubarItem disabled>Share</SbMenubarItem> </SbMenubarContent> </SbMenubarMenu> <SbMenubarMenu value="view"> <SbMenubarTrigger>View</SbMenubarTrigger> <SbMenubarContent> <SbMenubarCheckboxItem v-model="showMinimap">Minimap</SbMenubarCheckboxItem> <SbMenubarSeparator /> <SbMenubarLabel>Units</SbMenubarLabel> <SbMenubarRadioGroup v-model="units"> <SbMenubarRadioItem value="metric">Metric</SbMenubarRadioItem> <SbMenubarRadioItem value="imperial">Imperial</SbMenubarRadioItem> </SbMenubarRadioGroup> </SbMenubarContent> </SbMenubarMenu> </SbMenubar> <output class="text-sm text-fg-muted" >Last action: {{ last }} · minimap: {{ showMinimap }} · units: {{ units }}</output > </div> </SbProvider></template>SbMenubar└─ SbMenubarMenu (value) ├─ SbMenubarTrigger └─ SbMenubarContent └─ same items as SbDropdownMenuContent, prefixed SbMenubar| Part | Prop | Type | Default | Effect |
|---|---|---|---|---|
SbMenubar |
class |
HTMLAttributes['class'] |
none | Merged onto the bar |
SbMenubarContent |
variant |
'solid' | 'glass' |
'solid' |
glass blurs what is behind the menu |
sideOffset |
number |
6 |
Distance from the trigger (Reka) | |
align |
'start' | 'center' | 'end' |
'start' |
Aligns the menu with the trigger’s start edge (Reka) | |
SbMenubarSubContent |
sideOffset, alignOffset |
number |
4, -5 |
Sub menu placement (Reka) |
SbMenubarItem |
intent |
'default' | 'danger' |
'default' |
danger paints a destructive item |
SbMenubarItem, …Label, …SubTrigger |
inset |
boolean |
false |
Indents the label |
Keyboard and accessibility. The bar has the menubar role, and ArrowLeft and ArrowRight move between its triggers. Enter opens a menu on its first item; with a menu open, ArrowRight moves to the next menu. ArrowRight on a sub trigger opens its submenu, ArrowLeft closes it. Escape closes the open menu and returns focus to its trigger.
SbToaster and toasts
Section titled “SbToaster and toasts”Short, non-blocking notifications, built on Reka Toast. Mount one SbToaster inside SbProvider (which holds Reka’s ToastProvider), then queue toasts from anywhere with useToast.
<script setup lang="ts">import { ref } from 'vue'import { SbButton, SbProvider, SbToaster, useToast } from '@static-bloom/vue'
const { toast, dismiss } = useToast()const sold = ref(0)
function sell(): void { sold.value += 1 toast({ title: 'Item sold', description: 'Thermal katana for 2,600.', intent: 'success', action: { label: 'Undo', onClick: () => (sold.value -= 1) }, })}</script>
<template> <SbProvider> <SbToaster /> <div class="flex flex-wrap items-center gap-3"> <SbButton @click="sell">Sell item</SbButton> <SbButton intent="warning" variant="outline" @click=" toast({ title: 'Heat rising', description: 'Two patrols are heading your way.', intent: 'warning', }) " > Warn </SbButton> <SbButton variant="ghost" @click="dismiss()">Dismiss all</SbButton> <output class="text-sm text-fg-muted">{{ sold }} sold</output> </div> </SbProvider></template>SbToaster├─ SbToast (intent, variant)│ ├─ intent icon│ ├─ SbToastTitle│ ├─ SbToastDescription│ ├─ SbToastAction│ └─ SbToastClose└─ viewport (the notifications region)SbToaster prop |
Type | Default | Effect |
|---|---|---|---|
position |
'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center' |
'bottom-right' |
Corner or edge the stack docks to; the newest toast sits nearest the edge |
variant |
'glass' | 'solid' |
'glass' |
Surface of every toast |
label |
string |
'Notifications ({hotkey})' |
Name of the region; {hotkey} becomes the focus shortcut |
closeLabel |
string |
'Dismiss notification' |
Name of every close button |
class |
HTMLAttributes['class'] |
none | Merged onto the viewport |
How long a toast stays, its swipe direction and the queue limit are SbProvider props: toast-duration (5000 ms), toast-swipe-direction ('right') and toast-limit.
The parts are exported for a custom toaster: SbToast takes intent ('primary', 'secondary', 'neutral', 'info', 'success', 'warning', 'danger', default 'primary') and variant, plus Reka’s ToastRoot props; SbToastClose takes label (default 'Dismiss notification'); SbToastAction takes Reka’s required alt-text.
Accessibility. Toasts are announced through a live region: danger and warning toasts interrupt (foreground), the others wait (background). Every toast has a labelled close button, and an action button runs its callback, then closes the toast. The hotkey in label (F8 by default in Reka) moves focus to the region.
In-game notes
Section titled “In-game notes”- All of these render in the page; none depends on a browser-drawn popup, a native
<dialog>or atitleattribute, which OPEN//77’s in-game browser cannot be relied on to show. - Portals mount in
<body>by default. If anSbProviderscopes a flavor to a subtree, set itsteleport-toinside that subtree so menus and dialogs keep the flavor. - Menus use the popover layer, so they open above dialogs; toasts use their own
--sb-z-toastlayer. - Pick
variant="glass"for popovers, menus and toasts that sit over game imagery;solidkeeps maximum contrast. - Keep a visible close control on drawers and toasts for controller players, and call
window.Open77?.ready()only after mounting, as for any in-game page.