Re: [csswg-drafts] [css-values] random() function (#2826)

As far as I can tell, there are only a few cases, we'd need to account for, which could cover three different random functions, none of which would conflict with CSSWG's ruling, as @tabatkins mentioned:
>> While the CSSWG just decided https://github.com/w3c/csswg-drafts/issues/4688 that a direct equivalent of ECMA-262ʼs Math.random() function, which works on a range with static minimum 0.0 and maximum 1.0, was not appropriate for CSS,
>
> That's not what we decided. I purposely omitted random() from the discussion about other math functions because it's not a math function - it's not a pure function of its arguments, but rather a stateful function that you need to carefully control the invocation of; that's trivial in a traditional eager language like JavaScript, somewhat harder in a lazy language like Haskell, and very complex in a declarative language like CSS.

As far as I can tell, most uses can be consolidated into two different functions, **(1)** one general syntax to cover both integers and floats by exposing an **_optional_** third `interval` argument which I believe **_should_** also require a unit to interpolate between units that are explicitly convertible (e.g. `s` vs. `ms`), and **(2)** a function to account for selecting a random item from a provided list of items:

### 1. `random()` (generating random integers and floats)
    
**Syntax**
```css
* {
  --random-value: random(start, max[, interval = 1 * current unit]);
}
```
  **Examples**
```css
* {
  /* simple: random numbers */
  --random-integer: random(0, 10);
  --random-float: random(0, 10, .1);
  --random-integer-with-units: random(0px, 10px);
  --random-float-with-units: random(0rem, 10rem, .1rem);
  --random-percent: random(0%, 100%);
  --random-even-integer: random(0, 100, 2);
  --random-odd-integer: random(1, 100, 2);

  /* complex: generating random type-values */
  --random-rgb: rgb(random(0, 255) random(0, 255) random(0, 255) / random(0, 1, .1));
  --random-hsl: hsl(random(0deg, 360deg) random(0%, 100%) random(0%, 100%) / random(0, 1, .1));
  --random-length: random(.5rem, 10rem, .01rem);
  --random-time-s: random(.25s, 5s, .05s);
  --random-time-ms: random(250ms, 5000ms, 50ms);
  --random-time-s-AND-ms: random(250ms, 5s, 50ms);
}
```

Regarding the `--random-odd-integer` example above, I don't think the max number should not actually need **_match_** to match a number in the sequence `start + (interval * N)`. Rather, when the number matches OR exceeds the max number, the loop/search ends, so `random(1, 100, 2)` is effectively identical to `random(1, 99, 2)`.

This way, the `max` value could be set using CSS custom properties, and then an even or odd number could be randomly picked from between those numbers using this pattern (so long as the `start` value is known), for example—

```css
* {
  --start: 12;
  --max: 27;
  --random-integer-between: random(var(--start), var(--max));
  --random-even-integer-between: random(var(--start), var(--max), 2);
  --random-odd-integer-between: random(var(--start) + 1, var(--max), 2);
}
```

### 2. `random-pick()` (random item from list)

**Syntax**
```css
* {
  --random-item-from-list: random-pick(item1[, item2, item3, ...itemN]);
}
```
**Examples**
```css
* {
  --random-integer-from-list: random-pick(0, 2, 7, 15);
  --random-float-from-list: random-pick(0.4, 2.1, 5.2);
  --random-length-from-list: random-pick(20px, 1.5rem, 11vmin);
  --random-color-from-list: random(green, orange, rebeccapurple, currentcolor);
  --random-image-from-list: random-pick(
    url(image1.png),
    linear-gradient(red, blue),
    url(data:image/gif;base64,…)
  );
```

## Memory storage/caching and randomization-evaluation-time 🧩⁉️

One additional piece missing from this puzzle is a solution to the question of **_when_** a random number is stored/cached.

For instance, in the below example, do `#example-1` and `#example-2` have the same `margin-top` value, or is the value randomized each time that variable is used?
```css
:root {
  --random-length: random(.25rem, 6rem, .25rem); /* .75rem */
}
#example-1 {
  margin-top: var(--random-length); /* is this also .75rem? */
}
#example-2 {
  margin-top: var(--random-length); /* is this ALSO .75rem? */
}
```

The same question could be poses for inheritance. Does a child that inherits a randomly generated value inherit a newly generated random value or the same one.
```css
#example {
  margin-top: random(.25rem, 6rem, .25rem); /* 4.75rem */
}
#example > * {
  margin-top: inherit; /* is this also 4.75rem? */
}
```

This question certainly warrants a deeper discussion, but I believe that by default, once a random is generated, both should be stored in memory attached to that value— **(1)** the randomly generated value, and **(2)** the `random()` formula used to generate it. When referenced plainly in another property or by inheritance, the same—previously generated—random value should be used. So using the same examples from above, this is how that could look:
```css
:root {
  --random-length: random(.25rem, 6rem, .25rem); /* 2.75rem */
}
#example-1 {
  margin-top: var(--random-length); /* 2.75rem */
}
#example-2 {
  margin-top: var(--random-length); /* 2.75rem */
}
```
```css
#example {
  margin-top: random(.25rem, 6rem, .25rem); /* 5.25rem */
}
#example > * {
  margin-top: inherit; /* 5.25rem */
}
```

But what about times you want to want to define a `random()` formula once and then reference it each time you want a random number resulting from that formula. We clearly don't want redundancy like this:
```css
:root {
  --random-length-1: random(.25rem, 6rem, .25rem); /* 2.25rem */
  --random-length-2: random(.25rem, 6rem, .25rem); /* 5.5rem */
}
#example-1 {
  margin-top: var(--random-length-1); /* 2.25rem */
}
#example-2 {
  margin-top: var(--random-length-2); /* 5.5rem */
}
```

Instead, I propose extending the use of `random()` to accept a randomization formula as a single argument to re-generate a new number using the original formula once afresh. Alternatively, this functionality could be moved to a separate function like `shuffle()` (or `rerand()` or something along those lines).

For the below examples, I will be using `random()` as that is my preferred API, further empowering the `random()` function. Using the same examples again, for consistency:
```css
:root {
  --random-length: random(.25rem, 6rem, .25rem); /* 2.75rem */
}
#example-1 {
  margin-top: var(--random-length); /* 2.75rem */
}
#example-2 {
  margin-top: random(var(--random-length)); /* 2.75rem */
}
```
```css
#example {
  margin-top: random(.25rem, 6rem, .25rem); /* 5.25rem */
  margin-right: random(.25rem, 6rem, .25rem); /* .25rem */
  margin-bottom: random-pick(2rem, 5em, 1vmin, 15px); /* 5em */
  margin-left: 10px; /* .25rem */
}
#example > * {
  /* 5.25rem - same value when NOT wrapping result in `random()` */
  margin-top: inherit;

  /* .75rem - new random value when wrapping result in `random()` */
  margin-right: random(inherit);

  /* 1vmin - item freshly picked from list when wrapping result in `random()` */
  margin-bottom: random(inherit);

  /* 10px - when `random()` wraps a non-random intrinsic value like inherit, the original value is used */
  margin-left: random(inherit);
}
```

Furthermore, this would also naturally support lists stored in variables for `random-pick()`:
```css
:root {
  --primary-colors: red, yellow, blue;
}
* {
  --random-primary-color: random-pick(var(--primary-colors));
}
```

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


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

Received on Tuesday, 14 June 2022 16:28:44 UTC