Plugins
FieldsColor

Color utilities

The zero-dependency color engine behind the field, exported for frontend use.

@10x-media/fields/color/utils is the parser and converter the field itself runs, exported standalone. It has zero dependencies and no Payload imports, so it is safe in any bundle: server, client, worker.

import {
  parseColor,
  formatColor,
  convertColor,
  contrastRatio,
  relativeLuminance,
  lightDark,
  isColorSchemeValue,
  parsePresetReference,
} from '@10x-media/fields/color/utils'

Parse and format

const parsed = parseColor('rgb(59, 130, 246)')
// { mode: 'rgb', r: 59, g: 130, b: 246, alpha: 1 }, or null for invalid input

if (parsed) {
  formatColor(parsed, 'hex')    // '#3b82f6'
  formatColor(parsed, 'hsl')    // 'hsl(217 91% 60%)'
  formatColor(parsed, 'oklch')  // 'oklch(0.623 0.214 259.1)'
}

convertColor('#3b82f6', 'rgb')  // 'rgb(59 130 246)' (parse + format in one step; null on bad input)

parseColor accepts everything the field's text input accepts: hex 3/4/6/8, rgb()/rgba(), hsl()/hsla(), oklch(), named colors, transparent. Invalid input returns null; nothing throws. Parsed colors carry a mode (rgb or oklch) and an alpha channel. Formatted output uses modern space-separated CSS syntax.

Conversions from OKLCH map out-of-gamut colors into sRGB by reducing chroma, preserving hue and lightness, rather than clamping channels.

Contrast

contrastRatio('#0f172a', '#ffffff')  // ~18
relativeLuminance('#3b82f6')         // ~0.24

Both accept a CSS string or a parsed color. contrastRatio implements the WCAG 2.x relative-luminance formula. A common frontend use, picking a readable text color for a stored background:

const textColor = contrastRatio(doc.accent, '#ffffff') >= 4.5 ? '#ffffff' : '#0f172a'

Color schemes

lightDark('#3b82f6')                             // '#3b82f6'
lightDark({ light: '#ffffff', dark: '#000000' }) // 'light-dark(#ffffff, #000000)'

isColorSchemeValue(doc.accentResolved)  // narrows to { light: string; dark: string }

lightDark accepts either shape a resolved linked field can hold, so it works whichever resolve mode a field uses. The two-argument CSS output needs color-scheme: light dark on the element or an ancestor; without it, browsers resolve it as light.

isColorSchemeValue is the type guard for narrowing a string | ColorSchemeValue union, which is what preset values and resolved siblings hold.

Preset references

parsePresetReference('preset:brand/40')     // { key: 'brand', alpha: 40 }
parsePresetReference('preset:acme/surface') // { key: 'acme/surface', alpha: 100 }
parsePresetReference('#3b82f6')             // null

parsePresetReference splits a linked field's stored value into its preset key and opacity. It returns null for anything that is not a reference; alpha defaults to 100 when the value carries no suffix. The suffix is read off the last slash and only counts as an alpha when it is an integer 0 to 100, so slash-containing keys parse correctly. Use it wherever presentation code maps stored references onto CSS custom properties instead of regexing the raw string.

Guarantees

The engine is unit-tested against reference conversion vectors, and the field's server validate uses these exact functions. For concrete color values, if parseColor accepts a value the field accepts it; if it returns null, the field rejects it. Linked preset references like preset:brand never pass through parseColor; they follow the reference grammar above and the validation rules described in linked mode.

On this page