adobe / leonardo

Generate colors based on a desired contrast ratio
http://www.leonardocolor.io
Apache License 2.0
1.96k stars 107 forks source link

[RFC] Refactor Leonardo with class functions #113

Closed NateBaldwinDesign closed 3 years ago

NateBaldwinDesign commented 3 years ago

Description

This is a request for comment. If you use @adobe/leonardo-contrast-colors, we want to hear your perspectives and concerns

This would be a refactoring of the primary functions of the @adobe/leonardo-contrast-colors npm package. Existing functions would be replaced with javascript class functions. Naming of these class functions would be changed to fit a simpler mental model:

  1. Color class
  2. BackgroundColor class
  3. Theme class

Color class

Rather than defining color parameters as a constant, or directly passing the parameters to either the generateContrastColors or generateAdaptiveTheme functions, each color that you wish to create a scale for will be defined as a unique class

const myColor = new Color()

The Color class will house all parameters for the color (name, colorKeys, colorspace, ratios, smooth, output), provides setters and getters for those parameters, and it will generate a color scale.

The Color function houses the data for a color object, but does not return individual swatches. In order to do return contrast-based swatches, a Color will have to be passed (along with a BackgroundColor) to the Theme class.

BackgroundColor class

The BackgroundColor class extends the Color class. The primary difference is that the color scale it generates will only contain 100 colors, each selected based on an even distribution of lightnesses in HSLuv colorspace. The purpose for that is that it can be integrated with a range slider in your application to alter the brightness of the background within that range of 100 values.

const myBackground = new BackgroundColor()

Note that you should not support all 100 values in a brightness slider since midtones will lose any identifiability and will have drastically reduced contrast gamuts. Depending on the specific needs of your design system or application, you may want a wider or narrower range of available brightness, which is why Leonardo is not opinionated about what should be the acceptable range.

The BackgroundColor function should be used for only one color of your palette, and signifies to the Theme class which colors to base the contrast generation from.

Theme class

The Theme class will replace both the generateContrastColors and generateAdaptiveTheme functions. The former is a single-color instance of a "theme", whereas the latter includes more colors support. For that reason, they should be combined into a single function

const myTheme = new Theme()

This function accepts Colors and BackgroundColor, along with lightness, contrast, and output parameters. It provides setters and getters for those parameters (including colors), and returns color swatches based on the target ratio. These colors can be output as either a flat list of values (Theme.contrastColorValues) or in a hierarchical data format (Theme.contrastColors). The hierarchical data format does not change from the current output seen in generateAdaptiveTheme().

Comparison

To compare the way this would change, this is an example of the current method of creating a theme in Leonardo:

let theme = generateAdaptiveTheme({
    baseScale: "gray",
    lightness: 90,
    colorScales: [
      {
        name: "gray",
        colorKeys: ['#cacaca', '#323232'],
        colorspace: 'HSL',
        ratios: [1, 1.2, 1.4, 2, 3, 4.5, 6, 8, 12, 21]
      },
      {
        name: "blue",
        colorKeys: ['#0000ff'],
        colorspace: 'LAB',
        ratios: [2, 3, 4.5, 8, 12]
      },
      {
        name: "red",
        colorKeys: ['#ff0000'],
        colorspace: 'RGB',
        ratios: [2, 3, 4.5, 8, 12]
      }
    ]});

This method would be replaced by:

  let gray = new BackgroundColor({"name": 'gray', colorKeys: ['#cacaca', '#323232'], colorspace: 'HSL', ratios: [1, 1.2, 1.4, 2, 3, 4.5, 6, 8, 12, 21]});
  let blue = new Color({"name": 'blue', colorKeys: ['#0000ff'], colorspace: 'LAB', ratios: [2, 3, 4.5, 8, 12]});
  let red = new Color({"name": 'red', colorKeys: ['#ff0000'], colorspace: 'RGB', ratios: [2, 3, 4.5, 8, 12]});
  let theme = new Theme({colors: [gray, blue, red], backgroundColor: gray, lightness: 90}); 
  let themeColors = theme.contrastColors;

Why do you need this feature?

This update could help to enable applications with a more performant method of returning updated colors. More specifically, with stateful applications that desire to expose brightness and contrast controls for their end-users.

These class functions would create a separation of concerns, allowing for changing and returning new sets of contrasting color values without re-generating all the color scales. This is particularly impactful when working with a theme that has many different color scales. Similarly, a single color can be manipulated without regeneration of all color swatches (only affecting those that have changed).

Leonardo package and version

This change would be implemented as 1.0.0-alpha.9

Additional context

We would like to hear from teams that use Leonardo to identify what level of impact or desirability this refactoring would have on you, your team, or your product(s).

A draft PR for this proposal can be seen here: #110

Please comment below with any questions, concerns, or considerations you would like us to be aware of 🙌

NateBaldwinDesign commented 3 years ago

No comments in 25 days, moving forward with the refactor.

pham93 commented 3 years ago

This breaks typing for typescript support.

NateBaldwinDesign commented 3 years ago

Thank you for raising this issue @pham93