Re: [csswg-drafts] [css-mixins] custom mixins & functions should have some access to custom properties on the calling element. How much? (#9938)

My fairly strongly-held position is that all arguments visible to the function body should be lexically scoped - that is, explicitly declared in the function declaration.  So, for example:

```css
@function --foo(--arg1, --arg2) using (--var1, --var2) {
  /* In here, you *only* have access to the two passed arguments,
     from `--foo(1, 2)`,
     and the --var1 and --var2 custom properties from the calling context.
  */
}
```

I have a few reasons for holding this opinion strongly.

(1) In the future we'll likely want to be able to use JS to define the behavior of custom function, aka a `CSS.registerFunction("--foo", ...)` API. We probably don't want to pass in the entire set of custom properties to the callback; it's better if it knows exactly what custom props it'll need to look up for passing in.

(2) Lexical scoping makes it very obvious when shadowing is occurring. For example:

```css
@function --outer(--foo) {
  /* declares a --foo argument */
  result: calc(var(--foo) + --inner());
}
@function --inner() {
  /* implicitly grabs the --foo custom property from ~somewhere~ */
  result: calc(var(--foo) / 2);
}
.foo {
  --foo: 1;
  width: calc(--outer(10) * 1px);
}
```

What's the width? Does `--outer()`'s argument shadow the custom property that `--inner()` references, resulting in 15px (10 + 10/2)? Or does `--inner()` get to "escape" up to the element context and grab that custom property, resulting in 10.5px (10 + 1/2)?

If the answer is 15px, it's not particularly clear that the shadowing is intended. Every function needs to know precisely what variables are referenced by every function it calls (and every function they call, etc), or risk accidentally screwing up the behavior of a function they're not even aware is being called.

If the answer is 10.5px, then it's impossible to override that behavior. It means *only* the top-level context can pass all the "arguments" to a function; when functions call other functions, they only have access to pass *some* of the "arguments". It also means that if you just take a chunk of style that currently works, and abstract it into a function, it might break, because it *was* setting some custom props for a function it was using, and now those are no longer visible. This just sucks.

Having to explicitly list every variable you want to reference does kinda suck as well, I acknowledge. But it means you have all the power you need, refactoring stuff into a function works, and you don't have to worry about accidentally shadowing variables you don't even know about.

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


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

Received on Tuesday, 13 February 2024 00:13:11 UTC