scope-evaluation.html (11521B)
1 <!DOCTYPE html> 2 <title>@scope - evaluation</title> 3 <link rel="help" href="https://drafts.csswg.org/css-cascade-6/#scope-atrule"> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script> 7 8 function test_scope(script_element, callback_fn, description) { 9 test((t) => { 10 // The provided <script> element must be an immedate subsequent sibling of 11 // a <template> element. 12 let template_element = script_element.previousElementSibling; 13 assert_equals(template_element.tagName, 'TEMPLATE'); 14 15 t.add_cleanup(() => main.replaceChildren()); 16 17 main.append(template_element.content.cloneNode(true)); 18 19 callback_fn(); 20 }, description); 21 } 22 23 function assert_green(selector) { 24 assert_equals(getComputedStyle(main.querySelector(selector)).backgroundColor, 'rgb(0, 128, 0)'); 25 } 26 function assert_not_green(selector) { 27 assert_equals(getComputedStyle(main.querySelector(selector)).backgroundColor, 'rgb(0, 0, 0)'); 28 } 29 </script> 30 <style> 31 :where(main *) { 32 background-color: black; 33 } 34 </style> 35 <main id=main> 36 </main> 37 38 <!-- Tests follow --> 39 40 <template> 41 <style> 42 @scope (.a) { 43 span { background-color: green; } 44 } 45 </style> 46 <div class=a> 47 <span>green</span> 48 </div> 49 <div class=b> 50 <span>not green</span> 51 </div> 52 <span>not green</span> 53 </template> 54 <script> 55 test_scope(document.currentScript, () => { 56 assert_green('.a > span'); 57 assert_not_green('.b > span'); 58 assert_not_green(':scope > span'); 59 }, 'Single scope'); 60 </script> 61 62 <template> 63 <style> 64 @scope (.a) { 65 .a { background-color: green; } 66 } 67 </style> 68 <div class=a> <!-- green --> 69 <span>not green</span> 70 </div> 71 </template> 72 <script> 73 test_scope(document.currentScript, () => { 74 assert_not_green('.a'); 75 assert_not_green('.a > span'); 76 }, 'Scope can not match its own root without :scope'); 77 </script> 78 79 <template> 80 <style> 81 @scope (.a) { 82 :scope { background-color: green; } 83 } 84 </style> 85 <div class=a> <!-- green --> 86 <span>not green</span> 87 </div> 88 </template> 89 <script> 90 test_scope(document.currentScript, () => { 91 assert_green('.a'); 92 assert_not_green('.a > span'); 93 }, 'Selecting self with :scope'); 94 </script> 95 96 <template> 97 <style> 98 @scope (.a) to (.c) { 99 span { background-color: green; } 100 } 101 </style> 102 <div class=a> 103 <div class=b> 104 <span>green</span> 105 </div> 106 <div class=c> 107 <span>not green</span> 108 </div> 109 </div> 110 </template> 111 <script> 112 test_scope(document.currentScript, () => { 113 assert_green('.b > span'); 114 assert_not_green('.c > span'); 115 }, 'Single scope with limit'); 116 </script> 117 118 <template> 119 <style> 120 @scope (.a) { 121 :scope > span { background-color: green; } 122 } 123 </style> 124 <div class=a> 125 <span>green</span> 126 <div class=b> 127 <span>not green</span> 128 </div> 129 </div> 130 </template> 131 <script> 132 test_scope(document.currentScript, () => { 133 assert_green('.a > span'); 134 assert_not_green('.b > span'); 135 }, 'Single scope, :scope pseudo in main selector'); 136 </script> 137 138 <template> 139 <style> 140 @scope (.a) to (:scope > .b) { 141 span { background-color: green; } 142 } 143 </style> 144 <div class=a> 145 <div class=b> 146 <span>not green</span> 147 </div> 148 <div class=c> 149 <div class=b> 150 <span>green</span> 151 </div> 152 </div> 153 </div> 154 </template> 155 <script> 156 test_scope(document.currentScript, () => { 157 assert_not_green('.a > .b > span'); 158 assert_green('.a > .c > .b > span'); 159 }, 'Single scope, :scope pseudo in to-selector'); 160 </script> 161 162 <template> 163 <style> 164 @scope (.a) to (:scope > .b) { 165 span { background-color: green; } 166 } 167 </style> 168 <div class=a> 169 <div class=b> 170 <span>not green</span> 171 </div> 172 <div class=a> 173 <div class=b> 174 <span>green</span> 175 </div> 176 </div> 177 </div> 178 </template> 179 <script> 180 test_scope(document.currentScript, () => { 181 assert_not_green('.a > .b > span'); 182 // Note that this span is in the outer .a-scope, but not in the inner scope. 183 assert_green('.a > .a > .b > span'); 184 }, 'Multiple scopes, :scope pseudo in to-selector'); 185 </script> 186 187 <template> 188 <style> 189 @scope (.a) { 190 @scope (:scope > .b) { 191 span { background-color: green; } 192 } 193 } 194 </style> 195 <div class=a> 196 <div class=b> 197 <span>green</span> 198 </div> 199 <div> 200 <div class=b> 201 <span>not green</span> 202 </div> 203 </div> 204 </div> 205 </template> 206 <script> 207 test_scope(document.currentScript, () => { 208 assert_green('.a > .b > span'); 209 assert_not_green('.a > div > .b > span'); 210 }, 'Inner @scope with :scope in from-selector'); 211 </script> 212 213 <template> 214 <style> 215 @scope (.a) to (:scope > .b) { 216 .c { background-color: green; } 217 } 218 </style> 219 <div class=a> 220 <div> 221 <div class=a> 222 <div class=b> 223 <div class=c></div> 224 </div> 225 </div> 226 </div> 227 </div> 228 </template> 229 <script> 230 test_scope(document.currentScript, () => { 231 // Not in the inner scope, but is in the outer scope. 232 assert_green('.c'); 233 }, 'Multiple scopes from same @scope-rule, only one limited'); 234 </script> 235 236 <template> 237 <style> 238 @scope (.a) to (.b) { 239 .c { background-color: green; } 240 } 241 </style> 242 <div class=a> 243 <div> 244 <div class=a> 245 <div class=b> 246 <div class=c></div> 247 </div> 248 </div> 249 </div> 250 </div> 251 </template> 252 <script> 253 test_scope(document.currentScript, () => { 254 assert_not_green('.c'); 255 }, 'Multiple scopes from same @scope-rule, both limited'); 256 </script> 257 258 <template> 259 <style> 260 @scope (.a) { 261 @scope (.b) { 262 span { background-color: green; } 263 } 264 } 265 </style> 266 <div class=a> 267 <div class=b> 268 <span>green</span> 269 </div> 270 <span>not green</span> 271 </div> 272 <div class=b> 273 <span>not green</span> 274 </div> 275 </template> 276 <script> 277 test_scope(document.currentScript, () => { 278 assert_green('.a > .b > span'); 279 assert_not_green('.a > span'); 280 assert_not_green(':scope > .b > span'); 281 }, 'Nested scopes'); 282 </script> 283 284 <template> 285 <style> 286 @scope (.b) { 287 @scope (.a) { 288 span { background-color: green; } 289 } 290 } 291 </style> 292 <div class=a> 293 <div class=b> 294 <span>not green</span> 295 </div> 296 <span>not green</span> 297 </div> 298 </template> 299 <script> 300 test_scope(document.currentScript, () => { 301 assert_not_green('.a > .b > span'); 302 assert_not_green('.a > span'); 303 }, 'Nested scopes, reverse'); 304 </script> 305 306 307 <template> 308 <style> 309 @scope (.a) { 310 @scope (.b) to (.c) { 311 span { background-color: green; } 312 } 313 } 314 </style> 315 <div class=a> 316 <div class=b> 317 <span>green</span> 318 </div> 319 <div class=b> 320 <div class=c> 321 <span>not green</span> 322 </div> 323 </div> 324 <span>not green</span> 325 </div> 326 <div class=b> 327 <span>not green</span> 328 </div> 329 </template> 330 <script> 331 test_scope(document.currentScript, () => { 332 assert_green('.a > .b > span'); 333 assert_not_green('.a > span'); 334 assert_not_green('.a > .b > .c > span'); 335 assert_not_green(':scope > .b > span'); 336 }, 'Nested scopes, with to-selector'); 337 </script> 338 339 <template> 340 <style> 341 @scope (.a) { 342 :scope { background-color: green; } 343 } 344 </style> 345 <div class=a></div> 346 </template> 347 <script> 348 test_scope(document.currentScript, () => { 349 assert_green('.a'); 350 }, ':scope selecting itself'); 351 </script> 352 353 <template> 354 <style> 355 @scope (.a) to (.b) { 356 * { background-color: green; } 357 } 358 </style> 359 <div id=above> 360 <div class=a> 361 <div> 362 <div class=b> 363 <div id=below></div> 364 </div> 365 </div> 366 </div> 367 <div id=adjacent></div> 368 </div> 369 </template> 370 <script> 371 test_scope(document.currentScript, () => { 372 assert_not_green('#above'); 373 assert_not_green('#adjacent'); 374 assert_not_green('.a'); 375 assert_green('.a > div'); 376 assert_not_green('.b'); 377 assert_not_green('#below'); 378 }, 'The scoping limit is not in scope'); 379 </script> 380 381 <template> 382 <style> 383 @scope (.a) to (.b > *) { 384 * { background-color: green; } 385 } 386 </style> 387 <div id=above> 388 <div class=a> 389 <div> 390 <div class=b> 391 <div id=limit></div> 392 </div> 393 </div> 394 </div> 395 <div id=adjacent></div> 396 </div> 397 </template> 398 <script> 399 test_scope(document.currentScript, () => { 400 assert_not_green('#above'); 401 assert_not_green('#adjacent'); 402 assert_not_green('.a'); 403 assert_green('.a > div'); 404 assert_green('.b'); 405 assert_not_green('#limit'); 406 }, 'Simulated inclusive scoping limit'); 407 </script> 408 409 <template> 410 <style> 411 @scope (.a) to (:scope) { 412 * { background-color: green; } 413 } 414 </style> 415 <div id=above> 416 <div class=a> 417 <div> 418 <div class=b> 419 <div id=inner></div> 420 </div> 421 </div> 422 </div> 423 <div id=adjacent></div> 424 </div> 425 </template> 426 <script> 427 test_scope(document.currentScript, () => { 428 assert_not_green('#above'); 429 assert_not_green('#adjacent'); 430 assert_not_green('.a'); 431 assert_not_green('.a > div'); 432 assert_not_green('.b'); 433 assert_not_green('#inner'); 434 }, 'Scope with no elements'); 435 </script> 436 437 438 <template> 439 <style> 440 @scope (.a) { 441 :scope + .c { background-color: green; } 442 } 443 </style> 444 <div class=a> 445 <div class=a></div> 446 <div class=c></div> 447 </div> 448 </template> 449 <script> 450 test_scope(document.currentScript, () => { 451 // A :scope sibling can never match, as the scoping element must 452 // be on the ancestor chain. 453 assert_not_green('.c'); 454 }, ':scope direct adjacent sibling'); 455 </script> 456 457 458 <template> 459 <style> 460 @scope (.a) { 461 :scope + .c { background-color: green; } 462 } 463 </style> 464 <div class=a> 465 <div class=a></div> 466 <div></div> 467 <div class=c></div> 468 </div> 469 </template> 470 <script> 471 test_scope(document.currentScript, () => { 472 // A :scope sibling can never match, as the scoping element must 473 // be on the ancestor chain. 474 assert_not_green('.c'); 475 }, ':scope indirect adjacent sibling'); 476 </script> 477 478 479 <template> 480 <style> 481 @scope (.a) { 482 > span { background-color: green; } 483 } 484 </style> 485 <div class=a> 486 <span>green</span> 487 </div> 488 </template> 489 <script> 490 test_scope(document.currentScript, () => { 491 assert_green('.a > span'); 492 }, 'Relative selector inside @scope'); 493 </script> 494 495 496 <template> 497 <style> 498 @scope (.a) { 499 /* Can never match anything. */ 500 :scope > :scope { background-color: green; } 501 } 502 </style> 503 <div class=a> 504 <div id=inner class=a> 505 </div> 506 </div> 507 </template> 508 <script> 509 test_scope(document.currentScript, () => { 510 assert_not_green('.a'); 511 assert_not_green('#inner'); 512 }, ':scope in two different compounds'); 513 </script> 514 515 <template> 516 <style> 517 @scope (.a:has(.c)) { 518 .b { background-color:green; } 519 } 520 </style> 521 <div class=first> 522 <div class=a> 523 <div class=b> 524 <div class=c></div> 525 </div> 526 </div> 527 </div> 528 <div class=second> 529 <div class=a> 530 <div class=b> 531 <div class=d></div> 532 </div> 533 </div> 534 </div> 535 </template> 536 <script> 537 test_scope(document.currentScript, () => { 538 assert_not_green('.first .a'); 539 assert_green('.first .b'); 540 assert_not_green('.first .c'); 541 542 assert_not_green('.second .a'); 543 assert_not_green('.second .b'); 544 assert_not_green('.second .d'); 545 }, 'Scope root with :has()'); 546 </script> 547 548 <template> 549 <style> 550 @scope (.a) to (.b, .c) { 551 * { background-color:green; } 552 } 553 </style> 554 <div class=a> 555 <span id="in"></span> 556 <div class=b> 557 <span id="out"></span> 558 <div class=c></div> 559 </div> 560 </div> 561 </template> 562 <script> 563 test_scope(document.currentScript, () => { 564 assert_green('#in'); 565 assert_not_green('#out'); 566 }, 'Any scope limit makes the element out of scope'); 567 </script>