32 CSS Tab Designs19 / 32

Pure CSSMIT licensed

Drop Cap

Inactive tabs are mono single-letter initials. The active tab unfolds into a rotating-and-scaling serif drop cap followed by the rest of the word; tail unfolds left-to-right.

Published Updated

Live Demo
Try it

The code

<div class="tt07">
  <nav class="tt07n">
    <input type="radio" name="tt07" id="tt07-r1" checked />
    <label class="tt07b" for="tt07-r1">
      <span class="tt07cap">A</span>
      <span class="tt07rest">rticle</span>
    </label>
    <input type="radio" name="tt07" id="tt07-r2" />
    <label class="tt07b" for="tt07-r2">
      <span class="tt07cap">G</span>
      <span class="tt07rest">allery</span>
    </label>
    <input type="radio" name="tt07" id="tt07-r3" />
    <label class="tt07b" for="tt07-r3">
      <span class="tt07cap">N</span>
      <span class="tt07rest">otes</span>
    </label>
  </nav>
  <div class="tt07p">The body copy of an editorial feature.</div>
  <div class="tt07p">A curated set of plates and engravings.</div>
  <div class="tt07p">Marginalia and footnote annotations.</div>
</div>
.tt07 {
  min-height: 100vh;
  background: #e8dec8;
  padding: 22px 22px 18px;
  width: 100%;
}

.tt07n {
  display: flex;
  gap: 22px;
  align-items: baseline;
  border-bottom: 1px solid rgba(12, 85, 96, 0.22);
  padding-bottom: 14px;
}

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

.tt07b {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: baseline;
  color: #0c5560;
  transition: opacity 0.3s;
}

.tt07cap {
  font: 900 26px/1 ui-serif, Georgia, serif;
  color: #0c5560;
  display: inline-block;
  transform-origin: center bottom;
  transition: color 0.35s, transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tt07rest {
  font: italic 16px/1 ui-serif, Georgia, serif;
  color: #0c5560;
  margin-left: 1px;
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: max-width 0.5s cubic-bezier(0.65, 0, 0.35, 1) 0.1s;
  opacity: 0.7;
}

.tt07b {
  opacity: 0.4;
}

.tt07b:hover {
  opacity: 0.78;
}

.tt07n input:checked + .tt07b {
  opacity: 1;
}

.tt07n input:checked + .tt07b .tt07cap {
  color: #d44a2a;
  transform: rotate(-4deg) scale(1.15);
}

.tt07n input:checked + .tt07b .tt07rest {
  max-width: 200px;
}

.tt07p {
  display: none;
  padding-top: 14px;
  font: italic 12px/1.6 ui-serif, Georgia, serif;
  color: #3a3a42;
}

.tt07:has(#tt07-r1:checked) .tt07p:nth-of-type(1),
.tt07:has(#tt07-r2:checked) .tt07p:nth-of-type(2),
.tt07:has(#tt07-r3:checked) .tt07p:nth-of-type(3) {
  display: block;
}
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 Tab Design 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: Drop Cap
Source: https://codefronts.com/navigation/css-tabs/drop-cap/

Inactive tabs are mono single-letter initials. The active tab unfolds into a rotating-and-scaling serif drop cap followed by the rest of the word; tail unfolds left-to-right.
## HTML
```html
<div class="tt07">
  <nav class="tt07n">
    <input type="radio" name="tt07" id="tt07-r1" checked />
    <label class="tt07b" for="tt07-r1">
      <span class="tt07cap">A</span>
      <span class="tt07rest">rticle</span>
    </label>
    <input type="radio" name="tt07" id="tt07-r2" />
    <label class="tt07b" for="tt07-r2">
      <span class="tt07cap">G</span>
      <span class="tt07rest">allery</span>
    </label>
    <input type="radio" name="tt07" id="tt07-r3" />
    <label class="tt07b" for="tt07-r3">
      <span class="tt07cap">N</span>
      <span class="tt07rest">otes</span>
    </label>
  </nav>
  <div class="tt07p">The body copy of an editorial feature.</div>
  <div class="tt07p">A curated set of plates and engravings.</div>
  <div class="tt07p">Marginalia and footnote annotations.</div>
</div>
```
## CSS
```css
.tt07 {
  min-height: 100vh;
  background: #e8dec8;
  padding: 22px 22px 18px;
  width: 100%;
}

.tt07n {
  display: flex;
  gap: 22px;
  align-items: baseline;
  border-bottom: 1px solid rgba(12, 85, 96, 0.22);
  padding-bottom: 14px;
}

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

.tt07b {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: baseline;
  color: #0c5560;
  transition: opacity 0.3s;
}

.tt07cap {
  font: 900 26px/1 ui-serif, Georgia, serif;
  color: #0c5560;
  display: inline-block;
  transform-origin: center bottom;
  transition: color 0.35s, transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tt07rest {
  font: italic 16px/1 ui-serif, Georgia, serif;
  color: #0c5560;
  margin-left: 1px;
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: max-width 0.5s cubic-bezier(0.65, 0, 0.35, 1) 0.1s;
  opacity: 0.7;
}

.tt07b {
  opacity: 0.4;
}

.tt07b:hover {
  opacity: 0.78;
}

.tt07n input:checked + .tt07b {
  opacity: 1;
}

.tt07n input:checked + .tt07b .tt07cap {
  color: #d44a2a;
  transform: rotate(-4deg) scale(1.15);
}

.tt07n input:checked + .tt07b .tt07rest {
  max-width: 200px;
}

.tt07p {
  display: none;
  padding-top: 14px;
  font: italic 12px/1.6 ui-serif, Georgia, serif;
  color: #3a3a42;
}

.tt07:has(#tt07-r1:checked) .tt07p:nth-of-type(1),
.tt07:has(#tt07-r2:checked) .tt07p:nth-of-type(2),
.tt07:has(#tt07-r3:checked) .tt07p:nth-of-type(3) {
  display: block;
}
```

How this works

Drop Cap is a pure-CSS tab pattern that treats each label as an unfolding word from an editorial feature. On the bone #e8dec8 stage sits a flex nav (.tt07n) baseline-aligned so the serif drop caps and the italic tails share the typographic baseline. Each label is split into two spans: .tt07cap holds the single initial letter at 900 26px/1 ui-serif, and .tt07rest holds the remaining letters at italic 16px/1 ui-serif. Both spans render the deep teal #0c5560 at opacity: 0.4 when inactive.

Activation is driven by the sibling combinator on hidden radio inputs. .tt07n input:checked + .tt07b lifts opacity to 1, then two child rules do the work: the drop cap shifts to terracotta #d44a2a and transforms with rotate(-4deg) scale(1.15), and the tail expands from max-width: 0 to 200px. The rotate uses transform-origin: center bottom so the cap pivots from its baseline, echoing a physical woodblock tipping into place. Timing is 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) for the cap (with overshoot) and 0.5s cubic-bezier(0.65, 0, 0.35, 1) with a 0.1s delay for the tail, so the tail unfolds only after the cap has committed.

Panel switching runs on :has(): .tt07:has(#tt07-r1:checked) .tt07p:nth-of-type(1) flips the matching panel to display: block. Panels are ui-serif italic at 12px/1.6 in dark ash #3a3a42 — the ash sits softer than pure black on the bone stage and reads as marginalia. The whole component is semantic radio + label markup, keyboard-navigable, copy-paste, zero-dependency, framework-agnostic, and scoped under .tt07. It ships SSR-safe because the checked state is declared in HTML rather than derived from JavaScript.

Make it yours

  • Swap the terracotta accent #d44a2a for a foundry pick — an ochre #c68a2e reads Kelmscott press; a pressroom red #c43a32 reads Guardian broadsheet. Only .tt07n input:checked + .tt07b .tt07cap color changes.
  • Tune the rotation from -4deg to -8deg for a bolder woodblock lift, or drop to -1deg for a subtle nod. Pair larger rotations with a smaller scale (1.08) to keep the cap within the baseline row.
  • Lengthen the max-width: 200px tail to 320px for words like Chronicles or Compendium. If the word is short, matching max-width to word length prevents the tail from padding empty space at the right of the label.
  • Change the cap size ratio. Currently the cap is 26px and the tail is 16px — roughly 1.6x. Push to 32px vs 14px for a full illuminated-manuscript register, or match them at 20px for a subtler split-weight look.
  • Retune the tail delay. The tail waits 0.1s before unfolding, which reads as sequential. Removing the delay makes cap and tail arrive together; increasing to 0.25s gives a two-beat reveal that works well at large font sizes.

Gotchas — read before shipping

  • The tail relies on max-width transition from 0 to 200px. On words longer than roughly 12 characters at 16px, 200px will clip the text — measure the widest label and widen the value, or switch to max-width: 999px if you accept a slightly non-linear ease.
  • The rotation -4deg combined with scale(1.15) on the cap can push it into neighbouring labels when the nav gap is smaller than 16px. Test at your production label lengths; increase gap if caps overlap on activation.
  • The :has() panel selector requires Chrome 105+, Safari 15.4+, and Firefox 121+. On older Firefox users will see no panel body — either provide a JS fallback that toggles display: block on the matching panel, or ship a graceful @supports not selector(:has(*)) block.
  • The italic tail uses generic ui-serif. On systems without a true italic (e.g. some Linux minimal stacks) the browser will synthesize italics, which reads awkwardly at 16px. Ship an explicit fallback like Georgia or Charter before ui-serif if you need portability.
  • The radio inputs are visually hidden but keyboard-focusable. The visible focus state must live on .tt07n input:focus-visible + .tt07b. Without it, keyboard users tab through invisibly and WCAG 2.1 focus-visible fails.
  • There is no prefers-reduced-motion guard as written. The scale and rotate on the cap are noticeable at 26px — add a media block that zeros the transforms and the tail transition for reduced-motion users.

Browser support

ChromeSafariFirefoxEdge
105+15.4+121+105+

The :has() panel switcher gates the floor. Sibling-combinator activation, transforms, and max-width transitions have been stable everywhere since 2019.

Techniques used in this demo

Search CodeFronts

Loading…