27 / 34
All 34 in 34 CSS Floating Buttons
- 01Material Design 3 Floating Action Button (FAB)
- 02iOS-Style Compose Floating Button
- 03WhatsApp Floating Message Button CSS
- 04Intercom / Drift Chat Bubble Floating Button
- 05Accessible WAI-ARIA Floating Action Button (WCAG 2.2)
- 06Extended FAB with Icon and Label (Material Design 3)
- 07Speed Dial Radial Menu FAB with Backdrop
- 08Pure CSS FAB Speed Dial (No JavaScript)
- 09Material Gradient Floating Action Button CSS
- 10Vertical and Horizontal Speed Dial FAB Menu CSS
- 11Morphing FAB Button That Expands to Pill CSS
- 12Scroll-to-Top Floating Button with SVG Progress Ring
- 13Floating Chat Widget FAB with Notification Panel
- 14Fan and Arc Radial Spread FAB Menu CSS
- 15Apple Squircle Superellipse Floating Button CSS
- 16Social Share Floating Button Cluster CSS
- 17Glassmorphism Floating Action Button with Backdrop Blur
- 18Cyberpunk Neon Glow Floating Button CSS
- 19Floating Compose Button with Tooltip on Hover CSS
- 20Draggable Floating Action Button with Corner Snap CSS
- 21Notification Bell Floating Button with Badge Counter
- 22Floating Cart Button with Live Badge Counter CSS
- 23Magnetic Cursor Follower Floating Button CSS
- 24Dark Mode Theme Toggle Floating Button CSS
- 25Classic Gradient Circle Plus FAB CSS
- 26Pulse Ring Attention-Getter Floating Button CSS
- 27Pill-Shaped FAB with Icon and Text Label CSS
- 28Neo-Brutalist Hard Shadow Floating Button CSS
- 29Floating Button with Loading Spinner State CSS
- 30Draggable Floating Button with 6-Dot Grip Handle CSS
- 31Voice Recording Microphone Floating Button CSS
- 32Aurora Gradient Drift Premium Floating Button CSS
- 33Quick Emoji Reply Reaction Floating Button CSS
- 34Modern Squircle Rounded Square FAB CSS
Pill-Shaped FAB with Icon and Text Label CSS
A labelled pill FAB with the proportions worked out: the corner radius is always half the height, horizontal padding is 1.5× the vertical rhythm with a smaller leading pad because the icon already carries visual weight, and the optical centre is nudged 1px because a circle in a capsule never sits where the maths says it does. Four sizes, three tones, one token set.
Nucleo: 44,000+ SVG icons in five families · All Icons Bundle $149
Nucleo · Affiliate link
The code
<section class="fb-27" aria-label="Pill shaped floating button with label demo">
<div class="fb-27__stage">
<p class="fb-27__eyebrow">radius = height ÷ 2 · derived padding</p>
<h2 class="fb-27__title">Proportion, not guesswork</h2>
<p class="fb-27__sub">Padding, gap, radius and type size all derive from one height token. A new size is one line, and the geometry can never drift.</p>
<div class="fb-27__col">
<div class="fb-27__line">
<button class="fb-27__pill fb-27__pill--40" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--48" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--64" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
</div>
<p class="fb-27__cap">40 · 48 · 56 · 64 <span>one token each</span></p>
<div class="fb-27__line">
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4L18.5 9.5l-4-4L4 16z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/></svg><span>Filled</span></button>
<button class="fb-27__pill fb-27__pill--tonal" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="4" y="5" width="16" height="15" rx="2.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M8 3v4M16 3v4M4 10h16" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg><span>Tonal</span></button>
<button class="fb-27__pill fb-27__pill--out" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 17V6M8 10l4-4 4 4M5 19h14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg><span>Outlined</span></button>
</div>
<p class="fb-27__cap">filled · tonal · outlined <span>emphasis ladder</span></p>
</div>
<p class="fb-27__chip">lead pad .36h · trail pad .45h · gap grows 2px on hover</p>
</div>
</section><section class="fb-27" aria-label="Pill shaped floating button with label demo">
<div class="fb-27__stage">
<p class="fb-27__eyebrow">radius = height ÷ 2 · derived padding</p>
<h2 class="fb-27__title">Proportion, not guesswork</h2>
<p class="fb-27__sub">Padding, gap, radius and type size all derive from one height token. A new size is one line, and the geometry can never drift.</p>
<div class="fb-27__col">
<div class="fb-27__line">
<button class="fb-27__pill fb-27__pill--40" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--48" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--64" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
</div>
<p class="fb-27__cap">40 · 48 · 56 · 64 <span>one token each</span></p>
<div class="fb-27__line">
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4L18.5 9.5l-4-4L4 16z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/></svg><span>Filled</span></button>
<button class="fb-27__pill fb-27__pill--tonal" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="4" y="5" width="16" height="15" rx="2.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M8 3v4M16 3v4M4 10h16" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg><span>Tonal</span></button>
<button class="fb-27__pill fb-27__pill--out" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 17V6M8 10l4-4 4 4M5 19h14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg><span>Outlined</span></button>
</div>
<p class="fb-27__cap">filled · tonal · outlined <span>emphasis ladder</span></p>
</div>
<p class="fb-27__chip">lead pad .36h · trail pad .45h · gap grows 2px on hover</p>
</div>
</section>.fb-27 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-27-bg);
--fb-27-bg: oklch(0.97 0.01 235);
--fb-27-brand: oklch(0.52 0.15 235);
--fb-27-brandDeep: oklch(0.38 0.13 238);
--fb-27-ink: oklch(0.22 0.025 235);
--fb-27-mut: oklch(0.53 0.015 235);
--fb-27-h: 56px;
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-27-ink);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
}
.fb-27 *,
.fb-27 *::before,
.fb-27 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-27__stage {
width: min(100%,60ch);
display: grid;
gap: clamp(18px,3vw,28px);
justify-items: center;
text-align: center;
}
.fb-27__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--fb-27-brand);
}
.fb-27__title {
font-size: clamp(26px,4.6vw,44px);
line-height: 1.05;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-27__sub {
max-width: 50ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.62;
color: var(--fb-27-mut);
text-wrap: pretty;
}
.fb-27__col {
display: grid;
gap: 14px;
justify-items: center;
width: 100%;
}
.fb-27__line {
display: flex;
flex-wrap: wrap;
gap: 14px;
justify-content: center;
align-items: center;
}
.fb-27__cap {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-ink);
padding-bottom: 8px;
}
.fb-27__cap span {
color: var(--fb-27-mut);
}
.fb-27__pill {
display: inline-flex;
align-items: center;
block-size: var(--fb-27-h);
border: 0;
border-radius: calc(var(--fb-27-h) / 2);
padding-inline: calc(var(--fb-27-h) * .36) calc(var(--fb-27-h) * .45);
gap: calc(var(--fb-27-h) * .18);
font-family: inherit;
font-size: calc(var(--fb-27-h) * .27);
font-weight: 640;
letter-spacing: -.012em;
white-space: nowrap;
cursor: pointer;
color: oklch(0.99 0.01 235);
background: linear-gradient(150deg,oklch(0.58 0.16 232),var(--fb-27-brandDeep));
box-shadow: 0 12px 28px -12px oklch(0.38 0.13 238/.85),0 2px 5px oklch(0.3 0.06 238/.25);
transition: gap .24s cubic-bezier(.2,.9,.3,1),box-shadow .26s ease,transform .24s cubic-bezier(.2,1.25,.4,1),background .2s ease;
}
.fb-27__pill svg {
width: calc(var(--fb-27-h) * .4);
height: calc(var(--fb-27-h) * .4);
translate: 0 -.5px;
}
.fb-27__pill:hover {
gap: calc(var(--fb-27-h) * .18 + 2px);
transform: translateY(-2px);
box-shadow: 0 18px 38px -12px oklch(0.38 0.13 238/.9),0 2px 5px oklch(0.3 0.06 238/.25);
}
.fb-27__pill:active {
transform: scale(.97);
}
.fb-27__pill:focus-visible {
outline: 3px solid var(--fb-27-brandDeep);
outline-offset: 3px;
}
.fb-27__pill--40 {
--fb-27-h: 40px;
}
.fb-27__pill--48 {
--fb-27-h: 48px;
}
.fb-27__pill--64 {
--fb-27-h: 64px;
}
.fb-27__pill--tonal {
background: color-mix(in oklch,var(--fb-27-brand) 16%,oklch(1 0 0));
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--tonal:hover {
background: color-mix(in oklch,var(--fb-27-brand) 24%,oklch(1 0 0));
box-shadow: 0 14px 30px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--out {
background: oklch(1 0 0);
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px color-mix(in oklch,var(--fb-27-brand) 45%,transparent);
}
.fb-27__pill--out:hover {
box-shadow: 0 14px 30px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px var(--fb-27-brand);
}
.fb-27__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-mut);
padding: 8px 14px;
border: 1px solid oklch(0.22 0.025 235/.14);
border-radius: 99px;
}
@media (max-width: 480px) {
.fb-27__pill {
--fb-27-h: 48px;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-27__pill {
transition: none;
}
}.fb-27 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-27-bg);
--fb-27-bg: oklch(0.97 0.01 235);
--fb-27-brand: oklch(0.52 0.15 235);
--fb-27-brandDeep: oklch(0.38 0.13 238);
--fb-27-ink: oklch(0.22 0.025 235);
--fb-27-mut: oklch(0.53 0.015 235);
--fb-27-h: 56px;
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-27-ink);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
}
.fb-27 *,
.fb-27 *::before,
.fb-27 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-27__stage {
width: min(100%,60ch);
display: grid;
gap: clamp(18px,3vw,28px);
justify-items: center;
text-align: center;
}
.fb-27__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--fb-27-brand);
}
.fb-27__title {
font-size: clamp(26px,4.6vw,44px);
line-height: 1.05;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-27__sub {
max-width: 50ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.62;
color: var(--fb-27-mut);
text-wrap: pretty;
}
.fb-27__col {
display: grid;
gap: 14px;
justify-items: center;
width: 100%;
}
.fb-27__line {
display: flex;
flex-wrap: wrap;
gap: 14px;
justify-content: center;
align-items: center;
}
.fb-27__cap {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-ink);
padding-bottom: 8px;
}
.fb-27__cap span {
color: var(--fb-27-mut);
}
.fb-27__pill {
display: inline-flex;
align-items: center;
block-size: var(--fb-27-h);
border: 0;
border-radius: calc(var(--fb-27-h) / 2);
padding-inline: calc(var(--fb-27-h) * .36) calc(var(--fb-27-h) * .45);
gap: calc(var(--fb-27-h) * .18);
font-family: inherit;
font-size: calc(var(--fb-27-h) * .27);
font-weight: 640;
letter-spacing: -.012em;
white-space: nowrap;
cursor: pointer;
color: oklch(0.99 0.01 235);
background: linear-gradient(150deg,oklch(0.58 0.16 232),var(--fb-27-brandDeep));
box-shadow: 0 12px 28px -12px oklch(0.38 0.13 238/.85),0 2px 5px oklch(0.3 0.06 238/.25);
transition: gap .24s cubic-bezier(.2,.9,.3,1),box-shadow .26s ease,transform .24s cubic-bezier(.2,1.25,.4,1),background .2s ease;
}
.fb-27__pill svg {
width: calc(var(--fb-27-h) * .4);
height: calc(var(--fb-27-h) * .4);
translate: 0 -.5px;
}
.fb-27__pill:hover {
gap: calc(var(--fb-27-h) * .18 + 2px);
transform: translateY(-2px);
box-shadow: 0 18px 38px -12px oklch(0.38 0.13 238/.9),0 2px 5px oklch(0.3 0.06 238/.25);
}
.fb-27__pill:active {
transform: scale(.97);
}
.fb-27__pill:focus-visible {
outline: 3px solid var(--fb-27-brandDeep);
outline-offset: 3px;
}
.fb-27__pill--40 {
--fb-27-h: 40px;
}
.fb-27__pill--48 {
--fb-27-h: 48px;
}
.fb-27__pill--64 {
--fb-27-h: 64px;
}
.fb-27__pill--tonal {
background: color-mix(in oklch,var(--fb-27-brand) 16%,oklch(1 0 0));
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--tonal:hover {
background: color-mix(in oklch,var(--fb-27-brand) 24%,oklch(1 0 0));
box-shadow: 0 14px 30px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--out {
background: oklch(1 0 0);
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px color-mix(in oklch,var(--fb-27-brand) 45%,transparent);
}
.fb-27__pill--out:hover {
box-shadow: 0 14px 30px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px var(--fb-27-brand);
}
.fb-27__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-mut);
padding: 8px 14px;
border: 1px solid oklch(0.22 0.025 235/.14);
border-radius: 99px;
}
@media (max-width: 480px) {
.fb-27__pill {
--fb-27-h: 48px;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-27__pill {
transition: none;
}
}Here's a working CSS Floating Button 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: Pill-Shaped FAB with Icon and Text Label CSS
Source: https://codefronts.com/components/css-floating-buttons/fb05/
A labelled pill FAB with the proportions worked out: the corner radius is always half the height, horizontal padding is 1.5× the vertical rhythm with a smaller leading pad because the icon already carries visual weight, and the optical centre is nudged 1px because a circle in a capsule never sits where the maths says it does. Four sizes, three tones, one token set.
## HTML
```html
<section class="fb-27" aria-label="Pill shaped floating button with label demo">
<div class="fb-27__stage">
<p class="fb-27__eyebrow">radius = height ÷ 2 · derived padding</p>
<h2 class="fb-27__title">Proportion, not guesswork</h2>
<p class="fb-27__sub">Padding, gap, radius and type size all derive from one height token. A new size is one line, and the geometry can never drift.</p>
<div class="fb-27__col">
<div class="fb-27__line">
<button class="fb-27__pill fb-27__pill--40" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--48" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--64" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
</div>
<p class="fb-27__cap">40 · 48 · 56 · 64 <span>one token each</span></p>
<div class="fb-27__line">
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4L18.5 9.5l-4-4L4 16z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/></svg><span>Filled</span></button>
<button class="fb-27__pill fb-27__pill--tonal" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="4" y="5" width="16" height="15" rx="2.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M8 3v4M16 3v4M4 10h16" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg><span>Tonal</span></button>
<button class="fb-27__pill fb-27__pill--out" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 17V6M8 10l4-4 4 4M5 19h14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg><span>Outlined</span></button>
</div>
<p class="fb-27__cap">filled · tonal · outlined <span>emphasis ladder</span></p>
</div>
<p class="fb-27__chip">lead pad .36h · trail pad .45h · gap grows 2px on hover</p>
</div>
</section>
```
## CSS
```css
.fb-27 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-27-bg);
--fb-27-bg: oklch(0.97 0.01 235);
--fb-27-brand: oklch(0.52 0.15 235);
--fb-27-brandDeep: oklch(0.38 0.13 238);
--fb-27-ink: oklch(0.22 0.025 235);
--fb-27-mut: oklch(0.53 0.015 235);
--fb-27-h: 56px;
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-27-ink);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
}
.fb-27 *,
.fb-27 *::before,
.fb-27 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-27__stage {
width: min(100%,60ch);
display: grid;
gap: clamp(18px,3vw,28px);
justify-items: center;
text-align: center;
}
.fb-27__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--fb-27-brand);
}
.fb-27__title {
font-size: clamp(26px,4.6vw,44px);
line-height: 1.05;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-27__sub {
max-width: 50ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.62;
color: var(--fb-27-mut);
text-wrap: pretty;
}
.fb-27__col {
display: grid;
gap: 14px;
justify-items: center;
width: 100%;
}
.fb-27__line {
display: flex;
flex-wrap: wrap;
gap: 14px;
justify-content: center;
align-items: center;
}
.fb-27__cap {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-ink);
padding-bottom: 8px;
}
.fb-27__cap span {
color: var(--fb-27-mut);
}
.fb-27__pill {
display: inline-flex;
align-items: center;
block-size: var(--fb-27-h);
border: 0;
border-radius: calc(var(--fb-27-h) / 2);
padding-inline: calc(var(--fb-27-h) * .36) calc(var(--fb-27-h) * .45);
gap: calc(var(--fb-27-h) * .18);
font-family: inherit;
font-size: calc(var(--fb-27-h) * .27);
font-weight: 640;
letter-spacing: -.012em;
white-space: nowrap;
cursor: pointer;
color: oklch(0.99 0.01 235);
background: linear-gradient(150deg,oklch(0.58 0.16 232),var(--fb-27-brandDeep));
box-shadow: 0 12px 28px -12px oklch(0.38 0.13 238/.85),0 2px 5px oklch(0.3 0.06 238/.25);
transition: gap .24s cubic-bezier(.2,.9,.3,1),box-shadow .26s ease,transform .24s cubic-bezier(.2,1.25,.4,1),background .2s ease;
}
.fb-27__pill svg {
width: calc(var(--fb-27-h) * .4);
height: calc(var(--fb-27-h) * .4);
translate: 0 -.5px;
}
.fb-27__pill:hover {
gap: calc(var(--fb-27-h) * .18 + 2px);
transform: translateY(-2px);
box-shadow: 0 18px 38px -12px oklch(0.38 0.13 238/.9),0 2px 5px oklch(0.3 0.06 238/.25);
}
.fb-27__pill:active {
transform: scale(.97);
}
.fb-27__pill:focus-visible {
outline: 3px solid var(--fb-27-brandDeep);
outline-offset: 3px;
}
.fb-27__pill--40 {
--fb-27-h: 40px;
}
.fb-27__pill--48 {
--fb-27-h: 48px;
}
.fb-27__pill--64 {
--fb-27-h: 64px;
}
.fb-27__pill--tonal {
background: color-mix(in oklch,var(--fb-27-brand) 16%,oklch(1 0 0));
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--tonal:hover {
background: color-mix(in oklch,var(--fb-27-brand) 24%,oklch(1 0 0));
box-shadow: 0 14px 30px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--out {
background: oklch(1 0 0);
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px color-mix(in oklch,var(--fb-27-brand) 45%,transparent);
}
.fb-27__pill--out:hover {
box-shadow: 0 14px 30px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px var(--fb-27-brand);
}
.fb-27__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-mut);
padding: 8px 14px;
border: 1px solid oklch(0.22 0.025 235/.14);
border-radius: 99px;
}
@media (max-width: 480px) {
.fb-27__pill {
--fb-27-h: 48px;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-27__pill {
transition: none;
}
}
```Here's a working CSS Floating Button 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: Pill-Shaped FAB with Icon and Text Label CSS
Source: https://codefronts.com/components/css-floating-buttons/fb05/
A labelled pill FAB with the proportions worked out: the corner radius is always half the height, horizontal padding is 1.5× the vertical rhythm with a smaller leading pad because the icon already carries visual weight, and the optical centre is nudged 1px because a circle in a capsule never sits where the maths says it does. Four sizes, three tones, one token set.
## HTML
```html
<section class="fb-27" aria-label="Pill shaped floating button with label demo">
<div class="fb-27__stage">
<p class="fb-27__eyebrow">radius = height ÷ 2 · derived padding</p>
<h2 class="fb-27__title">Proportion, not guesswork</h2>
<p class="fb-27__sub">Padding, gap, radius and type size all derive from one height token. A new size is one line, and the geometry can never drift.</p>
<div class="fb-27__col">
<div class="fb-27__line">
<button class="fb-27__pill fb-27__pill--40" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--48" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
<button class="fb-27__pill fb-27__pill--64" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg><span>Create</span></button>
</div>
<p class="fb-27__cap">40 · 48 · 56 · 64 <span>one token each</span></p>
<div class="fb-27__line">
<button class="fb-27__pill" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4L18.5 9.5l-4-4L4 16z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/></svg><span>Filled</span></button>
<button class="fb-27__pill fb-27__pill--tonal" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="4" y="5" width="16" height="15" rx="2.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M8 3v4M16 3v4M4 10h16" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg><span>Tonal</span></button>
<button class="fb-27__pill fb-27__pill--out" type="button"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 17V6M8 10l4-4 4 4M5 19h14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg><span>Outlined</span></button>
</div>
<p class="fb-27__cap">filled · tonal · outlined <span>emphasis ladder</span></p>
</div>
<p class="fb-27__chip">lead pad .36h · trail pad .45h · gap grows 2px on hover</p>
</div>
</section>
```
## CSS
```css
.fb-27 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-27-bg);
--fb-27-bg: oklch(0.97 0.01 235);
--fb-27-brand: oklch(0.52 0.15 235);
--fb-27-brandDeep: oklch(0.38 0.13 238);
--fb-27-ink: oklch(0.22 0.025 235);
--fb-27-mut: oklch(0.53 0.015 235);
--fb-27-h: 56px;
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-27-ink);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
}
.fb-27 *,
.fb-27 *::before,
.fb-27 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-27__stage {
width: min(100%,60ch);
display: grid;
gap: clamp(18px,3vw,28px);
justify-items: center;
text-align: center;
}
.fb-27__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--fb-27-brand);
}
.fb-27__title {
font-size: clamp(26px,4.6vw,44px);
line-height: 1.05;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-27__sub {
max-width: 50ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.62;
color: var(--fb-27-mut);
text-wrap: pretty;
}
.fb-27__col {
display: grid;
gap: 14px;
justify-items: center;
width: 100%;
}
.fb-27__line {
display: flex;
flex-wrap: wrap;
gap: 14px;
justify-content: center;
align-items: center;
}
.fb-27__cap {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-ink);
padding-bottom: 8px;
}
.fb-27__cap span {
color: var(--fb-27-mut);
}
.fb-27__pill {
display: inline-flex;
align-items: center;
block-size: var(--fb-27-h);
border: 0;
border-radius: calc(var(--fb-27-h) / 2);
padding-inline: calc(var(--fb-27-h) * .36) calc(var(--fb-27-h) * .45);
gap: calc(var(--fb-27-h) * .18);
font-family: inherit;
font-size: calc(var(--fb-27-h) * .27);
font-weight: 640;
letter-spacing: -.012em;
white-space: nowrap;
cursor: pointer;
color: oklch(0.99 0.01 235);
background: linear-gradient(150deg,oklch(0.58 0.16 232),var(--fb-27-brandDeep));
box-shadow: 0 12px 28px -12px oklch(0.38 0.13 238/.85),0 2px 5px oklch(0.3 0.06 238/.25);
transition: gap .24s cubic-bezier(.2,.9,.3,1),box-shadow .26s ease,transform .24s cubic-bezier(.2,1.25,.4,1),background .2s ease;
}
.fb-27__pill svg {
width: calc(var(--fb-27-h) * .4);
height: calc(var(--fb-27-h) * .4);
translate: 0 -.5px;
}
.fb-27__pill:hover {
gap: calc(var(--fb-27-h) * .18 + 2px);
transform: translateY(-2px);
box-shadow: 0 18px 38px -12px oklch(0.38 0.13 238/.9),0 2px 5px oklch(0.3 0.06 238/.25);
}
.fb-27__pill:active {
transform: scale(.97);
}
.fb-27__pill:focus-visible {
outline: 3px solid var(--fb-27-brandDeep);
outline-offset: 3px;
}
.fb-27__pill--40 {
--fb-27-h: 40px;
}
.fb-27__pill--48 {
--fb-27-h: 48px;
}
.fb-27__pill--64 {
--fb-27-h: 64px;
}
.fb-27__pill--tonal {
background: color-mix(in oklch,var(--fb-27-brand) 16%,oklch(1 0 0));
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--tonal:hover {
background: color-mix(in oklch,var(--fb-27-brand) 24%,oklch(1 0 0));
box-shadow: 0 14px 30px -18px oklch(0.38 0.13 238/.9);
}
.fb-27__pill--out {
background: oklch(1 0 0);
color: var(--fb-27-brandDeep);
box-shadow: 0 10px 24px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px color-mix(in oklch,var(--fb-27-brand) 45%,transparent);
}
.fb-27__pill--out:hover {
box-shadow: 0 14px 30px -20px oklch(0.38 0.13 238/.9),inset 0 0 0 1.6px var(--fb-27-brand);
}
.fb-27__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-27-mut);
padding: 8px 14px;
border: 1px solid oklch(0.22 0.025 235/.14);
border-radius: 99px;
}
@media (max-width: 480px) {
.fb-27__pill {
--fb-27-h: 48px;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-27__pill {
transition: none;
}
}
```How this works
Everything derives from one height token:
.pill{ --h:56px;
block-size:var(--h); border-radius:calc(var(--h) / 2);
padding-inline:calc(var(--h) * .36) calc(var(--h) * .45);
gap:calc(var(--h) * .18);
font-size:calc(var(--h) * .27); }Deriving padding, gap, radius and type size from the height means a size variant is a one-line change and never needs re-tuning. It also guarantees the radius stays exactly half the height, which is what makes a capsule read as a capsule rather than a rounded rectangle.
Asymmetric padding matters: the leading side is tighter (0.36) than the trailing side (0.45) because the icon's silhouette fills less of its box than the text does. Equal padding makes the label look pushed toward the right edge — a classic optical, not mathematical, correction.
The hover state raises elevation and widens the gap by 2px. That tiny gap change reads as the label "breathing" and is far more subtle than a colour shift, which is what most pill buttons overuse.
Make it yours
- Sizes: 40 (compact), 48, 56 (default), 64 (hero). Change only
--fb-27-h. - Trailing icon: add a chevron after the label for a button that opens something, keeping the same padding ratios mirrored.
- Tonal set: filled for primary, tonal (12% accent background) for secondary, outlined for tertiary. All three are shown here.
- Full-width mobile: below 480px,
inline-size:100%; justify-content:centerturns the same pill into a bottom-docked call to action.
Gotchas — read before shipping
- Never set border-radius:9999px on a pill you might animate to a square — interpolate from calc(h/2) instead so the morph is linear.
- A pill whose label wraps to two lines breaks the capsule geometry. Use white-space:nowrap and shorten the label instead.
- Icon and text baselines rarely align automatically. Centre with flexbox, then nudge the icon by up to 1px — do not adjust the text.
- Long translations (German, Finnish) can double the pill's width. Test the widest locale before fixing any adjacent layout.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 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 111+.
Flexbox, calc() and custom properties are baseline everywhere. Only the oklch() colour tokens require a modern engine.