Visitar URL original
[css-values-5] Caching CSS random() values in if() · Issue #13662 · w3c/csswg-drafts · GitHub
Skip to content

[css-values-5] Caching CSS random() values in if() #13662

Closed

Description

@tursunova

There is an existing issue about random() and other tree scope functions used outside element context, so it is unclear how random() should behave in @container style queries now. However we can also use random() in style() condition inside if():

color: if(style(random(--rand property index, 1, 1000) = random(--rand property index, 1, 1000)): green; else: red;);

In the example above it's unclear whether both random() values should use same or different index for caching, in other words, what is the same property value in the statement below:

A nullable integer index: the index of the random function among other random functions in the same property value, if index or auto is specified; otherwise null.

if(style(random(1) = random(2)): random(3); else: random(4));

So are random(1), random(2), random(3) and random(4) all different property values?

cc: @tabatkins

Activity

  1. tabatkins commented on Apr 15, 2026

    @tabatkins
    Member

    Hm. So, here's my initial thoughts.

    The value clauses of the if() are unresolved, like fallbacks. Those shouldn't resolve their values until they're parsed for real as part of the property, after substitution. (And I should specify this is the case for all arb-sub functions.) So both of those (random(3) and random(4)) should end up with a random-ua-ident of ua-color-1.

    The test clauses have to resolve at computed-value time, before substitution occurs (during substitution, in fact). My kneejerk reaction was to just ban them, but no, style(random(0, 1) < .5)) is a meaningful test, and you can do that manually by putting the random() in a typed custom property and then using style(var(--my-rand) < .5), so banning doesn't help.

    So, we just need to define a random-ua-ident for it that gives useful behavior. It should include the name of the property the if() is in, so values aren't accidentally linked across properties, but it should be distinct from the values that actually show up in the property, again to prevent accidental linkage. So I think going with a scheme similar to what you proposed for custom functions should work - generate a random-ua-ident of "ua-if()-color-1".

    Only remaining question is how we should number the values. Does each test-clause restart the numbering, so they're linked across clauses and potentially across if() functions? I think the answer should be no, so we should keep a single incrementing counter across all instances in all test clauses. What about between separate if()s in the same property? I think these should be distinct as well, so we should chain the incrementing counter across all if if()s.

    Maybe this can just be a generic arb-sub functionality, then: if a random() is evaluated within an arb-sub function as part of substitution (rather than as the substitution value itself), its random-ua-ident is something like "ua-early-PROPERTY-INDEX", where PROPERTY is the name of the property it's used in, and INDEX is the nth usage of random() across all arb-subs in the property, in parse order. So every usage in every arb-sub function is distinct within a property.

  2. tursunova commented on Apr 23, 2026

    @tursunova
    MemberAuthor

    Did I understood your proposal correctly. So let's say we have:

    --rand: random(2)
    --x: random(1) var(--rand)  if(style(--rand): random(3); else: random(4)) if(style(random(5) = random(6)): random(7); else: random(8))
    

    Then will we have following random-ua-idents?

    random(1) -> ua---x-1
    var(--rand) -> ua---x-2
    style(--rand): -> ua--early---x-1
    random(3) -> ua---x-3
    random(4) -> ua---x-3
    random(5) -> ua-early---x-2
    random(6) -> ua-early---x-3
    random(7) -> ua---x-4
    random(8) -> ua---x-4
    
  3. tabatkins commented on Jun 4, 2026

    @tabatkins
    Member

    Reformatting just for readability:

    --rand: random(2)
    --x: random(1) 
         var(--rand)
         if(
           style(--rand): random(3); 
           else: random(4))
         if(
           style(random(5) = random(6)): random(7); 
           else: random(8))
         ;
    

    So, assuming --x has a grammar like <number>+ so it actually evaluates the random()s post-substitution, while --rand is grammarless and doesn't evaluate until used in the if() test, then... yes, all your examples are correct per my proposal.

    • the three values inside the tests use the ua-early-* pattern, with successive numbers, so they're distinct
    • the rest all use ua-*, assigning their numbers according to whichever actually get used, so 3/4 both use -3, 7/8 both use -4

    Does this feel okay impl-wise?


    I suppose we should note that while random() is a math function, random-item() is itself an arb-sub and should get the same ua-early behavior, even when it's used in values.

  4. css-meeting-bot commented on Jun 10, 2026

    @css-meeting-bot
    Member

    The CSS Working Group just discussed [css-values-5] Caching CSS random() values in if(), and agreed to the following:

    • RESOLVED: When random function needs to be evaluated as part of arbitrary substitution function, use a different UA ident, using own set of indices, unrelated to later index resolution
    The full IRC log of that discussion <dshin-moz> TabAtkins: Next one is similar, but different context
    <dshin-moz> ... What happens when you use random in arbitrary subtitution functions with early resolution?
    <dshin-moz> ... e.g. test of an if()
    <TabAtkins> if(style(random(1) = random(2)): random(3); else: random(4));
    <dshin-moz> ... 4 different random values, need to resolve first two during substitution, 3rd and 4th may not be resolved
    <dshin-moz> ... We don't want to get these tied together
    <dshin-moz> ... By default, we capture the index of the random usage. Using the same random function 2 times in the same property will give the same value by default
    <dshin-moz> ... How do we capture in case when some are resolved early, and some resolve later, as part of the value?
    <dshin-moz> ... If there are multiple ifs, how do the work?
    <dshin-moz> ... My proposal is that if we're resolving randoms early, we capture that we are resolving early, into UA random ident, to force them into later usages
    <dshin-moz> ... In the example, 1 and 2 are captured as an index, 3 and 4 are unresolved and substituted later
    <TabAtkins> calc( if(style(random(1) = random(2)): random(3); else: random(4)) + random(5))
    <dshin-moz> ... In the longer example, where we mix random(3) and (4) are not resolved early, random(5) is out of if
    <dshin-moz> ... end result, (1) and (2) are captured as early, different random values. (3) and (4) dont capture anything yet
    <TabAtkins> z-index: calc(random(3) + random(5))
    <Rossen> q
    <dshin-moz> ... Final result only contains the random from the if path + the one out of if
    <dshin-moz> ... So we don't tie early-resolved random invocations to later-resolved random calls
    <dshin-moz> Rossen: Ask is to accept this proposal?
    <dshin-moz> TabAtkins: Yes
    <dshin-moz> PROPOSED: When random function needs to be evaluated as part of arbitrary substitution function, use a different UA ident, using own set of indices, unrelated to later index resolution
    <dshin-moz> Rossen: Any objections?
    <flackr> q+
    <Rossen> ack flackr
    <dshin-moz> flackr: None of this applies if dev creates shared random context?
    <dshin-moz> TabAtkins: Correct, this is the UA default behaviour
    <dshin-moz> flackr: It's not really observable, then two stages of evaluation are different?
    <dshin-moz> TabAtkins: Yes
    <dshin-moz> flackr: That sounds reasonable
    <TabAtkins> (it will have disappeared as part of substitution, before the next point it's observable)
    <dshin-moz> RESOLVED: When random function needs to be evaluated as part of arbitrary substitution function, use a different UA ident, using own set of indices, unrelated to later index resolution
  5. added a commit that references this issue on Aug 17, 2026
  6. added a commit that references this issue on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions