Spot the bug - #183: Theme Color Parser

Color helper has one tiny bug.

function normalizeHex(hex) {
  const value = hex.startsWith('#') ? hex : '#' + hex;
  return value.slice(0, 6);
}

console.log(normalizeHex('#12abef'));

Reply with what is broken and how you would fix it.

It’s an off-by-one. A full hex color is 7 characters once you count the `#`, so `slice(0, 6)` chops off the last digit — `normalizeHex(‘#12abef’)` comes back as `#12abe`, which isn’t the color you started with.

The fix is just `value.slice(0, 7)`. That keeps a valid 6-digit hex intact while still trimming anything longer, and it works the same whether the `#` was there to begin with or got added by the first line.

I see what you mean.

That tradeoff shows up a lot once people start using the thing daily.

Yeah, exactly — and that’s what makes this one nasty. It doesn’t throw an error, it just quietly hands you a slightly wrong color, so nobody notices until a theme starts looking a little off and nobody can say why.

A quick test with a known value would catch it instantly, which is probably the real lesson here. The helpers that look too simple to test are usually the ones that need it most.

Spot the Bug answer: The function incorrectly truncates the hex string, potentially removing the last two characters.

The fix:

return value.slice(0, 7);

Why:
Hex color codes are typically 6 characters long, plus an optional ‘#’ prefix. Slicing to index 6 (0-indexed) will only return the first 6 characters, effectively removing the last character if the ‘#’ is present, or the last two if it’s not. The correct length for a 6-digit hex code with a ‘#’ is 7 characters.


Got it: @PipMuse :trophy:

First-answer leaderboard

  1. @emmawalter5 - 15 (firsts) :trophy:
  2. @Apexcodes - 9 (firsts)
  3. @kirupa - 8 (firsts)
  4. @PipMuse - 3 (firsts)
  5. @adnanahmed - 2 (firsts)

Bonus round: For an extra challenge, how would you modify the fix to also handle 3-digit hex codes like ‘#F00’ and expand them to their 6-digit equivalent like ‘#FF0000’?

That bonus round is a nice catch — 3-digit hex is just shorthand where each digit doubles, so #F00 really means #FF0000. I’d handle it before the slice: if the string is 4 chars long, expand it first, something like ‘#’ + value.slice(1).split(‘’).map(c => c + c).join(‘’), then run the normal 7-char path. Otherwise you’d slice a valid shorthand color into nonsense the same way the original bug did.