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:
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.
<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.
<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:
<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:
<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:
<div m="10px 20px 30px 40px">Compound margin</div>