Skip to content

ColorField ​

A colour field is the one colour control you type into. It is where a reader who already knows the value goes — pasting a hex from a brand guide — rather than hunting for it on a ColorArea or a ColorSlider.

ts
import { ColorField, ColorInputGroup, ColorInputGroupInput, ColorInputGroupPrefix } from "ropav";

ColorInputGroup is the bordered box, ColorInputGroupInput the text input inside it, and ColorInputGroupPrefix and ColorInputGroupSuffix hold whatever sits at either end — a ColorSwatch showing the value is the usual one.

template
<ColorField class="w-72" default-value="#0485f7">
  <Label>Brand colour</Label>
  <ColorInputGroup>
    <ColorInputGroupPrefix>
      <ColorSwatch />
    </ColorInputGroupPrefix>
    <ColorInputGroupInput />
  </ColorInputGroup>
</ColorField>

Hex or a channel ​

Without a channel the field edits the whole colour as a hex value. Give it one and the field edits that channel as a number instead, with the stepper keys a number field has.

color-field-channel.vue
vue
<script setup lang="ts">
import {
  ColorField,
  ColorInputGroup,
  ColorInputGroupInput,
  ColorInputGroupPrefix,
  ColorSwatch,
  Label,
} from "ropav";
</script>

<template>
  <div class="stack">
    <ColorField default-value="#0485f7">
      <Label>Hex</Label>
      <ColorInputGroup>
        <ColorInputGroupPrefix><ColorSwatch size="xs" /></ColorInputGroupPrefix>
        <ColorInputGroupInput />
      </ColorInputGroup>
    </ColorField>

    <ColorField channel="red" color-space="rgb" default-value="#0485f7">
      <Label>Red</Label>
      <ColorInputGroup>
        <ColorInputGroupInput />
      </ColorInputGroup>
    </ColorField>
  </div>
</template>

<style scoped>
.stack {
  display: flex;
  width: 100%;
  max-width: var(--rp-container-xs);
  flex-direction: column;
  gap: calc(var(--rp-spacing) * 4);
}
</style>

These are two different controls, not one control configured two ways: they have their own state, their own keyboard and their own DOM, so changing channel at runtime rebuilds the field. Decide which one a field is and leave it there.

That split reaches validation too. is-invalid and validate are read on the hex branch only — a channel field's validity is the number field's underneath, which is built without either.

Accessibility ​

  • The id lands on the input, because the input is the field as far as assistive technology is concerned. A Label pointing at the field reaches it.
  • The text being typed and the colour it parses to are held apart until the value is committed, so a half-typed #04 is not announced as an invalid colour while the reader is still in the middle of it.
  • A swatch in the prefix is decoration — the value is already in the input, and announcing it twice is worse than not announcing it at all.

API ​

PropTypeDefaultDescription
ariaDescribedbystring—
ariaLabelstring—
ariaLabelledbystring—
autoFocusboolean—
channel"alpha" | "blue" | "brightness" | "green" | "hue" | "lightness" | "red" | "saturation"—The channel the field edits. Without one the field edits the colour as a hex value, which is a different control: the two branches have their own state, their own keyboard and their own DOM, and changing this at runtime rebuilds the field rather than reconfiguring it.
colorSpace"hsb" | "hsl" | "rgb"—The colour space the field works in when a channel is given. Ignored without one — a hex field always shows the colour as an RGB hex value.
defaultValuestring | Color | null—Colour the field starts with, and goes back to when the form is reset.
formstring—Id of the form the field belongs to, when it is not nested inside one.
fullWidthboolean—Whether the field stretches to fill its container. Declared as a plain boolean rather than through the variants type: the SFC compiler cannot resolve an imported indexed-access type into a runtime prop type, and without type: Boolean Vue never casts a valueless attribute — <ColorField full-width> would arrive as "" and read as falsy, so the modifier would silently never apply.
idstring—Lands on the input, which is the field as far as assistive technology is concerned.
isDisabledboolean—
isInvalidboolean—Controlled validity. Present at all — true *or* false — pins the field. Only read on the hex branch. A channel field ignores it, along with validate, exactly as React does: its validation state is the number field's, which is built without either.
isReadOnlyboolean—
isRequiredboolean—
isWheelDisabledboolean—Whether the wheel over a focused field is ignored.
namestring—Name the field submits under.
validateValidationFunction<Color | null>—Only read on the hex branch.
validationBehavior"aria" | "native"—
valuestring | Color | null—Controlled colour. A string is parsed; one that will not parse is treated as absent.

The default slot receives:

Slot propType
channel"hex" | ColorChannel
colorValueColor | null
isDisabledboolean
isInvalidboolean
isReadOnlyboolean
isRequiredboolean
validationDetailsValidationDetails
validationErrorsstring[]
EventPayload
changeColor | null
focusChangeboolean
update:valueColor | null

The parts around the input come from ColorInputGroup, which a ColorPicker reuses.

ColorInputGroup

PropTypeDefaultDescription
fullWidthboolean—Whether the group stretches to fill its container.
isDisabledboolean—Whether the group reads as disabled. Taken from the surrounding field when unset, which is how the field's own disabled state reaches the shell around the control.
isInvalidboolean—Whether the group reads as invalid. Taken from the surrounding field when unset.
size"xs" | "sm" | "md" | "lg""md"How tall the group stands, matching a button of the same size.
variant"primary" | "secondary""primary"Visual variant.

The default slot receives:

Slot propType
isDisabledboolean
isFocusVisibleboolean
isFocusWithinboolean
isHoveredboolean
isInvalidboolean

ColorInputGroupInput

PropTypeDefaultDescription
placeholderstring—Placeholder shown while the control is empty. Declared so it can also be set here rather than only on the field; every other native attribute arrives by attribute fallthrough.

ColorInputGroupPrefix

Slots: default.

ColorInputGroupSuffix

Slots: default.