Re: [csswg-drafts] Declarative custom functions (#7490)

> There is a more fully fledged [custom function proposal in Houdini](https://github.com/w3c/css-houdini-drafts/issues/857) where the function will be defined in JS. I think that proposal would solve most of your needs.

**UPDATE:** I want to clarify— my main goal here in voicing my recommendations is evolving CSS itself and empowering CSS developers to build and use powerful functions with ease without **_needing_** to use JS if they so choose. Houdini is certainly the primary solution to the most complex use cases.

I'm certainly not suggesting CSS `@custom-function` cover all possible use cases, but if we can build CSS functions to be more powerful and flexible, I think we'd make them much more desirable to use for anyone. In many cases, it may be even simpler and more sensible to write a style-specific function in CSS rather than Houdini.

Even more important than utility cases, some of my recommendations such as using `@return` vs. `result` are more focused on the scalability of `@custom-function` rather than semantics. `result` works for the present use case of var substitution, but it could be harder t use for more logically-sophisticated use cases, whereas `@return` opens the door to better logic and short-circuiting and the future growth of `@custom-function`.

---

<details>
<summary><code>result</code> vs. <code>@return</code> examples & reasoning</summary>
<br />

In short— using `@return`:

```css
@custom-function (...) {
  @if (...) {
    @return 1; /* <-- this value gets used if the condition is true, per short-circuiting */
  }
  @return 2;
}
```

whereas, with `result`:

```css
@custom-function (...) {
  @if (...) {
    result: 1;
  }
  result: 2; /* <-- this value ALWAYS gets used, per the cascade, even if the condition is true */
}
```

To support short-circuiting in this style, we're forced to use the `@else` clause, even if undesirable.

```css
@custom-function (...) {
  @if (...) {
    result: 1;
  } @else {
    result: 2;
  }
}
```

For some use cases, this could seem a bit tedious, especially if you want to short-circuit before the bulk of the function's logic.

This…

```css
@custom-function (...) {
  @if (...) {
    @return "";
  }
  /* LOTS of logic here */
}
```

…becomes…

 ```css
@custom-function (...) {
  @if (...) {
    result: "";
  } @else {
    /* LOTS of logic here */
  }
}
```
</details>

---

<details open>
<summary>Houdini vs. <code>@custom-function</code> examples & other notes</summary>
<br />

Here is an example of what an if-statement custom function might look like when composed with Houdini, pulled from the related [proposal discussion](https://github.com/w3c/css-houdini-drafts/issues/857)…

### JavaScript (via Houdini)

```js
class ConditionalEvaluation {
  static get genericTypeArguments() { return ['<T>']; }
  static get inputArguments() { return ['<boolean>', CSS.CustomFunction.lazyArgument('<T>'), CSS.CustomFunction.lazyArgument('<T>?')]; }
  static get returnType() { return '<T> | <null>'; }

  conditional([condition, ifTrue, ifFalse], styleMap) {
    if(!!condition) {
      return CSS.CustomFunction.evaluate(ifTrue);
    } else if (ifFalse) {
      return CSS.CustomFunction.evaluate(ifFalse);
    } else {
      return null;
    }
  }
}

registerCustomFunction("--if", ConditionalEvaluation, "conditional");
```

vs. the same example built using `@custom-function`…

### CSS `@custom-function`

```css
@custom-function --if(--condition "<boolean>",  --ifTrue "<T>",  --ifFalse "<T> | <null>") "<T> | <null>" {
  @if (var(--condition)) {
    @return var(--ifTrue);
  }
  @return var(--ifFalse);
}
```

Here, the CSS implementation is just as explicit in terms of arg/return types (syntax) and less verbose.

Both can be invoked like this:

```css
:root {
  /* storing boolean value in custom property */
  --is-dark-mode: media(prefers-color-scheme: dark);
}
selector {
  /* exclude `ifFalse` value to omit rule if `condition` is false */
  background-color: --if(var(--is-dark-mode), "black");

  /* boolean evaluation inline */
  --element-width: 50px;
  content: --if(var(--element-width) < 32px, "small", "large");
}
```

</details>

---

-- 
GitHub Notification of comment by brandonmcconnell
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/7490#issuecomment-1258175184 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 26 September 2022 15:03:26 UTC