wpt / css / css-overflow / parsing / getComputedStyle-scroll-button.html

Status: FAIL | Duration: 53ms | Subtests: 0/5 | Open test on wpt.live | wpt.fyi

Data from commit:
a50cb89 wpt: run reference-named files which are themselves tests (#836) (2026-09-03)

SubtestStatusMessage
inline-start and left map to the same pseudo-element in horizontal LTRFAILassert_equals: inline-start should return cascaded 'B' expected "\"B\"" but got "normal"
Writing-mode vertical-rl LTR correctly maps logical directions to physical directionsFAILassert_equals: inline-start should map to up and be overwritten expected "\"V-Up\"" but got "normal"
getComputedStyle() resolves properties when pseudo-element is not created due to no contentFAILassert_equals: Should correctly resolve styled width expected "80px" but got "200px"
getComputedStyle() resolves properties when the originating element is not a scroll containerFAILassert_equals: Should correctly resolve styled content expected "\"<\"" but got "normal"
getComputedStyle() with invalid argument falls back cleanly and does not crashFAILassert_equals: Should fall back cleanly and return empty width expected "" but got "200px"

/css/css-overflow/parsing/getComputedStyle-scroll-button.html

<!doctype html>
<meta charset="utf-8">
<title>CSS Overflow Test: getComputedStyle() resolves different properties for scroll-button pseudo-elements</title>
<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
  .scroller {
    width: 200px;
    height: 200px;
    overflow: auto;
  }
  #scroller-vertical {
    writing-mode: vertical-rl;
    direction: ltr;
  }

  /* Test 1: Horizontal LTR synonym matching */
  #scroller-horizontal::scroll-button(inline-start) {
    content: "A";
    width: 30px;
    height: 20px;
  }
  #scroller-horizontal::scroll-button(left) {
    content: "B"; /* Should cascade and overwrite inline-start */
    width: 35px;
    height: 20px;
  }

  /* Test 2: Vertical-RL writing mode mapping */
  #scroller-vertical::scroll-button(inline-start) {
    content: "V-Start";
    width: 45px;
    height: 20px;
  }
  #scroller-vertical::scroll-button(up) {
    content: "V-Up"; /* Should cascade and overwrite inline-start (which is up) */
    width: 50px;
    height: 20px;
  }
  #scroller-vertical::scroll-button(block-start) {
    content: "V-BStart";
    width: 55px;
    height: 20px;
  }
  #scroller-vertical::scroll-button(right) {
    content: "V-Right"; /* Should cascade and overwrite block-start (which is right) */
    width: 60px;
    height: 20px;
  }

  /* Test 3: Scroll button styled without 'content' (not generated) */
  #scroller-no-content::scroll-button(inline-start) {
    width: 80px;
    height: 20px;
  }

  /* Test 4: Non-scroller originating element */
  #not-scroller::scroll-button(inline-start) {
    content: "<";
    width: 90px;
    height: 20px;
  }
</style>
<div id="scroller-horizontal" class="scroller">
  <div style="width: 500px; height: 100px;"></div>
</div>
<div id="scroller-vertical" class="scroller">
  <div style="width: 500px; height: 500px;"></div>
</div>
<div id="scroller-no-content" class="scroller">
  <div style="width: 500px; height: 100px;"></div>
</div>
<div id="not-scroller" style="width: 100px; height: 100px;"></div>
<script>
  test(() => {
    const scroller = document.getElementById("scroller-horizontal");
    const styleStart = getComputedStyle(scroller, "::scroll-button(inline-start)");
    const styleLeft = getComputedStyle(scroller, "::scroll-button(left)");

    // In horizontal LTR, inline-start and left map to the same pseudo-element.
    // Therefore, the 'left' rule (defined later) should cascade and overwrite
    // the 'inline-start' rule, and both queries must return identical values.
    assert_equals(styleStart.content, '"B"', "inline-start should return cascaded 'B'");
    assert_equals(styleStart.width, "35px", "inline-start should return cascaded 35px");

    assert_equals(styleLeft.content, '"B"', "left should return cascaded 'B'");
    assert_equals(styleLeft.width, "35px", "left should return cascaded 35px");
  }, "inline-start and left map to the same pseudo-element in horizontal LTR");

  test(() => {
    const scroller = document.getElementById("scroller-vertical");

    // In vertical-rl LTR, inline-start is up (top).
    // Therefore, 'up' (defined later) should overwrite 'inline-start'.
    const styleStart = getComputedStyle(scroller, "::scroll-button(inline-start)");
    const styleUp = getComputedStyle(scroller, "::scroll-button(up)");

    assert_equals(styleStart.content, '"V-Up"', "inline-start should map to up and be overwritten");
    assert_equals(styleStart.width, "50px", "inline-start width should be overwritten");
    assert_equals(styleUp.content, '"V-Up"', "up should return 'V-Up'");
    assert_equals(styleUp.width, "50px", "up width should be 50px");

    // In vertical-rl LTR, block-start is right.
    // Therefore, 'right' (defined later) should overwrite 'block-start'.
    const styleBStart = getComputedStyle(scroller, "::scroll-button(block-start)");
    const styleRight = getComputedStyle(scroller, "::scroll-button(right)");

    assert_equals(styleBStart.content, '"V-Right"', "block-start should map to right and be overwritten");
    assert_equals(styleBStart.width, "60px", "block-start width should be overwritten");
    assert_equals(styleRight.content, '"V-Right"', "right should return 'V-Right'");
    assert_equals(styleRight.width, "60px", "right width should be 60px");
  }, "Writing-mode vertical-rl LTR correctly maps logical directions to physical directions");

  test(() => {
    const scroller = document.getElementById("scroller-no-content");
    const styleStart = getComputedStyle(scroller, "::scroll-button(inline-start)");

    // The pseudo-element is not created because it has no content.
    // However, getComputedStyle() should still resolve and return the styled width.
    assert_equals(styleStart.content, "normal", "Should return default 'normal' content");
    assert_equals(styleStart.width, "80px", "Should correctly resolve styled width");
  }, "getComputedStyle() resolves properties when pseudo-element is not created due to no content");

  test(() => {
    const notScroller = document.getElementById("not-scroller");
    const styleStart = getComputedStyle(notScroller, "::scroll-button(inline-start)");

    // The originating element is not a scroll container, so the pseudo-element
    // is generated in a disabled state (since it has styled content).
    // getComputedStyle() should resolve and return its styled properties correctly.
    assert_equals(styleStart.content, '"<"', "Should correctly resolve styled content");
    assert_equals(styleStart.width, "90px", "Should correctly resolve styled width");
  }, "getComputedStyle() resolves properties when the originating element is not a scroll container");

  test(() => {
    const scroller = document.getElementById("scroller-horizontal");
    const styleInvalid = getComputedStyle(scroller, "::scroll-button(invalid-argument)");

    // Invalid argument should not crash the browser, and instead fall back cleanly.
    assert_equals(styleInvalid.width, "", "Should fall back cleanly and return empty width");
  }, "getComputedStyle() with invalid argument falls back cleanly and does not crash");
</script>