Convert hex to RGB

Hello,

Maybe someone can help me with this specific task.

I’m wondering how can I convert a hex color to a slice of integers.

input: #ffffff
output: 255,255,255

I need to do it in a template file (html), not in a SCSS file.

I need it to compare colors and decide if a color is suitable or not.

Any help would be appreciated.

I am not aware of a way to perform such a conversion directly in Hugo.

1 Like

Hi! I guess JavaScript is a good option here.

1 Like

That would be an option, but would be my last resort.

Anyway, now I have this partial:

{{ $c := . }}
{{ $conv := slice "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c" "d" "e" "f" }}
{{ $val := 0 }}
{{ $out := slice }}
{{ range $i, $c := ( slice (substr $c 1 1) (substr $c 2 1) (substr $c 3 1) (substr $c 4 1) (substr $c 5 1) (substr $c 6 1) ) }}
  {{ $mod := mod $i 2 }}
  {{ $dec := 0 }}
  {{ range $j, $v := $conv }}
    {{ if eq $v $c }}{{$dec = $j}}{{ end }}
  {{ end }}
  {{ $dec }}
  {{ $val = add $val ( mul $dec ( cond (eq $mod 0) 16 1 ) ) }}
  {{ if $mod }}{{ $out = $out | append $val}}{{ $val = 0}}{{end}}
{{ end }}
{{ return $out }}

And this is how I use it:

{{ partial "color/hex-to-rgb" "#bbbbbb" }}

But I don’t like this solution. I’m hoping someone can help me find a better one. Or at least make mine better.

2 Likes

Why ? It works, no ?

And I’ll borrow this, nice partial for my use case. Thanks.

It’s slow - if you run it 2000 times it will increase your build by near a second.
But you can use partialCached if your use case benefits from it.

But yes, it works.

I’m glad you liked it!

you could directly split the string, and use after to exclude the # character.

--{{ $c := . }}
{{ $conv := slice "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c" "d" "e" "f" }}
{{ $val := 0 }}
{{ $out := slice }}
++{{ range $i, $c := after 1 (split . "")  }}
--{{ range $i, $c := ( slice (substr $c 1 1) (substr $c 2 1) (substr $c 3 1) (substr $c 4 1) (substr $c 5 1) (substr $c 6 1) ) }}
  {{ $mod := mod $i 2 }}
  {{ $dec := 0 }}
  {{ range $j, $v := $conv }}
    {{ if eq $v $c }}{{$dec = $j}}{{ end }}
  {{ end }}
  {{ $dec }}
  {{ $val = add $val ( mul $dec ( cond (eq $mod 1) 16 1 ) ) }}
  {{ if $mod }}{{ $out = $out | append $val}}{{ $val = 0}}{{end}}
{{ end }}
{{ return $out }}

i dont know it’s going to improve the speed or not.

1 Like

What you’re doing is probably the best approach today. I would definitely use partialCached for this.

Would it be helpful if we added a hexDecode function? {{ $r := hexDecode "c0" }}

As shown by the OP there is a need for such a function. So +1.

The speed is almost the same, but it’s cleaner and works for colors with alpha like #ffffffff
But using a map in the conversion improved the performance.
Here is the updated version and thanks for the suggestion!

{{ $convMap := dict "0" 0 "1" 1 "2" 2 "3" 3 "4" 4 "5" 5 "6" 6 "7" 7 "8" 8 "9" 9 "a" 10 "b" 11 "c" 12 "d" 13 "e" 14 "f" 15 }}
{{ $val := 0 }}
{{ $out := slice }}
{{ range $i, $c := split (substr . 1) "" }}
  {{ $mod := mod $i 2 }}
  {{ $dec := index $convMap $c }}
  {{ $val = add $val ( mul $dec ( cond (eq $mod 0) 16 1 ) ) }}
  {{ if $mod }}{{ $out = $out | append $val}}{{ $val = 0}}{{end}}
{{ end }}
{{ return $out }}
3 Likes

Thanks for your help.

Yes, the function hexDecode would help! So +1 for that.

But I’m wondering if a more specialized function, related to colors - would also be interesting.

My use case is very specific, I’m not sure if this would create unnecessary noise.

Let me explain my use case:

I’m using Bootstrap in my theme. An editor can change the Bootstrap primary color variable in a Data file (theme.yaml).

Some sections of my website use the primary or secondary color as background. I want to know if it’s safe to use a btn-primary, btn-secondary or btn-danger in a specific section. If not, I want to decide which class would be better to use as a fallback, btn-light or btn-dark.

The logic is the same of this Bootstrap SCSS function:

@function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) {
  $r: red($color);
  $g: green($color);
  $b: blue($color);

  $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;

  @if ($yiq >= $yiq-contrasted-threshold) {
    @return $dark;
  } @else {
    @return $light;
  }
}

Adding color-specific template functions is a bit of a stretch. Let’s stick with hex encoding enhancements for now.

I have a working implementation in a dev branch, and I’m seeing about a 10x improvement in speed versus your partial above. I’ll try to get a PR submitted in the few days.

PS - Your partial has a bug. Use (eq $mod 0) instead of (eq $mod 1).

1 Like

In my efforts to learn Go I worked on a colour palette generator app a couple of years ago - the code is horrible but it’s here - Palegen

It relies on the go-colorful library which may be worth a look if you want to manipulate colours in Go.

(Wish I’d had the time and energy to push on and polish it, but it’s getting eclipsed by improvements in CSS and now it seems Hugo as well :))

Hi,

regarding you project PALEGEN
do you think that could help doing something like this:

{{- $hexColor := "#FF5733" -}}
{{- $hslColor := hexToHSL $hexColor -}}
<div>
  <div style="background-color: {{ $hexColor }};">
    Hex: {{ $hexColor }}<br>
    HSL: {{ $hslColor }}
  </div>
</div>

directly in hugo?

obvious by adding some special fuction

I’m using something like here in order to run SCSS calculation:

colors.scss

{{ partial "scratching/images.html" . }}
{{ $colors := $.Scratch.Get "_colors" }}

@mixin text-contrast($n) {
  $color-brightness: round((red($n) * 299) + (green($n) * 587) + (blue($n) * 114) / 1000);
  $light-color: round((red(#ffffff) * 299) + (green(#ffffff) * 587) + (blue(#ffffff) * 114) / 1000);
  
  @if abs($color-brightness) < ($light-color/2){
    --fg: white;
  }

  @else {
    --fg: black;
  }
}


body {
  --test: {{ $colors }};
  {{ range first 1 $colors }}
  @include text-contrast({{ . }});
  --bg: {{ . }};
  {{ end }}
}

then in template:

{{ $scssTemplate := resources.Get "scss/colors.scss" }}

  {{ $vars := $scssTemplate | resources.ExecuteAsTemplate "var.colors.scss" . | toCSS | minify | fingerprint }}

<style data-colors>
{{ $vars.Content | safeCSS }}
</style>

are you on the same page?

Not Palegen, no. But Palegen uses go-colorful to manipulate colours and that might be helpful for what you are trying to do. But I have no idea how you would call an external library.

Sass (as you have) or PostCSS seem better suited to the task and are already working in Hugo.

I have no idea about the performance implications, but I find converting to rgb values quite easy in Hugo using the int function and some printf/substr fidgeting:

{{ $hex := "#f5f5f5" }}
{{ $rgb := printf "rgb(%d,%d,%d)" (printf "0x%s" (substr $hex 1 2) | int) (printf "0x%s" (substr $hex 1 2) | int) (printf "0x%s" (substr $hex 1 2) | int) }}

gives you rgb(245,245,245). A hexadecimal number is just a number, right?

Am I missing something or doing something really silly here? (Better software engineers than me: have at it!)

This approach is fine, but you need to change the substr args.

{{ $rgb := printf "rgb(%d,%d,%d)" (printf "0x%s" (substr $hex 1 2) | int) (printf "0x%s" (substr $hex 3 2) | int) (printf "0x%s" (substr $hex 5 2) | int) }}

Note that performance isn’t great. To compare…

  • 4M iterations of the above: 22s
  • 4M iterations of a single printf statement: 4s

If this were a common need (which it isn’t) we’d probably want to implement a few functions in a colors namespace (e.g., colors.ToRGB, colors.ToRGBA, colors.IsLight, colors.IsDark, etc.).

1 Like