Color
The color family wraps Reka UI’s color primitives. Every part is drawn in the page, so it replaces <input type="color">, whose browser picker does not open in OPEN//77’s in-game browser.
Values are hex strings or Reka Color objects ({ space: 'hsl', h, s, l, alpha }); v-model hands back whatever the primitive produces, so bind a ref typed with the component’s modelValue prop when you share one value between parts.
SbColorPicker
Section titled “SbColorPicker”A complete picker behind one button: a Reka Popover holding a saturation and brightness area, hue and opacity sliders, a hex field, and optional presets. The trigger shows a swatch of the current color.
<script setup lang="ts">import { ref } from 'vue'import { SbColorPicker, SbProvider } from '@static-bloom/vue'
const paint = ref('#59a7ff')const presets = [ { value: '#fd65b2', label: 'Plasma pink' }, { value: '#b580ff', label: 'Ultraviolet' }, { value: '#66e6ba', label: 'Coolant green' }, { value: '#ffa73e', label: 'Sodium orange' }, { value: '#59a7ff', label: 'Ion blue' },]</script>
<template> <SbProvider> <div class="flex flex-wrap items-center gap-4"> <SbColorPicker v-model="paint" label="Vehicle paint" :presets="presets" /> <output class="font-mono text-sm text-fg-muted">{{ paint }}</output> </div> </SbProvider></template>SbColorPicker├─ trigger: SbColorSwatch + label (default slot, receives { value })└─ popover content ├─ title + close button ├─ SbColorArea (hsb: saturation × brightness) ├─ SbColorSlider channel="hue" ├─ SbColorSlider channel="alpha" ├─ SbColorField (hex) └─ SbColorSwatchPicker of presets (when `presets` is set)| Prop | Type | Default | Effect |
|---|---|---|---|
modelValue |
string | Color |
none | Current color; v-model emits a string in format |
defaultValue |
string | Color |
pure red | Uncontrolled starting color; without it the picker starts at hsl 0, 100%, 50% |
format |
ColorFormat |
'hex' |
Format of the emitted string, e.g. 'hex' or 'hsl' |
label |
string |
'Choose color' |
Trigger text and accessible name, also the popover title |
presets |
readonly { value; label? }[] |
none | Swatches under the field; value is a color string |
disabled |
boolean |
false |
Disables the trigger and every control |
open |
boolean |
none | Controlled open state, v-model:open |
defaultOpen |
boolean |
false |
Uncontrolled initial open state |
closeLabel |
string |
'Close color picker' |
Accessible name of the close button |
class |
HTMLAttributes['class'] |
none | Merged onto the trigger |
Emits update:modelValue (string) and update:open (boolean). Other attributes go to the trigger button.
Keyboard and accessibility. The trigger is a button named by label. Opening moves focus into the popover; Escape closes it and returns focus to the trigger. The area, sliders, field and presets work from the keyboard as described below.
SbColorArea
Section titled “SbColorArea”A two-dimensional picker over two channels of one color space, wrapping Reka ColorAreaRoot, ColorAreaArea and ColorAreaThumb. The default slot holds the thumb; SbColorAreaThumb is rendered when the slot is empty.
<script setup lang="ts">import { ref } from 'vue'import { SbColorArea, SbColorSwatch, SbProvider, type SbColorAreaProps } from '@static-bloom/vue'
const glow = ref<SbColorAreaProps['modelValue']>('#fd65b2')</script>
<template> <SbProvider> <div class="grid w-full max-w-sm gap-3"> <SbColorArea v-model="glow" color-space="hsb" x-channel="saturation" y-channel="brightness" aria-label="Neon sign saturation and brightness" /> <div class="flex items-center gap-3 text-sm text-fg-muted"> <SbColorSwatch :color="glow" label="Neon sign color" /> Neon sign </div> </div> </SbProvider></template>SbColorArea└─ SbColorAreaThumb (default slot content)| Prop | Type | Default | Effect |
|---|---|---|---|
modelValue |
string | Color |
none | Controlled color, v-model |
defaultValue |
string | Color |
'#ff0000' |
Uncontrolled starting color (Reka) |
colorSpace |
ColorSpace |
'hsl' |
Color space the channels belong to (Reka) |
xChannel |
ColorChannel |
'hue' |
Channel on the horizontal axis (Reka) |
yChannel |
ColorChannel |
'saturation' |
Channel on the vertical axis (Reka) |
disabled |
boolean |
false |
Blocks interaction and dims the area |
xName, yName |
string |
none | Input names for form submission (Reka) |
class |
HTMLAttributes['class'] |
none | Merged onto the root |
The other Reka ColorAreaRoot props and emits pass through.
Keyboard. The thumb is the focusable control: arrow keys move it along both axes. Give the area an aria-label that names both channels.
SbColorSlider
Section titled “SbColorSlider”One channel of a color on a gradient track, wrapping Reka ColorSliderRoot and ColorSliderThumb. The track paints the channel’s gradient over a checkerboard, so alpha is visible.
<script setup lang="ts">import { ref } from 'vue'import { SbColorSlider, SbColorSwatch, SbProvider, type SbColorSliderProps,} from '@static-bloom/vue'
const dye = ref<SbColorSliderProps['modelValue']>('#b580ffcc')const channels = [ { channel: 'hue', label: 'Hue' }, { channel: 'saturation', label: 'Saturation' }, { channel: 'lightness', label: 'Lightness' }, { channel: 'alpha', label: 'Opacity' },] as const</script>
<template> <SbProvider> <div class="grid w-full max-w-sm gap-2"> <div class="flex items-center gap-3 text-sm text-fg-muted"> <SbColorSwatch :color="dye" size="lg" label="Jacket dye" /> Jacket dye </div> <div v-for="item in channels" :key="item.channel" class="grid gap-1 text-xs text-fg-muted"> <span>{{ item.label }}</span> <SbColorSlider v-model="dye" :channel="item.channel" :label="item.label" /> </div> </div> </SbProvider></template>| Prop | Type | Default | Effect |
|---|---|---|---|
channel |
ColorChannel |
required | 'hue', 'saturation', 'lightness', 'alpha', … |
modelValue |
string | Color |
none | Controlled color, v-model |
defaultValue |
string | Color |
'#000000' |
Uncontrolled starting color (Reka) |
colorSpace |
ColorSpace |
'hsl' |
Color space of channel (Reka) |
orientation |
'horizontal' | 'vertical' |
'horizontal' |
Vertical sliders are 12rem tall; the gradient follows |
inverted |
boolean |
false |
Reverses the axis (Reka) |
step |
number |
channel step | Keyboard step (Reka) |
label |
string |
none | aria-label of the thumb, the focusable control |
disabled |
boolean |
false |
Blocks interaction and dims the slider |
class |
HTMLAttributes['class'] |
none | Merged onto the root |
The other Reka ColorSliderRoot props and emits pass through. SbColorSliderTrack is exported for custom slider layouts.
Keyboard. Arrow keys change the channel by step; the thumb exposes the slider role with its orientation. Set label so it has a name.
SbColorField
Section titled “SbColorField”A text input for a color, wrapping Reka ColorFieldRoot and ColorFieldInput. Without channel it edits the hex value; with channel it edits one number.
<script setup lang="ts">import { ref } from 'vue'import { SbColorField, SbColorSwatch, SbProvider, type SbColorFieldProps } from '@static-bloom/vue'
const trim = ref<SbColorFieldProps['modelValue']>('#66e6ba')</script>
<template> <SbProvider> <div class="grid w-full max-w-xs gap-4"> <label class="grid gap-2 text-sm text-fg-muted"> Trim color <div class="flex items-center gap-3"> <SbColorSwatch :color="trim" label="Trim color" /> <SbColorField v-model="trim" label="Trim color" /> </div> </label> <label class="grid gap-2 text-sm text-fg-muted"> Trim hue <SbColorField v-model="trim" channel="hue" size="sm" label="Trim hue" /> </label> </div> </SbProvider></template>| Prop | Type | Default | Effect |
|---|---|---|---|
modelValue |
string | Color |
none | Controlled color, v-model |
channel |
ColorChannel |
none | Edit one channel instead of the hex value (Reka) |
colorSpace |
ColorSpace |
'hsl' |
Space of channel (Reka) |
size |
'sm' | 'md' | 'lg' |
'md' |
Input height 2rem, 2.5rem or 3rem |
label |
string |
'Color value' |
aria-label of the input |
invalid |
boolean |
false |
Sets aria-invalid and the danger border |
disabled |
boolean |
false |
Disables the input |
readonly |
boolean |
false |
Read-only input (Reka) |
placeholder |
string |
none | Placeholder when empty (Reka) |
class |
HTMLAttributes['class'] |
none | Merged onto the input |
The other Reka ColorFieldRoot props and emits pass through; other attributes (such as id or aria-describedby) go to the input.
Keyboard and accessibility. The value is parsed when the input commits: invalid text is rejected and the previous color is kept; channel values are clamped. Arrow keys step the value. Point an error message at the input with aria-describedby and set invalid.
SbColorSwatch
Section titled “SbColorSwatch”A display-only preview of one color over a checkerboard, wrapping Reka ColorSwatch. Reka gives it role="img" and a name derived from the color unless you pass label.
<script setup lang="ts">import { SbColorSwatch, SbProvider } from '@static-bloom/vue'
const sizes = ['sm', 'md', 'lg'] as const</script>
<template> <SbProvider> <div class="flex flex-wrap items-center gap-4"> <SbColorSwatch v-for="size in sizes" :key="size" color="#ffa73e" :size="size" :label="`Sodium orange, ${size}`" /> <SbColorSwatch color="#ffa73e" size="lg" shape="square" label="Sodium orange, square" /> <SbColorSwatch color="#59a7ff80" size="lg" label="Ion blue at half opacity" /> </div> </SbProvider></template>| Prop | Type | Default | Effect |
|---|---|---|---|
color |
string | Color |
none | Color to show (Reka) |
label |
string |
color name | Accessible name (Reka) |
size |
'sm' | 'md' | 'lg' |
'md' |
1.5rem, 2rem or 2.75rem square |
shape |
'rounded' | 'square' |
'rounded' |
Corner radius |
class |
HTMLAttributes['class'] |
none | Merged onto the root |
SbColorSwatchPicker
Section titled “SbColorSwatchPicker”A grid of preset swatches, one of which is selected, wrapping Reka ColorSwatchPickerRoot (a listbox) and ColorSwatchPickerItem. Each item is 2.75rem square and shows a check when selected.
<script setup lang="ts">import { ref } from 'vue'import { SbColorSwatchPicker, SbColorSwatchPickerItem, SbProvider } from '@static-bloom/vue'
const crewColor = ref('#66e6ba')const presets = [ { value: '#fd65b2', label: 'Plasma pink' }, { value: '#b580ff', label: 'Ultraviolet' }, { value: '#66e6ba', label: 'Coolant green' }, { value: '#ffa73e', label: 'Sodium orange' }, { value: '#59a7ff', label: 'Ion blue' },]</script>
<template> <SbProvider> <div class="grid w-full max-w-xs gap-3"> <SbColorSwatchPicker v-model="crewColor" aria-label="Crew color"> <SbColorSwatchPickerItem v-for="preset in presets" :key="preset.value" :value="preset.value" :label="preset.label" /> </SbColorSwatchPicker> <output class="font-mono text-sm text-fg-muted">{{ crewColor }}</output> </div> </SbProvider></template>SbColorSwatchPicker└─ SbColorSwatchPickerItem (one per preset)| Part | Prop | Type | Default | Effect |
|---|---|---|---|---|
SbColorSwatchPicker |
modelValue |
string | string[] |
none | Selected color, v-model |
defaultValue |
string | string[] |
none | Uncontrolled selection | |
disabled |
boolean |
false |
Disables every item | |
class |
HTMLAttributes['class'] |
none | Merged onto the grid | |
SbColorSwatchPickerItem |
value |
string |
required | The color, a hex string |
label |
string |
none | Accessible name of the option and its swatch | |
shape |
'rounded' | 'square' |
'rounded' |
Swatch shape |
The picker accepts the other Reka listbox props (multiple, orientation, …) except by. The default slot of an item receives { selected } to replace the swatch.
Keyboard and accessibility. Give the picker an aria-label. Arrow keys move between swatches and select them, updating v-model.
In-game notes
Section titled “In-game notes”- Every popup and control here is DOM-rendered, so the picker works in OPEN//77’s in-game browser, where native color inputs do not open.
- The picker’s popover portals to
<body>(orSbProvider’steleport-to). On a scoped island, pointteleport-toinside the scope to keep the popover in its flavor. - The parts are sized for cursor and controller input: the trigger and swatch items are at least 2.75rem (44 px), sliders are 2.75rem tall, and thumbs extend their hit area 0.5rem beyond the visible ring.