94 lines
3.9 KiB
HTML
94 lines
3.9 KiB
HTML
<!DOCTYPE html>
|
|
<title>@scope - specificity</title>
|
|
<link rel="help" href="https://drafts.csswg.org/css-cascade-6/#scope-atrule">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<style id=style>
|
|
</style>
|
|
<main id=main>
|
|
<style id=styleImplicit></style>
|
|
<div id=a class=a>
|
|
<div id=b class=b>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<script>
|
|
|
|
// Format a scoped style rule using the selector at scoped_selector's last element,
|
|
// with each preceding array item representing an enclosing @scope rule.
|
|
//
|
|
// Example:
|
|
//
|
|
// scoped_selector=['@scope (foo)', '@scope (bar)', 'div']
|
|
// declarations='z-index:42'
|
|
// => '@scope (foo) { @scope (bar) { div { z-index:42 } } }'
|
|
function format_scoped_rule(scoped_selector, declarations) {
|
|
if (scoped_selector.length < 2) {
|
|
throw "Fail";
|
|
}
|
|
let scope_prelude = scoped_selector[0];
|
|
let remainder = scoped_selector.slice(1);
|
|
let content = remainder.length == 1
|
|
? `${remainder[0]} { ${declarations} }`
|
|
: format_scoped_rule(remainder, declarations);
|
|
return `${scope_prelude} { ${content} }`;
|
|
}
|
|
|
|
// Verify that the specificity of 'scoped_selector' is the same
|
|
// as the specificity of 'ref_selector'. Both selectors must select
|
|
// an element within #main.
|
|
function test_scope_specificity(scoped_selector, ref_selector, style) {
|
|
if (style === undefined) {
|
|
style = document.getElementById("style");
|
|
}
|
|
test(t => {
|
|
t.add_cleanup(() => { style.textContent = ''; });
|
|
|
|
let element = main.querySelector(ref_selector);
|
|
assert_not_equals(element, null);
|
|
|
|
let scoped_rule = format_scoped_rule(scoped_selector, 'z-index:1');
|
|
let ref_rule = `:is(${ref_selector}) { z-index:2 }`;
|
|
|
|
style.textContent = `${scoped_rule}`;
|
|
assert_equals(getComputedStyle(element).zIndex, '1', 'scoped rule');
|
|
|
|
style.textContent = `${ref_rule}`;
|
|
assert_equals(getComputedStyle(element).zIndex, '2', 'unscoped rule');
|
|
|
|
// The scoped rule should win due to proximity.
|
|
style.textContent = `${scoped_rule} ${ref_rule}`;
|
|
assert_equals(getComputedStyle(element).zIndex, '1', 'scoped + unscoped');
|
|
|
|
// The scoped rule should win due to proximity (reverse).
|
|
style.textContent = `${ref_rule} ${scoped_rule}`;
|
|
assert_equals(getComputedStyle(element).zIndex, '1', 'unscoped + scoped');
|
|
|
|
// Add one (1) to the specificty of the unscoped rule. This should
|
|
// cause the unscoped rule to win instead.
|
|
style.textContent = `div${ref_rule} ${scoped_rule}`;
|
|
assert_equals(getComputedStyle(element).zIndex, '2', 'unscoped + scoped');
|
|
}, format_scoped_rule(scoped_selector, '') + ' and ' + ref_selector);
|
|
}
|
|
|
|
// Selectors within @scope implicitly have `:scope <descendant-combinator>`
|
|
// added, but no specificity associated with it is added.
|
|
// See https://github.com/w3c/csswg-drafts/issues/10196
|
|
test_scope_specificity(['@scope (#main)', '.b'], '.b');
|
|
test_scope_specificity(['@scope (#main) to (.b)', '.a'], '.a');
|
|
test_scope_specificity(['@scope (#main, .foo, .bar)', '#a'], '#a');
|
|
test_scope_specificity(['@scope (#main)', 'div.b'], 'div.b');
|
|
test_scope_specificity(['@scope (#main)', ':scope .b'], '.a .b');
|
|
// Inherit the specificity of the scope-start selector.
|
|
test_scope_specificity(['@scope (#main)', '& .b'], '#main .b');
|
|
test_scope_specificity(['@scope (#main)', 'div .b'], 'div .b');
|
|
test_scope_specificity(['@scope (#main)', '@scope (.a)', '.b'], '.b');
|
|
// Explicit `:scope` adds specficity.
|
|
test_scope_specificity(['@scope (#main)', ':scope .b'], ':scope .b');
|
|
// Using & in scoped style with implicit scope root matches the same elements
|
|
// as `:scope`, but does not add any specificity.
|
|
// https://github.com/w3c/csswg-drafts/issues/10196
|
|
test_scope_specificity(['@scope', '& .b'], ':where(:scope) .b', styleImplicit);
|
|
// Using relative selector syntax does not add specificity
|
|
test_scope_specificity(['@scope (#main)', '> .a'], ':where(#main) > .a');
|
|
</script>
|