Pure CSSMIT licensed

Tabbed Category Content Slider

Tabbed category content slider — SaaS-style feature carousel where the pagination is a row of wide text tabs instead of dots. Clicking a tab reveals its matching panel (image plus copy) with a smooth cross-fade. Pure CSS via the radio-button pattern; the exact structure Stripe, Vercel, and Linear ship on their product feature pages without shadcn Tabs or Radix Tabs primitives.

Live Demo
Try it

Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects

ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link

10% off at checkout with code
Check ZTM Course

The code

<div class="cis-12">
  <input type="radio" name="cis-12" id="cis-12-1" checked>
  <input type="radio" name="cis-12" id="cis-12-2">
  <input type="radio" name="cis-12" id="cis-12-3">
  <div class="cis-12__tabs">
    <label for="cis-12-1">Overview</label>
    <label for="cis-12-2">Integrations</label>
    <label for="cis-12-3">Reporting</label>
  </div>
  <div class="cis-12__panels">
    <div class="cis-12__panel">
      <div class="cis-12__media"><img src="https://picsum.photos/seed/cis12a/800/560" alt="Overview screen"></div>
      <div class="cis-12__copy"><h3>One workspace for everything</h3><p>Bring docs, tasks and chat into a single, searchable home. Onboard a teammate in minutes.</p></div>
    </div>
    <div class="cis-12__panel">
      <div class="cis-12__media"><img src="https://picsum.photos/seed/cis12b/800/560" alt="Integrations screen"></div>
      <div class="cis-12__copy"><h3>Connect the tools you already use</h3><p>200+ native integrations, a typed API and webhooks that don't drop events.</p></div>
    </div>
    <div class="cis-12__panel">
      <div class="cis-12__media"><img src="https://picsum.photos/seed/cis12c/800/560" alt="Reporting screen"></div>
      <div class="cis-12__copy"><h3>Reports that build themselves</h3><p>Scheduled dashboards, export to anywhere, and alerts when the numbers move.</p></div>
    </div>
  </div>
</div>
.cis-12,
.cis-12 *,
.cis-12 *::before,
.cis-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cis-12 ::selection {
  background: oklch(0.82 0.14 265);
  color: #0a0620;
}

.cis-12 {
  --accent: oklch(0.72 0.16 270);
  --surface: #12131d;
  --line: #242637;
  --text: #eef0f7;
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--text);
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  padding: 44px 20px;
  background: radial-gradient(120% 110% at 50% 0%,#14162200,#0a0b12);
}

.cis-12 input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.cis-12__tabs {
  display: flex;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 6px;
  width: min(560px,94vw);
  overflow-x: auto;
  scrollbar-width: none;
}

.cis-12__tabs::-webkit-scrollbar {
  display: none;
}

.cis-12__tabs label {
  flex: 1;
  white-space: nowrap;
  text-align: center;
  padding: 11px 16px;
  border-radius: 9px;
  font-size: 14px;
  font-weight: 600;
  color: #9298ac;
  cursor: pointer;
  transition: color .25s,background .25s;
}

.cis-12__tabs label:hover {
  color: #fff;
}

#cis-12-1:checked ~ .cis-12__tabs label[for="cis-12-1"],
#cis-12-2:checked ~ .cis-12__tabs label[for="cis-12-2"],
#cis-12-3:checked ~ .cis-12__tabs label[for="cis-12-3"] {
  color: #fff;
  background: color-mix(in oklab,var(--accent) 30%,transparent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.cis-12__panels {
  position: relative;
  width: min(760px,94vw);
  min-height: 320px;
}

.cis-12__panel {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 24px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 20px;
  opacity: 0;
  transform: translateY(14px);
  pointer-events: none;
  transition: opacity .45s,transform .45s;
}

#cis-12-1:checked ~ .cis-12__panels .cis-12__panel:nth-child(1),
#cis-12-2:checked ~ .cis-12__panels .cis-12__panel:nth-child(2),
#cis-12-3:checked ~ .cis-12__panels .cis-12__panel:nth-child(3) {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.cis-12__media {
  aspect-ratio: 8/5.6;
  border-radius: 12px;
  overflow: hidden;
}

.cis-12__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cis-12__copy h3 {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.15;
  margin-bottom: 10px;
  text-wrap: balance;
}

.cis-12__copy p {
  font-size: 14.5px;
  line-height: 1.55;
  color: #9aa0b4;
}

@media (max-width: 640px) {
  .cis-12__panel {
    grid-template-columns: 1fr;
    overflow-y: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cis-12__panel {
    transition: opacity .2s;
  }
}
/* No JavaScript — text tabs are radio labels; :checked cross-fades the panels. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Image Slider from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tabbed Category Content Slider
Source: https://codefronts.com/components/css-image-slider/tabbed-category-content-slider/

Tabbed category content slider — SaaS-style feature carousel where the pagination is a row of wide text tabs instead of dots. Clicking a tab reveals its matching panel (image plus copy) with a smooth cross-fade. Pure CSS via the radio-button pattern; the exact structure Stripe, Vercel, and Linear ship on their product feature pages without shadcn Tabs or Radix Tabs primitives.
## HTML
```html
<div class="cis-12">
  <input type="radio" name="cis-12" id="cis-12-1" checked>
  <input type="radio" name="cis-12" id="cis-12-2">
  <input type="radio" name="cis-12" id="cis-12-3">
  <div class="cis-12__tabs">
    <label for="cis-12-1">Overview</label>
    <label for="cis-12-2">Integrations</label>
    <label for="cis-12-3">Reporting</label>
  </div>
  <div class="cis-12__panels">
    <div class="cis-12__panel">
      <div class="cis-12__media"><img src="https://picsum.photos/seed/cis12a/800/560" alt="Overview screen"></div>
      <div class="cis-12__copy"><h3>One workspace for everything</h3><p>Bring docs, tasks and chat into a single, searchable home. Onboard a teammate in minutes.</p></div>
    </div>
    <div class="cis-12__panel">
      <div class="cis-12__media"><img src="https://picsum.photos/seed/cis12b/800/560" alt="Integrations screen"></div>
      <div class="cis-12__copy"><h3>Connect the tools you already use</h3><p>200+ native integrations, a typed API and webhooks that don't drop events.</p></div>
    </div>
    <div class="cis-12__panel">
      <div class="cis-12__media"><img src="https://picsum.photos/seed/cis12c/800/560" alt="Reporting screen"></div>
      <div class="cis-12__copy"><h3>Reports that build themselves</h3><p>Scheduled dashboards, export to anywhere, and alerts when the numbers move.</p></div>
    </div>
  </div>
</div>
```
## CSS
```css
.cis-12,
.cis-12 *,
.cis-12 *::before,
.cis-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cis-12 ::selection {
  background: oklch(0.82 0.14 265);
  color: #0a0620;
}

.cis-12 {
  --accent: oklch(0.72 0.16 270);
  --surface: #12131d;
  --line: #242637;
  --text: #eef0f7;
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--text);
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  padding: 44px 20px;
  background: radial-gradient(120% 110% at 50% 0%,#14162200,#0a0b12);
}

.cis-12 input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.cis-12__tabs {
  display: flex;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 6px;
  width: min(560px,94vw);
  overflow-x: auto;
  scrollbar-width: none;
}

.cis-12__tabs::-webkit-scrollbar {
  display: none;
}

.cis-12__tabs label {
  flex: 1;
  white-space: nowrap;
  text-align: center;
  padding: 11px 16px;
  border-radius: 9px;
  font-size: 14px;
  font-weight: 600;
  color: #9298ac;
  cursor: pointer;
  transition: color .25s,background .25s;
}

.cis-12__tabs label:hover {
  color: #fff;
}

#cis-12-1:checked ~ .cis-12__tabs label[for="cis-12-1"],
#cis-12-2:checked ~ .cis-12__tabs label[for="cis-12-2"],
#cis-12-3:checked ~ .cis-12__tabs label[for="cis-12-3"] {
  color: #fff;
  background: color-mix(in oklab,var(--accent) 30%,transparent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.cis-12__panels {
  position: relative;
  width: min(760px,94vw);
  min-height: 320px;
}

.cis-12__panel {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 24px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 20px;
  opacity: 0;
  transform: translateY(14px);
  pointer-events: none;
  transition: opacity .45s,transform .45s;
}

#cis-12-1:checked ~ .cis-12__panels .cis-12__panel:nth-child(1),
#cis-12-2:checked ~ .cis-12__panels .cis-12__panel:nth-child(2),
#cis-12-3:checked ~ .cis-12__panels .cis-12__panel:nth-child(3) {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.cis-12__media {
  aspect-ratio: 8/5.6;
  border-radius: 12px;
  overflow: hidden;
}

.cis-12__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cis-12__copy h3 {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.15;
  margin-bottom: 10px;
  text-wrap: balance;
}

.cis-12__copy p {
  font-size: 14.5px;
  line-height: 1.55;
  color: #9aa0b4;
}

@media (max-width: 640px) {
  .cis-12__panel {
    grid-template-columns: 1fr;
    overflow-y: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cis-12__panel {
    transition: opacity .2s;
  }
}
```

## JavaScript
```js
/* No JavaScript — text tabs are radio labels; :checked cross-fades the panels. */
```

How this works

Radios back each tab; the tab row is a set of <label>s with a sliding accent underline. Panels are stacked and the :checked state fades in the active one (image + text side by side) while sliding it up slightly. A shared :checked rule also lights the active tab.

It's a single accessible form group, so tabs are reachable by keyboard and announced as radios; the layout collapses the two-up panel to stacked on small screens.

Make it yours

  • Rename or add categories by editing the tab labels and their paired panels.
  • Move the active underline styling to a pill by restyling the :checked tab rule.
  • Recolour with --accent.
  • Swap the fade for a horizontal slide by animating transform on the panels.

Gotchas — read before shipping

  • Keep tab for and panel order aligned to the radio ids.
  • Stacked panels need a min-height or the container will jump between panels of different length.
  • Wide text tabs can overflow on mobile — allow them to wrap or scroll (handled here).

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by oklch(), color-mix(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 88+.

Pure radio hack; universally supported.

Techniques used in this demo

Search CodeFronts

Loading…