Skip to content

Commit 46c9ad4

Browse files
authored
Merge pull request #1190 from sveltejs/gh-1118
Use classes for style encapsulation, rather than attributes
2 parents 0c3e44a + 7d4958b commit 46c9ad4

File tree

95 files changed

+172
-172
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+172
-172
lines changed

src/css/Stylesheet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class Rule {
6969
transform(code: MagicString, id: string, keyframes: Map<string, string>, cascade: boolean) {
7070
if (this.parent && this.parent.node.type === 'Atrule' && this.parent.node.name === 'keyframes') return true;
7171

72-
const attr = `[${id}]`;
72+
const attr = `.${id}`;
7373

7474
if (cascade) {
7575
this.selectors.forEach(selector => {

src/generators/dom/index.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,6 @@ export default function dom(
135135
builder.addBlock(generator.javascript);
136136
}
137137

138-
if (generator.needsEncapsulateHelper) {
139-
builder.addBlock(deindent`
140-
function @encapsulateStyles(node) {
141-
@setAttribute(node, "${generator.stylesheet.id}", "");
142-
}
143-
`);
144-
}
145-
146138
const { css, cssMap } = generator.stylesheet.render(options.filename, !generator.customElement);
147139
const styles = generator.stylesheet.hasStyles && stringify(options.dev ?
148140
`${css}\n/*# sourceMappingURL=${cssMap.toUrl()} */` :

src/generators/nodes/Attribute.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ export default class Attribute {
141141
shouldCache = true;
142142
}
143143

144+
if (node._needsCssAttribute && propertyName === 'className') {
145+
value = `(${value}) + " ${this.generator.stylesheet.id}"`;
146+
}
147+
144148
const isSelectValueAttribute =
145149
name === 'value' && node.name === 'select';
146150

@@ -191,17 +195,17 @@ export default class Attribute {
191195
block.builders.hydrate.addLine(
192196
`${node.var}.${propertyName} = ${init};`
193197
);
194-
updater = `${node.var}.${propertyName} = ${shouldCache || isSelectValueAttribute ? last : value};`;
198+
updater = `${node.var}.${propertyName} = ${shouldCache ? last : value};`;
195199
} else if (isDataSet) {
196200
block.builders.hydrate.addLine(
197201
`${node.var}.dataset.${camelCaseName} = ${init};`
198202
);
199-
updater = `${node.var}.dataset.${camelCaseName} = ${shouldCache || isSelectValueAttribute ? last : value};`;
203+
updater = `${node.var}.dataset.${camelCaseName} = ${shouldCache ? last : value};`;
200204
} else {
201205
block.builders.hydrate.addLine(
202206
`${method}(${node.var}, "${name}", ${init});`
203207
);
204-
updater = `${method}(${node.var}, "${name}", ${shouldCache || isSelectValueAttribute ? last : value});`;
208+
updater = `${method}(${node.var}, "${name}", ${shouldCache ? last : value});`;
205209
}
206210

207211
if (allDependencies.size || hasChangeableIndex || isSelectValueAttribute) {
@@ -223,17 +227,30 @@ export default class Attribute {
223227
);
224228
}
225229
} else {
226-
const value = this.value === true
227-
? 'true'
228-
: this.value.length === 0
229-
? `''`
230-
: stringify(this.value[0].data);
230+
const isScopedClassAttribute = (
231+
propertyName === 'className' &&
232+
this.parent._needsCssAttribute &&
233+
!this.generator.customElement
234+
);
235+
236+
const value = isScopedClassAttribute && this.value !== true
237+
? this.value.length === 0
238+
? `'${this.generator.stylesheet.id}'`
239+
: stringify(this.value[0].data.concat(` ${this.generator.stylesheet.id}`))
240+
: this.value === true
241+
? 'true'
242+
: this.value.length === 0
243+
? `''`
244+
: stringify(this.value[0].data);
231245

232246
const statement = (
233-
isLegacyInputType ? `@setInputType(${node.var}, ${value});` :
234-
propertyName ? `${node.var}.${propertyName} = ${value};` :
235-
isDataSet ? `${node.var}.dataset.${camelCaseName} = ${value};` :
236-
`${method}(${node.var}, "${name}", ${value});`
247+
isLegacyInputType
248+
? `@setInputType(${node.var}, ${value});`
249+
: propertyName
250+
? `${node.var}.${propertyName} = ${value};`
251+
: isDataSet
252+
? `${node.var}.dataset.${camelCaseName} = ${value};`
253+
: `${method}(${node.var}, "${name}", ${value});`
237254
);
238255

239256
block.builders.hydrate.addLine(statement);

src/generators/nodes/Element.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,13 @@ export default class Element extends Node {
214214

215215
// add CSS encapsulation attribute
216216
if (this._needsCssAttribute && !this.generator.customElement) {
217-
this.generator.needsEncapsulateHelper = true;
218-
block.builders.hydrate.addLine(
219-
`@encapsulateStyles(${name});`
220-
);
217+
if (!this.attributes.find(a => a.type === 'Attribute' && a.name === 'class')) {
218+
block.builders.hydrate.addLine(
219+
`${name}.className = "${this.generator.stylesheet.id}";`
220+
);
221+
}
221222

223+
// TODO move this into a class as well?
222224
if (this._cssRefAttribute) {
223225
block.builders.hydrate.addLine(
224226
`@setAttribute(${name}, "svelte-ref-${this._cssRefAttribute}", "");`
@@ -429,18 +431,22 @@ export default class Element extends Node {
429431

430432
let open = `<${node.name}`;
431433

432-
if (node._needsCssAttribute) {
433-
open += ` ${generator.stylesheet.id}`;
434-
}
435-
436434
if (node._cssRefAttribute) {
437435
open += ` svelte-ref-${node._cssRefAttribute}`;
438436
}
439437

440438
node.attributes.forEach((attr: Node) => {
441-
open += ` ${fixAttributeCasing(attr.name)}${stringifyAttributeValue(attr.value)}`
439+
const value = node._needsCssAttribute && attr.name === 'class'
440+
? attr.value.concat({ type: 'Text', data: ` ${generator.stylesheet.id}` })
441+
: attr.value;
442+
443+
open += ` ${fixAttributeCasing(attr.name)}${stringifyAttributeValue(value)}`
442444
});
443445

446+
if (node._needsCssAttribute && !node.attributes.find(a => a.name === 'class')) {
447+
open += ` class="${generator.stylesheet.id}"`;
448+
}
449+
444450
if (isVoidElementName(node.name)) return open + '>';
445451

446452
return `${open}>${node.children.map(toHTML).join('')}</${node.name}>`;

src/generators/server-side-rendering/visitors/Element.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,22 @@ export default function visitElement(
5050
block.contextualise(attribute.value[0].expression);
5151
openingTag += '${' + attribute.value[0].metadata.snippet + ' ? " ' + attribute.name + '" : "" }';
5252
} else {
53-
openingTag += ` ${attribute.name}="${stringifyAttributeValue(block, attribute.value)}"`;
53+
const value = attribute.name === 'class' && node._needsCssAttribute
54+
? attribute.value.concat({
55+
type: 'Text',
56+
data: ` ${generator.stylesheet.id}`
57+
})
58+
: attribute.value;
59+
60+
openingTag += ` ${attribute.name}="${stringifyAttributeValue(block, value)}"`;
5461
}
5562
});
5663

57-
if (node._needsCssAttribute) {
58-
openingTag += ` ${generator.stylesheet.id}`;
64+
if (node._needsCssAttribute && !node.attributes.find(a => a.type === 'Attribute' && a.name === 'class')) {
65+
openingTag += ` class="${generator.stylesheet.id}"`;
66+
}
5967

68+
if (node._needsCssAttribute) {
6069
if (node._cssRefAttribute) {
6170
openingTag += ` svelte-ref-${node._cssRefAttribute}`;
6271
}

src/utils/hash.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// https://github.com/darkskyapp/string-hash/blob/master/index.js
2-
export default function hash(str: string): number {
2+
export default function hash(str: string): string {
33
let hash = 5381;
44
let i = str.length;
55

66
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
7-
return hash >>> 0;
7+
return (hash >>> 0).toString(36);
88
}

test/css/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('css', () => {
9595
css: read(`test/css/samples/${dir}/expected.css`)
9696
};
9797

98-
assert.equal(dom.css.replace(/svelte-\d+/g, 'svelte-xyz'), expected.css);
98+
assert.equal(dom.css.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz'), expected.css);
9999

100100
// verify that the right elements have scoping selectors
101101
if (expected.html !== null) {
@@ -114,7 +114,7 @@ describe('css', () => {
114114
fs.writeFileSync(`test/css/samples/${dir}/_actual.html`, html);
115115

116116
assert.equal(
117-
normalizeHtml(window, html.replace(/svelte-\d+/g, 'svelte-xyz')),
117+
normalizeHtml(window, html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz')),
118118
normalizeHtml(window, expected.html)
119119
);
120120

@@ -133,7 +133,7 @@ describe('css', () => {
133133
assert.equal(
134134
normalizeHtml(
135135
window,
136-
component.render(config.data).html.replace(/svelte-\d+/g, 'svelte-xyz')
136+
component.render(config.data).html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz')
137137
),
138138
normalizeHtml(window, expected.html)
139139
);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[foo][svelte-xyz]{color:red}[baz][svelte-xyz]{color:blue}
1+
[foo].svelte-xyz{color:red}[baz].svelte-xyz{color:blue}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[foo=bar][svelte-xyz]{color:red}
1+
[foo=bar].svelte-xyz{color:red}

test/css/samples/basic/expected.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
div[svelte-xyz],[svelte-xyz] div{color:red}
1+
div.svelte-xyz,.svelte-xyz div{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
.foo[svelte-xyz]{}
1+
.foo.svelte-xyz{}

test/css/samples/cascade-false-global-keyframes/expected.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
@keyframes svelte-xyz-why{from{color:red}to{color:blue}}.animated[svelte-xyz]{animation:svelte-xyz-why 2s}
1+
@keyframes svelte-xyz-why{from{color:red}to{color:blue}}.animated.svelte-xyz{animation:svelte-xyz-why 2s}

test/css/samples/cascade-false-keyframes/expected.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/css/samples/cascade-false-pseudo-element/expected.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[svelte-xyz]{color:red}
1+
.svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div svelte-xyz=""></div>
1+
<div class="svelte-xyz"></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
div[svelte-xyz]{color:red}div.foo[svelte-xyz]{color:blue}.foo[svelte-xyz]{font-weight:bold}
1+
div.svelte-xyz{color:red}div.foo.svelte-xyz{color:blue}.foo.svelte-xyz{font-weight:bold}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
.test[svelte-xyz]>div[svelte-xyz]{color:#0af}
1+
.test.svelte-xyz>div.svelte-xyz{color:#0af}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div svelte-xyz="" class="test"><div svelte-xyz="">Testing...</div></div>
1+
<div class="test svelte-xyz"><div class="svelte-xyz">Testing...</div></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
div[svelte-xyz],[svelte-xyz] div{--test:10}
1+
div.svelte-xyz,.svelte-xyz div{--test:10}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
p[svelte-xyz] span[svelte-xyz]{color:red}
1+
p.svelte-xyz span.svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div><p svelte-xyz=''><span svelte-xyz=''>styled</span></p></div>
1+
<div><p class="svelte-xyz"><span class="svelte-xyz">styled</span></p></div>

test/css/samples/keyframes/expected.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
@media only screen and (min-width: 400px){div[svelte-xyz],[svelte-xyz] div{color:red}}
1+
@media only screen and (min-width: 400px){div.svelte-xyz,.svelte-xyz div{color:red}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
@media(min-width: 400px){[svelte-xyz].large-screen,[svelte-xyz] .large-screen{display:block}}
1+
@media(min-width: 400px){.svelte-xyz.large-screen,.svelte-xyz .large-screen{display:block}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo*='bar'][svelte-xyz]{color:red}
1+
[data-foo*='bar'].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<div><p svelte-xyz="" data-foo="foobarbaz">this is styled</p>
1+
<div><p class="svelte-xyz" data-foo="foobarbaz">this is styled</p>
22
<p data-foo="fooBARbaz">this is unstyled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo='bar' i][svelte-xyz]{color:red}
1+
[data-foo='bar' i].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<div><p svelte-xyz="" data-foo="BAR">this is styled</p>
1+
<div><p class="svelte-xyz" data-foo="BAR">this is styled</p>
22
<p data-foo="BAZ">this is unstyled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo='bar'][svelte-xyz]{color:red}
1+
[data-foo='bar'].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<div><p svelte-xyz="" data-foo="whatever">this is styled</p>
1+
<div><p class="svelte-xyz" data-foo="whatever">this is styled</p>
22
<p data-foo="baz">this is unstyled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo='bar'][svelte-xyz]{color:red}
1+
[data-foo='bar'].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<div><p svelte-xyz="" data-foo="bar">this is styled</p>
1+
<div><p class="svelte-xyz" data-foo="bar">this is styled</p>
22
<p data-foo="baz">this is unstyled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo|='bar'][svelte-xyz]{color:red}
1+
[data-foo|='bar'].svelte-xyz{color:red}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<div><p svelte-xyz="" data-foo="bar">this is styled</p>
2-
<p svelte-xyz="" data-foo="bar-baz">this is styled</p>
1+
<div><p class="svelte-xyz" data-foo="bar">this is styled</p>
2+
<p class="svelte-xyz" data-foo="bar-baz">this is styled</p>
33
<p data-foo="baz-bar">this is unstyled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo^='bar'][svelte-xyz]{color:red}
1+
[data-foo^='bar'].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<div><p svelte-xyz="" data-foo="barbaz">this is styled</p>
1+
<div><p class="svelte-xyz" data-foo="barbaz">this is styled</p>
22
<p data-foo="bazbar">this is unstyled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo$='bar'][svelte-xyz]{color:red}
1+
[data-foo$='bar'].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
<div><p data-foo="barbaz">this is unstyled</p>
2-
<p svelte-xyz="" data-foo="bazbar">this is styled</p></div>
2+
<p class="svelte-xyz" data-foo="bazbar">this is styled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[data-foo~='bar'][svelte-xyz]{color:red}
1+
[data-foo~='bar'].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<div><p svelte-xyz="" data-foo="qux bar">this is styled</p>
1+
<div><p class="svelte-xyz" data-foo="qux bar">this is styled</p>
22
<p data-foo="qux baz">this is unstyled</p></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[autoplay][svelte-xyz]{color:red}
1+
[autoplay].svelte-xyz{color:red}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<div><video svelte-xyz autoplay></video>
1+
<div><video class="svelte-xyz" autoplay></video>
22
<video></video></div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
.foo[svelte-xyz]{color:red}
1+
.foo.svelte-xyz{color:red}

0 commit comments

Comments
 (0)