Open wojtek-viirtue opened 2 years ago
Checking the flutter source, led me to: https://api.flutter.dev/flutter/dart-ui/Color/alphaBlend.html as the actual mechanism for tinting, and the specifics of which color and opacity seem to be covered in the Material design guidelines
To answer the elevation question, we're moving away from blending a base color + a surface tint color to fully providing roles for surface colors (e.g. surface-2, surface-1, surface, surface+1, etc.). That'll be coming this year.
surface-tint color is removed in favor of surface-tint
@wojtek-viirtue I'm figuring you've probably got a solution worked out by now, but here's my function for generating tailwind classes for the surface elevation tints until the official implementation is released. Not sure if it's 100% accurate, but it looks reasonably well in my project. You'd probably want to remove some stuff if you're not using tailwind in your project, but hope it helps!
const mapSurfaceTints = (
surfaceColor: number,
primaryColor: number,
schemeName: string
) => {
const tints = [0.05, 0.08, 0.11, 0.12, 0.14];
return tints.reduce((colors, tint, index) => {
colors[`surface-${index + 1}-${schemeName}`] = hexFromArgb(
Blend.cam16Ucs(surfaceColor, primaryColor, tint)
);
return colors;
}, {} as { [key: string]: string });
};
use:
mapSurfaceTints(
theme.schemes.light.surface,
theme.schemes.light.primary,
"light"
)
output:
{
'surface-1-light': '#eff4ec',
'surface-2-light': '#e8efe5',
'surface-3-light': '#e1e9df',
'surface-4-light': '#dfe8dd',
'surface-5-light': '#dae4d9'
}
Just want to call out surfaces are about to change. This will be reflected in the library soon.
The material theme builder figma plugin just updated to show the new roles (it uses material color utilities internally)
@pennzht I think this can be closed, yes?
@guidezpl Sorry but so is this available in the Typescript library already? I can't find anywhere in the implementation.
https://github.com/material-foundation/material-color-utilities/blob/main/typescript/dynamiccolor/material_dynamic_colors.ts#L145-L236 are all the explicit surface colors per the M3 guidelines.
I see. My issue was that they're missing from the Scheme
returned by themeFromSourceColor
/themeFromImage
.
Seems this is only available on dynamic color schemes. I don't think this should necessarily be the case?
Agreed yeah, those theme functions should be updated
So it's probably not on the roadmap yet to update them right? If anyone wants to use the correct surface colors they should use dynamic colors for now
But where's the surface-container-highest
and something others? They're not included in the TS library (at least not the release).
But where's the
surface-container-highest
and something others? They're not included in the TS library (at least not the release).
They are in the dynamic schemes part only. You can create them with for example, SchemeTonalSpot
, SchemeNeutral
, etc...
Something like this:
import {
argbFromHex,
hexFromArgb,
Hct,
SchemeContent,
MaterialDynamicColors,
} from "@material/material-color-utilities";
const mainColor = "#3f51b5";
const isDark = false;
const contrastLevel = 0.3;
const argb = argbFromHex(mainColor);
const source = Hct.fromInt(argb);
const dynamicScheme = new SchemeContent(source, isDark, contrastLevel);
const surfaceContainerHighest = hexFromArgb(MaterialDynamicColors["surfaceContainerHighest"].getArgb(dynamicScheme));
But where's the
surface-container-highest
and something others? They're not included in the TS library (at least not the release).
They are there, as @gpkc said, but using anything that calls the Scheme class won't return an object with them (i.e. themeFromImage(), themeFromSourceColor(), etc). Admittedly I'm new to Typescript, so please forgive any unnecessary work I'm about to suggest. Also, if there's a better way, I'm all ears!
If you add a couple blocks of code to two files here, you can get these to deliver in the Scheme object.
First up is adding these to the bottom of the getters for the Scheme class:
get surfaceDim() {
return this.props.surfaceDim;
}
get surfaceBright() {
return this.props.surfaceBright;
}
get surfaceContainerLowest() {
return this.props.surfaceContainerLowest;
}
get surfaceContainerLow() {
return this.props.surfaceContainerLow;
}
get surfaceContainer() {
return this.props.surfaceContainer;
}
get surfaceContainerHigh() {
return this.props.surfaceContainerHigh;
}
get surfaceContainerHighest() {
return this.props.surfaceContainerHighest;
}
get surfaceTint() {
return this.props.surfaceTint;
}
get primaryFixed() {
return this.props.primaryFixed;
}
get primaryFixedDim() {
return this.props.primaryFixedDim;
}
get onPrimaryFixed() {
return this.props.onPrimaryFixed;
}
get onPrimaryFixedVariant() {
return this.props.onPrimaryFixedVariant;
}
get secondaryFixed() {
return this.props.secondaryFixed;
}
get secondaryFixedDim() {
return this.props.secondaryFixedDim;
}
get onSecondaryFixed() {
return this.props.onSecondaryFixed;
}
get onSecondaryFixedVariant() {
return this.props.onSecondaryFixedVariant;
}
get tertiaryFixed() {
return this.props.tertiaryFixed;
}
get tertiaryFixedDim() {
return this.props.tertiaryFixedDim;
}
get onTertiaryFixed() {
return this.props.onTertiaryFixed;
}
get onTertiaryFixedVariant() {
return this.props.onTertiaryFixedVariant;
}
Next, adding this to lightFromCorePalette:
surfaceDim: core.n1.tone(87),
surfaceBright: core.n1.tone(98),
surfaceContainerLowest: core.n1.tone(100),
surfaceContainerLow: core.n1.tone(96),
surfaceContainer: core.n1.tone(94),
surfaceContainerHigh: core.n1.tone(92),
surfaceContainerHighest: core.n1.tone(90),
surfaceTint: core.a1.tone(40),
primaryFixed: core.a1.tone(90),
primaryFixedDim: core.a1.tone(80),
onPrimaryFixed: core.a1.tone(10),
onPrimaryFixedVariant: core.a1.tone(30),
secondaryFixed: core.a2.tone(90),
secondaryFixedDim: core.a2.tone(80),
onSecondaryFixed: core.a2.tone(10),
onSecondaryFixedVariant: core.a2.tone(30),
tertiaryFixed: core.a3.tone(90),
tertiaryFixedDim: core.a3.tone(80),
onTertiaryFixed: core.a3.tone(10),
onTertiaryFixedVariant: core.a3.tone(30)
Last up for this file is adding this to the darkFromCorePalette:
surfaceDim: core.n1.tone(6),
surfaceBright: core.n1.tone(24),
surfaceContainerLowest: core.n1.tone(4),
surfaceContainerLow: core.n1.tone(10),
surfaceContainer: core.n1.tone(12),
surfaceContainerHigh: core.n1.tone(17),
surfaceContainerHighest: core.n1.tone(22),
surfaceTint: core.a1.tone(80),
primaryFixed: core.a1.tone(90),
primaryFixedDim: core.a1.tone(80),
onPrimaryFixed: core.a1.tone(10),
onPrimaryFixedVariant: core.a1.tone(30),
secondaryFixed: core.a2.tone(90),
secondaryFixedDim: core.a2.tone(80),
onSecondaryFixed: core.a2.tone(10),
onSecondaryFixedVariant: core.a2.tone(30),
tertiaryFixed: core.a3.tone(90),
tertiaryFixedDim: core.a3.tone(80),
onTertiaryFixed: core.a3.tone(10),
onTertiaryFixedVariant: core.a3.tone(30)
Add this to the Scheme class:
get surfaceDim(): number;
get surfaceBright(): number;
get surfaceContainerLowest(): number;
get surfaceContainerLow(): number;
get surfaceContainer(): number;
get surfaceContainerHigh(): number;
get surfaceContainerHighest(): number;
get surfaceTint(): number;
get primaryFixed(): number;
get primaryFixedDim(): number;
get onPrimaryFixed(): number;
get onPrimaryFixedVariant(): number;
get secondaryFixed(): number;
get secondaryFixedDim(): number;
get onSecondaryFixed(): number;
get onSecondaryFixedVariant(): number;
get tertiaryFixed(): number;
get tertiaryFixedDim(): number;
get onTertiaryFixed(): number;
get onTertiaryFixedVariant(): number;
Lastly, add this to darkFromCorePalette:
surfaceDim: number;
surfaceBright: number;
surfaceContainerLowest: number;
surfaceContainerLow: number;
surfaceContainer: number;
surfaceContainerHigh: number;
surfaceContainerHighest: number;
surfaceTint: number;
primaryFixed: number;
primaryFixedDim: number;
onPrimaryFixed: number;
onPrimaryFixedVariant: number;
secondaryFixed: number;
secondaryFixedDim: number;
onSecondaryFixed: number;
onSecondaryFixedVariant: number;
tertiaryFixed: number;
tertiaryFixedDim: number;
onTertiaryFixed: number;
onTertiaryFixedVariant: number;
Now the Theme Utilities will pull a Scheme object that includes all of the Material Palette (at least the ones listed on m3.material.io).
While futzing with this, I noticed the values for lightFromCorePalette's background and surface and darkFromCorePalette's background, surface, and onErrorContainer are off in scheme.js, so I corrected them for my build, but wanted to leave them here in case it's helpful. Again, using m3.material.io for reference. If the differences are intentional please let me know.
NOTE: These blocks also have been reordered with the new additions to group things the way it was originally.
// Light
primary: core.a1.tone(40),
onPrimary: core.a1.tone(100),
primaryContainer: core.a1.tone(90),
onPrimaryContainer: core.a1.tone(10),
primaryFixed: core.a1.tone(90),
primaryFixedDim: core.a1.tone(80),
onPrimaryFixed: core.a1.tone(10),
onPrimaryFixedVariant: core.a1.tone(30),
secondary: core.a2.tone(40),
onSecondary: core.a2.tone(100),
secondaryContainer: core.a2.tone(90),
onSecondaryContainer: core.a2.tone(10),
secondaryFixed: core.a2.tone(90),
secondaryFixedDim: core.a2.tone(80),
onSecondaryFixed: core.a2.tone(10),
onSecondaryFixedVariant: core.a2.tone(30),
tertiary: core.a3.tone(40),
onTertiary: core.a3.tone(100),
tertiaryContainer: core.a3.tone(90),
onTertiaryContainer: core.a3.tone(10),
tertiaryFixed: core.a3.tone(90),
tertiaryFixedDim: core.a3.tone(80),
onTertiaryFixed: core.a3.tone(10),
onTertiaryFixedVariant: core.a3.tone(30),
error: core.error.tone(40),
onError: core.error.tone(100),
errorContainer: core.error.tone(90),
onErrorContainer: core.error.tone(10),
background: core.n1.tone(98),
onBackground: core.n1.tone(10),
surface: core.n1.tone(98),
onSurface: core.n1.tone(10),
surfaceVariant: core.n2.tone(90),
onSurfaceVariant: core.n2.tone(30),
surfaceDim: core.n1.tone(87),
surfaceBright: core.n1.tone(98),
surfaceContainerLowest: core.n1.tone(100),
surfaceContainerLow: core.n1.tone(96),
surfaceContainer: core.n1.tone(94),
surfaceContainerHigh: core.n1.tone(92),
surfaceContainerHighest: core.n1.tone(90),
surfaceTint: core.a1.tone(40),
outline: core.n2.tone(50),
outlineVariant: core.n2.tone(80),
shadow: core.n1.tone(0),
scrim: core.n1.tone(0),
inverseSurface: core.n1.tone(20),
inverseOnSurface: core.n1.tone(95),
inversePrimary: core.a1.tone(80),
// Dark
primary: core.a1.tone(80),
onPrimary: core.a1.tone(20),
primaryContainer: core.a1.tone(30),
onPrimaryContainer: core.a1.tone(90),
primaryFixed: core.a1.tone(90),
primaryFixedDim: core.a1.tone(80),
onPrimaryFixed: core.a1.tone(10),
onPrimaryFixedVariant: core.a1.tone(30),
secondary: core.a2.tone(80),
onSecondary: core.a2.tone(20),
secondaryContainer: core.a2.tone(30),
onSecondaryContainer: core.a2.tone(90),
secondaryFixed: core.a2.tone(90),
secondaryFixedDim: core.a2.tone(80),
onSecondaryFixed: core.a2.tone(10),
onSecondaryFixedVariant: core.a2.tone(30),
tertiary: core.a3.tone(80),
onTertiary: core.a3.tone(20),
tertiaryContainer: core.a3.tone(30),
onTertiaryContainer: core.a3.tone(90),
tertiaryFixed: core.a3.tone(90),
tertiaryFixedDim: core.a3.tone(80),
onTertiaryFixed: core.a3.tone(10),
onTertiaryFixedVariant: core.a3.tone(30),
error: core.error.tone(80),
onError: core.error.tone(20),
errorContainer: core.error.tone(30),
onErrorContainer: core.error.tone(90),
background: core.n1.tone(6),
onBackground: core.n1.tone(90),
surface: core.n1.tone(6),
onSurface: core.n1.tone(90),
surfaceVariant: core.n2.tone(30),
onSurfaceVariant: core.n2.tone(80),
surfaceDim: core.n1.tone(6),
surfaceBright: core.n1.tone(24),
surfaceContainerLowest: core.n1.tone(4),
surfaceContainerLow: core.n1.tone(10),
surfaceContainer: core.n1.tone(12),
surfaceContainerHigh: core.n1.tone(17),
surfaceContainerHighest: core.n1.tone(22),
surfaceTint: core.a1.tone(80),
outline: core.n2.tone(60),
outlineVariant: core.n2.tone(30),
shadow: core.n1.tone(0),
scrim: core.n1.tone(0),
inverseSurface: core.n1.tone(90),
inverseOnSurface: core.n1.tone(20),
inversePrimary: core.a1.tone(40),
@thekvd Scheme
is deprecated. you should be using dynamic schemes as gpkc explained.
I see that now. The issue I'm having there is that I can't get it to generate the same color palette. I used @gpkc code above, and it results in a much more muted palette vs the Theme Utils. That's why I went the Scheme route for now. I'm gonna need to circle back to that before going live.
I see that now. The issue I'm having there is that I can't get it to generate the same color palette. I used @gpkc code above, and it results in a much more muted palette vs the Theme Utils. That's why I went the Scheme route for now. I'm gonna need to circle back to that before going live.
Have you tried all of the possible schemes? SchemeTonalSpot
, SchemeVibrant
, SchemeExpressive
etc
It seems that the typescript library doesn't account for the
md.sys.color.surface-tint-color
token which should default to the primary color.In addition, is there a POV on how elevations should be handled?
I think at present it would require manually compositing the surface color + primary color (possibly to be replaced the aforementioned surface tint color) with opacity at a given elevation per the specification. Implemented as a custom color today?
Is there an example of how to use HCT to accomplish this to get the tonal variations for the elevations?
Thank you in advance! The library and concepts are awesome!