Skip to content
PorabuildHeroGPUI component library
v0.14.0 · Apache-2.0

Colors

Color Picker

A complete color picking surface in a popover.

Rust
use herogpui::components::color_picker::ColorPicker;

ColorPicker on docs.rsSource at v0.14.0

Usage

Showing Usage, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.

Usage

The panel flips near window edges and scrolls to keep the alpha control reachable in short windows. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ColorPicker, PickerColor};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let value = self.picker_color;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![ColorPicker::new("color-picker-main", value)
        // v3's Usage is uncontrolled; "Controlled" is separate.
        .default_value(value)
        .label("Accent")
        .font_family(MONO_FONT)
        .show_alpha(true)
        .on_change(cx.listener(|this, c: &PickerColor, _, cx| {
            this.picker_color = *c;
            cx.notify();
        }))
        .into_any_element()])

Also calls MONO_FONT (included when you copy).

Open / Placement

A controlled-open picker exercises the initial overlay mount and placement resolver without requiring a trigger press. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ColorPicker, Placement};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let value = self.picker_color;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![ColorPicker::new("color-picker-open", value)
        .is_open(true)
        .placement(Placement::BottomStart)
        .label("Accent")
        .font_family(MONO_FONT)
        .show_alpha(true)
        // Controlled open state intentionally ignores close
        // requests so the initial placement stays visible in
        // the visual specimen.
        .on_open_change(|_, _, _| {})
        .into_any_element()])

Also calls MONO_FONT (included when you copy).

Controlled

The caller owns the color value while the trigger owns its ordinary open state — the same split a dialog trigger composes. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, ColorPicker, PickerColor};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;

let value = self.picker_color;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ColorPicker::new("color-picker-controlled", value)
            .label("Brand")
            .on_change(cx.listener(|this, c: &PickerColor, _, cx| {
                this.picker_color = *c;
                cx.notify();
            },))
            .into_any_element(),
        para(&format!("Value: {}", value.to_hex()), cx),
    ])

Also calls para (included when you copy).

With Swatches

A preset row beside the picker — the default layout. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ColorPicker, ColorSwatchPicker, PickerColor};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let value = self.picker_color;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ColorSwatchPicker::new("color-picker-presets", palette())
            .value(value)
            .on_change(cx.listener(|this, c: &PickerColor, _, cx| {
                this.picker_color = *c;
                cx.notify();
            }))
            .into_any_element(),
    ])

Also calls palette (included when you copy).

With Fields

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ColorChannel, ColorField, ColorPicker};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let value = self.picker_color;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ColorField::new("color-picker-field", value)
            .state(self.demo_text("cp-field", "#0085F5", cx))
            .label("Hex")
            .into_any_element(),
        gpui::div()
            .flex()
            .flex_wrap()
            .items_center()
            .justify_center()
            .gap(px(12.))
            .children(vec![
                        ColorField::new("color-picker-field-h", value)
                            .state(self.demo_text("cp-field-h", "", cx))
                            .channel(ColorChannel::Hue)
                            .label("H")
                            .into_any_element(),
                        ColorField::new("color-picker-field-s", value)
                            .state(self.demo_text("cp-field-s", "", cx))
                            .channel(ColorChannel::Saturation)
                            .label("S")
                            .into_any_element(),
                        ColorField::new("color-picker-field-l", value)
                            .state(self.demo_text("cp-field-l", "", cx))
                            .channel(ColorChannel::Lightness)
                            .label("L")
                            .into_any_element(),
                    ])
            .into_any_element(),
    ])

With Sliders

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ColorChannel, ColorPicker, ColorSlider, PickerColor};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let value = self.picker_color;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ColorSlider::new("color-picker-sl-hue", value, ColorChannel::Hue)
            .on_change(cx.listener(|this, c: &PickerColor, _, cx| {
                this.picker_color = *c;
                cx.notify();
            }))
            .into_any_element(),
        ColorSlider::new("color-picker-sl-alpha", value, ColorChannel::Alpha)
            .on_change(cx.listener(|this, c: &PickerColor, _, cx| {
                this.picker_color = *c;
                cx.notify();
            }))
            .into_any_element(),
    ])

Helpers used by these examples

Plain functions over the public API, defined once here and copied with each example that calls them.

MONO_FONT
const MONO_FONT: &str = if cfg!(target_arch = "wasm32") {
    // The web shell bundles JetBrains Mono; the literal family "monospace"
    // resolves to nothing there and panics text resolution.
    "JetBrains Mono"
} else if cfg!(target_os = "macos") {
    "Menlo"
} else {
    "Consolas"
};
para
fn para(text: &str, cx: &App) -> gpui::AnyElement {
    let colors = cx.colors();
    gpui::div()
        .text_size(px(14.5))
        .line_height(px(24.))
        .text_color(colors.foreground)
        .max_w(px(720.))
        .child(text.to_owned())
        .into_any_element()
}
palette
/// The demo palette used by the color pages.
fn palette() -> Vec<PickerColor> {
    [
        "#0085F5", "#17C964", "#F5A524", "#F31260", "#7828C8", "#0E8AAA", "#71717A", "#18181B",
    ]
    .into_iter()
    .filter_map(PickerColor::from_hex)
    .collect()
}

Anatomy

Rust types

ColorPicker

Color Picker is assembled from these builders. The API reference lists each one.

Customization

Shared appearance belongs on ThemeBuilder::components and .recipe("name"). Typed builders change how Color Picker looks at one call site. Every builder also carries sx for placement, width and flex: it takes GPUI's own styling methods and refines them over the component's root after the theme, so an instance override wins. Behaviour builders live in the API reference below.

Styling

Color Picker styling
MethodValuesDescription
placement(Placement)PlacementPositions and flips the floating panel to the side with more room when the preferred side cannot fit through the shared placement engine, keeping a 12px cross-axis viewport inset with the scroller capped to the available height.
Rust
// The one slot for caller-owned low-level styling: GPUI's
// styling methods, refined over the root element last.
.sx(|el| {
    el.bg(gpui::rgba(0xffa500ff))             // background
        .text_color(gpui::rgba(0x000000ff))   // text
        .w(gpui::px(13.))                     // size
        .h(gpui::px(12.))
})

API reference

Against HeroUI v3.2.6: 12 entries ported, 6 partial, 4 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Color Picker builders
BuilderValuesDefaultDescription
new(id, value)string | Color—Controlled color value; owner acceptance drives the next frame.
default_value(PickerColor)string | Color—Seeds the picker-owned color once.
on_change(callback)Fn(color: Color) -> ()—Reports changes from the area and each rendered slider.
label / show_alphaPartialAnyElement—The Rust root renders a fixed trigger, area, hue slider, optional alpha slider, and readout rather than arbitrary compound children.
built-in triggerPartialAnyElement | render closure—The trigger is the fixed swatch and hexadecimal value and does not delegate Button render state.
is_disabled(bool)true | falsefalseInherited Button state disables the trigger and suppresses the panel.
placement(Placement)PlacementPlacement::BottomLeftPositions and flips the floating panel to the side with more room when the preferred side cannot fit through the shared placement engine, keeping a 12px cross-axis viewport inset with the scroller capped to the available height.
built-in popover contentPartialAnyElement—The built-in area, sliders, and hexadecimal readout cannot be replaced independently.
Not ported3 HeroUI entries not ported
Color Picker builders not ported
HeroUI v3Why it is not ported
ColorPicker.classNameBrowser styles are unavailable.
ColorPicker.Trigger.classNameBrowser styles are unavailable.
ColorPicker.Popover.classNameBrowser styles are unavailable.

Parts

Color Picker parts
PartDescription
ColorPickerColor-value and internal DialogTrigger state owner.
ColorPickerPartialFocusable swatch and hexadecimal-value trigger with fixed content.
ColorPickerPartialDismissible floating color controls with fixed built-in children.

States

Color Picker states
StateBuilderDescription
Openinternal keyed stateThe default trigger owns open state; Rust's composition-only is_open builder reports without mutating until its owner accepts the change.
Focusedtab_stop_handleThe trigger participates in tab order and regains focus after dismissal.
Focus visiblering_if_focusedKeyboard focus draws the status ring.
PressedPartialtrigger_pressed dismissal guardPress tracking protects outside dismissal but is not delegated to trigger content.
Disabledis_disabledThe trigger dims, leaves pointer interaction, and suppresses its panel.
EnteringMotion::LIST_INPanel fades and grows from 95% over 150ms with ease-smooth.
ExitingMotion::LIST_OUTPanel is retained while fading and shrinking to 95% over 100ms.
Not ported1 HeroUI entry not ported
Color Picker states not ported
HeroUI v3Why it is not ported
HoveredThe fixed Rust trigger does not expose Button hover render state.