Build a feature explorer with Tailwind CSS in two columns. The left column is a list of five rows; each row has a small square glyph tile, a medium-weight title, a one-line description and a chevron, and the active row gets a neutral-50 background with a neutral-900 left rule. The right column is a rounded-2xl bordered panel with a soft neutral-50 header strip showing a fake breadcrumb and three window dots, and a body that shows the active feature — a large glyph, a heading, a paragraph, and a two-column list of small facts with muted labels above medium values. Below the panel, a caption in muted small text. Then add JavaScript that switches the active row on both hover and click, swaps the panel content from a data map, and applies a short opacity transition to the panel body so the change reads as a fade rather than a jump. Stack the columns below lg with the panel underneath.
Paste it into Claude, Cursor, v0 or any AI coding tool. Prefer the
finished markup? .
Build a feature explorer with Tailwind CSS in two columns. The left column is a list of five rows; each row has a small square glyph tile, a medium-weight title, a one-line description and a chevron, and the active row gets a neutral-50 background with a neutral-900 left rule. The right column is a rounded-2xl bordered panel with a soft neutral-50 header strip showing a fake breadcrumb and three window dots, and a body that shows the active feature — a large glyph, a heading, a paragraph, and a two-column list of small facts with muted labels above medium values. Below the panel, a caption in muted small text. Then add JavaScript that switches the active row on both hover and click, swaps the panel content from a data map, and applies a short opacity transition to the panel body so the change reads as a fade rather than a jump. Stack the columns below lg with the panel underneath.
var DATA = {
rules: {
crumb: "workspace / rules",
icon: "⚡",
title: "Rules engine",
text: "Expressions compile to a decision tree, so a policy with four hundred branches still answers inside a request.",
facts: [["Median latency", "18 ms"], ["Evaluated daily", "1.4 billion"]],
},
audit: {
crumb: "workspace / audit",
icon: "▤",
title: "Audit trail",
text: "Every decision is written once, signed, and kept queryable for as long as your retention policy says.",
facts: [["Retention", "7 years"], ["Export formats", "CSV, Parquet"]],
},
alerts: {
crumb: "workspace / alerts",
icon: "✉",
title: "Alerting",
text: "Severity, owner and schedule decide where a notification lands — and who it wakes at three in the morning.",
facts: [["Delivery targets", "9"], ["Median notify", "4.2 s"]],
},
access: {
crumb: "workspace / access",
icon: "⛨",
title: "Access control",
text: "Roles map onto the groups you already maintain in your identity provider, synced on every login.",
facts: [["Providers", "Okta, Entra, Google"], ["Sync", "SCIM 2.0"]],
},
reports: {
crumb: "workspace / reports",
icon: "◈",
title: "Reporting",
text: "Build a view once and have it land in the right inboxes every Monday, with the filters already applied.",
facts: [["Schedules", "Hourly to monthly"], ["Formats", "PDF, CSV, link"]],
},
};
var rows = Array.prototype.slice.call(document.querySelectorAll(".feature-row"));
var body = document.getElementById("panelBody");
var crumb = document.getElementById("panelCrumb");
var icon = document.getElementById("panelIcon");
var title = document.getElementById("panelTitle");
var text = document.getElementById("panelText");
var facts = document.getElementById("panelFacts");
var active = "";
function paint(key) {
if (key === active) return;
active = key;
var d = DATA[key];
rows.forEach(function (r) {
var on = r.dataset.key === key;
r.classList.toggle("bg-neutral-50", on);
r.classList.toggle("border-neutral-900", on);
r.classList.toggle("border-transparent", !on);
});
// Fade out, swap while invisible, fade back in.
body.classList.add("opacity-0");
setTimeout(function () {
crumb.textContent = d.crumb;
icon.textContent = d.icon;
title.textContent = d.title;
text.textContent = d.text;
facts.innerHTML = d.facts
.map(function (f) {
return '<div><dt class="text-xs uppercase tracking-widest text-neutral-400">' + f[0] + '</dt><dd class="mt-1 font-medium text-neutral-900">' + f[1] + "</dd></div>";
})
.join("");
body.classList.remove("opacity-0");
}, 160);
}
rows.forEach(function (row) {
row.addEventListener("mouseenter", function () {
paint(row.dataset.key);
});
row.addEventListener("click", function () {
paint(row.dataset.key);
});
row.addEventListener("focus", function () {
paint(row.dataset.key);
});
});
paint("rules");