34 CSS Floating Buttons02 / 34
iOS-Style Compose Floating Button
What a compose button looks like on iOS: translucent rather than opaque, sitting on a blurred plate that picks up the colour of the content scrolling underneath, with a spring-loaded press that overshoots on release. Also handles the two things web clones forget — env(safe-area-inset-bottom) so the button clears the home indicator, and a specular top highlight that makes the glass read as a physical lens.
Published
The code
<section class="fb-02" aria-label="iOS style floating compose button demo">
<div class="fb-02__stage">
<div class="fb-02__phone">
<div class="fb-02__screen">
<div class="fb-02__status"><span>9:41</span><span class="fb-02__statusR"><i class="fb-02__sig"></i><i class="fb-02__bat"></i></span></div>
<div class="fb-02__nav"><span class="fb-02__navTitle">Notes</span><span class="fb-02__navSub">iCloud · 24 notes</span></div>
<div class="fb-02__scroll">
<article class="fb-02__note"><b>Type scale for 2027</b><span>Variable weight axis on the display face, 3 optical sizes…</span><time>Yesterday</time></article>
<article class="fb-02__note"><b>FAB motion spec</b><span>Press 90ms in, 380ms spring out, overshoot 1.04 max…</span><time>Tuesday</time></article>
<article class="fb-02__note fb-02__note--img"><b>Studio moodboard</b><span>Warm concrete, brushed steel, one hot accent.</span><time>Mon</time></article>
<article class="fb-02__note"><b>Glass tokens</b><span>blur 22, saturate 180, tint 78% — anything more is soup.</span><time>Mon</time></article>
</div>
<button class="fb-02__compose" type="button" aria-label="Compose a new note">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4l10.5-10.5-4-4L4 16v4z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/><path d="M14.5 5.5l4 4" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg>
</button>
<div class="fb-02__home" aria-hidden="true"></div>
</div>
</div>
<div class="fb-02__copy">
<p class="fb-02__eyebrow">backdrop-filter · spring easing</p>
<h2 class="fb-02__title">Glass that samples the scroll</h2>
<p class="fb-02__sub">Press it. Fast in at 90ms, springy out at 380ms with a 4% overshoot — the asymmetry is the whole trick.</p>
<p class="fb-02__chip">blur(22px) saturate(180%) · env(safe-area-inset-bottom)</p>
</div>
</div>
</section><section class="fb-02" aria-label="iOS style floating compose button demo">
<div class="fb-02__stage">
<div class="fb-02__phone">
<div class="fb-02__screen">
<div class="fb-02__status"><span>9:41</span><span class="fb-02__statusR"><i class="fb-02__sig"></i><i class="fb-02__bat"></i></span></div>
<div class="fb-02__nav"><span class="fb-02__navTitle">Notes</span><span class="fb-02__navSub">iCloud · 24 notes</span></div>
<div class="fb-02__scroll">
<article class="fb-02__note"><b>Type scale for 2027</b><span>Variable weight axis on the display face, 3 optical sizes…</span><time>Yesterday</time></article>
<article class="fb-02__note"><b>FAB motion spec</b><span>Press 90ms in, 380ms spring out, overshoot 1.04 max…</span><time>Tuesday</time></article>
<article class="fb-02__note fb-02__note--img"><b>Studio moodboard</b><span>Warm concrete, brushed steel, one hot accent.</span><time>Mon</time></article>
<article class="fb-02__note"><b>Glass tokens</b><span>blur 22, saturate 180, tint 78% — anything more is soup.</span><time>Mon</time></article>
</div>
<button class="fb-02__compose" type="button" aria-label="Compose a new note">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4l10.5-10.5-4-4L4 16v4z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/><path d="M14.5 5.5l4 4" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg>
</button>
<div class="fb-02__home" aria-hidden="true"></div>
</div>
</div>
<div class="fb-02__copy">
<p class="fb-02__eyebrow">backdrop-filter · spring easing</p>
<h2 class="fb-02__title">Glass that samples the scroll</h2>
<p class="fb-02__sub">Press it. Fast in at 90ms, springy out at 380ms with a 4% overshoot — the asymmetry is the whole trick.</p>
<p class="fb-02__chip">blur(22px) saturate(180%) · env(safe-area-inset-bottom)</p>
</div>
</div>
</section>.fb-02 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-02-bg);
--fb-02-bg: radial-gradient(120% 90% at 15% 0%,oklch(0.34 0.09 265),oklch(0.19 0.04 265) 62%);
--fb-02-tint: oklch(0.63 0.19 258);
--fb-02-ink: oklch(0.97 0.01 265);
--fb-02-mut: oklch(0.78 0.02 265);
font-family: -apple-system,'SF Pro Text','Segoe UI',system-ui,sans-serif;
color: var(--fb-02-ink);
display: grid;
place-items: center;
padding: clamp(20px,4vw,52px);
}
.fb-02 *,
.fb-02 *::before,
.fb-02 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-02__stage {
width: min(100%,880px);
display: grid;
gap: clamp(24px,4vw,46px);
justify-items: center;
align-items: center;
}
.fb-02__phone {
width: min(92vw,320px);
aspect-ratio: 9/19;
padding: 11px;
border-radius: 52px;
background: linear-gradient(160deg,oklch(0.42 0.01 265),oklch(0.2 0.01 265));
box-shadow: 0 40px 90px -30px oklch(0.1 0.03 265/.85),inset 0 1px 0 oklch(1 0 0/.28);
}
.fb-02__screen {
position: relative;
height: 100%;
border-radius: 42px;
overflow: hidden;
background: oklch(0.99 0.004 265);
color: oklch(0.2 0.01 265);
display: flex;
flex-direction: column;
isolation: isolate;
}
.fb-02__status {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 22px 2px;
font-size: 12.5px;
font-weight: 650;
}
.fb-02__statusR {
display: flex;
align-items: center;
gap: 5px;
}
.fb-02__sig {
width: 15px;
height: 10px;
background: currentColor;
clip-path: polygon(0 70%,22% 70%,22% 100%,0 100%,0 70%,33% 45%,55% 45%,55% 100%,33% 100%,33% 45%,66% 22%,88% 22%,88% 100%,66% 100%);
}
.fb-02__bat {
width: 22px;
height: 11px;
border: 1.4px solid currentColor;
border-radius: 3px;
position: relative;
}
.fb-02__bat::after {
content: "";
position: absolute;
inset: 1.6px;
width: 62%;
background: currentColor;
border-radius: 1px;
}
.fb-02__nav {
padding: 6px 20px 10px;
display: grid;
gap: 1px;
}
.fb-02__navTitle {
font-size: 27px;
font-weight: 750;
letter-spacing: -.03em;
}
.fb-02__navSub {
font-size: 11.5px;
color: oklch(0.58 0.01 265);
}
.fb-02__scroll {
flex: 1;
overflow: auto;
padding: 0 14px 96px;
display: grid;
gap: 9px;
align-content: start;
scrollbar-width: none;
}
.fb-02__scroll::-webkit-scrollbar {
display: none;
}
.fb-02__note {
display: grid;
gap: 3px;
padding: 13px 15px;
border-radius: 15px;
background: oklch(0.96 0.006 265);
}
.fb-02__note b {
font-size: 14px;
font-weight: 640;
letter-spacing: -.01em;
}
.fb-02__note span {
font-size: 12px;
line-height: 1.45;
color: oklch(0.55 0.01 265);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.fb-02__note time {
font-size: 10.5px;
color: oklch(0.65 0.01 265);
font-variant-numeric: tabular-nums;
}
.fb-02__note--img {
background: linear-gradient(180deg,oklch(0.35 0.06 40/.72),oklch(0.22 0.05 40/.9)),url("https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=900&auto=format&fit=crop") center/cover;
color: oklch(0.98 0.01 40);
}
.fb-02__note--img span,
.fb-02__note--img time {
color: oklch(0.9 0.02 40);
}
.fb-02__compose {
position: absolute;
right: 18px;
bottom: calc(30px + env(safe-area-inset-bottom));
z-index: 3;
inline-size: 58px;
block-size: 58px;
border: 0;
border-radius: 22px;
display: grid;
place-items: center;
cursor: pointer;
color: oklch(0.99 0.01 258);
background: color-mix(in oklch,var(--fb-02-tint) 78%,transparent);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
box-shadow: inset 0 1px 0 oklch(1 0 0/.55),inset 0 -1px 0 oklch(0 0 0/.1),0 10px 30px -8px oklch(0.2 0.06 258/.5);
transition: transform .38s cubic-bezier(.2,1.5,.4,1),box-shadow .3s ease;
}
.fb-02__compose svg {
width: 25px;
height: 25px;
}
.fb-02__compose:hover {
box-shadow: inset 0 1px 0 oklch(1 0 0/.6),0 14px 34px -8px oklch(0.2 0.06 258/.62);
}
.fb-02__compose:active {
transform: scale(.9);
transition-duration: .09s;
}
.fb-02__compose:focus-visible {
outline: 3px solid oklch(0.99 0.01 258);
outline-offset: 3px;
}
.fb-02__home {
position: absolute;
left: 50%;
bottom: 8px;
translate: -50% 0;
width: 118px;
height: 5px;
border-radius: 99px;
background: oklch(0.2 0.01 265/.35);
}
.fb-02__copy {
display: grid;
gap: 11px;
justify-items: center;
text-align: center;
max-width: 52ch;
}
.fb-02__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.8 0.12 258);
}
.fb-02__title {
font-size: clamp(25px,4.2vw,40px);
line-height: 1.07;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-02__sub {
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.6;
color: var(--fb-02-mut);
text-wrap: pretty;
}
.fb-02__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-02-mut);
padding: 8px 14px;
border: 1px solid oklch(1 0 0/.16);
border-radius: 99px;
}
@media (min-width: 800px) {
.fb-02__stage {
grid-template-columns: auto 1fr;
}
.fb-02__copy {
text-align: left;
justify-items: start;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-02__compose {
transition: none;
}
}.fb-02 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-02-bg);
--fb-02-bg: radial-gradient(120% 90% at 15% 0%,oklch(0.34 0.09 265),oklch(0.19 0.04 265) 62%);
--fb-02-tint: oklch(0.63 0.19 258);
--fb-02-ink: oklch(0.97 0.01 265);
--fb-02-mut: oklch(0.78 0.02 265);
font-family: -apple-system,'SF Pro Text','Segoe UI',system-ui,sans-serif;
color: var(--fb-02-ink);
display: grid;
place-items: center;
padding: clamp(20px,4vw,52px);
}
.fb-02 *,
.fb-02 *::before,
.fb-02 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-02__stage {
width: min(100%,880px);
display: grid;
gap: clamp(24px,4vw,46px);
justify-items: center;
align-items: center;
}
.fb-02__phone {
width: min(92vw,320px);
aspect-ratio: 9/19;
padding: 11px;
border-radius: 52px;
background: linear-gradient(160deg,oklch(0.42 0.01 265),oklch(0.2 0.01 265));
box-shadow: 0 40px 90px -30px oklch(0.1 0.03 265/.85),inset 0 1px 0 oklch(1 0 0/.28);
}
.fb-02__screen {
position: relative;
height: 100%;
border-radius: 42px;
overflow: hidden;
background: oklch(0.99 0.004 265);
color: oklch(0.2 0.01 265);
display: flex;
flex-direction: column;
isolation: isolate;
}
.fb-02__status {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 22px 2px;
font-size: 12.5px;
font-weight: 650;
}
.fb-02__statusR {
display: flex;
align-items: center;
gap: 5px;
}
.fb-02__sig {
width: 15px;
height: 10px;
background: currentColor;
clip-path: polygon(0 70%,22% 70%,22% 100%,0 100%,0 70%,33% 45%,55% 45%,55% 100%,33% 100%,33% 45%,66% 22%,88% 22%,88% 100%,66% 100%);
}
.fb-02__bat {
width: 22px;
height: 11px;
border: 1.4px solid currentColor;
border-radius: 3px;
position: relative;
}
.fb-02__bat::after {
content: "";
position: absolute;
inset: 1.6px;
width: 62%;
background: currentColor;
border-radius: 1px;
}
.fb-02__nav {
padding: 6px 20px 10px;
display: grid;
gap: 1px;
}
.fb-02__navTitle {
font-size: 27px;
font-weight: 750;
letter-spacing: -.03em;
}
.fb-02__navSub {
font-size: 11.5px;
color: oklch(0.58 0.01 265);
}
.fb-02__scroll {
flex: 1;
overflow: auto;
padding: 0 14px 96px;
display: grid;
gap: 9px;
align-content: start;
scrollbar-width: none;
}
.fb-02__scroll::-webkit-scrollbar {
display: none;
}
.fb-02__note {
display: grid;
gap: 3px;
padding: 13px 15px;
border-radius: 15px;
background: oklch(0.96 0.006 265);
}
.fb-02__note b {
font-size: 14px;
font-weight: 640;
letter-spacing: -.01em;
}
.fb-02__note span {
font-size: 12px;
line-height: 1.45;
color: oklch(0.55 0.01 265);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.fb-02__note time {
font-size: 10.5px;
color: oklch(0.65 0.01 265);
font-variant-numeric: tabular-nums;
}
.fb-02__note--img {
background: linear-gradient(180deg,oklch(0.35 0.06 40/.72),oklch(0.22 0.05 40/.9)),url("https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=900&auto=format&fit=crop") center/cover;
color: oklch(0.98 0.01 40);
}
.fb-02__note--img span,
.fb-02__note--img time {
color: oklch(0.9 0.02 40);
}
.fb-02__compose {
position: absolute;
right: 18px;
bottom: calc(30px + env(safe-area-inset-bottom));
z-index: 3;
inline-size: 58px;
block-size: 58px;
border: 0;
border-radius: 22px;
display: grid;
place-items: center;
cursor: pointer;
color: oklch(0.99 0.01 258);
background: color-mix(in oklch,var(--fb-02-tint) 78%,transparent);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
box-shadow: inset 0 1px 0 oklch(1 0 0/.55),inset 0 -1px 0 oklch(0 0 0/.1),0 10px 30px -8px oklch(0.2 0.06 258/.5);
transition: transform .38s cubic-bezier(.2,1.5,.4,1),box-shadow .3s ease;
}
.fb-02__compose svg {
width: 25px;
height: 25px;
}
.fb-02__compose:hover {
box-shadow: inset 0 1px 0 oklch(1 0 0/.6),0 14px 34px -8px oklch(0.2 0.06 258/.62);
}
.fb-02__compose:active {
transform: scale(.9);
transition-duration: .09s;
}
.fb-02__compose:focus-visible {
outline: 3px solid oklch(0.99 0.01 258);
outline-offset: 3px;
}
.fb-02__home {
position: absolute;
left: 50%;
bottom: 8px;
translate: -50% 0;
width: 118px;
height: 5px;
border-radius: 99px;
background: oklch(0.2 0.01 265/.35);
}
.fb-02__copy {
display: grid;
gap: 11px;
justify-items: center;
text-align: center;
max-width: 52ch;
}
.fb-02__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.8 0.12 258);
}
.fb-02__title {
font-size: clamp(25px,4.2vw,40px);
line-height: 1.07;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-02__sub {
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.6;
color: var(--fb-02-mut);
text-wrap: pretty;
}
.fb-02__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-02-mut);
padding: 8px 14px;
border: 1px solid oklch(1 0 0/.16);
border-radius: 99px;
}
@media (min-width: 800px) {
.fb-02__stage {
grid-template-columns: auto 1fr;
}
.fb-02__copy {
text-align: left;
justify-items: start;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-02__compose {
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: iOS-Style Compose Floating Button
Source: https://codefronts.com/components/css-floating-buttons/ios-style-compose-floating-button/
What a compose button looks like on iOS: translucent rather than opaque, sitting on a blurred plate that picks up the colour of the content scrolling underneath, with a spring-loaded press that overshoots on release. Also handles the two things web clones forget — env(safe-area-inset-bottom) so the button clears the home indicator, and a specular top highlight that makes the glass read as a physical lens.
## HTML
```html
<section class="fb-02" aria-label="iOS style floating compose button demo">
<div class="fb-02__stage">
<div class="fb-02__phone">
<div class="fb-02__screen">
<div class="fb-02__status"><span>9:41</span><span class="fb-02__statusR"><i class="fb-02__sig"></i><i class="fb-02__bat"></i></span></div>
<div class="fb-02__nav"><span class="fb-02__navTitle">Notes</span><span class="fb-02__navSub">iCloud · 24 notes</span></div>
<div class="fb-02__scroll">
<article class="fb-02__note"><b>Type scale for 2027</b><span>Variable weight axis on the display face, 3 optical sizes…</span><time>Yesterday</time></article>
<article class="fb-02__note"><b>FAB motion spec</b><span>Press 90ms in, 380ms spring out, overshoot 1.04 max…</span><time>Tuesday</time></article>
<article class="fb-02__note fb-02__note--img"><b>Studio moodboard</b><span>Warm concrete, brushed steel, one hot accent.</span><time>Mon</time></article>
<article class="fb-02__note"><b>Glass tokens</b><span>blur 22, saturate 180, tint 78% — anything more is soup.</span><time>Mon</time></article>
</div>
<button class="fb-02__compose" type="button" aria-label="Compose a new note">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4l10.5-10.5-4-4L4 16v4z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/><path d="M14.5 5.5l4 4" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg>
</button>
<div class="fb-02__home" aria-hidden="true"></div>
</div>
</div>
<div class="fb-02__copy">
<p class="fb-02__eyebrow">backdrop-filter · spring easing</p>
<h2 class="fb-02__title">Glass that samples the scroll</h2>
<p class="fb-02__sub">Press it. Fast in at 90ms, springy out at 380ms with a 4% overshoot — the asymmetry is the whole trick.</p>
<p class="fb-02__chip">blur(22px) saturate(180%) · env(safe-area-inset-bottom)</p>
</div>
</div>
</section>
```
## CSS
```css
.fb-02 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-02-bg);
--fb-02-bg: radial-gradient(120% 90% at 15% 0%,oklch(0.34 0.09 265),oklch(0.19 0.04 265) 62%);
--fb-02-tint: oklch(0.63 0.19 258);
--fb-02-ink: oklch(0.97 0.01 265);
--fb-02-mut: oklch(0.78 0.02 265);
font-family: -apple-system,'SF Pro Text','Segoe UI',system-ui,sans-serif;
color: var(--fb-02-ink);
display: grid;
place-items: center;
padding: clamp(20px,4vw,52px);
}
.fb-02 *,
.fb-02 *::before,
.fb-02 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-02__stage {
width: min(100%,880px);
display: grid;
gap: clamp(24px,4vw,46px);
justify-items: center;
align-items: center;
}
.fb-02__phone {
width: min(92vw,320px);
aspect-ratio: 9/19;
padding: 11px;
border-radius: 52px;
background: linear-gradient(160deg,oklch(0.42 0.01 265),oklch(0.2 0.01 265));
box-shadow: 0 40px 90px -30px oklch(0.1 0.03 265/.85),inset 0 1px 0 oklch(1 0 0/.28);
}
.fb-02__screen {
position: relative;
height: 100%;
border-radius: 42px;
overflow: hidden;
background: oklch(0.99 0.004 265);
color: oklch(0.2 0.01 265);
display: flex;
flex-direction: column;
isolation: isolate;
}
.fb-02__status {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 22px 2px;
font-size: 12.5px;
font-weight: 650;
}
.fb-02__statusR {
display: flex;
align-items: center;
gap: 5px;
}
.fb-02__sig {
width: 15px;
height: 10px;
background: currentColor;
clip-path: polygon(0 70%,22% 70%,22% 100%,0 100%,0 70%,33% 45%,55% 45%,55% 100%,33% 100%,33% 45%,66% 22%,88% 22%,88% 100%,66% 100%);
}
.fb-02__bat {
width: 22px;
height: 11px;
border: 1.4px solid currentColor;
border-radius: 3px;
position: relative;
}
.fb-02__bat::after {
content: "";
position: absolute;
inset: 1.6px;
width: 62%;
background: currentColor;
border-radius: 1px;
}
.fb-02__nav {
padding: 6px 20px 10px;
display: grid;
gap: 1px;
}
.fb-02__navTitle {
font-size: 27px;
font-weight: 750;
letter-spacing: -.03em;
}
.fb-02__navSub {
font-size: 11.5px;
color: oklch(0.58 0.01 265);
}
.fb-02__scroll {
flex: 1;
overflow: auto;
padding: 0 14px 96px;
display: grid;
gap: 9px;
align-content: start;
scrollbar-width: none;
}
.fb-02__scroll::-webkit-scrollbar {
display: none;
}
.fb-02__note {
display: grid;
gap: 3px;
padding: 13px 15px;
border-radius: 15px;
background: oklch(0.96 0.006 265);
}
.fb-02__note b {
font-size: 14px;
font-weight: 640;
letter-spacing: -.01em;
}
.fb-02__note span {
font-size: 12px;
line-height: 1.45;
color: oklch(0.55 0.01 265);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.fb-02__note time {
font-size: 10.5px;
color: oklch(0.65 0.01 265);
font-variant-numeric: tabular-nums;
}
.fb-02__note--img {
background: linear-gradient(180deg,oklch(0.35 0.06 40/.72),oklch(0.22 0.05 40/.9)),url("https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=900&auto=format&fit=crop") center/cover;
color: oklch(0.98 0.01 40);
}
.fb-02__note--img span,
.fb-02__note--img time {
color: oklch(0.9 0.02 40);
}
.fb-02__compose {
position: absolute;
right: 18px;
bottom: calc(30px + env(safe-area-inset-bottom));
z-index: 3;
inline-size: 58px;
block-size: 58px;
border: 0;
border-radius: 22px;
display: grid;
place-items: center;
cursor: pointer;
color: oklch(0.99 0.01 258);
background: color-mix(in oklch,var(--fb-02-tint) 78%,transparent);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
box-shadow: inset 0 1px 0 oklch(1 0 0/.55),inset 0 -1px 0 oklch(0 0 0/.1),0 10px 30px -8px oklch(0.2 0.06 258/.5);
transition: transform .38s cubic-bezier(.2,1.5,.4,1),box-shadow .3s ease;
}
.fb-02__compose svg {
width: 25px;
height: 25px;
}
.fb-02__compose:hover {
box-shadow: inset 0 1px 0 oklch(1 0 0/.6),0 14px 34px -8px oklch(0.2 0.06 258/.62);
}
.fb-02__compose:active {
transform: scale(.9);
transition-duration: .09s;
}
.fb-02__compose:focus-visible {
outline: 3px solid oklch(0.99 0.01 258);
outline-offset: 3px;
}
.fb-02__home {
position: absolute;
left: 50%;
bottom: 8px;
translate: -50% 0;
width: 118px;
height: 5px;
border-radius: 99px;
background: oklch(0.2 0.01 265/.35);
}
.fb-02__copy {
display: grid;
gap: 11px;
justify-items: center;
text-align: center;
max-width: 52ch;
}
.fb-02__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.8 0.12 258);
}
.fb-02__title {
font-size: clamp(25px,4.2vw,40px);
line-height: 1.07;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-02__sub {
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.6;
color: var(--fb-02-mut);
text-wrap: pretty;
}
.fb-02__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-02-mut);
padding: 8px 14px;
border: 1px solid oklch(1 0 0/.16);
border-radius: 99px;
}
@media (min-width: 800px) {
.fb-02__stage {
grid-template-columns: auto 1fr;
}
.fb-02__copy {
text-align: left;
justify-items: start;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-02__compose {
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: iOS-Style Compose Floating Button
Source: https://codefronts.com/components/css-floating-buttons/ios-style-compose-floating-button/
What a compose button looks like on iOS: translucent rather than opaque, sitting on a blurred plate that picks up the colour of the content scrolling underneath, with a spring-loaded press that overshoots on release. Also handles the two things web clones forget — env(safe-area-inset-bottom) so the button clears the home indicator, and a specular top highlight that makes the glass read as a physical lens.
## HTML
```html
<section class="fb-02" aria-label="iOS style floating compose button demo">
<div class="fb-02__stage">
<div class="fb-02__phone">
<div class="fb-02__screen">
<div class="fb-02__status"><span>9:41</span><span class="fb-02__statusR"><i class="fb-02__sig"></i><i class="fb-02__bat"></i></span></div>
<div class="fb-02__nav"><span class="fb-02__navTitle">Notes</span><span class="fb-02__navSub">iCloud · 24 notes</span></div>
<div class="fb-02__scroll">
<article class="fb-02__note"><b>Type scale for 2027</b><span>Variable weight axis on the display face, 3 optical sizes…</span><time>Yesterday</time></article>
<article class="fb-02__note"><b>FAB motion spec</b><span>Press 90ms in, 380ms spring out, overshoot 1.04 max…</span><time>Tuesday</time></article>
<article class="fb-02__note fb-02__note--img"><b>Studio moodboard</b><span>Warm concrete, brushed steel, one hot accent.</span><time>Mon</time></article>
<article class="fb-02__note"><b>Glass tokens</b><span>blur 22, saturate 180, tint 78% — anything more is soup.</span><time>Mon</time></article>
</div>
<button class="fb-02__compose" type="button" aria-label="Compose a new note">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 20h4l10.5-10.5-4-4L4 16v4z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/><path d="M14.5 5.5l4 4" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg>
</button>
<div class="fb-02__home" aria-hidden="true"></div>
</div>
</div>
<div class="fb-02__copy">
<p class="fb-02__eyebrow">backdrop-filter · spring easing</p>
<h2 class="fb-02__title">Glass that samples the scroll</h2>
<p class="fb-02__sub">Press it. Fast in at 90ms, springy out at 380ms with a 4% overshoot — the asymmetry is the whole trick.</p>
<p class="fb-02__chip">blur(22px) saturate(180%) · env(safe-area-inset-bottom)</p>
</div>
</div>
</section>
```
## CSS
```css
.fb-02 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-02-bg);
--fb-02-bg: radial-gradient(120% 90% at 15% 0%,oklch(0.34 0.09 265),oklch(0.19 0.04 265) 62%);
--fb-02-tint: oklch(0.63 0.19 258);
--fb-02-ink: oklch(0.97 0.01 265);
--fb-02-mut: oklch(0.78 0.02 265);
font-family: -apple-system,'SF Pro Text','Segoe UI',system-ui,sans-serif;
color: var(--fb-02-ink);
display: grid;
place-items: center;
padding: clamp(20px,4vw,52px);
}
.fb-02 *,
.fb-02 *::before,
.fb-02 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-02__stage {
width: min(100%,880px);
display: grid;
gap: clamp(24px,4vw,46px);
justify-items: center;
align-items: center;
}
.fb-02__phone {
width: min(92vw,320px);
aspect-ratio: 9/19;
padding: 11px;
border-radius: 52px;
background: linear-gradient(160deg,oklch(0.42 0.01 265),oklch(0.2 0.01 265));
box-shadow: 0 40px 90px -30px oklch(0.1 0.03 265/.85),inset 0 1px 0 oklch(1 0 0/.28);
}
.fb-02__screen {
position: relative;
height: 100%;
border-radius: 42px;
overflow: hidden;
background: oklch(0.99 0.004 265);
color: oklch(0.2 0.01 265);
display: flex;
flex-direction: column;
isolation: isolate;
}
.fb-02__status {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 22px 2px;
font-size: 12.5px;
font-weight: 650;
}
.fb-02__statusR {
display: flex;
align-items: center;
gap: 5px;
}
.fb-02__sig {
width: 15px;
height: 10px;
background: currentColor;
clip-path: polygon(0 70%,22% 70%,22% 100%,0 100%,0 70%,33% 45%,55% 45%,55% 100%,33% 100%,33% 45%,66% 22%,88% 22%,88% 100%,66% 100%);
}
.fb-02__bat {
width: 22px;
height: 11px;
border: 1.4px solid currentColor;
border-radius: 3px;
position: relative;
}
.fb-02__bat::after {
content: "";
position: absolute;
inset: 1.6px;
width: 62%;
background: currentColor;
border-radius: 1px;
}
.fb-02__nav {
padding: 6px 20px 10px;
display: grid;
gap: 1px;
}
.fb-02__navTitle {
font-size: 27px;
font-weight: 750;
letter-spacing: -.03em;
}
.fb-02__navSub {
font-size: 11.5px;
color: oklch(0.58 0.01 265);
}
.fb-02__scroll {
flex: 1;
overflow: auto;
padding: 0 14px 96px;
display: grid;
gap: 9px;
align-content: start;
scrollbar-width: none;
}
.fb-02__scroll::-webkit-scrollbar {
display: none;
}
.fb-02__note {
display: grid;
gap: 3px;
padding: 13px 15px;
border-radius: 15px;
background: oklch(0.96 0.006 265);
}
.fb-02__note b {
font-size: 14px;
font-weight: 640;
letter-spacing: -.01em;
}
.fb-02__note span {
font-size: 12px;
line-height: 1.45;
color: oklch(0.55 0.01 265);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.fb-02__note time {
font-size: 10.5px;
color: oklch(0.65 0.01 265);
font-variant-numeric: tabular-nums;
}
.fb-02__note--img {
background: linear-gradient(180deg,oklch(0.35 0.06 40/.72),oklch(0.22 0.05 40/.9)),url("https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=900&auto=format&fit=crop") center/cover;
color: oklch(0.98 0.01 40);
}
.fb-02__note--img span,
.fb-02__note--img time {
color: oklch(0.9 0.02 40);
}
.fb-02__compose {
position: absolute;
right: 18px;
bottom: calc(30px + env(safe-area-inset-bottom));
z-index: 3;
inline-size: 58px;
block-size: 58px;
border: 0;
border-radius: 22px;
display: grid;
place-items: center;
cursor: pointer;
color: oklch(0.99 0.01 258);
background: color-mix(in oklch,var(--fb-02-tint) 78%,transparent);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
box-shadow: inset 0 1px 0 oklch(1 0 0/.55),inset 0 -1px 0 oklch(0 0 0/.1),0 10px 30px -8px oklch(0.2 0.06 258/.5);
transition: transform .38s cubic-bezier(.2,1.5,.4,1),box-shadow .3s ease;
}
.fb-02__compose svg {
width: 25px;
height: 25px;
}
.fb-02__compose:hover {
box-shadow: inset 0 1px 0 oklch(1 0 0/.6),0 14px 34px -8px oklch(0.2 0.06 258/.62);
}
.fb-02__compose:active {
transform: scale(.9);
transition-duration: .09s;
}
.fb-02__compose:focus-visible {
outline: 3px solid oklch(0.99 0.01 258);
outline-offset: 3px;
}
.fb-02__home {
position: absolute;
left: 50%;
bottom: 8px;
translate: -50% 0;
width: 118px;
height: 5px;
border-radius: 99px;
background: oklch(0.2 0.01 265/.35);
}
.fb-02__copy {
display: grid;
gap: 11px;
justify-items: center;
text-align: center;
max-width: 52ch;
}
.fb-02__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.8 0.12 258);
}
.fb-02__title {
font-size: clamp(25px,4.2vw,40px);
line-height: 1.07;
letter-spacing: -.03em;
font-weight: 700;
text-wrap: balance;
}
.fb-02__sub {
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.6;
color: var(--fb-02-mut);
text-wrap: pretty;
}
.fb-02__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--fb-02-mut);
padding: 8px 14px;
border: 1px solid oklch(1 0 0/.16);
border-radius: 99px;
}
@media (min-width: 800px) {
.fb-02__stage {
grid-template-columns: auto 1fr;
}
.fb-02__copy {
text-align: left;
justify-items: start;
}
}
@media (prefers-reduced-motion: reduce) {
.fb-02__compose {
transition: none;
}
}
```How this works
The Apple look is three stacked layers on one element. A blurred plate, a translucent tint, and a light edge:
.compose{ background:color-mix(in oklch, var(--tint) 78%, transparent);
backdrop-filter:blur(22px) saturate(180%);
box-shadow:inset 0 1px 0 rgb(255 255 255/.55), /* specular top edge */
inset 0 -1px 0 rgb(0 0 0/.08),
0 10px 30px -8px rgb(0 0 0/.35);
transition:transform .38s cubic-bezier(.2,1.5,.4,1); }
.compose:active{ transform:scale(.9); transition-duration:.09s }The motion is the signature. iOS presses are fast in, springy out: the active state uses a 90ms linear-ish shrink, and releasing hands control back to a bouncy cubic-bezier with an overshoot past 1. Using one symmetrical transition for both directions is the single biggest reason web buttons feel non-native.
backdrop-filter only samples what is actually painted behind the element, so the button must not be inside a stacking context that isolates it from the scrolling list — no overflow:hidden ancestor with its own backdrop, no filter on the parent. Safe-area handling is one line: bottom:calc(20px + env(safe-area-inset-bottom)), which is 20px in a browser and clears the home indicator on a real device.
Make it yours
- Tinted glass: change
--fb-02-tintto any accent. Keep it above 70% in the color-mix or the icon loses contrast against bright content. - Pill compose: set
inline-size:auto; padding-inline:22px; border-radius:99pxand add a label — the same glass recipe covers Apple's capsule buttons. - Bottom bar: duplicate the plate as a full-width element with
padding-bottom:env(safe-area-inset-bottom)for an iOS-style floating toolbar. - Dark mode: swap the specular edge to
rgb(255 255 255/.22)and raise the tint's lightness — glass needs a brighter rim on dark backgrounds, not a darker one.
Gotchas — read before shipping
- backdrop-filter is expensive on large areas. Keep it on small floating elements; a full-screen blurred layer will drop frames on mid-range Android.
- Any ancestor with
filter,transformorwill-changecreates a containing block that can flatten the blur — check the parent chain if the glass looks grey. - env() insets are 0 unless the page ships
viewport-fit=coverin its viewport meta. Without it your safe-area calc silently does nothing. - Overshoot easing on a large element reads as a bug, not a bounce. Keep spring transforms under ~1.06 scale and under 400ms.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by oklch(), color-mix(), backdrop-filter, text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
backdrop-filter is unprefixed in Chrome 76+, Safari 18+ (with -webkit- back to Safari 9) and Firefox 103+. env(safe-area-inset-*) is Safari 11+ and Chrome 69+; both degrade to a solid button.