Blink promotes 'display' from inline to inline-block depending on 'appearance'

#568576344 Filed 2026-10-02 P4 / S3 Blink > CSS / Forms

Repro

A <span> or <a> has no native appearance, so appearance: button resolves to none on it. Per css-ui-4 the appearance property never affects display. Chrome up to M156 computes display: inline-block instead of the specified display: inline.

<span style="appearance: button; display: inline">span</span>

/* computed display */
Chrome 156  -> inline-block   (wrong)
Firefox 157 -> inline
Safari 27   -> inline

Live result

Computed display of elements without a native appearance. Every row must report the specified value.

Running...
Elementappearancespecified displaycomputed displayResult

Web-compat fallout

The case from Mozilla bug 2074147: an <a> with no in-flow content, appearance: button and a specified width/height. With the correct display: inline the box sizes to 0 and nothing is painted; with Chrome's promotion to inline-block a 240x60 red area appears, and sites start to depend on it.

Observed size

Running...

Root cause

LayoutTheme::AdjustStyle() coerces inline and table display values to inline-block (and list-item/table to block) for every element that carries an appearance value other than none -- before the effective appearance is known to be none. The coercion is a layout requirement of native widget boxes, which are atomic inline-level or block-level and can never be table parts, so it has to key off the element type rather than off the appearance value.

// third_party/blink/renderer/core/layout/layout_theme.cc
if (IsLaidOutAsWidget(element, appearance)) {
  AdjustDisplayForWidget(builder);
}

// Widget elements keep the adjusted display even when author styles
// suppress their effective appearance; elements without a native
// appearance keep the specified display.
bool IsLaidOutAsWidget(const Element& element, AppearanceValue appearance) {
  return appearance != AppearanceValue::kNone ||
         AutoAppearanceFor(element) != AppearanceValue::kNone;
}

Related

#408002786 -- same code path suppressed ::marker on <details>/<li> with appearance: auto. The element-type condition fixes it too.

#492945641 -- the earlier attempt at #408002786 keyed off the effective appearance and broke <button style="display: table-cell; border: none"> in tables (P0, reverted via flag). Those buttons stay inline-block here, and a unit test covers the case.

WPT: css/css-ui/appearance-display-001.html, css/css-ui/appearance-auto-details-list-item.html.