Skip to main content

ColorInput

Overview​

The ColorInput component is a form field for a color: type it, or open a color picker from its launcher or its swatch.

Reach for it wherever a form asks for a color: a brand or theme color in settings, the color of a label, a calendar or a chart series. The field holds the value as text, so it can be typed or pasted, and a swatch at its start shows the color. Its launcher, a click on the swatch, or Alt+ArrowDown opens a ColorPicker in a popover, the same one the date pickers use.

The value is a CSS color string, HSL by default, or empty, so the field can be left blank and required works the way it does on any text field.

info

Bulma has no color field, so ColorInput brings its own styles, in bestax.css and its variants, or in extras.css next to your own Bulma.


Import​

import { ColorInput } from '@allxsmith/bestax-bulma';

Usage​

Basic Usage​

Give it a label and, uncontrolled, a defaultValue. Type a color, or open the panel from the launcher, the swatch or Alt+ArrowDown, and Escape brings you back to the field.

<ColorInput label="Brand color" defaultValue="hsl(202, 67%, 36%)" />

Empty​

Without a value the field is empty and the swatch shows a checkerboard. Clearing the text and leaving the field empties it again, and onChange gets ''.

<ColorInput label="Highlight" placeholder="No color" />

Typing​

Typed text is read as hex, rgb(), hsl() or a CSS color name, and committed on Enter or on leaving the field, written back in format. Text it can't read is marked invalid; Escape, or leaving the field, puts the value back.

<ColorInput label="Type tomato, then Enter" format="hex" />

Formats and Alpha​

format writes the value as hsl (the default), hex or rgb. alpha adds a slider for transparency to the panel and writes it into the value.

<ColorInput
  label="Overlay"
  alpha
  format="rgb"
  defaultValue="rgba(10, 10, 10, 0.6)"
/>

Swatches​

swatches adds preset colors to the panel, and swatches="bulma" the page's own theme colors. closeOnSwatchClick closes the panel once one is chosen with a click, Enter or Space; the arrow keys only move between them.

<ColorInput
  label="Label color"
  format="hex"
  closeOnSwatchClick
  swatches={[
    { color: '#1e6b99', label: 'Ocean' },
    { color: '#00d1b2', label: 'Turquoise' },
    { color: '#ff6685', label: 'Pink' },
    { color: '#ffdd57', label: 'Sunflower' },
  ]}
/>

Controlled, and Theming With It​

onChange fires on every step of a drag in the panel, and onChangeEnd once a change is done, the place to save. An HSL value splits into the channels Theme takes; an empty field has none, so Theme gets none and keeps the page's own primary.

function example() {
  const [color, setColor] = useState('hsl(202, 67%, 36%)');
  const [h, s, l] = color.match(/[\d.]+/g) ?? [];
  return (
    <>
      <ColorInput label="Primary" value={color} onChange={setColor} />
      <Theme primaryH={h} primaryS={s && `${s}%`} primaryL={l && `${l}%`}>
        <Buttons>
          <Button color="primary">Primary</Button>
          <Button color="primary" isOutlined>
            Outlined
          </Button>
        </Buttons>
      </Theme>
    </>
  );
}

Opening on Focus​

openOnFocus opens the panel when the field takes focus and moves focus into it, as DateInput does. It's off by default, since typing is the quick way in. Focus coming back from the panel leaves it closed.

<ColorInput label="Accent" openOnFocus defaultValue="#ffdd57" />

In a Form​

name sends the text the field shows, the value in format, with the form, and required fails an empty field. Inside a disabled Fieldset, or with disabled, nothing opens.

<form onSubmit={event => event.preventDefault()}>
  <ColorInput label="Calendar color" name="calendarColor" required />
  <ColorInput label="Locked" disabled defaultValue="#ff6685" />
</form>

Sizes and Colors​

size, color and isRounded style the field as they do an Input. The swatch and the launcher grow with it.

<>
  <ColorInput label="Small" size="small" isRounded defaultValue="#00d1b2" />
  <ColorInput label="Large" size="large" color="info" defaultValue="#1e6b99" />
</>

Right to Left​

In a right-to-left page the swatch sits on the right and the launcher on the left, and the panel's square and sliders run from the right.

<LocaleProvider locale="ar" dir="rtl">
  <ColorInput
    label="اللون"
    defaultValue="hsl(150, 60%, 40%)"
    labels={{ chooseColor: 'اختر لونًا' }}
  />
</LocaleProvider>

Without the Field​

ColorInputBase is the field and its panel with no Field or Control around it, for composing those yourself.

<Field label="Bare">
  <Control>
    <ColorInputBase defaultValue="#ff6685" />
  </Control>
</Field>


Accessibility​

  • The field is a combobox that opens a dialog, as the date pickers' is: role="combobox", aria-haspopup="dialog", aria-expanded and aria-controls, named by its label. The launcher is a button named "Choose color" with the same attributes.
  • Keys in the field: Alt+ArrowDown opens the panel; Enter commits typed text; Escape puts typed text back without closing a Modal around the field.
  • The panel is a non-modal dialog named "Choose color" that holds focus while it's open. Focus starts on the saturation and brightness square, and Escape, Alt+ArrowUp or a press outside closes it and hands focus back to the field.
  • The square and the sliders are ColorPicker's: native range inputs, the square one 2D slider with one tab stop. See ColorPicker.
  • The swatch in the field is for the pointer only: it's hidden from assistive tech and takes no tab stop, since the launcher does the same from the keyboard.
  • The value is always text in the field, and text it can't read is marked aria-invalid.
  • Forced colors: the swatch keeps its color with an edge in the system's text color.
  • Strings come from labels, with English defaults.

  • ColorPicker: The panel on its own, always on the page.
  • ColorSwatch: Shows a color without choosing one.
  • Input: type="color", the browser's own color well, with no JavaScript.
  • DateInput: The same field and popover, for a date.
  • Helper Props: Bulma helper props for spacing, color, etc.

Additional Resources​


Props​

PropTypeDefaultDescription
labelReact.ReactNode—Field label (component auto-wraps in a Field if not already inside). Automatically associated with the text field via htmlFor, using your id when provided, otherwise a generated one. Dropped inside an outer Field, whose own label associates instead when that Field generates a target id (not grouped/hasAddons, no explicit labelProps.htmlFor) and you set no id here.
labelSize'small' | 'normal' | 'medium' | 'large'—Size for the label.
labelPropsReact.LabelHTMLAttributes<HTMLLabelElement> & { [key: string]: unknown; }—Props for the label element. An explicit htmlFor here overrides the automatic association (no id is generated then).
horizontalbooleanfalseRender the field with horizontal layout.
isLoadingbooleanfalseShows a loading spinner on the Control it renders, and hides the launcher (triggerIcon) while it does. Inside your own Control it renders none, so this draws nothing and warns in development; set isLoading on that Control.
triggerIconbooleantrueShows the launcher on the field's end that toggles the panel. Hidden by default while a spinner shows at the same edge: this component's isLoading when it renders its own Control, or the enclosing Control's isLoading inside one.
isExpandedbooleanfalseExpand the control to fill its container.
controlSize'small' | 'medium' | 'large'—Size of the wrapping Control.
messageReact.ReactNode—Help/validation text below the field.
messageColor'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Color modifier for the help message.
fieldClassNamestring—Additional CSS classes for the Field wrapper.
controlClassNamestring—Additional CSS classes for the Control wrapper.
valuestring—The color, controlled, as a CSS color the picker reads (hex, rgb(), hsl() or a color name), or '' for none.
defaultValuestring—The color to start with, uncontrolled; empty without one.
onChange(value: string) => void—Called with the color, in format, as it changes: on every step of a drag in the panel, on a key or a swatch, and when typed text is committed. Clearing the field calls it with ''.
onChangeEnd(value: string) => void—Called with the color, in format, once a change is done: when a drag ends, and with onChange for everything else. The place to save it.
format'hsl' | 'hex' | 'rgb''hsl'How the value is written: 'hsl' (hsl(171, 100%, 41%)), 'hex' or 'rgb', with alpha as hsla(), #rrggbbaa and rgba(). Typed text in any of them is written back in this one.
alphabooleanfalseAdds an alpha slider to the panel and writes the alpha into the value.
swatchesColorPickerSwatch[] | 'bulma'—Preset colors in the panel: a list of CSS colors, each a string or { color, label }, or "bulma" for the page's theme colors.
eyeDropperbooleanfalseAdds a button to the panel that picks a color from anywhere on the screen, where the browser has the EyeDropper API.
onOpen() => void—Fired when the panel opens.
onClose() => void—Fired when the panel closes.
openOnFocusbooleanfalseOpens the panel when the field takes focus, and moves focus into it. Off by default, since typing a value is the quick way in and the launcher, a click on the swatch or Alt+ArrowDown open it.
closeOnSwatchClickbooleanfalseCloses the panel once a swatch is chosen.
position'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'auto''bottom-left'The panel's place relative to the field.
appendToBodybooleanfalseRenders the panel through a portal, into the portalContainer a ConfigProvider sets, document.body by default.
color'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Bulma color modifier for the field.
size'small' | 'medium' | 'large'—The field's size.
isRoundedbooleanfalseRenders the field with rounded ends.
disabledbooleanfalseDisables the field and its panel. A disabled Fieldset does too.
readOnlybooleanfalseMakes the field read-only, with no panel.
placeholderstring—Text shown while the field is empty.
triggerIconNamestring'chevron-down'The launcher's glyph.
labelsColorPickerLabels—The strings it says, each with an English default.
classNamestring—Additional CSS classes.
namestring—Form field name. The field submits the text it shows, the value in format, or nothing when it's empty.
requiredbooleanfalseMarks the field required, so an empty one fails the form's constraint validation.
refReact.Ref<HTMLInputElement>—Forwarded to the text <input>.
...All standard <input> attributes and Bulma helper props—Helper props go on the .colorinput <div>, with className. The other attributes go on the text <input>. See Helper Props

CSS & Sass Variables​

ColorInput registers these variables on its own .colorinput element. Override them there (or via className) — a value set on an ancestor is only inherited, and loses to the component-level declaration. See Theme.

CSS VariableSass VariableDefault
--bulma-colorinput-side-width$colorinput-side-width2.5em
--bulma-colorinput-swatch-size$colorinput-swatch-size1.25em
--bulma-colorinput-trigger-color$colorinput-trigger-colorvar(--bulma-text)
--bulma-colorinput-trigger-hover-color$colorinput-trigger-hover-colorvar(--bulma-text-strong)
--bulma-colorinput-disabled-opacity$colorinput-disabled-opacity0.5
--bulma-picker-popover-z-index ‡$picker-popover-z-index30
--bulma-picker-popover-background ‡$picker-popover-backgroundvar(--bulma-scheme-main)
--bulma-picker-popover-radius ‡$picker-popover-radiusvar(--bulma-radius-large)
--bulma-picker-popover-shadow ‡$picker-popover-shadow0 8px 24px hsla(0, 0%, 0%, 0.18)
--bulma-picker-popover-border-color ‡$picker-popover-border-colorvar(--bulma-border)
--bulma-picker-popover-padding ‡$picker-popover-padding0.75rem
--bulma-picker-popover-offset ‡$picker-popover-offset4px
--bulma-picker-popover-animation-duration ‡$picker-popover-animation-duration0.15s

‡ declared on a constituent element: values set via className, the style prop, or an ancestor are only inherited and lose — target the declaring element in your CSS.