Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
matthew-dean opened this issue Mar 25, 2023 · 5 comments

Comments

@matthew-dean
Copy link

matthew-dean commented Mar 25, 2023

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.

@matthew-dean
Copy link
Author

matthew-dean commented Mar 25, 2023

Additional use cases #1

Importing / sharing JS values with CSS

import { breakpoints } from './media.js'

registerValue('media', class {
  static get inputArguments() { return ['small | medium | large']; }
  static get output() { return ['<length>']; }

  value(args) {
    return CSS.px(breakpoints[args[0].value])
  }
})
@media (min-width: use(media, 'large')) {
  // ...
}

@matthew-dean
Copy link
Author

This seems somewhat related to #1007, albeit this proposal I'd say maintains stronger typing in the CSS type space.

@matthew-dean
Copy link
Author

@mirisuzanne I'd love to get your thoughts here sometime.

@mirisuzanne
Copy link

The goal on our end is to approach this from both sides: a fully declarative syntax in CSS for many of the simpler use-cases, which can be extended by a more imperative Houdini approach. I haven't spent as much time looking at the Houdini side of this, but I think the longer running conversation to reference here is probably #857, and @tabatkins would have a lot more context. I'll dig into this more when I can find some time as well. Thanks for pointing me to it.

@matthew-dean
Copy link
Author

@mirisuzanne Oh wow, okay, very similar ideas on #857. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants