npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

material-chalk

v1.1.1

Published

Generate beautiful colors from namespaces based on color theory

Readme

Overview

material-chalk is a library for generating beautiful colors for namespaces based on color theory in a way that is deterministic and extensible.

It is built using the latest Typescript and ESM standards, while being compatible with older standards when needed.

It is focused on being an opinionated standard to encourage consistent colors for the same namespace across different programming languages and tools, while still being composable with any frontend from terminal-focused libraries to full GUI applications.

How to Use

Simple color

:warning: your terminal must support truecolor to see all possible colors. Although tools like chalk attempt to detect this automatically, you may have to set chalk.level manually or set export COLORTERM=truecolor in your shell (many shells use xterm-256color by default which limits the color range).

import chalk from 'chalk';
import { createMaterial, Format } from 'material-chalk'

const chalkFormat = Format.Chalk(chalk)
const namespaceFoo = createMaterial('foo').formatAs(chalkFormat);
console.log(
  namespaceFoo(`Hello, world!`)
);

There are multiple format options available, notably:

  • Format.Htc for the direct HCT (hue, chroma, tone) representation from Material Design
  • Format.Hex for a hex color representation (ex: #ff0000)
  • Format.Custom for a custom format function that you provide
  • Format.Chalk for to use Chalk to print with color
  • Format.Scheme for generating palettes (see below)

Creating nested namespaces

material-chalk allows you to define namespaces both statically and dynamically.

Static nested namespaces

const nestedNamespace =
  createMaterial(['parent', 'child'])
  .formatAs(chalkFormat);

Dynamic nested namespaces

const parentNamespace = createMaterial('parent');
const nestedNamespace = parentNamespace
  .subMaterial('child')
  .formatAs(chalkFormat);

Namespaces leverage the blend concept in Material Design which shifts the hue of the children towards that of the parent.

blend-example

Building a full palette

You can easily build full UIs around the color generated for a namespace using Material Design v3 dynamic color schemes.

Material Design allows you to create your own color scheme manually, but it also comes with many built-in schemes.

Material Design will modify the hue/chroma/tone of your colors as needed for (this is by design), but we provide a helper function to convert a scheme to one that more strongly retains the color generated for the namespace: sourceAsPrimary.

import chalk from 'chalk';
import { createMaterial, sourceAsPrimary, Format } from 'material-chalk'
import { hexFromArgb, SchemeVibrant } from "@material/material-color-utilities";

// convert one of the many built-in schemes
// to one that more accurately uses the namespace's color
const SchemeVibrantNamespace = Format.Scheme(sourceAsPrimary(SchemeVibrant))(
  true, // isDark
  0 // contrast level (for accessibility). 0 is normal contrast
)

// instantiate our new color scheme on a specific namespace
const namespaceFoo = createMaterial('foo').formatAs(SchemeVibrantNamespace);

// utility function just to demo functionality
function printWithChalk(color: number, text: string) {
  console.log(chalk.hex(hexFromArgb(color)).bold(text));
}

printWithChalk(namespaceFoo.primaryPaletteKeyColor, 'Primary');
printWithChalk(namespaceFoo.secondaryPaletteKeyColor, 'Secondary');
printWithChalk(namespaceFoo.tertiaryPaletteKeyColor, 'Tertiary');
printWithChalk(namespaceFoo.neutralPaletteKeyColor, 'Neutral');
printWithChalk(namespaceFoo.neutralVariantPaletteKeyColor, 'Neutral Variant');

Here are some examples of how a namespace looks like under different schemes:

scheme-preview

Material Design v2

If you're still using Material Design v2, you can still use this library to generate your palette

import chalk from 'chalk';
import { createMaterial, Format } from "@material/material-color-utilities";
import { hctForNamespace } from 'material-chalk'

const namespaceFoo = createMaterial('foo').formatAs(Format.Hct);

// utility function just to demo functionality
function printWithChalk(color: number, text: string) {
  console.log(chalk.hex(hexFromArgb(color)).bold(text));
}

const corePalette = CorePalette.of(namespaceFoo.toInt());
printWithChalk(corePalette.a1.keyColor.toInt(), "Primary");
printWithChalk(corePalette.a2.keyColor.toInt(), "Secondary");
printWithChalk(corePalette.a3.keyColor.toInt(), "Tertiary");
printWithChalk(corePalette.n1.keyColor.toInt(), "Neutral 1");
printWithChalk(corePalette.n2.keyColor.toInt(), "Neutral 2");

Utility functions

material-chalk provides a few utility functions to make it easier to work with colors.

Brand colors with registerBrand and matchColor

Sometimes, for business reasons, you need to enforce that a namespace is associated with a specific brand color. In those cases, you can registerBrand to override cache the color that should be generated for a specific namespace.

If you want to make sure your color matches the same brightness as other colors in material-chalk, you can use the matchColor function to adjust the brightness (internally, this will try its best to maintain your brand color)

import { createMaterial, matchColor, registerBrand, Format } from 'material-chalk'

const brandColor = matchColor("#ff0000");
registerBrand("my-brand", brandColor.formatAs(Format.Hct));

// this will retrieve the cached color
const namespace = createMaterial("my-brand").formatAs(Format.Hct);

Printing chains of namespaces with chainedMessage

Often times you want to print a chain of all the namespaces leading up to a specific message. You can use chainedMessage to do this:

import chalk from 'chalk';
import { createMaterial, chainedMessage, Format } from 'material-chalk'

const chalkFormat = Format.Chalk(chalk)
const namespaceFoo = createMaterial('foo').formatAs(chalkFormat);
console.log(
  chainedMessage(chalk, ['root-namespace', 'middle', 'child'], 'Hello, world!')
);

Here is a preview of what it looks like for different chain lengths:

chained-message-preview

Performance

material-chalk caches namespaces generated by default, meaning the performance hit is negligible for the majority of use-cases.

Performance does matter in the not-so-realistic scenarios that you are generating millions of random strings as namespaces. In this case:

  1. It takes 0.01 milliseconds per namespace color generation (cached)
  2. It takes some memory to cache the namespaces

If you need to save memory, you can disable the cache:

import { createMaterial } from 'material-chalk'

const namespaceFoo = createMaterial(
  'foo',
  { cache: false }
);

Problems with existing implementations

There are other ad-hoc implementations of generating colors from strings, but they generally have at least one of the following issues:

| Issue | Other libraries | material-chalk | |---------------------------------------------|----------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------| | Legacy code | old and do not have Typescript/ESM support | built on the latest best practices | | Poor color choices | no justification based on color theory for why a specific choice was made, leading to poor color choices | full justification for choices here | | Not extensible ex: color sub-namespaces | provide no opinionated way on how to extend it for a given namespace | leverage Material Design to build for palettes for your namespace | | Lack of standardization | provide too many configurations leading to inconsistent colors across tools & languages for the same namespace | opinionated deterministic choices based on color theory | | Limited to RGB | only support the RGB range | supports colors outside the standard RGB range | | Poor randomness | depend on non-deterministic poor sources of randomness like Math.random() internally | uses fnv-1a for good deterministic seeding of randomness |