Skip to content

Vue overview

@static-bloom/vue is a Vue 3 component library built on Reka UI, the headless primitives that provide keyboard support, focus management and ARIA. Every component is styled with the Static Bloom Tailwind theme, so it looks like the matching sb-* CSS class and follows the flavor and accent of its scope.

preference auto · resolved midnight · theme dark

This is SbFlavorSelect, hydrated as an Astro island. It is a Reka toggle group, not a native <select>, so it also works in OPEN//77 in-game pages.

  • Every export is prefixed Sb, and compound components mirror Reka’s anatomy: SbSelect, SbSelectTrigger, SbSelectContent, SbSelectItem.
  • Props and events are forwarded to the Reka part, so v-model, as-child and the Reka options work as documented by Reka.
  • Many components add intent, size or variant props with the same names as the CSS classes.
  • Every component accepts class, merged with cn() so your utilities replace the component’s own.
  • Popups (select lists, menus, popovers, tooltips, dialogs) render in the page through Reka portals. No browser-drawn popup is used.
  1. Install the components, the Tailwind theme they are styled with, and Tailwind.

    Terminal window
    pnpm add @static-bloom/vue @static-bloom/tailwind vue
    pnpm add -D tailwindcss @tailwindcss/vite @vitejs/plugin-vue

    Reka UI, tailwind-variants, tailwind-merge, @lucide/vue and @vueuse/core are dependencies of the package; Vue 3.5 or later is a peer dependency. Add @lucide/vue to your own dependencies too if your code imports icons.

  2. Register the Vue and Tailwind plugins.

    vite.config.ts
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import tailwindcss from '@tailwindcss/vite'
    export default defineConfig({ plugins: [vue(), tailwindcss()] })
  3. Import Tailwind, the theme, and the component sources in your stylesheet.

    src/style.css
    @import 'tailwindcss';
    @import '@static-bloom/tailwind';
    @import '@static-bloom/vue/tailwind.css';

    The last line is an @source for the package’s dist folder, so Tailwind generates every class the components use.

  4. Wrap the app in SbProvider and set a flavor on <html>.

    src/App.vue
    <script setup lang="ts">
    import { SbFlavorSelect, SbProvider } from '@static-bloom/vue'
    </script>
    <template>
    <SbProvider>
    <SbFlavorSelect show-labels />
    </SbProvider>
    </template>
    index.html
    <html lang="en" data-flavor="midnight" data-theme="dark"></html>

SbProvider wraps Reka’s ConfigProvider, TooltipProvider and ToastProvider. Put one at the root of every Vue app, and one in every Astro island, since each island is its own app.

Prop Default Effect
flavor none Scopes a flavor to the subtree: a wrapper with data-flavor, data-theme and color-scheme
accent none Scopes an accent to the subtree with data-accent
dir 'ltr' Reading direction for every Reka primitive
locale 'en' Locale for every Reka primitive, dates included
teleportTo body Where portalled content (menus, dialogs, toasts) is mounted
tooltipDelay 300 Tooltip open delay in ms
tooltipSkipDelay 200 Window in ms to open the next tooltip without delay
toastDuration 5000 How long a toast stays visible, in ms
toastSwipeDirection 'right' Swipe direction that dismisses a toast
toastLabel 'Notification' Screen-reader label of the toast region
toastLimit none Maximum number of toasts shown at once
tooltip none Other Reka TooltipProvider props, passed as-is

It also hands Vue’s useId to Reka, so generated ids match between the server render and hydration.

Import components where you use them. The package ships one ES module per component and marks only its CSS as a side effect, so a bundler keeps just what you import:

import {
SbSelect,
SbSelectContent,
SbSelectItem,
SbSelectTrigger,
SbSelectValue,
} from '@static-bloom/vue'

StaticBloomPlugin registers every Sb* component globally instead. It suits prototypes; it also pulls every component into the bundle.

src/main.ts
import { createApp } from 'vue'
import { StaticBloomPlugin } from '@static-bloom/vue'
import App from './App.vue'
import './style.css'
createApp(App).use(StaticBloomPlugin).mount('#app')

useFlavor() reads and sets the flavor, and SbFlavorSelect is its picker. They share the static-bloom:flavor storage key and the page runtime with every other package; Flavors & theming covers the composable and its options.

SbFlavorSelect prop Default Effect
show-labels false Shows the flavor names next to the icons
size 'md' 'sm' or 'md'
label 'Flavor' Accessible name of the group
disabled false Disables every option

The package also exports cn and tv, configured for the theme: see Class merging.

The components render on the server. useFlavor touches neither window nor document until a component mounts, and ids come from Vue’s useId. In Astro, each client:* component is a separate Vue app: wrap its content in SbProvider, and use client:visible unless the component is interactive above the fold. When the page has a flavor runtime (window.staticBloom, installed by the Astro integration or the Starlight plugin), useFlavor delegates to it, which is how the island above follows the header. Vue islands has a complete example.

  • Start in Blackout: <html data-flavor="blackout" data-theme="dark">, or <SbProvider flavor="blackout"> for one subtree.
  • Use the Vue selection, date and color components instead of native <select>, date, time and color inputs; their popups are drawn in the page.
  • Keep <html> and <body> transparent so the game shows through, and put backgrounds on panels.
  • Without a page runtime, useFlavor applies the flavor itself; pass target to scope it to one element.
  • Call window.Open77?.ready() after mounting, so the client sends the first state.

In-game UIs lists every platform rule, and Resource template packages a Vite app as a resource.

Family Components
Foundations SbProvider, SbFlavorSelect, useFlavor, StaticBloomPlugin, cn, tv
Actions & HUD SbButton, SbButtonGroup, SbToggle, SbToggleGroup, SbToolbar, SbBadge, SbKbd, SbKeyHint, SbCard, SbSeparator, SbAvatar, SbAspectRatio, SbSkeleton, SbSpinner, SbAlert, SbLink, SbProgress, SbRating, SbTable, SbHudPanel, SbStatBar, SbNeonText
Forms SbLabel, SbInput, SbInputGroup, SbTextarea, SbField, SbForm, SbCheckbox, SbCheckboxGroup, SbRadioGroup, SbSwitch, SbSlider, SbNumberField, SbPinInput, SbTagsInput, SbEditable, SbTagGroup
Selection SbSelect, SbCombobox, SbAutocomplete, SbListbox
Date and time SbCalendar, SbRangeCalendar, SbDateField, SbDateRangeField, SbTimeField, SbTimeRangeField, SbDatePicker, SbDateRangePicker, SbMonthPicker, SbMonthRangePicker, SbYearPicker, SbYearRangePicker
Color SbColorArea, SbColorField, SbColorSlider, SbColorSwatch, SbColorSwatchPicker, SbColorPicker
Overlays SbDialog, SbAlertDialog, SbSheet, SbDrawer, SbPopover, SbTooltip, SbHoverCard, SbDropdownMenu, SbContextMenu, SbMenubar, SbToast, SbToaster
Navigation SbTabs, SbAccordion, SbCollapsible, SbNavigationMenu, SbPagination, SbBreadcrumb, SbStepper, SbTree, SbScrollArea, SbSplitterGroup

Each family also exports the compound parts of its components, such as SbTabsList, SbTabsTrigger and SbTabsContent.