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-values-5] Maybe min, max and step should not be part of the random-caching-key #11742

Open
smfr opened this issue Feb 19, 2025 · 6 comments

Comments

@smfr
Copy link
Contributor

smfr commented Feb 19, 2025

Currently, CSS Values 5 says that the min, max and step are part of the random-caching-key.

However, this prevents some possibly useful techniques where an element shares the same random value, but with different min, max and step with another property on the same element, or with another element.

Conceptually, I would expect that the underlying 0-1 random number is sampled based only on the dashed-ident or per-element keyword, and then min, max and step are just math applied to that underlying value.

Consider

        .box {
            background-color: blue;
            height: random(per-element, 50px, 100px);
            width: random(per-element, 100px, 200px);
        }

I want this box to have a random size, but always a 2/1 aspect ratio. With the current spec, width and height get their own random values.

Another example might be that you have two elements that want a width based on a single random value, but they quantize that value in different ways.

@tabatkins
Copy link
Member

The issue is that if min/max/step aren't included, then it exposes details of exactly how you generate the random value.

To avoid that, I'd have to specify that the random value is, specifically, a 0-1 real, and then specify exactly how it's turned into the random value. (aka, scaled to the desired range, or converted to a 0-N integer for the step)

I definitely could do that, and it would avoid some other oddities (like 1em and 16px potentially being the same caching key). It just might slightly restrict how you can actually generate the random value a bit.

@fantasai
Copy link
Collaborator

fantasai commented Feb 19, 2025

I want this box to have a random size, but always a 2/1 aspect ratio. With the current spec, width and height get their own random values.

I think the more obvious way to do this would be to use the same random() function in both, like this: width: calc(random(per-element, 50px, 100px)*2).

Another example might be that you have two elements that want a width based on a single random value, but they quantize that value in different ways.

You can do the same thing for this: pull out the random() function and pipe it into a round() function.

I think these use cases are relatively rare, and it's more obvious what you're doing if you synchronize the random() functions.


I think what's more likely, actually, is that you want two random() functions with identical arguments to return different values. For example, maybe you want a bunch of randomly sized boxes on your page with their widths and heights between 10px and 100px:

width: random(10px, 100px);
height: random(10px, 100px);

But this will generate squares right now, right? It seems to me that requiring a shared name to tie them together makes more sense than requiring different names to differentiate. As you say, if you accidentally end up with two random() functions with shared used values right now, they'll get accidentally tied together.

The important thing is to make sure that random()'s internal randomness value is part of its computed value so that it inherits correctly; but having each unnamed instance be unique actually makes more sense to me than making identical arguments return identical values... unless they're explicit about wanting to repeat a value, people usually want more randomness out of their random()s, not less, right?

@smfr
Copy link
Contributor Author

smfr commented Feb 19, 2025

To avoid that, I'd have to specify that the random value is, specifically, a 0-1 real, and then specify exactly how it's turned into the random value. (aka, scaled to the desired range, or converted to a 0-N integer for the step)

That's how my brain expected it to work. But maybe that's programmer brain.

@smfr
Copy link
Contributor Author

smfr commented Feb 19, 2025

See also #11747, which would be resolved in the min/max/step were not part of the random key.

@tabatkins
Copy link
Member

These arguments are pretty convincing, and being able to knock out #11747 would be nice. However...

But this will generate squares right now, right? It seems to me that requiring a shared name to tie them together makes more sense than requiring different names to differentiate. As you say, if you accidentally end up with two random() functions with shared used values right now, they'll get accidentally tied together.

The issue with making a "default" random() (without an explicit caching key) assume a unique key is that it's not very clear to authors when such a key would be generated. For example:

.rectangle {
  /* these would be different random values */
  width: random(10px, 100px);
  height: random(10px, 100px);
}

.rectangle-2 {
  /* these would be different random values */
  --size: random(10px, 100px);
  width: var(--size);
  height: var(--size);
}

@property --size2 { syntax: "<length>"; inherits: true; initial-value: 0px; }
.square {
  /* these would be the *same* random value, making squares */
  --size2: random(10px, 100px);
  width: var(--size);
  height: var(--size);
}

To an extent authors already have this confusion, since --foo: 1em; has different inherited behavior for registered vs unregistered custom props, but this random() behavior would make it visible even before inheritance. (Technically the same behavior distinction also occurs for values like 1em, it just doesn't matter in practical terms whether 1em resolves in the custom prop or in the substituting prop.)

This is how I arrived at the current caching behavior - it can result in accidentally linked random values sometimes, but it also ensures that authors don't have to care about when the substitution occurs.

So, hm, tradeoffs either way. Maybe it is better to force authors to think about the variable resolution time in return for having "more random" behavior by default and avoiding the value-resolution dependence.

@tabatkins
Copy link
Member

tabatkins commented Feb 24, 2025

Ah, right, the other reason I made the current behavior is it reduces the coupling to the precise source document. That is, currently, you can have multiple rules applying the same declaration to an element, and it's the exact same behavior as only having one of them apply. With "implicit unique key", you lose this: having multiple stylesheets applying the same styles to an element will give different results. This exposes things like stylesheet deduping, which currently are undetectable from inside the page. It also exposes the difference between applying a style to multiple elements via a selector and via style attribute. And the difference between .foo, .bar { width: random(); } and .foo { width: random(); } .bar { width: random(); }. All of these things are indistinguishable today, and I'm betting a lot of tooling implicitly depends on this.

The current behavior instead makes the caching key based purely on the source text itself, not its location, so you can't tell whether the same value is applied from several rules or just one, or if a repeated stylesheet is duped or deduped, etc.

The accidental linking of functions that happen to have the same arguments is just an unfortunate side effect. I included as much data as possible in the key precisely to make this less likely to happen by accident.

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

No branches or pull requests

3 participants