material-foundation / material-color-utilities

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

[TS][Discussion] Adding a "just do stuff" function #87

Open WillsterJohnson opened 1 year ago

WillsterJohnson commented 1 year ago

Context

Personally I love digging into the weeds with libraries (such as this one) which allow me to. But I was having a conversation with a developer friend earlier who had the sentiment "that seems like a lot, I would just want the theme to work". I realised that even just taking a color, building a theme, and applying it to your page requires a bit of thought and programming dexterity that many of potential users may not have or be willing to invest. The example in the readme works well enough, but customising it to fit your page can end up being a bit of a rabbit hole. For example, frontend developers who are more used to working in very abstract frameworks, or who work more with CSS than they do with JS may have a hard time navigating the myriad of tools provided by this library. Having a function which is effectively "just use this and you'll be able to do everything you need" may be useful.

It's unlikely that there would be a need to port this to Java or Dart, or especially to C++ (though there's nothing to say it shouldn't happen), as the developers using those languages are typically more capable regarding this kind of tinkering, whereas JavaScript developers often just want to make good looking websites and not worry about math or color spaces or all the other lower-level magic going on in this library (that's not to say there are no JS devs willing to pick things apart, just that JS has a smaller portion of such devs than some other languages).

Intended Feature

There would be some function, let's call it autoMaterialize for now, which at it's most basic level will do the following;

  1. Accept a single argument (the source color or image)
  2. Generate the full theme
  3. Determine color scheme preference from matchMedia
  4. Apply the theme to the root element

For example, the following code will apply a dark-first theme in a cool deep-ocean color for a user who prefers dark mode;

import { autoMaterialize } from "@material/material-color-utilities";
await autoMaterialize("#008673"); // HCT 180 100 50

But some things might need tweaking, for example how would you add custom colors? Enforce always-light or always-dark regardless of client preference (even if that's a bad idea)? Use a dark/light mode button instead of system preference? Apply the theme to a different target?

This is a top-of-my-head draft, but I think a signature somewhat like this would be a good balance of access to features with ease of use;

async function autoMaterialize(source: Color | HTMLImageElement, options: AutoMaterializeOptions = {}): Promise<AutoMaterializeActions>;

export type Color = 
  | string // hex 
  | { 
    format: "hsl" | "xyz" | "hct" | "rgb" | "lab" | "lch" | ...; // other supported color formats
    values: [number, number, number];
  }  // maybe this object could be replaced with a union of formats, but several color formats are identical as far as JS can tell
  | ... // built in color classes, etc

export interface AutoMaterializeOptions {
  useTheme?: "dark" | "light" | "system"; // default: system
  customColors?: CustomColor[];
  target?: HTMLElement; // default: document.body
  // ... more options
}

export interface AutoMaterializeActions {
  theme: Theme;
  addCustomColor(customColor: CustomColor): this;
  setTheme(to: "dark" | "light" | "system" | "invertCurrent"): this;
  // ... more modification actions
}

Where Do We Go From Here

I think a long discussion is needed for this to be properly refined into something that makes sense in the context of this library. The examples in this comment are literally me just making it up as I go along based on what the concept is and how I've seen "just do stuff" functions implemented elsewhere. I'm not tied to the names, shapes, or signatures of anything here. The goal of this feature request is to have a discussion about adding a function which can be used to access enough of the library that most users will be satisfied with it, and will have a better DX because of it.

rodydavis commented 1 year ago

The goal of this library specifically is to be the foundation that a lot of other libraries interop with and depend on (including the flutter SDK).

For an alternate approach I did make this: https://github.com/rodydavis/postcss-color-hct

Which you just work with css and the hct color space.

rodydavis commented 1 year ago

Also for a theme there is for typescript: https://github.com/material-foundation/material-color-utilities/blob/main/typescript/utils/theme_utils.ts

WillsterJohnson commented 1 year ago

My idea here was to have a single interface for the theme generation utils that is both highly customisable and usable without configuration, a sort of "if you don't know where to start, start here" function. The existing exports would still be there for tinkerers, but for frontend devs just wanting to make an attractive UI it may become overwhelming to be interfacing with the lower-level systems, this will be especially true for people who are newer to web dev who have seen the You system and want to use it in their next project.

Instead of calling an argbFromColorFormat, passing it to themeFromSourceColor, then passing that to applyTheme, a developer could simply run autoMaterialize(<thier color in any format>) and not have to worry about color conversion, theme generation, or theme application. However if they choose to start exploring this lib, they would be able to do so gradually with this interface rather than by digging in to the source code/other people's code to figure out how to make the various systems interact correctly.

kwaa commented 1 year ago

I don't feel much need for it: putting functions together doesn't require much thought and programming dexterity.

import { argbFromHex, themeFromSourceColor, applyTheme } from '@material/material-color-utilities'

// Apply the theme to the body by updating custom properties for material tokens
applyTheme(
  themeFromSourceColor(argbFromHex('#f82506'), [
    {
      name: 'custom-1',
      value: argbFromHex('#ff0000'),
      blend: true,
    },
  ]),
  {
    target: document.body,
    dark: window.matchMedia('(prefers-color-scheme: dark)').matches,
  }
)
WillsterJohnson commented 1 year ago

As part of a separate project I ended up creating a messy & unrefined version of this, if this is a not-planned then I'll probably work on and release it on npm.

If the team have come to a decision on this I'd appreciate if they could share. Supporting it means it will be made by smarter folks tham me, not supporting it means I get to make a package for the ecosystem - either way is good news.