Color Field
Enter a color value as text.
use herogpui::components::color_picker::ColorField;ColorField 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.
Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field", value)
.state(self.demo_text("cf-usage", "#0085F5", cx))
// v3's Usage is uncontrolled: `defaultValue="#0085F5"`.
.default_value(value)
.label("Color")
.radius(px(4.))
.into_any_element()])ColorField accepts an explicit null value. The empty field keeps its label and placeholder, omits the swatch, and can be filled later. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ColorField, PickerColor};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-empty", None::<PickerColor>)
.default_value(None::<PickerColor>)
.state(self.demo_text("cf-empty", "", cx))
.label("Brand color")
.placeholder("#000000")
.is_required(true)
.into_any_element()])`height`, `padding_x` and `is_bare` cover both paths: the editable field here, and the static display box. The bare specimen is the editable path. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-custom-box", value)
.state(self.demo_text("cf-custom-box", "#0085F5", cx))
.label("Compact editable")
.height(px(28.))
.padding_x(px(8.))
.is_bare(true)
.font_family(MONO_FONT)
.into_any_element()])Also calls MONO_FONT (included when you copy).
`sx` refines the field's root, the column holding the label, the box and the description, on the editable and the static path alike; the box keeps its own chrome. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![
ColorField::new("color-field-sx-editable", value)
.state(self.demo_text("cf-sx-editable", "#0085F5", cx))
.label("Editable")
.description("Padded, tinted column")
.sx(|s| s.p(px(8.)).rounded(px(12.)).bg(gpui::hsla(0.58, 0.9, 0.5, 0.08)))
.into_any_element(),
ColorField::new("color-field-sx-static", value)
.label("Static")
.description("Same slot, display-only box")
.sx(|s| s.p(px(8.)).rounded(px(12.)).bg(gpui::hsla(0.58, 0.9, 0.5, 0.08)))
.into_any_element(),
])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ColorField, FieldVariant};
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-field-primary", value)
.state(self.demo_text("cf-v-primary", "#0085F5", cx))
.label("Primary")
.into_any_element(),
ColorField::new("color-field-secondary", value)
.state(self.demo_text("cf-v-secondary", "#0085F5", cx))
.label("Secondary")
.variant(FieldVariant::Secondary)
.into_any_element(),
])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ColorField, FieldVariant, Surface};
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![Surface::new()
.padding(px(24.))
.gap(px(16.))
.child(
ColorField::new("color-field-surface", value)
.state(self.demo_text("cf-surface", "#0085F5", cx))
.label("Color")
.variant(FieldVariant::Secondary),
)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-desc", value)
.state(self.demo_text("cf-desc", "#0085F5", cx))
.label("Brand color")
.description("Any CSS hex value")
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-req", value)
.state(self.demo_text("cf-req", "", cx))
.label("Color")
.is_required(true)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-dis", value)
.state(self.demo_text("cf-dis", "#0085F5", cx))
.label("Color")
.is_disabled(true)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.w(px(400.))
.child(gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
ColorField::new("color-field-full", value)
.state(self.demo_text("cf-full", "#0085F5", cx))
.label("Color")
.full_width(true)
.into_any_element(),
ColorField::new("color-field-full-display", value)
.label("Display color")
.full_width(true)
.into_any_element(),
])
.into_any_element())Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-invalid", value)
.state(self.demo_text("cf-invalid", "not-a-color", cx))
.label("Color")
.is_required(true)
.is_invalid(true)
.validation_errors(["Enter a valid hex colour"])
.into_any_element()])Edit individual HSL channels: Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ColorChannel, ColorField, ColorSpace, ColorSwatch};
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![gpui::div()
.flex()
.flex_wrap()
.items_center()
.justify_center()
.gap(px(12.))
.children(vec![
ColorField::new("color-field-ch-hue", value)
.state(self.demo_text("cf-ch-hue", "", cx))
// `colorSpace` names the channel set; `channel`
// picks one of them.
.color_space(ColorSpace::Hsl)
.channel(ColorChannel::Hue)
// `ColorField.Suffix` -- the unit after the value.
.suffix(gpui::div().child("\u{00b0}"))
.label("Hue")
.into_any_element(),
ColorField::new("color-field-ch-sat", value)
.state(self.demo_text("cf-ch-sat", "", cx))
.channel(ColorChannel::Saturation)
.label("Saturation")
.into_any_element(),
ColorField::new("color-field-ch-light", value)
.state(self.demo_text("cf-ch-light", "", cx))
.channel(ColorChannel::Lightness)
.label("Lightness")
.into_any_element(),
ColorSwatch::new(value).into_any_element(),
])
.into_any_element(),])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, ColorField, ColorSwatch, 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![
ColorField::new("color-field-ctl", value)
.state(self.demo_text("cf-ctl", "#0085F5", cx))
.label("Color")
.on_change(cx.listener(
|this, parsed: &Option<PickerColor>, _, cx| {
if let Some(c) = parsed {
this.picker_color = *c;
}
cx.notify();
},
))
.into_any_element(),
gpui::div()
.flex()
.flex_wrap()
.items_center()
.justify_center()
.gap(px(12.))
.children(vec![
ColorSwatch::new(value).into_any_element(),
para(&format!("Value: {}", value.to_hex()), cx),
])
.into_any_element(),
])Also calls para (included when you copy).
Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
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![{
let state = self.demo_text("cf-render", "#0085F5", cx);
let inner = state.clone();
ColorField::new("color-field-render-root", value)
.state(state)
.is_required(true)
.content(move |field| {
gpui::div()
.flex()
.flex_col()
.gap(px(4.))
.child(
ColorField::new("color-field-render-input", value)
.state(inner.clone())
.label("Rendered color")
.is_disabled(field.is_disabled)
.is_invalid(field.is_invalid)
.is_read_only(field.is_read_only)
.is_required(field.is_required),
)
.child(format!(
"required={} focused={} focus-within={} focus-visible={}",
field.is_required,
field.is_focused,
field.is_focus_within,
field.is_focus_visible,
))
.into_any_element()
})
.into_any_element()
}])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Button, ColorField, Form, FormField};
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![{
let state = self.demo_text("cf-form", "#0085F5", cx);
Form::new()
.field(FormField::text(state.clone()).name("color"))
.child(
ColorField::new("color-field-form", value)
.state(state)
.label("Brand color")
.name("color")
.is_required(true),
)
.child(Button::new("color-field-form-submit").label("Save"))
.into_any_element()
}])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ColorField, PickerColor};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-hex", value)
.state(self.demo_text("cf-hex", "#0085F5", cx))
.label("Brand color")
.description("Type a hex value such as #0085F5.")
.placeholder(value.to_hex())
.on_change(cx.listener(|this, parsed: &Option<PickerColor>, _, cx| {
// Unparseable text reports None; the swatch
// holds its last good colour.
if let Some(c) = parsed {
this.picker_color = *c;
}
cx.notify();
},))
.into_any_element()])Commits a colour field on blur: when focus leaves, text that does not parse to a colour restores the last committed value, valid text is left alone, and an emptied field stays empty. `on_blur` hooks the focus loss itself and fires ahead of that revert. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ColorField, Input};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![{
let blurs = self.demo_text("cf-blur-log", "not blurred yet", cx);
ColorField::new("color-field-blur", value)
.state(self.demo_text("cf-blur", "#0085F5", cx))
.default_value(value)
.label("Color")
.on_blur(move |_, cx| {
blurs.update(cx, |state, cx| {
state.set_value("blur: focus left the field");
cx.notify();
});
})
.into_any_element()
},
Input::new(self.demo_text("cf-blur-log", "not blurred yet", cx))
.is_read_only(true)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::ColorField;
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![ColorField::new("color-field-display", value)
.label("Current value")
.description("Without a text state the field is a display.")
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ColorChannel, ColorField};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let value = self.picker_color;
gpui::div()
.flex()
.flex_wrap()
.items_center()
.justify_center()
.gap(px(12.))
.children(vec![
ColorField::new("color-field-hue", value)
.channel(ColorChannel::Hue)
.label("Hue")
.into_any_element(),
ColorField::new("color-field-red", value)
.channel(ColorChannel::Red)
.label("Red")
.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.
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"
};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()
}Anatomy
Rust types
Color Field 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 Field 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
| Method | Values | Description |
|---|---|---|
full_width(bool) | true | false | Stretches the root and input group in editable and display modes. |
variant(FieldVariant) | primary | secondary | Selects field or default fill and primary shadow. |
// 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: 35 entries ported, 5 partial, 12 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.
Builders
| Builder | Values | Default | Description |
|---|---|---|---|
content( | AnyElement | | — | Replacement content receives disabled, invalid, read-only, required, focused, focus-within, and focus-visible state. |
full_width( | true | | false | Stretches the root and input group in editable and display modes. |
focus_ring( | true | | true | GPUI extension: hides only the editable field's visual focus ring while retaining focus and editing. |
new( | string | — | Stable GPUI element identity is required by the constructor. |
new( | Color | | — | Controlled color values may be concrete or null; null renders the documented empty field and onChange can return None while text is incomplete. |
default_value( | Color | | — | Seeds picker-owned state with a concrete color or an explicit null empty value; the outer Rust Option distinguishes omission from null. |
on_change( | Fn( | — | Reports a parsed color or None while text is incomplete or invalid. |
on_blur( | Fn( | — | Focus loss commits the field like onBlur commit: unparseable text restores the formatted last-committed value while valid text and an emptied field are untouched. The hook fires ahead of that built-in revert, the way chains the caller's onBlur ahead of commit. |
color_space( | ColorSpace | — | Selects the channel interpretation when channel is present. |
channel( | ColorChannel | — | Switches from hexadecimal editing to one numeric channel. |
is_required( | true | | false | Marks the label and form field required; an empty null value is treated as missing during native submission. |
is_invalid( | true | | — | Forces invalid chrome and render state. |
validate( | Fn( | — | Custom validation contributes to field chrome, form validity, error text, and render state. |
validation_behavior( | native | | ValidationBehavior:: | Native blocks submission; Allow exposes invalid state without blocking. |
is_disabled( | true | | false | Removes editing, focus, wheel changes, and successful form submission. |
is_read_only( | true | | false | Keeps the editable input focusable while preventing changes. |
is_wheel_disabled( | true | | false | Suppresses focused wheel stepping for channel fields. |
name( | string | — | Registers the current hex text or channel number for FormData. |
auto_focus( | true | | false | Focuses the editable input on its first render. |
variant( | primary | | FieldVariant:: | Selects field or default fill and primary shadow. |
placeholder( | string | — | Shows caller text, or the resolved current color when omitted. |
built-in ColorSwatchPartial | AnyElement | — | The built-in ColorSwatch follows the current color and is omitted for the null empty state; arbitrary replacement remains unavailable. |
suffix( | AnyElement | — | Caller content remains present in editable and display compositions. |
Not ported12 HeroUI entries not ported
| HeroUI v3 | Why it is not ported |
|---|---|
ColorField.className | Browser styles are unavailable. |
ColorField.style | Browser inline styles are unavailable. |
ColorField.render | Root-element substitution is unavailable. |
ColorField.aria-label | GPUI exposes no accessibility tree attribute surface. |
ColorField.aria-labelledby | GPUI exposes no accessibility tree attribute surface. |
ColorField.aria-describedby | GPUI exposes no accessibility tree attribute surface. |
ColorField.aria-details | GPUI exposes no accessibility tree attribute surface. |
ColorField.Group.className | Browser styles are unavailable. |
ColorField.Group.render | The group cannot be replaced independently. |
ColorField.Input.className | Browser styles are unavailable. |
ColorField.Prefix.className | Browser styles are unavailable. |
ColorField.Suffix.className | Browser styles are unavailable. |
Parts
| Part | Description |
|---|---|
ColorField | Value, validation, form, and complete render-state owner. |
ColorFieldPartial | Optional string label with required, disabled, and invalid state; composes field::Label. |
ColorField | Field chrome drawn by ColorField surrounding prefix, input, and suffix; the input is a composed Input. |
ColorSwatchPartial | Fixed current-color swatch; arbitrary replacement is unavailable. |
ColorField | State-backed text input with parsing, channel keys, wheel editing, and focus. Each simultaneously rendered editable field needs its own input::InputState identity. |
ColorField | Caller-provided trailing content in placeholder color. |
ColorFieldPartial | Optional helper text replaced by resolved error content when invalid; composes field::Description. |
ColorFieldPartial | Resolved controlled, server, or validator message handed to the composed Input's error_message; arbitrary child composition is unavailable. |
States
| State | Builder | Description |
|---|---|---|
Invalid | validation::resolve | Hides description, shows the resolved message, draws danger chrome, blocks native forms, and reaches render state. |
Required | is_required | Marks the label and reaches render state; a null or empty color fails native required validation. |
Disabled | is_disabled | Dims the group, removes input and wheel behavior, omits FormData, and reaches render state. |
Read only | is_read_only | Keeps the editable input focusable but blocks mutations and reaches render state. |
Focused | ColorFieldRenderState | Reports focus on the state-backed input. |
Focus within | focus_handle.contains_focused | Focus anywhere in the composed field drives focused chrome and render state. |
Focus visible | focus_visible | Keyboard-origin focus reaches the render function and the shared field ring. |
Hovered | hover_fade_with_duration_and_easing(150ms, EaseSmooth) | Enabled, unfocused groups interpolate the field/default hover fill over 150ms and apply the field-hover border endpoint. |