forked from w3c/csswg-drafts
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathOverview.bs
1006 lines (838 loc) · 34 KB
/
Overview.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<pre class='metadata'>
Title: CSS Functions and Mixins Module
Shortname: css-mixins
Level: 1
Status: ED
Work Status: Exploring
Group: CSSWG
ED: https://drafts.csswg.org/css-mixins/
TR: https://www.w3.org/TR/css-mixins-1/
Editor: Miriam E. Suzanne, Invited Expert, http://miriamsuzanne.com/contact, w3cid 117151
Editor: Tab Atkins-Bittner, Google, http://xanthir.com/contact/, w3cid 42199
Abstract: This module provides the ability to define custom functional notations.
Default Highlight: css
Ignored Terms: cssText
</pre>
<pre class=link-defaults>
spec:infra; type:dfn; text:list
spec:infra; type:dfn; for:list; text:append
spec:css-cascade-5; type:dfn;
text:inherit
text:computed value
text:specified value
spec:css-properties-values-api; type:dfn;
text:supported syntax component name
text:syntax component
spec:css-syntax-3; type:dfn;
text:declaration; for:CSS
text:descriptor;
spec:css-values-4; type:dfn;
text:keyword;
text:identifier;
spec:css-values-5; type:dfn; text:invalid at computed-value time
spec:cssom-1; type:dfn; text:specified order;
</pre>
<style>
/* Put nice boxes around each algorithm. */
[data-algorithm]:not(.heading) {
padding: .5em;
border: thin solid #ddd; border-radius: .5em;
margin: .5em calc(-0.5em - 1px);
}
[data-algorithm]:not(.heading) > :first-child {
margin-top: 0;
}
[data-algorithm]:not(.heading) > :last-child {
margin-bottom: 0;
}
[data-algorithm] [data-algorithm] {
margin: 1em 0;
}
</style>
<!-- Big Text: intro
████ █ █▌ █████▌ ████▌ ███▌
▐▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌
▐▌ ██▌ █▌ █▌ █▌ █▌ █▌ █▌
▐▌ █▌▐█ █▌ █▌ ████▌ █▌ █▌
▐▌ █▌ ██▌ █▌ █▌▐█ █▌ █▌
▐▌ █▌ █▌ █▌ █▌ ▐█ █▌ █▌
████ █▌ ▐▌ █▌ █▌ █▌ ███▌
-->
Introduction {#intro}
=====================
<em>This section is not normative.</em>
Note: At this time, this specification only defines [=custom functions=],
which operate at the level of CSS values.
It is expected that it will define "mixins" later,
which are functions that operate at the style rule level.
[=Custom properties=] give authors a lot of power
to define useful, sometimes complex values
in one place,
and then re-use them across their stylesheet.
They can vary across the document,
or based on Media Queries or other conditionals,
making them very flexible and responsive.
However, their values are <em>fixed</em> at the point they're defined,
unable to be changed except by fully overriding their previous definition:
a ''--shadow: 2px 2px var(--shadow-color)'' declaration
takes its ''--shadow-color'' value from the element it's declared on,
and later changes to ''--shadow-color'' on descendant elements
don't alter the value of ''--shadow'' for them;
they continue to use the shadow color defined where ''--shadow'' was defined.
This is a common source of confusion for authors
making heavy use of composite variables like this.
[=Custom functions=] allow authors the same power as [=custom properties=],
but <em>parameterized</em>:
they have the same flexibility and conditionality as a [=custom property=] definition,
but take values from other custom properties
(or explicitly as arguments)
<em>at the point of use</em>.
For example, instead of a ''--shadow'' [=custom property=],
a ''--shadow()'' [=custom function=] could be defined instead,
like:
<xmp highlight=css>
@function --shadow(--shadow-color <color> : inherit) {
/* If --shadow-color argument isn't passed,
or doesn't successfully parse as a <color>,
try to use the --shadow-color *property*
from the element instead */
/* var(--shadow-color) refers to the --shadow-color parameter,
rather than a custom property,
but can still use a fallback value as normal */
result: 2px 2px var(--shadow-color, black);
}
.foo {
--shadow-color: blue;
box-shadow: --shadow(); /* produces a blue shadow */
/* or just */
box-shadow: --shadow(blue);
}
</xmp>
<!-- Big Text: @function
████▌ █████▌ █▌ █▌ █ █▌ ███▌ █████▌ ████ ███▌ █ █▌
█▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌ █▌
█▌▐█ █▌ █▌ █▌ █▌ ██▌ █▌ █▌ █▌ ▐▌ █▌ █▌ ██▌ █▌
█▌▐█ █▌ ████ █▌ █▌ █▌▐█ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌▐█ █▌
█▌ ██▌ █▌ █▌ █▌ █▌ ██▌ █▌ █▌ ▐▌ █▌ █▌ █▌ ██▌
█▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌ █▌
████▌ █▌ ███▌ █▌ ▐▌ ███▌ █▌ ████ ███▌ █▌ ▐▌
-->
Defining Custom Functions {#defining-custom-functions}
======================================================
A [=custom function=] can be thought of as an advanced [=custom property=],
which instead of being substituted by a single fixed value,
computes its substitution value based on [=function parameters=]
and the value of [=custom properties=] at the point it's invoked.
Rather than the ''var()'' syntax that [=custom properties=] use for substitution,
[=custom functions=] are invoked by <<dashed-function>> syntax,
allowing additional values to be passed as arguments.
<div class='example'>
A simple [=custom function=] to negate a value can be defined as follows:
<pre class='lang-css'>
@function --negative(--value) {
result: calc(-1 * var(--value));
}
</pre>
Then, that function can be referenced with ''--negative()''
in some declaration:
<pre class='lang-css'>
html {
--gap: 1em;
padding: --negative(var(--gap));
/* or by passing the value explicitly, like: */
padding: --negative(1em);
}
</pre>
</div>
<<dashed-function>>s are [=arbitrary substitution functions=],
like ''var()''.
Their presence in a property's value
causes it to be assumed valid at parse time,
and only evaluated and parsed at computed-value time,
after [=arbitrary substitution=] has occurred.
The <dfn>@function</dfn> Rule {#function-rule}
----------------------------------------------
The ''@function'' rule defines a <dfn>custom function</dfn>,
and consists of a name,
a list of [=function parameter|parameters=],
a <dfn for="custom function">function body</dfn>,
and optionally a <dfn for="custom function">return type</dfn> described by a [=syntax definition=].
Each <dfn>function parameter</dfn> consists of a name (<<custom-property-name>>);
optionally a <dfn>parameter type</dfn>, described by a [=syntax definition=];
and optionally a <dfn>default value</dfn>.
<pre class="prod def" nohighlight>
<@function> = @function <<function-token>> <<function-parameter>>#? )
[ returns <<css-type>> ]?
{
<<declaration-rule-list>>
}
<dfn><<function-parameter>></dfn> = <<custom-property-name>> <<css-type>>? [ : <<declaration-value>> ]?
<dfn><<css-type>></dfn> = <<syntax-component>> | <<type()>>
<dfn function lt="type()" for="@function"><type()></dfn> = type( <<syntax>> )
</pre>
<h4 id=function-preamble>
The Function Preamble</h4>
The <<function-token>> production
must start with two dashes (U+002D HYPHEN-MINUS),
similar to <<dashed-ident>>,
or else the definition is invalid.
The name of the resulting [=custom function=] is given by the name of the <<function-token>>,
the optional [=function parameters=]
are given by the <<function-parameter>> values
(defaulting to an empty set),
and the optional [=custom function/return type=] is given by the <<css-type>> following the <css>returns</css> keyword
(defaulting to ''type(*)'').
<div class='example'>
If the <<css-type>> of a [=function parameter=] or [=custom function/return type=]
can be described by a single <<syntax-component>>,
then the ''type()'' function can be omitted:
<xmp class='lang-css'>
@function --foo(--a <length>) { /* ... */ }
@function --foo(--a <color>) { /* ... */ }
@function --foo(--a <length>+) { /* ... */ }
</xmp>
However,
any <<syntax>> that requires a <<syntax-combinator>>
needs to be wrapped in the ''type()'' function:
<xmp class='lang-css'>
@function --foo(--a type(<number> | <percentage>)) { /* ... */ }
</xmp>
</div>
The name of a ''@function'' rule is a [=tree-scoped name=].
If more than one ''@function'' exists for a given name,
then the rule in the stronger cascade layer wins,
and rules defined later win within the same layer.
If the [=function parameters=]
contain the same <<custom-property-name>> more than once,
then the ''@function'' rule is invalid.
<h4 id=function-body>
The Function Body</h4>
The body of a ''@function'' rule accepts [=conditional group rules=],
such as ''@media''.
Additionally, it accepts the following descriptors:
* The '@function/result' descriptor,
which determines the result of [=evaluating a custom function|evaluating the function=].
If no '@function/result' descriptor exists,
the function is valid,
but always returns the [=guaranteed-invalid value=].
* [=Custom properties=],
providing <dfn>local variables</dfn>.
Unknown descriptors are invalid and ignored,
but do not make the ''@function'' rule itself invalid.
The '@function/result' Descriptor {#the-result-descriptor}
----------------------------------------------------------
<pre class='descdef'>
Name: result
Value: <<declaration-value>>?
For: @function
Initial: n/a (see prose)
</pre>
The '@function/result' descriptor
defines the result of [=evaluate a custom function|evaluating=]
the [=custom function=] defined by its ''@function'' rule.
Using ''var()'' functions,
it can reference [=function parameters=], [=local variables=],
as well as other [=custom functions=] via <<dashed-function>>s.
The '@function/result' descriptor itself does not have a type,
but its [=resolve function styles|resolved=] value is type-checked
during the [=substitute a dashed function|substitution=] of a <<dashed-function>>.
Arguments & Local Variables {#args}
-----------------------------------
<em>This section is non-normative.</em>
Within a [=custom function's=] [=function body=],
the ''var()'' function can access
[=local variables=]
(the [=custom properties=] defined in the [=function body=]),
[=function parameters=]
(the values passed to the function, or set to default values),
and [=custom properties=] defined at the <em>call site</em>
(an element, or another [=custom function=]).
In that list, earlier things "win" over later things of the same name--
if you have a [=local variable=] named '--foo',
''var(--foo)'' will be substituted by that [=local variable=],
not by an argument or a custom property defined outside.
The other values can still be <em>accessed</em>, however:
setting the '--foo' local variable to ''initial''
will resolve it to the '--foo' parameter,
while ''inherit'' will resolve it
to the '--foo' custom property from the call site.
<div class='example'>
A [=custom function=] can access [=local variables=]
and [=function parameters=]
from functions higher up in the call stack:
<xmp class='lang-css'>
@function --outer(--outer-arg) {
--outer-local: 2;
result: --inner();
}
@function --inner() returns <number> {
result: calc(var(--outer-arg) + var(--outer-local));
}
div {
z-index: --outer(1); /* 3 */
}
</xmp>
Similarly, [=custom properties=] are implicitly available:
<xmp class='lang-css'>
@function --double-z() returns <number> {
result: calc(var(--z) * 2);
}
div {
--z: 3;
z-index: --double-z(); /* 6 */
}
</xmp>
But [=function parameters=] "shadow" [=custom properties=],
and [=local variables=] "shadow" both:
<xmp class='lang-css'>
@function --add-a-b-c(--b, --c) {
--c: 300;
result: calc(var(--a) + var(--b) + var(--c));
/* uses the --a from the call site's custom property,
the --b from the function parameter,
and the --c from the local variable */
}
div {
--a: 1;
--b: 2;
--c: 3;
z-index: --add-a-b-c(20, 30); /* 321 */
}
</xmp>
</div>
<!-- Big Text: using
█▌ █▌ ███▌ ████ █ █▌ ███▌
█▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌ █▌
█▌ █▌ █▌ ▐▌ ██▌ █▌ █▌
█▌ █▌ ███▌ ▐▌ █▌▐█ █▌ █▌ ██▌
█▌ █▌ █▌ ▐▌ █▌ ██▌ █▌ █▌
█▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌ █▌
███▌ ███▌ ████ █▌ ▐▌ ███▌
-->
Using Custom Functions {#using-custom-functions}
================================================
Similar to how the value of a [=custom property=] can be substituted
into the value of another property with ''var()'',
the result of a [=custom function=] evaluation can be substituted
into the value of a property
with a <<dashed-function>>.
A <dfn><<dashed-function>></dfn> is a [=functional notation=]
whose function name starts with two dashes (U+002D HYPHEN-MINUS).
Its syntax is:
<pre class="prod informative" nohighlight>
<dashed-function> = --*( <<declaration-value>>#? )
</pre>
A <<dashed-function>> can only be used where ''var()'' is allowed.
If a property contains one or more <<dashed-function>>s,
the entire property’s grammar must be assumed to be valid at parse time.
At computed-value time,
every <<dashed-function>> must be [=substitute a dashed function|substituted=]
before finally being checked against the property's grammar.
Note: Within the body of a [=custom function=],
''var()'' functions might resolve differently
than on the element the <<dashed-function>> is used on.
See [[#evaluating-custom-functions]].
A <<dashed-function>> is evaluated in some context:
either in a property value on an element
(or in a descriptor that is eventually treated like a property on an element,
such as in ''@keyframes''),
or in a descriptor in the [=function body=] of another [=custom function=]
that is being applied to a "hypothetical" element.
Either way, this provides a <dfn>calling context</dfn>,
which contains the property or descriptor name containing the <<dashed-function>>,
and the element (or "hypothetical" element) that property/descriptor is being applied to.
As [=calling contexts=] are nested by <<dashed-function>> evaluations
<em>inside of</em> [=custom functions=],
a [=calling context's=] <dfn for="calling context">root element</dfn>
is the real element at the root of the [=calling context=] stack.
<div algorithm>
To <dfn>substitute a dashed function</dfn> in a value,
with |dashed function| being a <<dashed-function>>:
1. Let |function| be the result of dereferencing
the |dashed function|'s name as a [=tree-scoped reference=].
If no such name exists, return failure.
2. [=substitute arbitrary substitution functions|Substitute=]
any [=arbitrary substitution functions=]
within |dashed function|'s arguments,
then parse it as ''<<declaration-value>>#''
and let |arguments| be the result
(a comma-separated list of CSS values).
3. If |dashed function| is being substituted into a property on an element,
let |calling context| be a [=calling context=]
with that element and that property
Otherwise, it's being substituted into a descriptor
on a "hypothetical element",
while evaluating another [=custom function=].
Let |calling context| be a [=calling context=]
with that "hypothetical element" and that descriptor.
5. [=Evaluate a custom function=],
using |function|, |arguments|, and |calling context|,
and replace the <<dashed-function>> with the [=equivalent token sequence=]
of the value resulting from the evaluation.
</div>
If [=substitute a dashed function=] fails,
and the substitution is taking place on a property's value,
then the declaration containing the <<dashed-function>> becomes
[=invalid at computed-value time=].
<div class='example'>
A [=comma-containing productions|comma-containing value=]
may be passed as a single argument
by wrapping the value in curly braces, <code>{}</code>:
<pre class='lang-css'>
@function --max-plus-x(--list, --x) {
result: calc(max(var(--list)) + var(--x));
}
div {
width: --max-plus-x({ 1px, 7px, 2px }, 3px); /* 10px */
}
</pre>
</div>
Evaluating Custom Functions {#evaluating-custom-functions}
----------------------------------------------------------
[=Custom functions=] are evaluated by, essentially,
pretending their function body is a [=style rule=]
being applied to a hypothetical element,
resolving styles as normal,
and then returning the value of the '@function/result' descriptor on that hypothetical element.
The hypothetical element "inherits" the values of all custom properties
as if it were a child of its [=calling context=],
with its [=function parameters=] overriding "inherited" custom properties of the same name.
<div algorithm>
To <dfn>evaluate a custom function</dfn> |custom function|,
given a [=calling context=] |calling context|
and a list of CSS values |arguments|,
returning a CSS value:
1. If the number of items in |arguments|
is greater than the number of [=function parameters=] in |custom function|,
return the [=guaranteed-invalid value=].
2. Let |registrations| be an initially empty set of [=custom property registrations=].
3. For each [=function parameter=] of |custom function|,
create a [=custom property registration=]
with the parameter's name,
a syntax of the [=parameter type=],
an inherit flag of "true",
and no initial value.
Add the registration to |registrations|.
4. If |custom function| has a [=custom function/return type=],
create a [=custom property registration=]
with the name "return"
(violating the usual rules for what a registration's name can be),
a syntax of the [=custom function/return type=],
an inherit flag of "false",
and no initial value.
Add the registration to |registrations|.
5. Let |argument rule| be an initially empty [=style rule=].
6. For each [=function parameter=] of |custom function|:
1. Let |arg value| be the value of the corresponding argument in |arguments|,
or the [=guaranteed-invalid value=] if there is no corresponding argument.
2. Let |default value| be the parameter's [=default value=].
3. Add a [=custom property=] to |argument rule|
with a name of the parameter's name,
and a value of ''first-valid(|arg value|, |default value|)''.
7. [=Resolve function styles=] using |argument styles|, |registrations|, and |calling context|.
Let |argument styles| be the result.
8. Let |body rule| be the [=function body=] of |custom function|,
as a [=style rule=].
9. For each [=custom property registration=] of |registrations|,
set its initial value
to the corresponding value in |argument styles|,
set its syntax
to the [=universal syntax definition=],
and prepend a [=custom property=] to |body rule|
with the property name and value in |argument styles|.
10. [=Resolve function styles=] using |body rule|, |registrations|, and |calling context|.
Let |body styles| be the result.
11. Return the value of the '@function/result' property in |body styles|.
</div>
<div algorithm>
To <dfn>resolve function styles</dfn>,
given a style rule |rule|,
a set of [=custom property registrations=] |registrations|,
and a [=calling context=] |calling context|,
returning a set of [=computed value|computed=] styles:
1. Create a "hypothetical element" |el|
that acts as a child of |calling context|'s element.
|el| is [=featureless=],
and only [=custom properties=]
and the '@function/result' descriptor apply to it.
2. Apply |rule| to |el| to the [=specified value=] stage,
with the following changes:
* Only the [=custom property registrations=] in |registrations| are visible;
all other [=custom properties=] are treated as unregistered.
* The [=inherited value=] of |calling context|'s property
is the [=guaranteed-invalid value=].
* On custom properties,
the [=CSS-wide keywords=] ''initial'' and ''inherit'' have their usual effect;
all other [=CSS-wide keywords=] resolve to the [=guaranteed-invalid value=].
Note: ''initial'' references the [=custom property registration=]
created from the [=function parameters=],
letting you "reset" a property to the passed value.
''inherit'' inherits from the [=calling context=]'s element.\
On '@function/result',
all [=CSS-wide keywords=] are left unresolved.
Note: ''result: inherit'', for example,
will cause the <<dashed-function>> to <em>evaluate to</em> the ''inherit'' keyword,
similar to ''var(--unknown, inherit)''.
3. Determine the [=computed value=] of all [=custom properties=]
and the '@function/result' "property" on |el|,
as defined in [[css-properties-values-api#calculation-of-computed-values]],
with changes from the previous step,
and the following:
* Aside from references to [=custom properties=]
(which use the values on |el| as normal)
and numbers/percentages
(which are left unresolved in custom properties, as normal),
all values which would normally refer to the element being styled
instead refer to |calling context|'s [=calling context/root element=].
Note: For example, ''attr()'' in a property,
or ''@container'' queries in the rule.
4. Return |el|'s styles.
Note: Only [=custom properties=] and the '@function/result' descriptor
will be used from these styles.
</div>
Cycles {#cycles}
----------------
The ''@function/result'' descriptor and [=local variables=]
within a [=custom function=]
may reference other [=custom functions=] or [=custom properties=],
and may therefore create [[css-variables-1#cycles|cycles]].
For each element, add a node for every specified [=custom function=]
to the graph described in [[css-variables-1#cycles]];
add a node for each [=local variable=]
defined within each of those functions;
then, for each [=custom function=] <var>func</var>, add edges as follows:
* From <var>func</var> to any [=custom function=]
referenced by a <<dashed-function>> within <var>func</var>'s body.
* From <var>func</var> to any [=custom property=] or [=local variable=]
referenced by a ''var()'' within <var>func</var>'s body.
* To <var>func</var> from any [=custom property=] or [=local variable=]
that references <var>func</var>
using a <<dashed-function>>.
A <<dashed-function>> referencing a [=custom function=]
which is part of a cycle
makes the containing [=declaration=] [=invalid at computed-value time=].
Note: Cycles are disallowed even through branches that are not taken
during execution.
<div class='example'>
In the following,
<code>--foo()</code> is in a cycle with itself,
even though the media query never evaluates to "true":
<pre class='lang-css'>
@function --foo(--x) {
@media (unknown-feature) {
result: --foo(42);
}
result: 1;
}
</pre>
Similarly,
<code>--bar()</code> is in a cycle with itself,
even though the local variable <code>--x</code> is never referenced:
<pre class='lang-css'>
@function --bar() {
--x: --bar();
result: 1;
}
</pre>
</div>
<div class='example'>
The function <code>--baz()</code> is not in a cycle in the example below:
even though <code>var(--x)</code> and <code>var(--y)</code> appear in the function body,
they refer to a [=function parameter=] and [=local variable=], respectively.
The [=custom properties=] <code>--x</code> and <code>--y</code>
both reference <code>--baz()</code>, but that's fine:
those [=custom properties=] are not referenced within <code>--baz()</code>.
<pre class='lang-css'>
@function --baz(--x) {
--y: 10px;
result: calc(var(--x) + var(--y));
}
div {
--x: --baz(1px);
--y: --baz(2px);
width: var(--x); /* 11px */
height: var(--y); /* 12px */
}
</pre>
</div>
<!-- Big Text: execution
█████▌ █ █ █████▌ ███▌ █▌ █▌ █████▌ ████ ███▌ █ █▌
█▌ █ █ █▌ █▌ █▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌ █▌
█▌ █ █ █▌ █▌ █▌ █▌ █▌ ▐▌ █▌ █▌ ██▌ █▌
████ █ ████ █▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌▐█ █▌
█▌ █ █ █▌ █▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌ ██▌
█▌ █ █ █▌ █▌ █▌ █▌ █▌ █▌ ▐▌ █▌ █▌ █▌ █▌
█████▌ █ █ █████▌ ███▌ ███▌ █▌ ████ ███▌ █▌ ▐▌
-->
Execution Model of Custom Functions {#execution-model}
======================================================
Like the rest of CSS,
[=custom functions=] adhere to a declarative model.
The [=local variable=] descriptors
and '@function/result' descriptor
can appear in any order,
and may be provided multiple times.
If this happens, then declarations appearing later win over earlier ones.
<div class='example'>
<pre class='lang-css'>
@function --mypi() {
result: 3;
result: 3.14;
}
</pre>
The value of the '@function/result' descriptor of <code>--mypi</code>
is <code>3.14</code>.
</div>
<div class='example'>
<pre class='lang-css'>
@function --circle-area(--r) {
result: calc(pi * var(--r2));
--r2: var(--r) * var(--r);
}
</pre>
[=Local variable=] descriptors may appear before or after
they are referenced.
</div>
Conditional Rules {#conditional-rules}
--------------------------------------
A [=conditional group rule=] that appears within a ''@function''
becomes a [=nested group rule=],
with the additional restriction
that only descriptors allowed within ''@function''
are allowed within the [=nested group rule=].
[=Conditional group rules=] within ''@function''
are <a href="https://drafts.csswg.org/css-conditional-3/#processing">processed</a> as normal,
acting as if the contents of the rule were present
at the [=conditional group rule=]'s location
when the condition is true,
or acting as if nothing exists at that location otherwise.
<div class='example'>
<pre class='lang-css'>
@function --suitable-font-size() {
result: 16px;
@media (width > 1000px) {
result: 20px;
}
}
</pre>
The value of the '@function/result' descriptor
is <code>20px</code> if the media query's condition is true,
and <code>16px</code> otherwise.
</div>
<div class='example'>
Note that due to the execution model,
"early return" is not possible within a ''@function'':
<pre class='lang-css'>
@function --suitable-font-size() {
@media (width > 1000px) {
result: 20px;
}
result: 16px;
}
</pre>
The value of the '@function/result' descriptor
is always <code>16px</code> in the above example.
</div>
<div class='example'>
[=Local variables=] are also valid within conditional rules:
<pre class='lang-css'>
@function --suitable-font-size() {
--size: 16px;
@media (width > 1000px) {
--size: 20px;
}
result: var(--size);
}
</pre>
</div>
<!-- Big Text: cssom
███▌ ███▌ ███▌ ███▌ █ █
█▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ ██ ██
█▌ █▌ █▌ █▌ █▌ █▌█ █▐█
█▌ ███▌ ███▌ █▌ █▌ █▌ █ ▐█
█▌ █▌ █▌ █▌ █▌ █▌ ▐█
█▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ █▌ ▐█
███▌ ███▌ ███▌ ███▌ █▌ ▐█
-->
CSSOM {#cssom}
==============
The {{CSSFunctionRule}} Interface {#the-function-interface}
-----------------------------------------------------------
The {{CSSFunctionRule}} interface represents a ''@function'' rule.
<pre class='idl' export>
[Exposed=Window]
interface CSSFunctionRule : CSSGroupingRule {
readonly attribute CSSOMString name;
sequence<FunctionParameter> getParameters();
readonly attribute CSSOMString returnType;
};
</pre>
<dl dfn-for=CSSFunctionRule dfn-type=attribute>
<dt><dfn>name</dfn>
<dd>
The name of the [=custom function=].
<dt><dfn>returnType</dfn>
<dd>
The [=custom function/return type=] of the [=custom function=],
represented as a [[css-properties-values-api-1#syntax-strings|syntax string]].
If the [=custom function=] has no return type,
returns <code>"type(*)"</code>.
</dl>
<pre class='idl' export>
dictionary FunctionParameter {
required CSSOMString name;
required CSSOMString type;
CSSOMString? defaultValue;
};
</pre>
<dl dfn-for=FunctionParameter>
<dt>name
<dd>
The name of the [=function parameter=].
<dt>type
<dd>
The [=parameter type|type=] of the [=function parameter=],
represented as a [[css-properties-values-api-1#syntax-strings|syntax string]],
or <code>"type(*)"</code> if the [=function parameter|parameter=] has no type.
<dt>defaultValue
<dd>
The [=default value=] of the [=function parameter=],
or `null` if the argument does not have a default.
</dl>
While declarations may be specified directly within a ''@function'' rule,
they are not represented as such in the CSSOM.
Instead, consecutive segments of declarations
appear as if wrapped in {{CSSFunctionDeclarations}} rules.
Note: This also applies to the "leading" declarations in the ''@function'' rule,
i.e those that do not follow another nested rule.
<div class='example'>
<pre class='lang-css'>
@function --bar() {
--x: 42;
result: var(--y);
@media (width > 1000px) {
/* ... */
}
--y: var(--x);
}
</pre>
The above will appear in the CSSOM as:
<pre class='lang-css'>
@function --bar() {
/* CSSFunctionDeclarations { */
--x: 42;
result: var(--y);
/* } */
@media (width > 1000px) {
/* ... */
}
/* CSSFunctionDeclarations { */
--y: var(--x);
/* } */
}
</pre>
</div>
<div algorithm>
To <dfn export>serialize a CSSFunctionRule</dfn>,
return the concatenation of the following:
1. The string <code>"@function"</code> followed by a single SPACE (U+0020).
2. The result of performing <a>serialize an identifier</a>
on the name of the [=custom function=],
followed by a single LEFT PARENTHESIS (U+0028).
4. The result of [=serialize a function parameter=]
on each of the [=custom function's=] [=function parameter|parameters=],
all joined by <code>", "</code>
(COMMA U+002C, followed by a single SPACE U+0020).
5. A single RIGHT PARENTHESIS (U+0029).
6. If the [=custom function=] has [=custom function/return type=],
and that [=custom function/return type=]
is not the [=universal syntax definition=] ("*"):
* A single SPACE (U+0020),
followed by the string <code>"returns"</code>,
followed by a single SPACE (U+0020).
* The result of performing [=serialize a CSS type=]
on that [=custom function/return type|type=],
followed by a single SPACE (U+0020).
7. A single LEFT CURLY BRACKET (U+007B),
followed by a SPACE (U+0020).
8. The result of performing [=serialize a CSS rule=]
on each rule in cssRules,
filtering out empty strings,
all joined by a single SPACE (U+0020).
Note: [=Serialize a CSS rule=] can return an empty string
when serializing an empty {{CSSFunctionDeclarations}} rule.
9. A single SPACE (U+0020),
followed by a single RIGHT CURLY BRACKET (U+007D).
</div>
<div algorithm>
To <dfn export>serialize a function parameter</dfn>,
return the concatenation of the following:
1. The result of performing <a>serialize an identifier</a>
on the name of the [=function parameter=].
2. If the [=function parameter=] has a [=parameter type|type=],
and that [=parameter type|type=]
is not the [=universal syntax definition=]:
* A single SPACE (U+0020),
followed by the result of performing [=serialize a CSS type=]
on that [=parameter type|type=].
3. If the [=function parameter=] has a [=default value=]:
* A single COLON (U+003A),
followed by a single SPACE (U+0020),
followed by the result of performing [=serialize a CSS value=]
on that value.
</div>
<div algorithm>
To <dfn export>serialize a CSS type</dfn>,
return the concatenation of the following:
1. If the <<css-type>> consists of a single <<syntax-component>>,
return the corresponding [[css-properties-values-api-1#syntax-strings|syntax string]].
2. Otherwise,
return the concatenation of the following:
* The string <code>"type("</code>,
i.e. <code>"type"</code>
followed by a single LEFT PARENTHESIS (U+0028).
* The corresponding [[css-properties-values-api-1#syntax-strings|syntax string]].
* The string <code>")"</code>,
i.e. a single RIGHT PARENTHESIS (U+0029).
</div>
The {{CSSFunctionDeclarations}} Interface {#the-function-declarations-interface}
--------------------------------------------------------------------------------
The {{CSSFunctionDeclarations}} interface represents a run
of consecutive [=declarations=] within a ''@function'' rule.
<xmp class=idl>
[Exposed=Window]
interface CSSFunctionDescriptors : CSSStyleDeclaration {
attribute [LegacyNullToEmptyString] CSSOMString result;
};
[Exposed=Window]
interface CSSFunctionDeclarations : CSSRule {
[SameObject, PutForwards=cssText] readonly attribute CSSFunctionDescriptors style;
};
</xmp>
<div algorithm>
The <dfn attribute for=CSSFunctionDeclarations>style</dfn> attribute
must return a {{CSSFunctionDescriptors}} object for the rule,
with the following properties:
: [=CSSStyleDeclaration/computed flag=]
:: Unset
: [=CSSStyleDeclaration/readonly flag=]
:: Unset
: [=CSSStyleDeclaration/declarations=]
:: The declared declarations in the rule, in [=specified order=].
<span class=note>This includes any [=local variables=].</span>
: [=CSSStyleDeclaration/parent CSS rule=]
:: [=this=]
: [=CSSStyleDeclaration/owner node=]
:: Null
</div>
The {{CSSFunctionDeclarations}} rule, like {{CSSNestedDeclarations}},
[=serialize a CSS rule|serializes=] as if its [=CSS declaration block|declaration block=]
had been [=serialize a CSS declaration block|serialized=] directly.
Privacy Considerations {#privacy}
===============================================
The constructs defined by this specification
are defined and used entirely within CSS;