Skip to content

[css-properties-values-api][css-paint-api] Feature proposal: Value Transform worklets #1088

@matthew-dean

Description

@matthew-dean

Hi, all. Thanks for continuing to push this forward. Recently, I was working on a blog post to compare the current state of CSS to what's currently possible with Less / Sass.

When I got to a section talking about custom functions / utilities in Less & Sass, I dived back into Houdini specs and documentation to look at what's possible or would be possible in the current spec.

Interestingly, while Houdini's Paint API is very powerful, I found it actually has a gap in emulating / supporting the most common use case of Less / Sass functions and mixins, which is simple value transforms.

The Current Scenario

Say you wanted to implement something like Less's darken function, to derive a color based on another given color. A common use case for this is to set a color theme, and derive colors based on theme colors.

In Less, you could do something like:

.box {
  border: 1px solid darken(#80e619, 20%);
}

In Houdini, in order to emulate this, you could, I suppose, create a canvas, and draw the pixels for the border and then reference it using the border-image property, but you would of course, need to do all your own calculations in JavaScript for things like border radius, or various border thickness or styles. In other words, you'd be re-implementing entire sections of browser rendering code just to change the color of a border.

The advantage with a value transform is that the calculations / drawing a much more efficient, because the rendering is still left entirely to the browser.

The Proposal

Instead, I propose an extension to worklets that, instead of providing an image, provide a CSS value, and can be used wherever that value is allowed in CSS.

The form of the worklet could be something like:

registerValue('darken', class {

  static get inputArguments() { return ['<color>', '<percentage>']; }
  static get output() { return ['<color>']; }

  value(args) {
    // return a simple transform of values
    // see: https://github.com/less/less.js/blob/master/packages/less/src/less/functions/color.js#L282
  }
})

Including in CSS

After the worklet is registered...

CSS.valueWorklet.addModule("color-extensions.js");

...then it could be called with a simple referencing syntax.

border-color: use(darken, #80e619, 20%);
Other syntax exploration I'm not sure what's best / most logical for CSS here, but some options I thought of are:
border-color: use.darken(#80e619, 20%);
border-color: use-darken(#80e619, 20%);
border-color: fn.darken(#80e619, 20%);
border-color: fn-darken(#80e619, 20%);

However those seemed less elegant.

I also considered, similar to this comment, the use of call. However:

  • A call is more imperative, and use feels more declarative
  • A call implies a single call at evaluation time. CSS deals with computed values, and the worklet may be called several times depending on its value / var dependencies.

Why?

Just to re-iterate, there are lots of Less and Sass utility functions (as well as other CSS value transformation utilities on NPM) which would be trivially or near-trivially immediately implementable with a value transform, and would be extremely non-trivial to implement using the Paint API.

Why not just continue to use pre-processors?

CSS, in the last decade, has largely taken a "pave the cowpaths" approach to common pre-processor patterns, such as the Nesting proposal and the Conditional At-Rules proposal, not to mention Custom Properties which can, in many cases, be a replacement for pre-processor variables. (It's also worth noting that CSS has often adopted functions that exist / existed in Sass / Less.) Sass and Less (or at least I can speak for Less) are intended to enable easier DX patterns, but with the understanding that the easiest DX may be, in the future, to simply use CSS.

In addition, there are many use cases for defining properties / values at runtime based on certain conditions or user input. A Value Transform worklet could replace use cases for approaches like CSS-in-JS, by using a much-more efficient worklet model to derive CSS values.

Note

Btw, my apologies if something like this is already possible using the Paint API. I've searched through everything I could find, and I couldn't find anything for doing simple value transforms.

Additional Note

If someone has additional use cases, please add them in comments.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions