✅ Types of Attribute Selectors
Syntax | Meaning |
|---|---|
| Selects elements with the attribute |
| Selects elements where attribute exactly equals value |
| Attribute contains a space-separated list, one of which is value |
| Attribute value starts with value |
| Attribute value ends with value |
| Attribute value contains value anywhere |
✅ Example Using Separate HTML and CSS
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Attribute Selector Anatomy</title>
<style>
.infographic-attrsel {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
box-sizing: border-box;
margin: 0 auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--paper);
color: var(--ink);
border-radius: 14px;
overflow: hidden;
}
.infographic-attrsel[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.ha-layout { display: flex; flex-direction: column; gap: 1.5rem; }
.ha-code-wrap {
background: #0b0b0b;
border: 1px solid #2a2a2a;
border-radius: 10px;
overflow: hidden;
}
.ha-filetabs {
display: flex;
background: #161616;
border-bottom: 1px solid #2a2a2a;
}
.ha-filetab {
border: none;
background: transparent;
color: #8a8a8a;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.78rem;
font-weight: 700;
padding: 0.6rem 1rem;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.ha-filetab:hover { color: #e5e5e5; }
.ha-filetab.ha-tab-on {
color: #ffffff;
border-bottom-color: #5b8def;
background: #0b0b0b;
}
.ha-code {
padding: 1rem 1.8rem 1rem 0.9rem;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.74rem;
line-height: 1.8;
overflow-x: auto;
}
.ha-codepane { display: none; }
.ha-codepane.ha-pane-on { display: block; }
.ha-line {
display: block;
white-space: pre;
color: #8a8a8a;
border-radius: 5px;
padding: 0.05rem 0.4rem;
}
.ha-line.ha-clickable {
cursor: pointer;
color: #e5e5e5;
transition: background 0.15s ease, box-shadow 0.15s ease;
}
.ha-line.ha-clickable:hover { background: #1c2333; }
.ha-line.ha-active {
background: #1c2333;
box-shadow: inset 3px 0 0 #5b8def;
color: #ffffff;
}
.ha-tag { color: #60a5fa; font-weight: 700; }
.ha-attr { color: #f2b96b; }
.ha-str { color: #9ece6a; }
.ha-sel { color: #f472b6; font-weight: 700; }
.ha-bracket { color: #ffd166; font-weight: 800; }
.ha-prop { color: #c586c0; }
.ha-val { color: #ce9178; }
.ha-comment { color: #5c6370; font-style: italic; }
.ha-browser {
border: 1px solid var(--line);
border-radius: 10px;
overflow: hidden;
background: var(--paper);
}
.ha-browser-bar {
background: var(--paper-alt);
padding: 0.5rem 0.7rem;
display: flex;
align-items: center;
gap: 0.5rem;
border-bottom: 1px solid var(--line);
}
.ha-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.ha-tab {
background: var(--paper);
border: 1px solid var(--line);
border-bottom: none;
border-radius: 6px 6px 0 0;
font-size: 0.72rem;
color: var(--ink-soft);
padding: 0.25rem 0.6rem;
}
.ha-urlrow {
background: var(--paper-alt);
padding: 0.35rem 0.7rem 0.6rem;
font-size: 0.72rem;
color: var(--ink-soft);
font-family: "SFMono-Regular", Consolas, monospace;
}
.ha-screen {
background: #ffffff;
color: #111;
padding: 1.2rem 1.4rem 1.4rem;
font-family: Georgia, "Times New Roman", serif;
}
.ha-link-row { display: flex; flex-direction: column; gap: 0.2rem; margin-bottom: 0.9rem; }
.ha-screen a { text-decoration: underline; font-size: 0.95rem; }
.ha-link-ext { color: tomato; }
.ha-link-int { color: blue; }
.ha-input-row { margin-bottom: 0.7rem; }
.ha-screen input {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 0.85rem;
padding: 0.4rem 0.6rem;
border: 1px solid #999;
border-radius: 2px;
}
.ha-input-email { border: 2px solid tomato; }
.ha-input-name { background-color: lightgreen; }
.ha-img-row { display: flex; gap: 0.8rem; margin-top: 0.9rem; }
.ha-img-box {
flex: 1;
height: 130px;
border-radius: 3px;
overflow: hidden;
}
.ha-img-jpg { border: 2px dashed tomato; }
</style>
</head>
<body>
<div class="infographic-attrsel" id="ha-root">
<div class="ha-inner">
<div class="ha-layout">
<div class="ha-code-wrap">
<div class="ha-filetabs">
<button class="ha-filetab ha-tab-on" id="tab-html">index.html</button>
<button class="ha-filetab" id="tab-css">style.css</button>
</div>
<div class="ha-code">
<div class="ha-codepane ha-pane-on" id="pane-html">
<span class="ha-line"><<span class="ha-tag">body</span>></span>
<span class="ha-line ha-clickable" data-part="ext"> <<span class="ha-tag">a</span> <span class="ha-attr">href</span>=<span class="ha-str">"https://example.com"</span> <span class="ha-attr">target</span>=<span class="ha-str">"_blank"</span>>External Link</<span class="ha-tag">a</span>></span>
<span class="ha-line ha-clickable" data-part="int"> <<span class="ha-tag">a</span> <span class="ha-attr">href</span>=<span class="ha-str">"page.html"</span>>Internal Link</<span class="ha-tag">a</span>></span>
<span class="ha-line ha-clickable" data-part="name"> <<span class="ha-tag">input</span> <span class="ha-attr">type</span>=<span class="ha-str">"text"</span> <span class="ha-attr">placeholder</span>=<span class="ha-str">"Your name"</span> /></span>
<span class="ha-line ha-clickable" data-part="email"> <<span class="ha-tag">input</span> <span class="ha-attr">type</span>=<span class="ha-str">"email"</span> <span class="ha-attr">placeholder</span>=<span class="ha-str">"Your email"</span> /></span>
<span class="ha-line"> <<span class="ha-tag">img</span> <span class="ha-attr">src</span>=<span class="ha-str">"image.png"</span> <span class="ha-attr">alt</span>=<span class="ha-str">"sample image"</span> /></span>
<span class="ha-line ha-clickable" data-part="jpg"> <<span class="ha-tag">img</span> <span class="ha-attr">src</span>=<span class="ha-str">"banner.jpg"</span> <span class="ha-attr">alt</span>=<span class="ha-str">"banner image"</span> /></span>
<span class="ha-line"></<span class="ha-tag">body</span>></span>
</div>
<div class="ha-codepane" id="pane-css">
<span class="ha-line ha-comment">/* has a 'target' attribute */</span>
<span class="ha-line ha-clickable" data-part="ext"><span class="ha-sel">a</span><span class="ha-bracket">[target]</span> {</span>
<span class="ha-line ha-clickable" data-part="ext"> <span class="ha-prop">color</span>: <span class="ha-val">tomato</span>;</span>
<span class="ha-line ha-clickable" data-part="ext">}</span>
<span class="ha-line ha-comment">/* type is exactly 'email' */</span>
<span class="ha-line ha-clickable" data-part="email"><span class="ha-sel">input</span><span class="ha-bracket">[type="email"]</span> {</span>
<span class="ha-line ha-clickable" data-part="email"> <span class="ha-prop">border</span>: <span class="ha-val">2px solid tomato</span>;</span>
<span class="ha-line ha-clickable" data-part="email">}</span>
<span class="ha-line ha-comment">/* src ends with '.jpg' */</span>
<span class="ha-line ha-clickable" data-part="jpg"><span class="ha-sel">img</span><span class="ha-bracket">[src$=".jpg"]</span> {</span>
<span class="ha-line ha-clickable" data-part="jpg"> <span class="ha-prop">border</span>: <span class="ha-val">2px dashed tomato</span>;</span>
<span class="ha-line ha-clickable" data-part="jpg">}</span>
<span class="ha-line ha-comment">/* placeholder contains 'name' */</span>
<span class="ha-line ha-clickable" data-part="name"><span class="ha-sel">input</span><span class="ha-bracket">[placeholder*="name"]</span> {</span>
<span class="ha-line ha-clickable" data-part="name"> <span class="ha-prop">background-color</span>: <span class="ha-val">lightgreen</span>;</span>
<span class="ha-line ha-clickable" data-part="name">}</span>
</div>
</div>
</div>
<div class="ha-browser">
<div class="ha-browser-bar">
<span class="ha-dot"></span><span class="ha-dot"></span><span class="ha-dot"></span>
<div class="ha-tab">🌐 127.0.0.1:5500/html</div>
</div>
<div class="ha-urlrow">127.0.0.1:5500/html</div>
<div class="ha-screen">
<div class="ha-link-row">
<a class="ha-link-ext" id="out-ext">External Link</a>
<a class="ha-link-int" id="out-int">Internal Link</a>
</div>
<div class="ha-input-row"><input class="ha-input-name" id="out-name" placeholder="Your name"></div>
<div class="ha-input-row"><input class="ha-input-email" id="out-email" type="email" placeholder="Your email"></div>
<div class="ha-img-row">
<div class="ha-img-box" id="out-png">
<svg width="100%" height="100%" viewBox="0 0 300 190" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice">
<rect width="300" height="190" fill="#dfe7ee"/>
<rect y="140" width="300" height="50" fill="#c7d2dc"/>
<circle cx="90" cy="70" r="30" fill="#a9b8c7"/>
<rect x="60" y="95" width="60" height="55" rx="10" fill="#a9b8c7"/>
<circle cx="220" cy="60" r="18" fill="#f4d58d"/>
</svg>
</div>
<div class="ha-img-box ha-img-jpg" id="out-jpg">
<svg width="100%" height="100%" viewBox="0 0 300 190" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice">
<rect width="300" height="190" fill="#cfe0ea"/>
<rect y="120" width="300" height="70" fill="#3d5b6b"/>
<polygon points="0,120 60,80 110,120" fill="#5a7d6f"/>
<polygon points="90,120 150,70 210,120" fill="#4f7263"/>
<polygon points="180,120 240,85 300,120" fill="#5a7d6f"/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('ha-root');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function () {
const tabHtml = document.getElementById('tab-html');
const tabCss = document.getElementById('tab-css');
const paneHtml = document.getElementById('pane-html');
const paneCss = document.getElementById('pane-css');
tabHtml.addEventListener('click', () => {
tabHtml.classList.add('ha-tab-on');
tabCss.classList.remove('ha-tab-on');
paneHtml.classList.add('ha-pane-on');
paneCss.classList.remove('ha-pane-on');
});
tabCss.addEventListener('click', () => {
tabCss.classList.add('ha-tab-on');
tabHtml.classList.remove('ha-tab-on');
paneCss.classList.add('ha-pane-on');
paneHtml.classList.remove('ha-pane-on');
});
const outMap = { ext: 'out-ext', int: 'out-int', name: 'out-name', email: 'out-email', jpg: 'out-jpg' };
document.querySelectorAll('.ha-line.ha-clickable').forEach(line => {
line.addEventListener('click', () => {
document.querySelectorAll('.ha-line').forEach(l => l.classList.remove('ha-active'));
line.classList.add('ha-active');
document.querySelectorAll('#out-ext,#out-int,#out-name,#out-email,#out-jpg').forEach(el => el.style.outline = '');
const part = line.getAttribute('data-part');
const target = document.getElementById(outMap[part]);
target.style.outline = '2px solid #2563eb';
target.style.outlineOffset = '2px';
});
});
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});🖥️ Output Summary
The external link (with
target="_blank") is red.The email input has a blue border.
The .jpg image has a dashed green border.
The input with
"name"in the placeholder has a light blue background.
🚀 Try and Play Around
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Attribute Selectors</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,wght@0,500;0,600;1,500&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
.attrsel-infographic {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #faf8f4;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-deep:#1741a6;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--code-bg: #1e1e2e;
--code-text: #d4d4dc;
--code-key: #7aa2f7;
--code-string:#9ece6a;
--code-punct: #6b7280;
--display-font: "Newsreader", serif;
--body-font: "Inter", sans-serif;
--mono-font: "IBM Plex Mono", monospace;
color: var(--ink);
font-family: var(--body-font);
max-width: 640px;
margin: 0 auto;
box-sizing: border-box;
line-height: 1.5;
}
.attrsel-infographic[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #14171f;
--paper-alt: #1d212b;
--accent: #5b8def;
--accent-deep:#5b8def;
--accent-soft:#202942;
--line: #2c313d;
background: var(--paper);
}
.attrsel-infographic[data-theme="dark"] .tab.is-active { color: #14171f; }
.attrsel-infographic[data-theme="dark"] .apply-btn.is-on { color: #14171f; }
.attrsel-infographic * { box-sizing: border-box; }
.attrsel-infographic .head { margin-bottom: 1.5rem; }
.attrsel-infographic .head h1 {
font-family: var(--display-font);
font-weight: 600;
font-size: clamp(1.6rem, 4.5vw, 2.2rem);
margin: 0 0 0.4rem;
}
.attrsel-infographic .head .sub {
color: var(--ink-soft);
font-size: 0.92rem;
margin: 0;
text-align: center;
}
/* tabs */
.attrsel-infographic .tabs {
display: flex;
gap: 0.35rem;
flex-wrap: wrap;
margin-bottom: 1.1rem;
}
.attrsel-infographic .tab {
font-family: var(--mono-font);
font-size: 0.76rem;
font-weight: 500;
padding: 0.42rem 0.7rem;
border: 1px solid var(--line);
background: var(--paper);
color: var(--ink-soft);
border-radius: 5px;
cursor: pointer;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.attrsel-infographic .tab:hover { background: var(--paper-alt); }
.attrsel-infographic .tab.is-active {
background: var(--accent-deep);
border-color: var(--accent-deep);
color: #fff;
}
/* one block style used for HTML and CSS code */
.attrsel-infographic .block { margin-bottom: 0.9rem; }
.attrsel-infographic .block-label {
font-size: 0.68rem;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 0.4rem;
}
.attrsel-infographic .code-box {
background: var(--code-bg);
color: var(--code-text);
font-family: var(--mono-font);
font-size: 0.86rem;
padding: 0.8rem 0.95rem;
border-radius: 7px;
white-space: pre-wrap;
word-break: break-word;
}
.attrsel-infographic .code-box.css-box {
line-height: 1;
}
.attrsel-infographic .code-box .tag-name { color: #e0af68; }
.attrsel-infographic .code-box .attr-key { color: var(--code-key); }
.attrsel-infographic .code-box .attr-val { color: var(--code-string); }
.attrsel-infographic .code-box .punct { color: var(--code-punct); }
/* CSS block visually reflects state, but the button drives the action */
.attrsel-infographic .css-box {
border: 1px solid transparent;
transition: border-color 0.15s, background 0.15s;
}
.attrsel-infographic .css-box.is-on {
border-color: var(--code-key);
background: #232338;
}
.attrsel-infographic .css-box .prop { color: #9999ad; }
.attrsel-infographic .css-box .val { color: #f0a96e; }
/* apply button, sits right under the CSS block */
.attrsel-infographic .apply-btn {
display: inline-flex;
align-items: center;
gap: 0.4rem;
margin-top: 0.55rem;
font-family: var(--body-font);
font-size: 0.8rem;
font-weight: 600;
padding: 0.5rem 0.9rem;
border: 1px solid var(--accent);
background: var(--paper);
color: var(--accent-deep);
border-radius: 6px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.attrsel-infographic .apply-btn:hover { background: var(--accent-soft); }
.attrsel-infographic .apply-btn.is-on {
background: var(--accent-deep);
border-color: var(--accent-deep);
color: #fff;
}
/* note block: sits right under Output */
.attrsel-infographic .note-box {
font-size: 0.78rem;
color: var(--ink-soft);
background: var(--paper-alt);
border-left: 3px solid var(--line);
padding: 0.55rem 0.7rem;
border-radius: 4px;
transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.attrsel-infographic .note-box strong { color: var(--ink); }
.attrsel-infographic .note-box.is-on {
border-left-color: var(--accent);
background: var(--accent-soft);
color: var(--accent-deep);
}
.attrsel-infographic .note-box.is-on strong { color: var(--accent-deep); }
/* output block: the live element */
.attrsel-infographic .output-box {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 7px;
padding: 1.1rem;
display: flex;
align-items: center;
min-height: 64px;
}
.attrsel-infographic .output-box input,
.attrsel-infographic .output-box a,
.attrsel-infographic .output-box img,
.attrsel-infographic .output-box .fake-img,
.attrsel-infographic .output-box .fake-div {
font: inherit;
font-size: 0.85rem;
}
.attrsel-infographic .output-box input {
padding: 0.5rem 0.65rem;
border: 1px solid var(--line);
border-radius: 4px;
background: var(--paper);
color: var(--ink);
width: 200px;
}
.attrsel-infographic .output-box a {
color: var(--accent);
text-decoration: underline;
}
.attrsel-infographic .output-box .fake-div {
padding: 0.5rem 0.9rem;
border: 1px solid var(--line);
border-radius: 4px;
background: var(--paper);
color: var(--ink-soft);
}
.attrsel-infographic .output-box .fake-img {
display: inline-flex;
align-items: center;
justify-content: center;
width: 56px;
height: 56px;
border: 1px solid var(--line);
border-radius: 4px;
background: var(--paper);
color: var(--ink-soft);
font-size: 0.65rem;
}
/* transition for whichever style gets applied */
.attrsel-infographic .output-box * { transition: all 0.2s; }
.attrsel-infographic [data-tabpanel] { display: none; }
.attrsel-infographic [data-tabpanel].is-active { display: block; }
.attrsel-infographic .footer-note {
margin-top: 1.4rem;
font-size: 0.85rem;
color: var(--ink-soft);
text-align: center;
}
@media (prefers-reduced-motion: reduce) {
.attrsel-infographic *, .attrsel-infographic *::before, .attrsel-infographic *::after {
transition-duration: 0.001ms !important;
animation-duration: 0.001ms !important;
}
}
</style>
</head>
<body>
<div class="attrsel-infographic">
<div class="tabs">
<button class="tab is-active" type="button" data-tab-btn="exists">[attr]</button>
<button class="tab" type="button" data-tab-btn="exact">[attr="val"]</button>
<button class="tab" type="button" data-tab-btn="contains-word">[attr~="val"]</button>
<button class="tab" type="button" data-tab-btn="starts-with">[attr^="val"]</button>
<button class="tab" type="button" data-tab-btn="ends-with">[attr$="val"]</button>
<button class="tab" type="button" data-tab-btn="contains-any">[attr*="val"]</button>
</div>
<!-- [attr] -->
<div data-tabpanel="exists" class="is-active">
<div class="block">
<div class="block-label">HTML</div>
<div class="code-box"><<span class="tag-name">input</span> <span class="attr-key">required</span> <span class="attr-key">type</span>=<span class="punct">"</span><span class="attr-val">email</span><span class="punct">"</span> /></div>
</div>
<div class="block">
<div class="block-label">Output</div>
<div class="output-box"><input type="email" data-output placeholder="Email field" /></div>
</div>
<div class="block">
<div class="block-label">Note</div>
<div class="note-box" data-note>Matches because the element simply <strong>has</strong> the <code style="color:inherit">required</code> attribute — its value doesn't matter.</div>
</div>
<div class="block">
<div class="block-label">CSS</div>
<div class="code-box css-box" data-rule data-style="border:2px solid #2563eb;outline:none;background:#e3ebfd;">
<span class="tag-name">input</span><span class="punct">[</span><span class="attr-key">required</span><span class="punct">]</span> <span class="punct">{</span><br>
<span class="prop">border</span>: <span class="val">2px solid blue</span>;<br>
<span class="punct">}</span>
</div>
<button class="apply-btn" type="button" data-apply-btn>Apply style</button>
</div>
</div>
<!-- [attr="value"] -->
<div data-tabpanel="exact">
<div class="block">
<div class="block-label">HTML</div>
<div class="code-box"><<span class="tag-name">input</span> <span class="attr-key">type</span>=<span class="punct">"</span><span class="attr-val">text</span><span class="punct">"</span> /></div>
</div>
<div class="block">
<div class="block-label">Output</div>
<div class="output-box"><input type="text" data-output placeholder="Username" /></div>
</div>
<div class="block">
<div class="block-label">Note</div>
<div class="note-box" data-note>Matches only when the value is an <strong>exact</strong> match — "text", nothing else.</div>
</div>
<div class="block">
<div class="block-label">CSS</div>
<div class="code-box css-box" data-rule data-style="background:#fff7e0;border:2px solid #c9971f;outline:none;">
<span class="tag-name">input</span><span class="punct">[</span><span class="attr-key">type</span>=<span class="punct">"</span><span class="attr-val">text</span><span class="punct">"</span><span class="punct">]</span> <span class="punct">{</span><br>
<span class="prop">background</span>: <span class="val">khaki</span>;<br>
<span class="punct">}</span>
</div>
<button class="apply-btn" type="button" data-apply-btn>Apply style</button>
</div>
</div>
<!-- [attr~="value"] -->
<div data-tabpanel="contains-word">
<div class="block">
<div class="block-label">HTML</div>
<div class="code-box"><<span class="tag-name">div</span> <span class="attr-key">class</span>=<span class="punct">"</span><span class="attr-val">box card shadow</span><span class="punct">"</span>></<span class="tag-name">div</span>></div>
</div>
<div class="block">
<div class="block-label">Output</div>
<div class="output-box"><span class="fake-div" data-output>Product card</span></div>
</div>
<div class="block">
<div class="block-label">Note</div>
<div class="note-box" data-note>The class is a <strong>space-separated list</strong> — matches because "card" is one of the words.</div>
</div>
<div class="block">
<div class="block-label">CSS</div>
<div class="code-box css-box" data-rule data-style="background:#eafbf1;border:2px solid #1f9d55;border-radius:8px;">
<span class="tag-name">div</span><span class="punct">[</span><span class="attr-key">class</span>~=<span class="punct">"</span><span class="attr-val">card</span><span class="punct">"</span><span class="punct">]</span> <span class="punct">{</span><br>
<span class="prop">border-radius</span>: <span class="val">8px</span>;<br>
<span class="punct">}</span>
</div>
<button class="apply-btn" type="button" data-apply-btn>Apply style</button>
</div>
</div>
<!-- [attr^="value"] -->
<div data-tabpanel="starts-with">
<div class="block">
<div class="block-label">HTML</div>
<div class="code-box"><<span class="tag-name">a</span> <span class="attr-key">href</span>=<span class="punct">"</span><span class="attr-val">https://docs.dev</span><span class="punct">"</span>>Docs</<span class="tag-name">a</span>></div>
</div>
<div class="block">
<div class="block-label">Output</div>
<div class="output-box"><a href="#" data-output onclick="return false;">Docs</a></div>
</div>
<div class="block">
<div class="block-label">Note</div>
<div class="note-box" data-note>Matches because the value <strong>starts with</strong> "https" — handy for flagging secure links.</div>
</div>
<div class="block">
<div class="block-label">CSS</div>
<div class="code-box css-box" data-rule data-style="color:#1741a6;font-weight:600;text-decoration:underline;">
<span class="tag-name">a</span><span class="punct">[</span><span class="attr-key">href</span>^=<span class="punct">"</span><span class="attr-val">https</span><span class="punct">"</span><span class="punct">]</span> <span class="punct">{</span><br>
<span class="prop">color</span>: <span class="val">darkblue</span>;<br>
<span class="punct">}</span>
</div>
<button class="apply-btn" type="button" data-apply-btn>Apply style</button>
</div>
</div>
<!-- [attr$="value"] -->
<div data-tabpanel="ends-with">
<div class="block">
<div class="block-label">HTML</div>
<div class="code-box"><<span class="tag-name">img</span> <span class="attr-key">src</span>=<span class="punct">"</span><span class="attr-val">logo.png</span><span class="punct">"</span> /></div>
</div>
<div class="block">
<div class="block-label">Output</div>
<div class="output-box"><span class="fake-img" data-output>logo.png</span></div>
</div>
<div class="block">
<div class="block-label">Note</div>
<div class="note-box" data-note>Matches because the value <strong>ends with</strong> ".png" — useful for targeting file types.</div>
</div>
<div class="block">
<div class="block-label">CSS</div>
<div class="code-box css-box" data-rule data-style="border:2px dashed #c23b5a;">
<span class="tag-name">img</span><span class="punct">[</span><span class="attr-key">src</span>$=<span class="punct">"</span><span class="attr-val">.png</span><span class="punct">"</span><span class="punct">]</span> <span class="punct">{</span><br>
<span class="prop">border</span>: <span class="val">2px dashed red</span>;<br>
<span class="punct">}</span>
</div>
<button class="apply-btn" type="button" data-apply-btn>Apply style</button>
</div>
</div>
<!-- [attr*="value"] -->
<div data-tabpanel="contains-any">
<div class="block">
<div class="block-label">HTML</div>
<div class="code-box"><<span class="tag-name">div</span> <span class="attr-key">id</span>=<span class="punct">"</span><span class="attr-val">profile-user-42</span><span class="punct">"</span>></<span class="tag-name">div</span>></div>
</div>
<div class="block">
<div class="block-label">Output</div>
<div class="output-box"><span class="fake-div" data-output>Profile card</span></div>
</div>
<div class="block">
<div class="block-label">Note</div>
<div class="note-box" data-note>Matches because the value contains "user" <strong>anywhere</strong> — even mid-word.</div>
</div>
<div class="block">
<div class="block-label">CSS</div>
<div class="code-box css-box" data-rule data-style="background:#f3edff;border:2px solid #7c4dd1;">
<span class="tag-name">div</span><span class="punct">[</span><span class="attr-key">id</span>*=<span class="punct">"</span><span class="attr-val">user</span><span class="punct">"</span><span class="punct">]</span> <span class="punct">{</span><br>
<span class="prop">background</span>: <span class="val">lavender</span>;<br>
<span class="punct">}</span>
</div>
<button class="apply-btn" type="button" data-apply-btn>Apply style</button>
</div>
</div>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.attrsel-infographic')
|| document.querySelector('.attrsel-infographic');
if (!wrapper) return;
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
var params = new URLSearchParams(window.location.search);
var paramVal = params.get('darkMode');
if (paramVal === 'true') {
dark = true;
} else if (paramVal === 'false') {
dark = false;
} else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
window.addEventListener('message', function (e) {
if (e && e.data && typeof e.data === 'object' && 'darkMode' in e.data) {
wrapper.setAttribute('data-theme', e.data.darkMode ? 'dark' : 'light');
}
});
})();
</script>
<script>
(function() {
var scope = document.querySelector('.attrsel-infographic');
if (!scope) return;
var tabBtns = scope.querySelectorAll('[data-tab-btn]');
var panels = scope.querySelectorAll('[data-tabpanel]');
tabBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
var key = btn.getAttribute('data-tab-btn');
tabBtns.forEach(function(b) { b.classList.toggle('is-active', b === btn); });
panels.forEach(function(p) { p.classList.toggle('is-active', p.getAttribute('data-tabpanel') === key); });
});
});
// each panel: clicking the Apply button toggles the inline style on its output element
panels.forEach(function(panel) {
var ruleBox = panel.querySelector('[data-rule]');
var applyBtn = panel.querySelector('[data-apply-btn]');
var output = panel.querySelector('[data-output]');
var note = panel.querySelector('[data-note]');
if (!ruleBox || !applyBtn || !output) return;
var styleStr = ruleBox.getAttribute('data-style');
var originalStyle = output.getAttribute('style') || '';
applyBtn.addEventListener('click', function() {
var isOn = ruleBox.classList.toggle('is-on');
applyBtn.classList.toggle('is-on', isOn);
applyBtn.textContent = isOn ? 'Remove style' : 'Apply style';
output.setAttribute('style', isOn ? (originalStyle + ';' + styleStr) : originalStyle);
if (note) note.classList.toggle('is-on', isOn);
});
});
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});