Standardizing Color Hex Values

Jul 19, 2023 · 2min

We have a requirement: when a user enters a hex color value into an input field, automatically convert it to a valid 6-character hex string.

If the value is invalid, revert to the last valid input.

Hex Color Rules

The standard rules for hex colors are:

Valid characters: 0-9, a-f, A-F.

Valid lengths:

  • 6 characters: e.g. ABCDEF

  • 3 characters: e.g. ABC (equivalent to AABBCC)

Handling User Input

User input is unpredictable and often invalid. Simply rejecting invalid input and reverting immediately is not good for user experience.

For better UX, we optimize the input on user confirmation (e.g., pressing Enter):

Convert to uppercase (for consistent formatting).

  1. Extract the first valid substring from left to right using regex: /[0-9A-F]{1,6}/

  1. Matches 1–6 valid hex characters.

  2. If no match is found, return an empty string to trigger revert to last valid value.

  3. If matched, process based on length:

  4. Length = 6: return directly.

  5. Length = 4 or 5: truncate to 3 characters (or pad with 0 to 6).

  6. Length = 3: expand ABCAABBCC.

  7. Length = 1 or 2: repeat to fill 6 characters:

ABABABAB, AAAAAAA.

Example:

User inputs #0 → we return 000000 instead of rejecting input.

Implementation

const normalizeHex = (hex: string) => {
  // 1. Convert to uppercase
  hex = hex.toUpperCase();

  // 2. Extract first valid hex substring
  const match = hex.match(/[0-9A-F]{1,6}/);

  if (!match) {
    return '';
  }

  hex = match[0];

  // 3. Handle 6-digit valid case
  if (hex.length === 6) {
    return hex;
  }

  // 4. Truncate 4/5 digits to 3
  if (hex.length === 4 || hex.length === 5) {
    hex = hex.slice(0, 3);
  }

  // 5. Expand 3-digit: ABC → AABBCC
  if (hex.length === 3) {
    return hex
      .split('')
      .map((c) => c + c)
      .join('');
  }

  // 6. Expand 1/2-digit: AB → ABABAB, A → AAAAAA
  return hex.padEnd(6, hex);
};