200ok-ch/organice

View on GitHub
src/lib/color.js

Summary

Maintainability
D
2 days
Test Coverage
// Interpolates between two colors.
// colorA and colorB should be objects with keys {r, g, b, a}.
// interpolationFactor should be a number between 0 and 1 representing how far from colorA to
// colorB it should interpolate.
// An object with keys {r, g, b, a} will be returned.
export const interpolateColors = (colorA, colorB, interpolationFactor) => {
  return {
    r: parseInt((colorB.r - colorA.r) * interpolationFactor + colorA.r, 10),
    g: parseInt((colorB.g - colorA.g) * interpolationFactor + colorA.g, 10),
    b: parseInt((colorB.b - colorA.b) * interpolationFactor + colorA.b, 10),
    a: (colorB.a - colorA.a) * interpolationFactor + colorA.a,
  };
};

export const rgbaObject = (r, g, b, a) => {
  return { r, g, b, a };
};

export const rgbaString = (rgba) => {
  return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`;
};

// assumes var is either a longform-hex or rgb(a) color value
export const readRgbaVariable = (varName) => {
  const varValue = getComputedStyle(document.documentElement).getPropertyValue(varName);
  if (varValue.charAt(0) === '#') {
    const hexValue = varValue.substring(1, 7);
    return rgbaObject(
      parseInt(hexValue.substring(0, 2), 16),
      parseInt(hexValue.substring(2, 4), 16),
      parseInt(hexValue.substring(4, 6), 16),
      1
    );
  } else {
    const rgbaValues = [...varValue.matchAll(/[0-9.]+/g)].map((a) => +a[0]);
    if (rgbaValues.length === 3) {
      return rgbaObject(...rgbaValues, 0);
    } else if (rgbaValues.length === 4) {
      return rgbaObject(...rgbaValues);
    } else {
      return rgbaObject(0, 0, 0, 0);
    }
  }
};

const themes = {
  Solarized: {
    /* 
      https://ethanschoonover.com/solarized/ 
    */
    Light: {
      '--base3': '#fdf6e3',
      '--base2': '#eee8d5',
      '--base1': '#93a1a1',
      '--base0': '#839496',
      // highlights
      '--base00': '#657b83',
      '--base01': '#586e75',
      '--base02': '#073642',
      '--base03': '#002b36',
      // shadows
      '--base0-soft': 'rgba(131, 148, 150, 0.75)',
      // highlighted backgrounds
      '--base1-soft': 'rgba(147, 161, 161, 0.4)',
      // header colors
      '--blue': '#268bd2',
      '--green': '#859900',
      '--cyan': '#2aa198',
      '--yellow': '#b58900',
      // additional colors
      '--orange': '#cb4b16',
      '--red': '#dc322f',
      '--magenta': '#d33682',
      '--violet': '#6c71c4',
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
    Dark: {
      // backgrounds
      '--base3': '#002b36',
      '--base2': '#073642',
      '--base1': '#586e75',
      '--base0': '#657b83',
      // highlights
      '--base03': '#fdf6e3',
      '--base02': '#eee8d5',
      '--base01': '#93a1a1',
      '--base00': '#839496',
      // shadows
      '--base0-soft': 'rgba(0, 21, 27, 0.75)',
      // highlighted backgrounds
      '--base1-soft': 'rgba(0, 21, 27, 0.4)',
      // header colors
      '--blue': '#268bd2',
      '--green': '#859900',
      '--cyan': '#2aa198',
      '--yellow': '#b58900',
      // additional colors
      '--orange': '#cb4b16',
      '--red': '#dc322f',
      '--magenta': '#6c71c4',
      '--violet': '#d33682',
      // table highlight
      '--green-soft:': 'rgba(133, 153, 0, 0.28)',
    },
  },
  Gruvbox: {
    Light: {
      /*
        see https://github.com/morhetz/gruvbox for palette
        this theme uses the 'faded' version of the color palette
      */
      // background and highlights
      '--base3': '#fbf1c7', // bg0
      '--base2': '#d5c4a1', // bg2
      '--base1': '#bdae93', // bg3
      '--base0': '#a89984', // bg4
      '--base00': '#7c6f64', // fg4
      '--base01': '#665c54', // fg3
      '--base02': '#504945', // fg2
      '--base03': '#282828', // fg0
      // transparent versions
      '--base0-soft': 'rgba(168, 153, 132, 0.75)',
      '--base1-soft': 'rgba(189, 174, 147, 0.4)',
      // header colors
      '--blue': '#076678',
      '--green': '#79740e',
      '--cyan': '#427b58',
      '--yellow': '#b57614',
      // additional colors
      '--orange': '#af3a03',
      '--red': '#9d0006',
      '--magenta': '#8f3f71', // faded purple
      '--violet': '#b16286', // neutral purple
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
    Dark: {
      /*
        see https://github.com/morhetz/gruvbox for palette
        this theme uses the 'neutral' version of the color palette
      */
      // background and highlights
      '--base3': '#282828', // bg0
      '--base2': '#504945', // bg2
      '--base1': '#665c54', // bg3
      '--base0': '#7c6f64', // bg4
      '--base00': '#a89984', // fg4
      '--base01': '#bdae93', // fg3
      '--base02': '#d5c4a1', // fg2
      '--base03': '#fbf1c7', // fg0
      // transparent versions
      '--base0-soft': 'rgba(20, 20, 20, 0.75)',
      '--base1-soft': 'rgba(20, 20, 20, 0.4)',
      // header colors
      '--blue': '#458588',
      '--green': '#98971a',
      '--cyan': '#689d6a',
      '--yellow': '#d79921',
      // additional colors
      '--orange': '#d65d0e',
      '--red': '#cc241d',
      '--magenta': '#b16286', // neutral purple
      '--violet': '#d3869b', // bright purple
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
  },
  One: {
    Light: {
      /*
        adaptation of atom one light theme
        https://github.com/atom/atom/tree/master/packages/one-light-ui
      */
      // background and highlights
      '--base03': '#383A42',
      '--base02': '#585A5F',
      '--base01': '#79797C',
      '--base00': '#999999',
      '--base0': '#A0A1A7',
      '--base1': '#C0C0C4',
      '--base2': '#DFE0E2',
      '--base3': '#FFFFFF',
      // transparent versions
      '--base0-soft': 'rgba(160, 161, 167, 0.75)',
      '--base1-soft': 'rgba(192, 192, 196, 0.4)',
      // header colors
      '--blue': '#1492ff',
      '--green': '#2db448',
      '--cyan': '#D831B0', // pink
      '--yellow': '#d5880b',
      // additional colors
      '--orange': '#f42a2a', //red
      '--red': '#f42a2a',
      '--magenta': 'hsl(208, 100%, 56%)', // blue
      '--violet': '#D831B0', // pink
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
    Dark: {
      /*
        adaptation of atom one dark theme
        https://github.com/atom/atom/tree/master/packages/one-dark-ui
      */
      // background and highlights
      '--base03': '#abb2bf',
      '--base02': '#9198A5',
      '--base01': '#767D8A',
      '--base00': '#5c6370',
      '--base0': '#4c5263',
      '--base1': '#404553',
      '--base2': '#343944',
      '--base3': '#282c34',
      // transparent versions
      '--base0-soft': 'rgba(20, 22, 26, 0.75)',
      '--base1-soft': 'rgba(20, 22, 26, 0.4)',
      // header colors
      '--blue': 'rgb(100, 148, 237)',
      '--green': 'rgb(115, 201, 144)',
      '--cyan': 'rgb(204, 133, 51)', //orange
      '--yellow': 'rgb(226, 192, 141)',
      // additional colors
      '--orange': 'rgb(255, 99, 71)', //red
      '--red': '#D831B0', //pink
      '--magenta': 'rgb(0, 136, 255)', //blue
      '--violet': '#d33682',
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
  },
  Smyck: {
    /*
      see http://color.smyck.org/ for palette
    */
    Light: {
      // background and highlights
      '--base3': '#F7F7F7',
      '--base2': '#DFDFDF',
      '--base1': '#C8C8C8',
      '--base0': '#B0B0B0',
      '--base00': '#5D5D5D',
      '--base01': '#3E3E3E',
      '--base02': '#1F1F1F',
      '--base03': '#000000',
      // transparent versions
      '--base0-soft': 'rgba(176, 176, 176, 0.75)',
      '--base1-soft': 'rgba(200, 200, 200, 0.4)',
      // header colors
      '--blue': '#72B3CC',
      '--green': '#8EB33B',
      '--cyan': '#218693',
      '--yellow': '#D0B03C',
      // additional colors
      '--orange': '#C75646',
      '--red': '#E09690',
      '--magenta': '#77DFD8',
      '--violet': '#FBB1F9',
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
    Dark: {
      // background and highlights
      '--base3': '#000000',
      '--base2': '#1F1F1F',
      '--base1': '#3E3E3E',
      '--base0': '#5D5D5D',
      '--base00': '#B0B0B0',
      '--base01': '#C8C8C8',
      '--base02': '#DFDFDF',
      '--base03': '#F7F7F7',
      // transparent versions
      '--base0-soft': 'rgba(31, 31, 31, 0.75)',
      '--base1-soft': 'rgba(31, 31, 31, 0.4)',
      // header colors
      '--blue': '#72B3CC',
      '--green': '#8EB33B',
      '--cyan': '#218693',
      '--yellow': '#D0B03C',
      // additional colors
      '--orange': '#C75646',
      '--red': '#E09690',
      '--magenta': '#77DFD8',
      '--violet': '#FBB1F9',
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
  },
  Code: {
    Light: {
      /*
        a rough approximation of Light+ (default light theme of VS Code)
        https://github.com/microsoft/vscode/blob/master/extensions/theme-defaults/themes/dark_plus.json
      */
      // backgrounds
      '--base3': '#FFFFFF',
      '--base2': '#DBDBDB',
      '--base1': '#B6B6B6',
      '--base0': '#929292',
      // highlights
      '--base00': '#6D6D6D',
      '--base01': '#494949',
      '--base02': '#242424',
      '--base03': '#000000',
      // shadows
      '--base0-soft': 'rgba(146, 146, 146, 0.75)',
      // highlighted backgrounds
      '--base1-soft': 'rgba(172, 172, 172, 0.4)',
      // header colors
      '--blue': '#0000ff',
      '--green': '#098658',
      '--cyan': '#267f99',
      '--yellow': '#795e26',
      // additional colors
      '--orange': '#ee0000',
      '--red': '#a31515',
      '--magenta': '#0070c1',
      '--violet': '#811f3f',
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
    Dark: {
      /*
        a rough approximation of Dark+ (default dark theme of VS Code)
        https://github.com/microsoft/vscode/blob/master/extensions/theme-defaults/themes/dark_plus.json
      */
      // backgrounds
      '--base3': '#1E1E1E',
      '--base2': '#2C2C32',
      '--base1': '#303036',
      '--base0': '#34343a',
      // highlights
      '--base00': '#C8C8C8',
      '--base01': '#cccccc',
      '--base02': '#D0D0D0',
      '--base03': '#D4D4D4',
      // shadows
      '--base0-soft': 'rgba(15, 15, 15, 0.75)',
      // highlighted backgrounds
      '--base1-soft': 'rgba(15, 15, 15, 0.4)',
      // header colors
      '--blue': 'rgb(79,193,255)',
      '--green': 'rgb(106, 153, 85)',
      '--cyan': '#ce9178', // orange (cyan is #4EC9B0)
      '--yellow': '#d7ba7d',
      // additional colors
      '--orange': '#d16969', // red
      '--red': '#d16969',
      '--magenta': '#569cd6', // light blue
      '--violet': '#C586C0',
      // table highlight
      '--green-soft': 'rgba(133, 153, 0, 0.28)',
    },
  },
};

export const loadTheme = (theme = 'Solarized', colorScheme = 'Light') => {
  if (colorScheme === 'OS') {
    const osPreference = window.matchMedia('(prefers-color-scheme: dark)');
    if ('matches' in osPreference) {
      colorScheme = osPreference.matches ? 'Dark' : 'Light';
    } else {
      colorScheme = 'Light';
    }
  }
  const style = document.documentElement.style;
  Object.entries(themes[theme][colorScheme]).forEach(([k, v]) => style.setProperty(k, v));

  // set theme color on android
  document
    .querySelector('meta[name="theme-color"]')
    .setAttribute('content', themes[theme]['--base3']);
};