Material Color
Build a Material color scheme to map material dynamic color, derived from your branded theme color, or from your any custom source color. The Material will generates color roles (color scheme) and key colors (palettes) from your source color
import {
MaterialColor,
} from "material-color-react-native"
const materialColor = new MaterialColor("#ffde3f")
The source color has to be in Hex code format, e.g #RRGGBB, or #RRGGBBAA, or #AARRGGBB
Check the MaterialColor TypeScript definitions to see all its members, methods, and statics.
Material Options
Material color provide options to build their color, for instance, light/dark theme, contrast level, variant, spec version, override key colors, and other options provided by @material/material-color-utilities
const materialColor = new MaterialColor(
"#ffde3f",
{
contrastLevel: MaterialColor.ContrastLevelPresets.DEFAULT, // DEFAULT is 0
isDark: true, // generate material color only in dark theme
spec: "2025",
secondaryPalette: "#a5ff3f",
}
)
Default Options
| Option | Remark |
|---|---|
contrastLevel | 0 (zero) or MaterialColor.ContrastLevelPresets.DEFAULT |
isDark | Automatically use Appearance from React Native, whether the app is in dark or light color scheme. |
spec | It's handled automatically by @material/material-color-utilities's DynamicScheme. See the source code. |
variant | MaterialColor.Variant.TONAL_SPOT |
Color Scheme
You will get 26+ color roles (color scheme) organized into six groups: primary, secondary, tertiary, error, surface, and outline
console.log(
"colorScheme",
materialColor.sourceColor, // #ffde3f (your original source color)
materialColor.colorScheme.primary, // #6d5e0f
materialColor.colorScheme.onPrimary, // #ffffff
materialColor.colorScheme.primaryContainer, // #f8e287
materialColor.colorScheme.onPrimaryContainer, // #534600
materialColor.colorScheme.secondary, // #665e40
materialColor.colorScheme.onSecondary, // #ffffff
materialColor.colorScheme.secondaryContainer, // #eee2bc
materialColor.colorScheme.onSecondaryContainer, // #4e472a
// ... other color scheme ...
)
Palettes
The MaterialColor also generates key colors in six complimentary key colors: primary, secondary, tertiary, error, neutral, and neutralVariant
console.log(
"palettes",
materialColor.sourceColor, // #ffde3f (your original source color)
materialColor.primaryPalette.keyColor, // #ffde3f
materialColor.secondaryPalette.keyColor, // #9b9168
materialColor.tertiaryPalette.keyColor, // #6d9b7b
materialColor.errorPalette.keyColor, // #ff5449
materialColor.neutralVariant.keyColor, // #949088
materialColor.neutralVariantPalette.keyColor, // #969080
)
Tonal Palettes
The system interprets each key color into tonal palettes. From the key colors, you access access the color with tone adjustment from 0 - 100
console.log(
"tonalPalettes",
materialColor.sourceColor, // #ffde3f (your original source color)
materialColor.primaryPalette.tone(95), // hex color
materialColor.secondaryPalette.tone(40), // hex color
materialColor.tertiaryPalette.tone(30), // hex color
materialColor.errorPalette.tone(100), // hex color
materialColor.neutralPalette.tone(99) // hex color
materialColor.neutralVariantPalette.tone(0), // hex color
)
Based on the Material Design, `the system then manipulates tone and chroma values to generate five complimentary key colors`, instead of six, which is the absence of error palette. Meanwhile the original source code from @material/material-color-utilities is still giving us the error palette. It's probably deprecated or removed in their next spec version.
Build from Source Image
This library provide an option for you to build Material color with an image, either it's your static bundled image file, or a local image you get from a device file or photos or gallery or even camera, or an image from a network in React Native app.
It's straightforward method, you can use these static methods
MaterialColor.fromSourceImageis for a static bundled imageMaterialColor.fromSourceImageUriis for a local image file or an online image
import {
MaterialColor,
} from "material-color-react-native"
// You can use this top import, or the `require()` style import
import YourLocalImage from "./_your-image.png"
// or this
const onlineImage = "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"
// or this, assumed you get this from document file / photo picker, or even from a camera
const localImageFileURI = "file:///xxxxxx"
export function ReactComponent() {
useEffect(() => {
MaterialColor.fromSourceImage(YourLocalImage)
.then(materialColor => {
console.log(
"colorScheme",
materialColor.sourceColor, // source color from an image
materialColor.colorScheme.primary,
materialColor.colorScheme.onPrimary,
)
})
MaterialColor.fromSourceImageUri(onlineImage).then(materialColor => {
// your logics
})
MaterialColor.fromSourceImageUri(localImageFileURI).then(materialColor => {
// your logics
})
}, [])
}
From the static method, you will get the same MaterialColor instance. The difference is the instance could be null, or thrown an error if the process is timed out or aborted by your own AbortSignal.
Processing Options
From the static method, the second argument is the material options, and the third is the processing options. See the processing options below
MaterialColor.fromSourceImage(
YourImage,
// material options
{
dark: true,
},
// processing options
{
maxWidthOrHeight: 640,
},
)
maxWidthOrHeight
This option is recommended to increase performance by limiting the image Bitmap. While it cannot guarantee to get the same result as the original image size, because the possibility of original color in a pixel will be blended into another and lose its channel if it shrunk, but you can still save to use it. Default value is the original image size (undefined).
For your references, Bitmap represents as color each pixel in a image. In a Android, iOS, macOS, and Web, this library holds the buffer of the image's Bitmap in a 32 bit unsigned integer which contain 4 bytes each ARGB/RGBA color pixels or 8 bits per color channel pixel. For instance, an 1000x1000 image will has 4MB buffer allocated on the heap.
timeout
A duration in milliseconds format to limit how long the fetch and image processing can be waited. Default is 0 which means no duration limit.
signal
You can provide your own AbortSignal to control when the process needs to be aborted. It does nothing if the fetch and image process are already done.
Only Source Color
In some cases, you want to get only the source color from an image, and do your own things, you can use the ImageUtils.sourceHexColorFromImageUri
import {
Image,
} from "react-native"
import {
ImageUtils,
} from "material-color-react-native"
import YourLocalImage from "./_your-image.png"
const yourLocalImageUri = Image.resolveAssetSource(YourLocalImage).uri
ImageUtils.sourceColorFromImageUri(yourLocalImageUri) // second argument for the image processing options
.then(sourceColor => {
console.log(sourceColor)
// sourceColor in hex code string
})
// you can use this
const onlineImage = "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"
// or this
const localImageFileURI = "file:///xxxxxx"
ImageUtils.sourceColorFromImageUri(onlineImage) // second argument for the image processing options
.then(sourceColor => {
console.log(sourceColor)
// sourceColor in hex code string
})
Build Material from Source Image is not compatible in React Native for Windows. Hopefully, we can bring this utility to it.
Error Handling
The promise resolver guarantee that return a valid source color from bare ImageUtils, or return a valid MaterialColor instance from the static method, but it might throw an exception if something unexpected ocurred.
If this is happened, this library will throw ImageUtils.SourceColorFromImageException or MaterialColor.SourceImageException class. Both classes are same. You can cast with the instanceof operator, and check what type of error happened from the type member from that class
import {
MaterialColor,
} from "material-color-react-native"
// in case you get this from an document or photo picker
const localFileUri = "file:///xxxxxx"
MaterialColor.fromSourceImage(localFileUri)
.then(materialColor => {
console.log(
materialColor.sourceColor,
materialColor.colorScheme.primary,
)
})
.catch(err => {
if(err instanceof MaterialColor.SourceImageException) {
if(err.type == MaterialColor.SourceColorFromImageException.TIMEDOUT) {
// If it's timed out …
} else if(err.type == MaterialColor.SourceColorFromImageException.ABORTED) {
// your own AbortSignal was aborted …
} else if(err.type == MaterialColor.SourceColorFromImageException.UNPROCESSABLE) {
// the image is unprocessable …
}
}
})
React Hooks
useMaterialColor
This is a perfect case to build and get Material color declaratively
import {
useMaterialColor,
} from "material-color-react-native"
export function YourReactComponent() {
const [sourceColor, setColor] = useState("#ffde3f"),
const materialColor = useMaterialColor(sourceColor)
console.log(
materialColor.colorScheme.primary,
materialColor.colorScheme.onPrimary,
materialColor.primaryPalette.keyColor,
materialColor.primaryPalette.tone(80),
)
}
useMaterialColorFromImage
You can also get material color from an image, but in React Hooks style
You can pass a null or undefined to these hooks below in its first argument. In case, you want to get material color from an image later, or after some events.
import {
useMaterialColorFromImage,
} from "material-color-react-native"
// You can use this top import, or the `require()` style import
import YourLocalImage from "./_your-image.png"
export function YourReactComponent() {
const { data, isLoading, error } = useMaterialColorFromImage(YourLocalImage)
if(data) { // `data` is MaterialColor instance
console.log(
data.sourceColor,
data.colorScheme.primary,
data.primaryPalette.tone(80),
)
}
}
useMaterialColorFromImageUri
import {
useMaterialColorFromImageUri,
} from "material-color-react-native"
// use this
const onlineImage = "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"
// or this
const localImageFileURI = "file:///xxxxxx"
export function YourReactComponent() {
const { data, isLoading, error } = useMaterialColorFromImageUri(onlineImage) // file uri
if(data) { // `data` is MaterialColor instance
console.log(
data.sourceColor,
data.colorScheme.primary,
data.primaryPalette.tone(80),
)
}
}
Definitions
See definitions.