Skip to content

Commit

Permalink
[css-values-4] Add min()/max().
Browse files Browse the repository at this point in the history
  • Loading branch information
tabatkins committed Sep 1, 2017
1 parent 22687d6 commit 371d0a1
Showing 1 changed file with 117 additions and 45 deletions.
162 changes: 117 additions & 45 deletions css-values/Overview.bs
Expand Up @@ -1583,14 +1583,20 @@ Functional Notations</h2>
-->

<h3 id="calc-notation">
Mathematical Expressions: ''calc()''</h3>
Mathematical Expressions: ''calc()'', ''min()'', and ''max()''</h3>

The <dfn>calc()</dfn> function allows mathematical expressions
The <dfn>math functions</dfn>,
<dfn>calc()</dfn>, <dfn>min()</dfn>, and <dfn>max()</dfn>,
allow mathematical expressions
with addition (''+''), subtraction (''-''), multiplication (''*''), and division (''/'')
to be used as component values.
The ''calc()'' expression represents the result of the mathematical calculation it contains,
using standard operator precedence rules.
It can be used wherever
A ''calc()'' function represents the result of the mathematical calculation it contains,
using standard operator precedence rules;
a ''min()'' or ''max()'' function represents
the smallest (most negative) or largest (most positive), respectively,
comma-separated calculation it contains.

A [=math function=] can be used wherever
<<length>>,
<<frequency>>,
<<angle>>,
Expand All @@ -1599,8 +1605,10 @@ Mathematical Expressions: ''calc()''</h3>
<<number>>, or
<<integer>>
values are allowed.
Components of a ''calc()'' expression can be literal values or
''attr()'' or ''calc()'' expressions.
Components of a [=math function=] can be literal values,
other [=math functions=],
or other expressions, such as ''attr()'',
that evaluate to a valid argument type (like <<length>>).

<div class="example">
<pre>
Expand Down Expand Up @@ -1664,10 +1672,12 @@ Mathematical Expressions: ''calc()''</h3>
<h4 id='calc-syntax'>
Syntax</h4>

The syntax of a ''calc()'' function is:
The syntax of a [=math function=] is:

<pre class='prod'>
<<calc()>> = calc( <<calc-sum>> )
<<min()>> = min( <<calc-sum>># )
<<max()>> = max( <<calc-sum>># )
<dfn>&lt;calc-sum></dfn> = <<calc-product>> [ [ '+' | '-' ] <<calc-product>> ]*
<dfn>&lt;calc-product></dfn> = <<calc-value>> [ '*' <<calc-value>> | '/' <<calc-number-value>> ]*
<dfn>&lt;calc-value></dfn> = <<number>> | <<dimension>> | <<percentage>> | ( <<calc-sum>> )
Expand All @@ -1676,38 +1686,53 @@ Syntax</h4>
<dfn>&lt;calc-number-value></dfn> = <<number>> | ( <<calc-number-sum>> )
</pre>

In addition, <a href="https://www.w3.org/TR/css-syntax/#whitespace">white space</a>
In addition, [=whitespace=]
is required on both sides of the ''+'' and ''-'' operators.
(The ''*'' and ''/'' operaters can be used without white space around them.)

UAs must support ''calc()'' expressions of at least 20 terms,
UAs must support [=math function=] expressions of at least 20 terms,
where each <code>NUMBER</code>, <code>DIMENSION</code>, or <code>PERCENTAGE</code> is a term.
If a ''calc()'' expression contains more than the supported number of terms,
If a [=math function=] contains more than the supported number of terms,
it must be treated as if it were invalid.


<h4 id='calc-type-checking'>
Type Checking</h4>

A math expression has a <dfn>resolved type</dfn>, which is one of
A [=math function=] has a <dfn>resolved type</dfn>, which is one of
<<length>>,
<<frequency>>,
<<angle>>,
<<time>>,
<<percentage>>,
<<number>>, or
<<integer>>.
The <a>resolved type</a> must be valid for where the expression is placed;
The <a>resolved type</a> must be valid for where the [=math function=] is placed;
otherwise, the expression is invalid.
The <a>resolved type</a> of the expression is determined by the types of the values it contains.
<<number-token>>s are of type <<number>> or <<integer>>.
A <<dimension-token>>’s type is given by its unit
(''cm'' is <<length>>, ''deg'' is <<angle>>, etc.).
An ''attr()'' expression's type is given by its <<type-or-unit>> argument.

The <a>resolved type</a> of a [=math function=] is determined by the values and calculations it contains:

* <<number-token>>s are of type <<number>> or <<integer>>.
* A <<dimension-token>>’s type is given by its unit
(''cm'' is <<length>>, ''deg'' is <<angle>>, etc.).
* Functions’ types are defined by their specification
(nested ''calc()'' has its type determined by its own contents,
''attr()''’s type is determined by its <<type-or-unit>> argument,
etc).
* Larger calculations’ types are determined by the types of their arguments
and the rules for their operation,
as defined below.

A ''calc()'' expression's <a>resolved type</a> is the <a>resolved type</a> of the calculation it contains.
The <a>resolved type</a> of a ''min()'' or ''max()'' expression
is the <a>resolved type</a> of any of the calculations it contains.
If a ''min()'' or ''max()'' expression's arguments have different <a>resolved types</a>,
the expression is invalid.

Note: Because <<number-token>>s are always interpreted as <<number>>s or <<integer>>s,
"unitless 0" <<length>>s aren't supported in ''calc()''.
"unitless 0" <<length>>s aren't supported in [=math functions=].
That is, ''width: calc(0 + 5px);'' is invalid,
because it's trying to add a <<number>> to a <<length>>,
even though both ''width: 0;''
and ''width: 5px;''
are valid.
Expand Down Expand Up @@ -1778,38 +1803,42 @@ Type Checking</h4>
(as purely-numeric expressions can be evaluated without any additional
information at parse time).

Note: Algebraic simplifications do not affect the validity of the ''calc()'' expression or its resolved type.
Note: Algebraic simplifications do not affect the validity of a [=math function=] or its resolved type.
For example, ''calc(5px - 5px + 10s)'' and ''calc(0 * 5px + 10s)'' are both invalid
due to the attempt to add a length and a time.

<h4 id='calc-computed-value'>
Computed Value</h4>

The computed value of a ''calc()'' expression is the expression
The computed value of a ''calc()'' function is the expression
with all components computed.
The computed value of a ''min()'' or ''max()'' function
is the comma-separated list of expressions,
with each expression having all its component computed.

Where percentages are not resolved at computed-value time,
they are not resolved in ''calc()'' expressions,
they are not resolved in [=math functions=],
e.g. ''calc(100% - 100% + 1em)'' resolves to ''calc(1em + 0%)'',
not to ''1em''.
If there are special rules for computing percentages in a value
(e.g. <a href="https://www.w3.org/TR/CSS21/visudet.html#the-height-property">the <css>height</css> property</a>),
they apply whenever a ''calc()'' expression contains percentages.
they apply whenever a [=math function=] contains percentages.

<div class=note>
Note: The serialization rules do not preserve the structure of the computation,
so implementations can simplify the expressions
further than what is required here
when storing the values internally;
in particular, all ''calc()'' expressions can be reduced
in particular, all [=math function=] expressions can be reduced
to a sum of a <<number>>, a <<percentage>>, and some <<dimension>>s,
eliminating all multiplication or division,
and combining terms with identical units.

At this time, all units can be absolutized
to a single unit per type at computed-value time,
so at that point the ''calc()'' expression can be reduced
to just a <<number>>, a <<percentage>>, and a single absolute <<dimension>> of the appropriate type.
so at that point the [=math function=] can be reduced
to just a <<number>>, a <<percentage>>, and a single absolute <<dimension>> of the appropriate type,
per expression.
</div>

<div class='example'>
Expand Down Expand Up @@ -1848,7 +1877,7 @@ Computed Value</h4>
<h4 id='calc-range'>
Range Checking</h4>

Parse-time range-checking of values is not performed within ''calc()'',
Parse-time range-checking of values is not performed within [=math functions=],
and therefore out-of-range values do not cause the declaration to become invalid.
However, the used value resulting from an expression
must be clamped to the range allowed in the target context.
Expand Down Expand Up @@ -1876,33 +1905,76 @@ Serialization</h4>

Issue: This section is still <a href="https://lists.w3.org/Archives/Member/w3c-css-wg/2016AprJun/0239.html">under discussion</a>.

To serialize a ''calc()'' value:
<div algorithm="serialize a calc() value">
To <dfn>serialize a ''calc()'' value</dfn>

1. [=Simplify the expression=] inside of it.

1. Simplify the expression by:
2. If this simplification process results in only a single value
(one <<number>>, one <<dimension>>, or one <<percentage>>),
and the value being serialized is a <a>computed value</a> or later,
serialize it just as that one value,
without the ''calc()'' wrapper.
If this value is outside the allowed range for the context,
it must be clamped to the nearest allowed value.

3. Otherwise,
[=serialize the summation=],
prefix the result with "calc("
and suffix it with ")",
then return it.
</div>

* Replacing nested ''calc()'' values with parentheses containing their contents
* Resolving all multiplications and divisions
* Combining identical units
<div algorithm="serialize a min()/max() value">
To <dfn lt="serialize a min() value|serialize a max() value">serialize a ''min()'' or ''max()'' value</dfn>:

1. For each comma-separated expression inside of it,
[=simplify the expression=].

2. Let |s| initially be "min(" or "max(", as appropriate.

3. [=serialize the summation|Serialize each summation=],
then join them into a single string,
with ", " between each term.
Append the result to |s|.

4. Append ")" to |s|,
then return it.
</div>

<div algorithm="simplify an expression">
To <dfn lt="simplify an expression | simplify the expression" for="math function">simplify an expression</dfn>:

1. Replace any ''calc()'' values with parentheses containing their contents.
2. Resolve all multiplications and divisions.
3. Combine identical units.
4. Recurse into ''min()'' or ''max()'' values.
5. Return the result.

Note: The value-computation process can transform disparate units into identical ones.
For example, ''em'' and ''px'' are obviously different at specified-value time,
but at computed-value time they're both absolutized to ''px''.

The result must be a summation of unique units.
The result must be a summation of unique units and/or [=math functions=].
(Terms with a value of zero <strong>must</strong> be preserved in this summation.)
</div>

2. If this simplification process results in only a single value
(one <<number>>, one <<dimension>>, or one <<percentage>>),
and the value being serialized is a <a>computed value</a> or later,
serialize it just as that one value,
without the ''calc()'' wrapper.
If this value is outside the allowed range for the context,
it must be clamped to the nearest allowed value.

3. Otherwise, serialize as a ''calc()'' containing the summation,
with the units ordered <a>ASCII case-insensitive</a> alphabetically,
the number (if present) coming before all units,
and the percentage (if present) coming after all units.
<div algorithm="serialize a summation">
To <dfn lt="serialize a summation|serialize the summation" for="math function">serialize a summation</dfn>:

1. Sort the terms in the following order:

1. The number, if present
2. The dimensions, ordered by their units <a>ASCII case-insensitive</a> alphabetically
3. The percentage, if present
4. The ''min()'' and ''max()'' functions,
in the order they appeared in the original expression.

2. Serialize all the terms,
then join them into a single string,
with " + " between each term.
Return the result.
</div>

<div class="example">
For example, ''calc(20px + 30px)'' would serialize as ''calc(50px)'' as a specified value,
Expand Down

1 comment on commit 371d0a1

@tomhodgins
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's so beautiful 😭

Thank you for adding this!

Please sign in to comment.