32 CSS Tab Designs19 / 32
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
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><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;
}.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;
}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;
}
```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
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 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.