tailwindcolorshades icon indicating copy to clipboard operation
tailwindcolorshades copied to clipboard

Real-World Color Variations

Open tleish opened this issue 3 years ago • 4 comments

What are your thoughts about enhancing this tool to support Real-World Color Variations as described in Why Color Theory Sucks (& what framework to use instead)?

tleish avatar Feb 25 '21 16:02 tleish

FYI, with some adjusted code I have the following: image

tleish avatar Feb 25 '21 19:02 tleish

Oh that looks really interesting, i havent seen that before but I'm open to give it a try. Thanks for sharing, I'll take a look in some days (busy times!) like tomorrow or the weekend and see how/if implement this, thank you!

javisperez avatar Feb 25 '21 20:02 javisperez

Using color-convert you can do the following code:

// color.ts
import convert from 'color-convert'

const CMY_HUES = [180, 300, 60];
const RGB_HUES = [360, 240, 120, 0];

function hueShift(hues: Array<number>, hue: number, intensity: number) {
  const closestHue = hues.sort((a, b) => (Math.abs(a - hue) - Math.abs(b - hue)))[0],
    hueShift = closestHue - hue;
  return Math.round(intensity * hueShift * 0.5);
}

function realWorldLighten(hex: string, intensity: number): string {
  const color = hexToRgb(`#${hex}`);

  if (!color) {
    return "";
  }

  const [h, s, v] = convert.hex.hsv(hex),
    hNew = h + hueShift(CMY_HUES, h, intensity),
    sNew = s - Math.round(s * intensity),
    vNew = v + Math.round((100 - v) * intensity);

  return `#${convert.hsv.hex([hNew, sNew, vNew])}`;
}

function realWorldDarken(hex: string, intensity: number): string {
  const inverseIntensity =  (1 - intensity);
  const [h, s, v] = convert.hex.hsv(hex),
    hNew = h + hueShift(RGB_HUES, h, inverseIntensity),
    sNew = s + Math.round((100 - s) * inverseIntensity),
    vNew = v - Math.round(v * inverseIntensity);

  return `#${convert.hsv.hex([hNew, sNew, vNew])}`;
}

export default function(baseColor: string, useRealWorld = false): Palette {
  //...

  const lightenMethod = useRealWorld ? realWorldLighten : lighten;
  [50, 100, 200, 300, 400].forEach(level => {
    response.colors[level] = lightenMethod(baseColor, intensityMap[level]);
  });

  const darkenMethod = useRealWorld ? realWorldDarken : darken;
  [600, 700, 800, 900].forEach(level => {
    response.colors[level] = darkenMethod(baseColor, intensityMap[level]);
  });

  return response as Palette;
}


tleish avatar Feb 25 '21 22:02 tleish

second this! would be lovely to add :)

sarahsau avatar Jun 11 '21 09:06 sarahsau

This is now in prod. Thanks @tleish for your code, it worked beautifully so I basically just used it with minor adjustments.

This is the only generation method now btw, made more sense to me to just have this instead of making it optional, because when compared with a few colors (including Tailwind's defaults) it was way better than the previous version.

javisperez avatar Jun 05 '23 19:06 javisperez

I see you used this color shade strategy by default, instead of as an opt-in.

Tested the prod, works great.

tleish avatar Jun 05 '23 19:06 tleish

I see you used this color shade strategy by default, instead of as an opt-in.

Tested the prod, works great.

Yeah, I forgot to mention this in my original reply. I edited and added the note. It made more sense to me really. Sorry for the delay but it works great. I also didn't know the color-convert library, that's an amazing library.

javisperez avatar Jun 05 '23 19:06 javisperez