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.
ABCDEF3 characters: e.g.
ABC(equivalent toAABBCC)
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).
Extract the first valid substring from left to right using regex:
/[0-9A-F]{1,6}/
Matches 1–6 valid hex characters.
If no match is found, return an empty string to trigger revert to last valid value.
If matched, process based on length:
Length = 6: return directly.
Length = 4 or 5: truncate to 3 characters (or pad with 0 to 6).
Length = 3: expand
ABC→AABBCC.Length = 1 or 2: repeat to fill 6 characters:
AB → ABABAB, A → AAAAAA.
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);
};