@@ -6,16 +6,24 @@ many interacting layers and a lot of intricate case analysis.
6
6
7
7
## Table of Contents
8
8
9
- * [ Definition] ( #definition )
9
+ * [ Definitions] ( #definitions )
10
+ * [ Extender] ( #extender )
11
+ * [ Target] ( #target )
12
+ * [ Extension] ( #extension )
13
+ * [ Extendee] ( #extendee )
14
+ * [ The ` extend() ` Function] ( #the-extend-function )
15
+ * [ Semantics] ( #semantics )
16
+ * [ Executing an ` @extend ` Rule] ( #executing-an-extend-rule )
17
+ * [ Resolving a Module's Extensions] ( #resolving-a-modules-extensions )
10
18
* [ Limitations] ( #limitations )
11
19
* [ Specificity] ( #specificity )
12
20
* [ The First Law] ( #the-first-law )
13
21
* [ The Second Law] ( #the-second-law )
14
22
15
- ## Definition
23
+ ## Definitions
16
24
17
- First, let's give names to the various selectors involved with a given use of
18
- ` @extend ` :
25
+ These definitions provide names to the various selectors involved with a given
26
+ use of ` @extend ` :
19
27
20
28
``` scss
21
29
.extender {
@@ -28,16 +36,185 @@ First, let's give names to the various selectors involved with a given use of
28
36
}
29
37
```
30
38
31
- The ** extender** is the selector list for the style rule in which the ` @extend `
32
- rule appears. The ** target** is the simple selector that's used as an argument
33
- to ` @extend ` . The ** extendee** is the selector list elsewhere in the stylesheet
34
- that contains the target and is modified to include the extender as well. We
35
- also use the function ` extend(extendee, target, extender) ` to refer to the
36
- result of extending ` extendee ` with ` extender {@extend target} ` (much like the
37
- Sass function ` selector-extend() ` ).
39
+ ### Extender
38
40
39
- Given these definitions, the ` @extend ` rule means that ** all elements matching
40
- the extender should be styled as though they match the target as well** .
41
+ An ` @extend ` rule's * extender* is the [ selector list] [ ] for the style rule in
42
+ which the ` @extend ` rule appears.
43
+
44
+ [ selector list ] : https://drafts.csswg.org/selectors-4/#selector-list
45
+
46
+ ### Target
47
+
48
+ An ` @extend ` rule's * target* is the [ simple selector] [ ] that's used as an
49
+ argument to ` @extend ` .
50
+
51
+ [ simple selector ] : https://drafts.csswg.org/selectors-4/#simple
52
+
53
+ ### Extension
54
+
55
+ An * extension* is a collection of various properties.
56
+
57
+ > An extension is a more abstract representation of the information inherent in
58
+ > an ` @extend ` rule. As such, all ` @extend ` rules define extensions, but not all
59
+ > extensions directly correspond to ` @extend ` rules.
60
+
61
+ * The * extender* , a [ selector list] [ ] .
62
+ * The * target* , a [ simple selector] [ ] .
63
+
64
+ ### Extendee
65
+
66
+ An * extendee* is a selector list being modified by an [ extension] ( #extension ) .
67
+ It's only defined within the scope of a single application of a given extension.
68
+
69
+ > If an extendee contains that extensions's target, it will usually be modified
70
+ > to include the extension's extender as well.
71
+
72
+ ### The ` extend() ` Function
73
+
74
+ As a shorthand, we use the function notation `extend(extendee, target,
75
+ extender)` to refer to the result of extending ` extendee` with ` extender
76
+ {@extend target}` (much like the Sass function ` selector-extend()`). We further
77
+ use ` extend(extendee, extension) ` as a shorthand for `extend(extendee,
78
+ extension.target, extension.extender)`.
79
+
80
+ ## Semantics
81
+
82
+ The ` @extend ` rule means that all elements matching the [ extender] ( #extender )
83
+ should be styled as though they match the [ target] ( #target ) as well. The
84
+ ` @extend ` rule only applies to CSS in the module in which it's defined and
85
+ that module's transitive dependencies.
86
+
87
+ > Because Sass can't directly affect how the browser applies styles to elements,
88
+ > these semantics are approximated by duplicating each [ extendee] ( #extendee )
89
+ > with the target replaced by the extender. Rather than being a naïve textual
90
+ > replacement, the extender is integrated intelligently into the extendee to
91
+ > match the semantics as best as possible.
92
+
93
+ ### Executing an ` @extend ` Rule
94
+
95
+ To execute an ` @extend ` rule ` rule ` :
96
+
97
+ * If there is no [ current style rule] [ ] , throw an error.
98
+
99
+ [ current style rule ] : ../style-rules.md#current-style-rule
100
+
101
+ * Let ` selector ` be the result of evaluating all interpolation in ` rule ` 's
102
+ selector and parsing the result as a list of simple selectors.
103
+
104
+ * If ` selector ` contains any parent selectors, throw an error.
105
+
106
+ * Let ` extension ` be an [ extension] ( #extension ) whose extender is the current
107
+ style rule's selector and whose target is ` selector ` .
108
+
109
+ * Add ` extension ` to [ the current module] [ ] 's extensions.
110
+
111
+ [ the current module ] : ../spec.md#the-current-module
112
+
113
+ > Note that this adds the extension to the module being evaluated, not the
114
+ > module in which the ` @extend ` lexically appears. This means that ` @extend ` s
115
+ > are effectively dynamically scoped, not lexically scoped.
116
+
117
+ ### Resolving a Module's Extensions
118
+
119
+ This algorithm takes a [ module] [ ] ` starting-module ` and returns a [ CSS tree] [ ]
120
+ that includes CSS for * all* modules transitively used or forwarded by
121
+ ` starting-module ` .
122
+
123
+ [ module ] : ../modules.md#module
124
+ [ CSS tree ] : ../modules.md#css-tree
125
+
126
+ * Let ` new-selectors ` be an empty map from style rules to selectors. For the
127
+ purposes of this map, style rules are compared using * reference equality* ,
128
+ meaning that style rules at different points in the CSS tree are always
129
+ considered different even if their contents are the same.
130
+
131
+ * Let ` new-extensions ` be an empty map from modules to sets of
132
+ [ extensions] ( #extension ) .
133
+
134
+ * Let ` extended ` be the subgraph of the [ module graph] [ ] containing
135
+ modules that are transitively reachable from ` starting-module ` .
136
+
137
+ [ module graph ] : ../modules.md#module-graph
138
+
139
+ * For each module ` domestic ` in ` extended ` , in reverse [ topological] [ ] order:
140
+
141
+ [ topological ] : https://en.wikipedia.org/wiki/Topological_sorting
142
+
143
+ * Let ` downstream ` be the set of modules in ` extended ` whose dependencies
144
+ include ` domestic ` .
145
+
146
+ * For each style rule ` rule ` in ` domestic ` 's CSS:
147
+
148
+ * Let ` selector ` be the result of applying ` domestic ` 's extensions to
149
+ ` rule ` 's selector.
150
+
151
+ * Let ` selector-lists ` be an empty set of selector lists.
152
+
153
+ * For each module ` foreign ` in ` downstream ` :
154
+
155
+ * Let ` extended-selector ` be ` extend(selector, new-extensions[foreign]) ` .
156
+
157
+ > ` new-extensions[foreign] ` is guaranteed to be populated at this point
158
+ > because ` extended ` is traversed in reverse topological order, which
159
+ > means that ` foreign ` 's own extensions will already have been resolved
160
+ > by the time we start working on modules upstream of it.
161
+
162
+ * Add ` selector ` to ` selector-lists ` .
163
+
164
+ * Set ` new-selectors[rule] ` to a selector that matches the union of all
165
+ elements matched by selectors in ` selector-lists ` . This selector must obey
166
+ [ the specificity laws] ( #specificity ) relative to the selectors from which
167
+ it was generated. For the purposes of [ the first law] ( #the-first-law ) ,
168
+ "the original extendee" is considered only to refer to selectors that
169
+ appear in ` domestic ` 's CSS, * not* selectors that were added by other
170
+ modules' extensions.
171
+
172
+ > Implementations are expected to trim redundant selectors from
173
+ > ` selector-lists ` as much as possible. For the purposes of the first law
174
+ > of extend, "the original extendee" is * only* the selectors in ` rule ` 's
175
+ > selector. The new complex selectors in ` selector ` generated from
176
+ > ` domestic ` 's extensions don't count as "original", and may be optimized
177
+ > away.
178
+
179
+ * For every extension ` extension ` whose extender appears in ` rule ` 's
180
+ selector:
181
+
182
+ * For every complex selector ` complex ` in ` new-selectors[rule] ` :
183
+
184
+ * Add a copy of ` extension ` with its extender replaced by ` complex ` to
185
+ ` new-extensions[domestic] ` .
186
+
187
+ * Let ` css ` be an empty CSS tree.
188
+
189
+ * Define a mutating recursive procedure, * traversing* , which takes a module
190
+ ` domestic ` :
191
+
192
+ * If ` domestic ` has already been traversed, do nothing.
193
+
194
+ * Otherwise, traverse every module in ` domestic ` 's dependencies.
195
+
196
+ > Because this traverses modules depth-first, it emits CSS in reverse
197
+ > topological order.
198
+
199
+ * Let ` initial-imports ` be the longest initial subsequence of top-level
200
+ statements in ` domestic ` 's CSS tree that contains only comments and
201
+ ` @import ` rules * and* that ends with an ` @import ` rule.
202
+
203
+ * Insert a copy of ` initial-imports ` in ` css ` after the last ` @import ` rule, or
204
+ at the beginning of ` css ` if it doesn't contain any ` @import ` rules.
205
+
206
+ * For each top-level statement ` statement ` in ` domestic ` 's CSS tree after
207
+ ` initial-imports ` :
208
+
209
+ * If ` statement ` is an ` @import ` rule, insert a copy of ` statement ` in ` css `
210
+ after the last ` @import ` rule, or at the beginning of ` css ` if it doesn't
211
+ contain any ` @import ` rules.
212
+
213
+ * Otherwise, add a copy of ` statement ` to the end of ` css ` , with any style
214
+ rules' selectors replaced with the corresponding selectors in
215
+ ` new-selectors ` .
216
+
217
+ * Return ` css ` .
41
218
42
219
### Limitations
43
220
@@ -76,7 +253,6 @@ required to meet the full definition.
76
253
When modifying the extendee during extension, the implementation must provide
77
254
two guarantees about the result. These are known as the "laws of extend".
78
255
79
-
80
256
#### The First Law
81
257
82
258
The first law of ` @extend ` says that the specificity of the first generated
0 commit comments