[csswg-drafts] [css-mixins-1] How are cycles broken? (#12595)

sesse has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-mixins-1] How are cycles broken? ==
Imagine a mixin A that includes an A/B loop:

```css
@mixin a() {
  --last-processed: a;
  @apply b();
}
@mixin b() {
  --last-processed: b;
  @apply c();
}
@mixin c() {
  --last-processed: c;
  @apply b();
}
```

Now suppose someone applies a() within a rule. What is the value of `--last-processed`; a, b, c or even unset? (You could ask similar questions about declarations after the `@apply` rules.)

If we're breaking cycles at the last possible moment, then the value is c. This is a fairly simple model; you just keep a stack of everything that is in the process of being applied, and when you see an `@apply` against something already in your stack, you ignore it.

However, CSS custom properties don't work that way; they declare the entire cycle as illegal, so `@apply b();` has no effect and the result is a.  (Similarly, you could argue that including a cycle is illegal and the value should not even be set, but I don't think that matches CSS custom properties. In any case, they're not quite the same because they have explicit fallbacks.) The problem with this is that we cannot easily rollback already-applied declarations, so before every mixin that we try to apply, we would need a separate cycle-finding step. (We cannot easily do this parse-time, since the mixins may come from different style sheets and/or tree scopes.)

So I'd argue that it should be either a or c, but it's not really obvious which one. c is much easier for a performant implementation, though.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12595 using your GitHub account


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

Received on Tuesday, 12 August 2025 13:31:43 UTC