material-foundation / material-color-utilities

Color libraries for Material You
Apache License 2.0
1.57k stars 134 forks source link

theme helper for new dynamic schemes! #101

Closed Nevro closed 11 months ago

Nevro commented 1 year ago

Currently missing link... I'm not sure if this correct approach, but work for me!

import * as m3utils from '@material/material-color-utilities';
/**
 * All available color tokens 
 */
const tokens = [
    'primary', 'onPrimary', 'primaryContainer', 'onPrimaryContainer', 'inversePrimary', 'inverseOnPrimary',
    'primaryFixed', 'primaryFixedDim', 'onPrimaryFixed', 'onPrimaryFixedVariant',   
    'secondary', 'onSecondary', 'secondaryContainer', 'onSecondaryContainer',
    'secondaryFixed', 'secondaryFixedDim', 'onSecondaryFixed', 'onSecondaryFixedVariant',
    'tertiary', 'onTertiary', 'tertiaryContainer', 'onTertiaryContainer',
    'tertiaryFixed', 'tertiaryFixedDim', 'onTertiaryFixed', 'onTertiaryFixedVariant',
    'error', 'onError', 'errorContainer', 'onErrorContainer',
    'surfaceDim', 'surface', 'surfaceBright',
    'surfaceContainerLowest', 'surfaceContainerLow', 'surfaceContainer', 'surfaceContainerHigh', 'surfaceContainerHighest',
    'onSurface', 'onSurfaceVariant', 'outline', 'outlineVariant',
    'inverseSurface', 'inverseOnSurface',
    'surfaceVariant', 'surfaceTintColor',
    'background', 'onBackground',
    'shadow', 'scrim',
];
/**
 * Generate custom color group from source and target color
 *
 * @param source Source color
 * @param color Custom color
 * @param variant Scheme variant, equal to scheme class name (SchemeMonochrome, SchemeNeutral, SchemeTonalSpot,...)
 * @param contrastLevel Contrast level between -1.0 and 1.0
 * @return Custom color group
 *
 * @link https://m3.material.io/styles/color/the-color-system/color-roles
 */
export function customColor(source, color, variant = 'SchemeTonalSpot', contrastLevel = 0.0) {
    let value = color.value;
    const from = value;
    const to = source;
    if (color.blend) {
        value = m3utils.Blend.harmonize(from, to);
    }
    const hct = m3utils.Hct.fromInt(value);
    const scheme = new m3utils[variant](hct, false, contrastLevel);
    const darkScheme = new m3utils[variant](hct, true, contrastLevel);
    const getDynamicColor = (token, scheme) => m3utils.MaterialDynamicColors[token].getArgb(scheme);
    return {
        color,
        value,
        light: {
            color: getDynamicColor('primary', scheme),
            onColor: getDynamicColor('onPrimary', scheme),
            colorContainer: getDynamicColor('primaryContainer', scheme),
            onColorContainer: getDynamicColor('onPrimaryContainer', scheme),
        },
        dark: {
            color: getDynamicColor('primary', darkScheme),
            onColor: getDynamicColor('onPrimary', darkScheme),
            colorContainer: getDynamicColor('primaryContainer', darkScheme),
            onColorContainer: getDynamicColor('onPrimaryContainer', darkScheme),
        },
    };
}
/**
 * Generate a theme from a source color
 *
 * @param source Source color
 * @param customColors Array of custom colors
 * @param variant Scheme variant, equal to scheme class name (SchemeMonochrome, SchemeNeutral, SchemeTonalSpot,...)
 * @param contrastLevel Contrast level between -1.0 and 1.0
 * @return Theme object
 */
export function themeFromSourceColor(source, variant = 'SchemeTonalSpot', contrastLevel = 0.0, customColors = []) {
    const hct = m3utils.Hct.fromInt(source);
    const scheme = new m3utils[variant](hct, false, contrastLevel);
    const darkScheme = new m3utils[variant](hct, true, contrastLevel);
    const getDynamicColors = (scheme) => Object.fromEntries(tokens.map(token => [token, m3utils.MaterialDynamicColors[token].getArgb(scheme)]));
    const theme = {
        source,
        schemes: {
            light: getDynamicColors(scheme),
            dark: getDynamicColors(darkScheme),
        },
        palettes: {
            primary: scheme.primaryPalette,
            secondary: scheme.secondaryPalette,
            tertiary: scheme.tertiaryPalette,
            neutral: scheme.neutralPalette,
            neutralVariant: scheme.neutralVariantPalette,
            error: scheme.errorPalette,
        },
        customColors: customColors.map((c) => customColor(source, c, variant, contrastLevel)),
    };
    theme.schemes.__proto__.toJSON = function() {
        return this;
    }
    return theme;
}
/**
 * Generate a theme from an image source
 *
 * @param image Image element
 * @param variant Scheme variant, equal to scheme class name (SchemeMonochrome, SchemeNeutral, SchemeTonalSpot,...)
 * @param contrastLevel Contrast level between -1.0 and 1.0
 * @param customColors Array of custom colors
 * @return Theme object
 */
export async function themeFromImage(image, variant = 'SchemeTonalSpot', contrastLevel = 0.0, customColors = []) {
    const source = await m3utils.sourceColorFromImage(image);
    return themeFromSourceColor(source, variant, contrastLevel, customColors);
}
/**
 * Sample code
 */
const source = m3utils.argbFromHex("#4285f4");
// Get the theme from a source color
const theme = themeFromSourceColor(source, 'SchemeTonalSpot', 0.0, [
    {
      name: "custom-1",
      value: m3utils.argbFromHex("#ff0000"),
      blend: true,
    }
]);
// Print out the theme as JSON
console.log(JSON.stringify(theme, null, 2));
// Check if the user has dark mode turned on
const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
// Apply the theme to the body by updating custom properties for material tokens
m3utils.applyTheme(theme, {target: document.body, dark: systemDark});
{
  "source": 4282549748,
  "schemes": {
    "light": {
      "primary": 4282343064,
      "onPrimary": 4294967295,
      "primaryContainer": 4292403967,
      "onPrimaryContainer": 4278196801,
      "inversePrimary": 4289578751,
      "inverseOnPrimary": 4278464103,
      "primaryFixed": 4292403967,
      "primaryFixedDim": 4289578751,
      "onPrimaryFixed": 4278196801,
      "onPrimaryFixedVariant": 4280567423,
      "secondary": 4283915889,
      "onSecondary": 4294967295,
      "secondaryContainer": 4292600569,
      "onSecondaryContainer": 4279507756,
      "secondaryFixed": 4292600569,
      "secondaryFixedDim": 4290758364,
      "onSecondaryFixed": 4279507756,
      "onSecondaryFixedVariant": 4282337113,
      "tertiary": 4285617523,
      "onTertiary": 4294967295,
      "tertiaryContainer": 4294694908,
      "onTertiaryContainer": 4280881965,
      "tertiaryFixed": 4294694908,
      "tertiaryFixedDim": 4292787423,
      "onTertiaryFixed": 4280881965,
      "onTertiaryFixedVariant": 4283973211,
      "error": 4290386458,
      "onError": 4294967295,
      "errorContainer": 4294957782,
      "onErrorContainer": 4282449922,
      "surfaceDim": 4292467168,
      "surface": 4294572543,
      "surfaceBright": 4294572543,
      "surfaceContainerLowest": 4294967295,
      "surfaceContainerLow": 4294177786,
      "surfaceContainer": 4293783028,
      "surfaceContainerHigh": 4293453806,
      "surfaceContainerHighest": 4293059305,
      "onSurface": 4279900960,
      "onSurfaceVariant": 4282664783,
      "outline": 4285691005,
      "outlineVariant": 4291086032,
      "inverseSurface": 4281282614,
      "inverseOnSurface": 4293980407,
      "surfaceVariant": 4292993772,
      "surfaceTintColor": 4282343064,
      "background": 4294572543,
      "onBackground": 4279900960,
      "shadow": 4278190080,
      "scrim": 4278190080
    },
    "dark": {
      "primary": 4289578751,
      "onPrimary": 4278464103,
      "primaryContainer": 4280567423,
      "onPrimaryContainer": 4292403967,
      "inversePrimary": 4282343064,
      "inverseOnPrimary": 4294967295,
      "primaryFixed": 4292403967,
      "primaryFixedDim": 4289578751,
      "onPrimaryFixed": 4278196801,
      "onPrimaryFixedVariant": 4280567423,
      "secondary": 4290758364,
      "onSecondary": 4280889409,
      "secondaryContainer": 4282337113,
      "onSecondaryContainer": 4292600569,
      "secondaryFixed": 4292600569,
      "secondaryFixedDim": 4290758364,
      "onSecondaryFixed": 4279507756,
      "onSecondaryFixedVariant": 4282337113,
      "tertiary": 4292787423,
      "onTertiary": 4282394691,
      "tertiaryContainer": 4283973211,
      "onTertiaryContainer": 4294694908,
      "tertiaryFixed": 4294694908,
      "tertiaryFixedDim": 4292787423,
      "onTertiaryFixed": 4280881965,
      "onTertiaryFixedVariant": 4283973211,
      "error": 4294948011,
      "onError": 4285071365,
      "errorContainer": 4287823882,
      "onErrorContainer": 4294957782,
      "surfaceDim": 4279309080,
      "surface": 4279309080,
      "surfaceBright": 4281809214,
      "surfaceContainerLowest": 4278980115,
      "surfaceContainerLow": 4279900960,
      "surfaceContainer": 4280164133,
      "surfaceContainerHigh": 4280822319,
      "surfaceContainerHighest": 4281546042,
      "onSurface": 4293059305,
      "onSurfaceVariant": 4291086032,
      "outline": 4285691005,
      "outlineVariant": 4282664783,
      "inverseSurface": 4293059305,
      "inverseOnSurface": 4281282614,
      "surfaceVariant": 4282664783,
      "surfaceTintColor": 4289578751,
      "background": 4279309080,
      "onBackground": 4293059305,
      "shadow": 4278190080,
      "scrim": 4278190080
    }
  },
  "palettes": {
    "primary": {
      "hue": 265.97939535792614,
      "chroma": 40,
      "cache": {}
    },
    "secondary": {
      "hue": 265.97939535792614,
      "chroma": 16,
      "cache": {}
    },
    "tertiary": {
      "hue": 325.97939535792614,
      "chroma": 24,
      "cache": {}
    },
    "neutral": {
      "hue": 265.97939535792614,
      "chroma": 6,
      "cache": {}
    },
    "neutralVariant": {
      "hue": 265.97939535792614,
      "chroma": 8,
      "cache": {}
    },
    "error": {
      "hue": 25,
      "chroma": 84,
      "cache": {}
    }
  },
  "customColors": [
    {
      "color": {
        "name": "custom-1",
        "value": 4294901760,
        "blend": true
      },
      "value": 4294639703,
      "light": {
        "color": 4287842128,
        "onColor": 4294967295,
        "colorContainer": 4294957788,
        "onColorContainer": 4282188817
      },
      "dark": {
        "color": 4294947513,
        "onColor": 4284029477,
        "colorContainer": 4285935674,
        "onColorContainer": 4294957788
      }
    }
  ]
}
--md-sys-color-primary: #adc6ff
--md-sys-color-on-primary: #042e67
--md-sys-color-primary-container: #24467f
--md-sys-color-on-primary-container: #d8e2ff
--md-sys-color-inverse-primary: #3f5e98
--md-sys-color-inverse-on-primary: #ffffff
--md-sys-color-primary-fixed: #d8e2ff
--md-sys-color-primary-fixed-dim: #adc6ff
--md-sys-color-on-primary-fixed: #001a41
--md-sys-color-on-primary-fixed-variant: #24467f
--md-sys-color-secondary: #bfc6dc
--md-sys-color-on-secondary: #293041
--md-sys-color-secondary-container: #3f4759
--md-sys-color-on-secondary-container: #dbe2f9
--md-sys-color-secondary-fixed: #dbe2f9
--md-sys-color-secondary-fixed-dim: #bfc6dc
--md-sys-color-on-secondary-fixed: #141b2c
--md-sys-color-on-secondary-fixed-variant: #3f4759
--md-sys-color-tertiary: #debcdf
--md-sys-color-on-tertiary: #402843
--md-sys-color-tertiary-container: #583e5b
--md-sys-color-on-tertiary-container: #fbd7fc
--md-sys-color-tertiary-fixed: #fbd7fc
--md-sys-color-tertiary-fixed-dim: #debcdf
--md-sys-color-on-tertiary-fixed: #29132d
--md-sys-color-on-tertiary-fixed-variant: #583e5b
--md-sys-color-error: #ffb4ab
--md-sys-color-on-error: #690005
--md-sys-color-error-container: #93000a
--md-sys-color-on-error-container: #ffdad6
--md-sys-color-surface-dim: #111318
--md-sys-color-surface: #111318
--md-sys-color-surface-bright: #37393e
--md-sys-color-surface-container-lowest: #0c0e13
--md-sys-color-surface-container-low: #1a1b20
--md-sys-color-surface-container: #1e1f25
--md-sys-color-surface-container-high: #282a2f
--md-sys-color-surface-container-highest: #33353a
--md-sys-color-on-surface: #e2e2e9
--md-sys-color-on-surface-variant: #c4c6d0
--md-sys-color-outline: #72747d
--md-sys-color-outline-variant: #44474f
--md-sys-color-inverse-surface: #e2e2e9
--md-sys-color-inverse-on-surface: #2f3036
--md-sys-color-surface-variant: #44474f
--md-sys-color-surface-tint-color: #adc6ff
--md-sys-color-background: #111318
--md-sys-color-on-background: #e2e2e9
--md-sys-color-shadow: #000000
--md-sys-color-scrim: #000000