New CSS attr() Syntax Demo

An educational, self-contained demo inspired by zhangxinxu's article. It shows the core idea without republishing the article: CSS custom properties can carry typed attr() expressions, and JavaScript can resolve those expressions into live styles.

Traditional attr(): String Content Only

Classic CSS can read attributes as text in generated content. Hover or focus the button to reveal the tooltip.

Code:
<span data-title="Tip from data-title" tabindex="0">Button</span>

span:hover::after,
span:focus-visible::after {
  content: attr(data-title);
}
Result:
Button

Hover or tab-focus the button.

Typed Attribute Values for Button Styles

The showcased CSS uses custom properties as messengers. The embedded polyfill resolves attr(bgcolor color) and attr(radius px, 4px) into real values.

Code:
<button bgcolor="skyblue" radius="4">Button</button>
<button bgcolor="#00000040" radius="1rem">Button</button>
<button bgcolor="red" radius="50%">Button</button>
<button bgcolor="orange" radius="100% / 50%">Button</button>

button {
  border: 0;
  padding: .65em 1.05em;
  --attr-bg: attr(bgcolor color);
  background-color: var(--attr-bg);
  --attr-radius: attr(radius px, 4px);
  border-radius: var(--attr-radius);
}
Result:

Live Attribute Mutation

Change the attributes below. A MutationObserver reruns the resolver, so the button updates immediately.

Code:
<button class="live-button" bgcolor="seagreen" radius="10">Live button</button>

.live-button {
  --attr-bg: attr(bgcolor color);
  background-color: var(--attr-bg);
  --attr-radius: attr(radius px, 4px);
  border-radius: var(--attr-radius);
}

bgInput.addEventListener("input", () => {
  liveButton.setAttribute("bgcolor", bgInput.value);
});

radiusInput.addEventListener("input", () => {
  liveButton.setAttribute("radius", radiusInput.value);
});
Result:
10px

<button class="live-button" bgcolor="seagreen" radius="10">Live button</button>

Default Styles with Attribute Overrides

Attribute selectors keep the default button intact, while elements with custom attributes opt into the dynamic values.

Code:
<button>Default</button>
<button bgcolor="mediumvioletred">Color only</button>
<button radius="24">Radius only</button>

button {
  color: #fff;
  background-color: deepskyblue;
  border-radius: 6px;
}

button[bgcolor] {
  --attr-bg: attr(bgcolor color);
  background-color: var(--attr-bg);
}

button[radius] {
  --attr-radius: attr(radius px, 4px);
  border-radius: var(--attr-radius);
}
Result:

Spacing Utilities from HTML Attributes

This pattern can turn compact HTML attributes into reusable spacing controls.

Code:
<div mt="28" pl="36">Top margin and left padding</div>

[mt] {
  --mt: attr(mt px, 0);
  margin-top: var(--mt);
}

[pl] {
  --pl: attr(pl px, 0);
  padding-left: var(--pl);
}
Result:
Top margin and left padding

<div mt="28" pl="36">Top margin and left padding</div>

Compound Attribute Values

A polyfill can also pass compound values such as four-part margins.

Code:
<div m="10px 20px 30px 40px">Compound margin</div>

[m] {
  --m: attr(m);
  margin: var(--m);
}
Result:
Compound margin

<div m="10px 20px 30px 40px">Compound margin</div>