Skip to content

Instantly share code, notes, and snippets.

@ErickPetru
Last active March 16, 2023 22:28
Show Gist options
  • Save ErickPetru/d56a698f6600d19697181fa475671ce5 to your computer and use it in GitHub Desktop.
Save ErickPetru/d56a698f6600d19697181fa475671ce5 to your computer and use it in GitHub Desktop.
Extract CSS custom properties (CSS variables) from Tailwind Color Palette by Web scrapping its documentation
/* Extract CSS custom properties (CSS variables) from Tailwind Color Palette
* Navigate to: https://tailwindcss.com/docs/customizing-colors
* Then open browser's console and run the following code
*/
const palette = [
...document
.querySelector("#content-wrapper > .grid.grid-cols-1")
.querySelectorAll(".flex.flex-col.space-y-3"),
]
.map((row) => ({
color: row
.querySelector(".w-16.shrink-0")
.textContent.toString()
.toLowerCase(),
shades: [...row.querySelectorAll(".relative.flex > .cursor-pointer")].map(
(color) => ({
shade: color.querySelector(".font-medium").textContent.toString(),
rgb: color
.querySelector(".h-10")
.style.backgroundColor.replace(/[rgb|(|)|,]/g, "")
.replace(")", ""),
hex: color
.querySelector(".font-mono")
.textContent.toString()
.toLowerCase(),
})
),
}))
.reduce(
(acc, curr) => ({
...acc,
[curr.color]: curr.shades.reduce(
(acc2, curr2) => ({
...acc2,
[curr2.shade]: { rgb: curr2.rgb, hex: curr2.hex },
}),
{}
),
}),
{}
);
console.log(
Object.keys(palette)
.map(
(color) =>
`/* ${color[0].toUpperCase()}${color.substr(1)} */\n` +
Object.keys(palette[color])
.map(
(shade) =>
`--color-${color}-${shade}-rgb: rgb(${palette[color][shade].rgb});\n--color-${color}-${shade}-hex: ${palette[color][shade].hex};`
)
.join("\n")
)
.join("\n\n")
);
@ErickPetru
Copy link
Author

Generating a new 950 shade for each color, and outputting a reversed color palette for automatic dark themes:

function getLowestMiddleHighest(rgbArray) {
  let highest = { val: -1, index: -1 };
  let lowest = { val: Infinity, index: -1 };

  rgbArray.forEach((val, index) => {
    if (val > highest.val) highest = { val: val, index: index };
    if (val < lowest.val) lowest = { val: val, index: index };
  });

  if (lowest.index === highest.index) {
    lowest.index = highest.index + 1;
  }

  const middle = { index: 3 - highest.index - lowest.index };
  middle.val = rgbArray[middle.index];
  return [lowest, middle, highest];
}

function darken(color) {
  const rgbArray = [...color.rgb.matchAll(/(\d+),? (\d+),? (\d+)/gi)][0]
    .slice(1)
    .map((n) => parseInt(n, 10));

  const [lowest, middle, highest] = getLowestMiddleHighest(rgbArray);

  if (highest.val === 0) return color;

  const result = [];

  const deviation = highest.val > 50 ? 30.5 : 12.5;
  result[highest.index] = highest.val - Math.min(highest.val, deviation);
  const decreaseFraction = (highest.val - result[highest.index]) / highest.val;
  result[middle.index] = middle.val - middle.val * decreaseFraction;
  result[lowest.index] = lowest.val - lowest.val * decreaseFraction;

  return {
    rgb: `${result.map((n) => Math.round(n).toString(10)).join(" ")}`,
    hex: `#${result.map((n) => n.toString(16).padStart(2, "0")).join("")}`,
  };
}

const palette = [
  ...document
    .querySelector("#content-wrapper > .grid.grid-cols-1")
    .querySelectorAll(".flex.flex-col.space-y-3"),
]
  .map((row) => ({
    color: row
      .querySelector(".w-16.shrink-0")
      .textContent.toString()
      .toLowerCase(),
    shades: [...row.querySelectorAll(".relative.flex > .cursor-pointer")].map(
      (color) => ({
        shade: color.querySelector(".font-medium").textContent.toString(),
        rgb: color
          .querySelector(".h-10")
          .style.backgroundColor.replace(/[rgb|(|)|,]/g, "")
          .replace(")", ""),
        hex: color
          .querySelector(".font-mono")
          .textContent.toString()
          .toLowerCase(),
      })
    ),
  }))
  .reduce(
    (acc, curr) => ({
      ...acc,
      [curr.color]: curr.shades.reduce(
        (acc2, curr2) => ({
          ...acc2,
          [curr2.shade]: { rgb: curr2.rgb, hex: curr2.hex },
        }),
        { 950: darken(curr.shades[curr.shades.length - 1]) }
      ),
    }),
    {}
  );

console.log(
  Object.keys(palette)
    .map(
      (color) =>
        `/* ${color[0].toUpperCase()}${color.substring(1)} */\n` +
        Object.keys(palette[color])
          .map(
            (shade) =>
              `--color-${color}-${shade}: rgb(${palette[color][
                shade
              ].rgb.replaceAll(" ", ", ")});`
          )
          .join("\n")
    )
    .join("\n\n")
);

const reversedShade = {
  50: "950",
  100: "900",
  200: "800",
  300: "700",
  400: "600",
  500: "500",
  600: "400",
  700: "300",
  800: "200",
  900: "100",
  950: "50",
};

console.log(
  Object.keys(palette)
    .map(
      (color) =>
        `/* ${color[0].toUpperCase()}${color.substring(1)} */\n` +
        Object.keys(palette[color])
          .map(
            (shade) =>
              `--color-${color}-${reversedShade[shade]}: rgb(${palette[color][
                shade
              ].rgb.replaceAll(" ", ", ")});`
          )
          .reverse()
          .join("\n")
    )
    .join("\n\n")
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment