How to Generate Material 3 Color Palettes in JS/SCSS?
Asked Answered
T

2

5

Background:

I'm trying to generate a color palette in SASS/JS based on Google's Material Theme Builder 3 which requires tonal palette's to be generated based on relative luminance and not lightness/brightness function.

enter image description here

Problem:

I can get the luminance value using the following function in SCSS:

@function get-luminance($color) {
    $colors: (
        'red': red($color),
        'green': green($color),
        'blue': blue($color),
    );

    @each $name, $value in $colors {
        $adjusted: 0;
        $value: $value / 255;

        @if $value < 0.03928 {
            $value: $value / 12.92;
        } @else {
            $value: ($value + 0.055) / 1.055;
            $value: math.pow($value, 2.4);
        }

        $colors: map-merge($colors, ($name: $value));
    }

    @return (map-get($colors, 'red') * 0.2126) + (map-get($colors, 'green') * 0.7152) + (map-get($colors, 'blue') * 0.0722);
}

But what I'm looking for is to create a function that can adjust a certain color's luminance e.g.

@function adjust-luminance($color, $luminance-value) {
    // Calculations required to adjust luminance here
    @return $adjusted-luminance-color;
}

$seed-color: #6750A4;

.color-tone-99 {
    background: adjust-luminance($seed-color, 97.4); // Output: #FFFBFE
}

I haven't been able to figure out the calculations part above. I've also come across this Color Luminance Figma plugin that does it in Figma but how does it do it is the question.

Any help would be highly appreciated!

Thanks

Twopenny answered 12/12, 2021 at 13:22 Comment(0)
T
9

Material 3 uses a new system called HCT:

It turns out James O'Leary, Color Scientist, Platforms & Ecosystems at Google developed a brand new, perceptually accurate, color system is called HCT, which stands for hue, chroma, tone.

He has written a great article explaining why and how it was created here: The Science of Color & Design.

The Material Foundation has also released their codebase in TypeScript here: Material Color Utilities GitHub Repo.

How To Generate Material 3 Color Palette in JavaScript:

1. Install the Material Color Utilities via NPM:

npm i @material/material-color-utilities

2. Import the Utilities:

import { argbFromHex, hexFromArgb, themeFromSourceColor, TonalPalette } from '@material/material-color-utilities';

3. Getting Theme JSON:

const m3ThemeColorsJSON = themeFromSourceColor(argbFromHex('#6750A4'), []);

4. This will return a JSON map of ARGB colors, to convert them to HEX colors, use the hexFromArgb() function. Example:

const primary = hexFromArgb(theme.schemes.light.primary);

5. If you want to get a color from the Tonal Palette, use the following:

const primary98 = TonalPalette.fromInt(primary).tone(98);

Support in SCSS:

The Material Foundation intends to release a version in SCSS in the future but anybody can use this in JavaScript to generate an SCSS file in the meantime.

Twopenny answered 22/7, 2022 at 9:0 Comment(3)
Are there already ports of this code? I am thinking of generating the palette based on a base color on Android.Encipher
@david you can check the official repo, it has java utils: github.com/material-foundation/material-color-utilities/tree/…Twopenny
I think there isn’t a way to directly modify luminance per se, but getting different colors from the tonal (T in HCT) palette this way will give you lighter or darker (or seemingly illuminated or less illimunated) colors. I posted an answer about modifying the T (or H or C) values of a color in HCT space directly.Civilian
C
0

I believe the tonal palettes generated by Material Design 3 are created via the HCT color space. You may get something similar to what you ask for (changing luminance of a color) by modifing the (T)one value of a color in that space.

The following tests roundtrip conversion from an sRGB hex color code to HCT color space and back, and also modifies the resulting HCT values and converts those back to sRGB hex. (Doing the roundtrip conversion first is to demonstrate consistency of the return-trip value to the original without modifying the HCT values.) It uses the npm (nodejs-installable) library @material/material-color-utilities (which uses HCT space). My angle on doing this is demonstrating from a script invoked from CLI. I hope it might be adaptable for your purposes.

DEPENDENCIES: nodejs and the material-color-utilities package installed, either locally (from a terminal, run) : npm install -g @material/material-color-utilities -- or locally in a project folder you make: npm install @material/material-color-utilities

For this use case I had to override the package.json of package/@material/material-color-utilities (where it is installed) to look like this:

{
  "dependencies": {
    "@material/material-color-utilities": "^0.2.7",
    "type": "module"
  }
}

--adding that "type": "module" line -- and then save the following code to a file named (e.g.) sRGB_to_HCT_to_sRGB.mjs

NOTE: the file must have the .mjs extension for this to work.*

file/code: sRGB_to_HCT_to_sRGB.mjs:

import { Hct } from "@material/material-color-utilities"

// Hct.fromInt will accept a hex value in format a r g b (eg 0xff4285f4) OR just rgb (e.g. 4285f4) :

var originalValue = 0x8fe5b9

console.log("original sRGB values, hex format:\t", originalValue.toString(16))

var color = Hct.fromInt(originalValue)

console.log("HCT values:")
console.log("Hue:", color.hue)
console.log("Chroma:", color.chroma)
console.log("Tone:", color.tone)

var reconstructed = Hct.from(
    color.hue,
    color.chroma,
    color.tone,
).toInt().toString(16).slice(2)

console.log("Reconstructed sRGB values, hex format:\t", reconstructed)

// MODIFYING VALUES IN HCT SPACE:
color.hue +=1
color.chroma += 2
color.tone += 3

console.log("\nModded HCT values:")
console.log("Hue:", color.hue)
console.log("Chroma:", color.chroma)
console.log("Tone:", color.tone)

// becomes after convert back to sRGB: 91efc1:
reconstructed = Hct.from(
    color.hue,
    color.chroma,
    color.tone,
).toInt().toString(16).slice(2)

console.log("~Modded sRGB values, hex format:\t", reconstructed)

To run the code from e.g. a terminal, open a terminal to the path with the file, and run:

node sRGB_to_HCT_to_sRGB.mjs

Output:

original sRGB values, hex format:        8fe5b9
HCT values:
Hue: 164.34413232095446
Chroma: 41.27889003143768
Tone: 84.67970676233278
Reconstructed sRGB values, hex format:   8fe5b9

Modded HCT values:
Hue: 165.11479101939187
Chroma: 43.531612419379805
Tone: 87.77714825778628
~Modded sRGB values, hex format:         91efc1

Modification of values in HCT space would probably best be done within the min/max ranges allowed for (H)ue, (C)hroma, (T)one. Wherever there is reference about that. Looking that up is my next step for my project :)

Pieced together and adapted from:

*FEEDBACK TO THE NODEJS project: it is extremely janky to make it necessary to hack a package like that just to import a module for CLI use. You should re-introduce the CLI switch lovingly and carefully begged for by many developers for this purpose, and provide CLI feedback suggesting use of that switch if you detect a user may wish to. The research and hacking effort involved just to get it working (and figure out how to!) for this very common use case (CLI script) was very excessive. You made it way too difficult.

Civilian answered 4/1 at 5:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.