34 CSS Floating Buttons28 / 34
Neo-Brutalist Hard Shadow Floating Button CSS
Neo-brutalism where the interaction, not just the look, is physical: a zero-blur offset shadow acting as a solid slab, and a press that translates the button exactly onto its own shadow so the slab collapses to nothing. Get that arithmetic right and the button feels like a real key being pushed flat against the page.
Published Updated
The code
<section class="fb-28" aria-label="Neo brutalist hard shadow floating button demo">
<div class="fb-28__stage">
<p class="fb-28__eyebrow">zero blur · press lands on the slab</p>
<h2 class="fb-28__title">PUSH<br>IT<span>.</span></h2>
<p class="fb-28__sub">Translate equals shadow offset. Press it and the button lands exactly on its own shadow — the mass stays put, only the elevation goes.</p>
<div class="fb-28__row">
<button class="fb-28__fab" type="button" aria-label="Add">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4v16M4 12h16" stroke="currentColor" stroke-width="3" stroke-linecap="square"/></svg>
</button>
<button class="fb-28__pill" type="button">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="square"/></svg>
<span>Ship it</span>
</button>
<button class="fb-28__fab fb-28__fab--pop" type="button" aria-label="Favourite">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4l2.5 5.6 6.1.6-4.6 4.1 1.3 6-5.3-3.2-5.3 3.2 1.3-6L3.4 10.2l6.1-.6z" fill="currentColor"/></svg>
</button>
<button class="fb-28__fab fb-28__fab--sticker" type="button" aria-label="Bookmark">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 3h12v18l-6-4.6L6 21z" fill="currentColor"/></svg>
</button>
</div>
<p class="fb-28__chip">6px offset · 3px border · 100ms linear</p>
</div>
</section><section class="fb-28" aria-label="Neo brutalist hard shadow floating button demo">
<div class="fb-28__stage">
<p class="fb-28__eyebrow">zero blur · press lands on the slab</p>
<h2 class="fb-28__title">PUSH<br>IT<span>.</span></h2>
<p class="fb-28__sub">Translate equals shadow offset. Press it and the button lands exactly on its own shadow — the mass stays put, only the elevation goes.</p>
<div class="fb-28__row">
<button class="fb-28__fab" type="button" aria-label="Add">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4v16M4 12h16" stroke="currentColor" stroke-width="3" stroke-linecap="square"/></svg>
</button>
<button class="fb-28__pill" type="button">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="square"/></svg>
<span>Ship it</span>
</button>
<button class="fb-28__fab fb-28__fab--pop" type="button" aria-label="Favourite">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4l2.5 5.6 6.1.6-4.6 4.1 1.3 6-5.3-3.2-5.3 3.2 1.3-6L3.4 10.2l6.1-.6z" fill="currentColor"/></svg>
</button>
<button class="fb-28__fab fb-28__fab--sticker" type="button" aria-label="Bookmark">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 3h12v18l-6-4.6L6 21z" fill="currentColor"/></svg>
</button>
</div>
<p class="fb-28__chip">6px offset · 3px border · 100ms linear</p>
</div>
</section>.fb-28 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-28-bg);
--fb-28-bg: oklch(0.93 0.13 100);
--fb-28-line: oklch(0.18 0.02 95);
--fb-28-fill: oklch(0.99 0.005 100);
--fb-28-pop: oklch(0.68 0.21 20);
--fb-28-cy: oklch(0.78 0.14 200);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-28-line);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
background-image: repeating-linear-gradient(45deg,oklch(0.18 0.02 95/.05) 0 2px,transparent 2px 12px);
}
.fb-28 *,
.fb-28 *::before,
.fb-28 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-28__stage {
width: min(100%,58ch);
display: grid;
gap: clamp(16px,3vw,26px);
justify-items: center;
text-align: center;
}
.fb-28__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: .18em;
text-transform: uppercase;
background: var(--fb-28-line);
color: var(--fb-28-bg);
padding: 6px 12px;
}
.fb-28__title {
font-size: clamp(44px,11vw,104px);
line-height: .86;
letter-spacing: -.05em;
font-weight: 900;
text-transform: uppercase;
-webkit-text-stroke: 2px var(--fb-28-line);
color: var(--fb-28-fill);
text-shadow: 7px 7px 0 var(--fb-28-line);
}
.fb-28__title span {
color: var(--fb-28-pop);
}
.fb-28__sub {
max-width: 46ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.55;
font-weight: 560;
text-wrap: pretty;
}
.fb-28__row {
display: flex;
flex-wrap: wrap;
gap: clamp(18px,5vw,34px);
justify-content: center;
align-items: center;
padding: 12px 0 6px;
}
.fb-28__fab {
display: grid;
place-items: center;
inline-size: 68px;
block-size: 68px;
border: 3px solid var(--fb-28-line);
border-radius: 0;
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__fab svg {
width: 28px;
height: 28px;
}
.fb-28__fab:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-cy);
}
.fb-28__fab:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__fab:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__fab--pop {
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__fab--pop:hover {
background: oklch(0.75 0.2 22);
}
.fb-28__fab--sticker {
rotate: -4deg;
background: var(--fb-28-cy);
box-shadow: 6px 6px 0 0 var(--fb-28-line),12px 12px 0 0 var(--fb-28-pop);
}
.fb-28__fab--sticker:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line),15px 15px 0 0 var(--fb-28-pop);
background: oklch(0.85 0.14 195);
}
.fb-28__fab--sticker:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line),6px 6px 0 0 var(--fb-28-pop);
}
.fb-28__pill {
display: inline-flex;
align-items: center;
gap: 11px;
block-size: 68px;
padding-inline: 22px 26px;
border: 3px solid var(--fb-28-line);
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
font-family: inherit;
font-size: 19px;
font-weight: 800;
letter-spacing: -.02em;
text-transform: uppercase;
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__pill svg {
width: 26px;
height: 26px;
}
.fb-28__pill:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__pill:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__pill:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
font-weight: 600;
padding: 8px 14px;
border: 2px solid var(--fb-28-line);
background: var(--fb-28-fill);
}
@media (prefers-reduced-motion: reduce) {
.fb-28 * {
transition: none !important;
}
}.fb-28 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-28-bg);
--fb-28-bg: oklch(0.93 0.13 100);
--fb-28-line: oklch(0.18 0.02 95);
--fb-28-fill: oklch(0.99 0.005 100);
--fb-28-pop: oklch(0.68 0.21 20);
--fb-28-cy: oklch(0.78 0.14 200);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-28-line);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
background-image: repeating-linear-gradient(45deg,oklch(0.18 0.02 95/.05) 0 2px,transparent 2px 12px);
}
.fb-28 *,
.fb-28 *::before,
.fb-28 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-28__stage {
width: min(100%,58ch);
display: grid;
gap: clamp(16px,3vw,26px);
justify-items: center;
text-align: center;
}
.fb-28__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: .18em;
text-transform: uppercase;
background: var(--fb-28-line);
color: var(--fb-28-bg);
padding: 6px 12px;
}
.fb-28__title {
font-size: clamp(44px,11vw,104px);
line-height: .86;
letter-spacing: -.05em;
font-weight: 900;
text-transform: uppercase;
-webkit-text-stroke: 2px var(--fb-28-line);
color: var(--fb-28-fill);
text-shadow: 7px 7px 0 var(--fb-28-line);
}
.fb-28__title span {
color: var(--fb-28-pop);
}
.fb-28__sub {
max-width: 46ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.55;
font-weight: 560;
text-wrap: pretty;
}
.fb-28__row {
display: flex;
flex-wrap: wrap;
gap: clamp(18px,5vw,34px);
justify-content: center;
align-items: center;
padding: 12px 0 6px;
}
.fb-28__fab {
display: grid;
place-items: center;
inline-size: 68px;
block-size: 68px;
border: 3px solid var(--fb-28-line);
border-radius: 0;
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__fab svg {
width: 28px;
height: 28px;
}
.fb-28__fab:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-cy);
}
.fb-28__fab:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__fab:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__fab--pop {
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__fab--pop:hover {
background: oklch(0.75 0.2 22);
}
.fb-28__fab--sticker {
rotate: -4deg;
background: var(--fb-28-cy);
box-shadow: 6px 6px 0 0 var(--fb-28-line),12px 12px 0 0 var(--fb-28-pop);
}
.fb-28__fab--sticker:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line),15px 15px 0 0 var(--fb-28-pop);
background: oklch(0.85 0.14 195);
}
.fb-28__fab--sticker:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line),6px 6px 0 0 var(--fb-28-pop);
}
.fb-28__pill {
display: inline-flex;
align-items: center;
gap: 11px;
block-size: 68px;
padding-inline: 22px 26px;
border: 3px solid var(--fb-28-line);
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
font-family: inherit;
font-size: 19px;
font-weight: 800;
letter-spacing: -.02em;
text-transform: uppercase;
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__pill svg {
width: 26px;
height: 26px;
}
.fb-28__pill:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__pill:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__pill:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
font-weight: 600;
padding: 8px 14px;
border: 2px solid var(--fb-28-line);
background: var(--fb-28-fill);
}
@media (prefers-reduced-motion: reduce) {
.fb-28 * {
transition: none !important;
}
}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: Neo-Brutalist Hard Shadow Floating Button CSS
Source: https://codefronts.com/components/css-floating-buttons/fb09/
Neo-brutalism where the interaction, not just the look, is physical: a zero-blur offset shadow acting as a solid slab, and a press that translates the button exactly onto its own shadow so the slab collapses to nothing. Get that arithmetic right and the button feels like a real key being pushed flat against the page.
## HTML
```html
<section class="fb-28" aria-label="Neo brutalist hard shadow floating button demo">
<div class="fb-28__stage">
<p class="fb-28__eyebrow">zero blur · press lands on the slab</p>
<h2 class="fb-28__title">PUSH<br>IT<span>.</span></h2>
<p class="fb-28__sub">Translate equals shadow offset. Press it and the button lands exactly on its own shadow — the mass stays put, only the elevation goes.</p>
<div class="fb-28__row">
<button class="fb-28__fab" type="button" aria-label="Add">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4v16M4 12h16" stroke="currentColor" stroke-width="3" stroke-linecap="square"/></svg>
</button>
<button class="fb-28__pill" type="button">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="square"/></svg>
<span>Ship it</span>
</button>
<button class="fb-28__fab fb-28__fab--pop" type="button" aria-label="Favourite">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4l2.5 5.6 6.1.6-4.6 4.1 1.3 6-5.3-3.2-5.3 3.2 1.3-6L3.4 10.2l6.1-.6z" fill="currentColor"/></svg>
</button>
<button class="fb-28__fab fb-28__fab--sticker" type="button" aria-label="Bookmark">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 3h12v18l-6-4.6L6 21z" fill="currentColor"/></svg>
</button>
</div>
<p class="fb-28__chip">6px offset · 3px border · 100ms linear</p>
</div>
</section>
```
## CSS
```css
.fb-28 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-28-bg);
--fb-28-bg: oklch(0.93 0.13 100);
--fb-28-line: oklch(0.18 0.02 95);
--fb-28-fill: oklch(0.99 0.005 100);
--fb-28-pop: oklch(0.68 0.21 20);
--fb-28-cy: oklch(0.78 0.14 200);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-28-line);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
background-image: repeating-linear-gradient(45deg,oklch(0.18 0.02 95/.05) 0 2px,transparent 2px 12px);
}
.fb-28 *,
.fb-28 *::before,
.fb-28 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-28__stage {
width: min(100%,58ch);
display: grid;
gap: clamp(16px,3vw,26px);
justify-items: center;
text-align: center;
}
.fb-28__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: .18em;
text-transform: uppercase;
background: var(--fb-28-line);
color: var(--fb-28-bg);
padding: 6px 12px;
}
.fb-28__title {
font-size: clamp(44px,11vw,104px);
line-height: .86;
letter-spacing: -.05em;
font-weight: 900;
text-transform: uppercase;
-webkit-text-stroke: 2px var(--fb-28-line);
color: var(--fb-28-fill);
text-shadow: 7px 7px 0 var(--fb-28-line);
}
.fb-28__title span {
color: var(--fb-28-pop);
}
.fb-28__sub {
max-width: 46ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.55;
font-weight: 560;
text-wrap: pretty;
}
.fb-28__row {
display: flex;
flex-wrap: wrap;
gap: clamp(18px,5vw,34px);
justify-content: center;
align-items: center;
padding: 12px 0 6px;
}
.fb-28__fab {
display: grid;
place-items: center;
inline-size: 68px;
block-size: 68px;
border: 3px solid var(--fb-28-line);
border-radius: 0;
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__fab svg {
width: 28px;
height: 28px;
}
.fb-28__fab:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-cy);
}
.fb-28__fab:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__fab:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__fab--pop {
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__fab--pop:hover {
background: oklch(0.75 0.2 22);
}
.fb-28__fab--sticker {
rotate: -4deg;
background: var(--fb-28-cy);
box-shadow: 6px 6px 0 0 var(--fb-28-line),12px 12px 0 0 var(--fb-28-pop);
}
.fb-28__fab--sticker:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line),15px 15px 0 0 var(--fb-28-pop);
background: oklch(0.85 0.14 195);
}
.fb-28__fab--sticker:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line),6px 6px 0 0 var(--fb-28-pop);
}
.fb-28__pill {
display: inline-flex;
align-items: center;
gap: 11px;
block-size: 68px;
padding-inline: 22px 26px;
border: 3px solid var(--fb-28-line);
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
font-family: inherit;
font-size: 19px;
font-weight: 800;
letter-spacing: -.02em;
text-transform: uppercase;
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__pill svg {
width: 26px;
height: 26px;
}
.fb-28__pill:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__pill:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__pill:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
font-weight: 600;
padding: 8px 14px;
border: 2px solid var(--fb-28-line);
background: var(--fb-28-fill);
}
@media (prefers-reduced-motion: reduce) {
.fb-28 * {
transition: none !important;
}
}
```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: Neo-Brutalist Hard Shadow Floating Button CSS
Source: https://codefronts.com/components/css-floating-buttons/fb09/
Neo-brutalism where the interaction, not just the look, is physical: a zero-blur offset shadow acting as a solid slab, and a press that translates the button exactly onto its own shadow so the slab collapses to nothing. Get that arithmetic right and the button feels like a real key being pushed flat against the page.
## HTML
```html
<section class="fb-28" aria-label="Neo brutalist hard shadow floating button demo">
<div class="fb-28__stage">
<p class="fb-28__eyebrow">zero blur · press lands on the slab</p>
<h2 class="fb-28__title">PUSH<br>IT<span>.</span></h2>
<p class="fb-28__sub">Translate equals shadow offset. Press it and the button lands exactly on its own shadow — the mass stays put, only the elevation goes.</p>
<div class="fb-28__row">
<button class="fb-28__fab" type="button" aria-label="Add">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4v16M4 12h16" stroke="currentColor" stroke-width="3" stroke-linecap="square"/></svg>
</button>
<button class="fb-28__pill" type="button">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="square"/></svg>
<span>Ship it</span>
</button>
<button class="fb-28__fab fb-28__fab--pop" type="button" aria-label="Favourite">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 4l2.5 5.6 6.1.6-4.6 4.1 1.3 6-5.3-3.2-5.3 3.2 1.3-6L3.4 10.2l6.1-.6z" fill="currentColor"/></svg>
</button>
<button class="fb-28__fab fb-28__fab--sticker" type="button" aria-label="Bookmark">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 3h12v18l-6-4.6L6 21z" fill="currentColor"/></svg>
</button>
</div>
<p class="fb-28__chip">6px offset · 3px border · 100ms linear</p>
</div>
</section>
```
## CSS
```css
.fb-28 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--fb-28-bg);
--fb-28-bg: oklch(0.93 0.13 100);
--fb-28-line: oklch(0.18 0.02 95);
--fb-28-fill: oklch(0.99 0.005 100);
--fb-28-pop: oklch(0.68 0.21 20);
--fb-28-cy: oklch(0.78 0.14 200);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
color: var(--fb-28-line);
display: grid;
place-items: center;
padding: clamp(20px,5vw,56px);
background-image: repeating-linear-gradient(45deg,oklch(0.18 0.02 95/.05) 0 2px,transparent 2px 12px);
}
.fb-28 *,
.fb-28 *::before,
.fb-28 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.fb-28__stage {
width: min(100%,58ch);
display: grid;
gap: clamp(16px,3vw,26px);
justify-items: center;
text-align: center;
}
.fb-28__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: .18em;
text-transform: uppercase;
background: var(--fb-28-line);
color: var(--fb-28-bg);
padding: 6px 12px;
}
.fb-28__title {
font-size: clamp(44px,11vw,104px);
line-height: .86;
letter-spacing: -.05em;
font-weight: 900;
text-transform: uppercase;
-webkit-text-stroke: 2px var(--fb-28-line);
color: var(--fb-28-fill);
text-shadow: 7px 7px 0 var(--fb-28-line);
}
.fb-28__title span {
color: var(--fb-28-pop);
}
.fb-28__sub {
max-width: 46ch;
font-size: clamp(13.5px,1.7vw,16.5px);
line-height: 1.55;
font-weight: 560;
text-wrap: pretty;
}
.fb-28__row {
display: flex;
flex-wrap: wrap;
gap: clamp(18px,5vw,34px);
justify-content: center;
align-items: center;
padding: 12px 0 6px;
}
.fb-28__fab {
display: grid;
place-items: center;
inline-size: 68px;
block-size: 68px;
border: 3px solid var(--fb-28-line);
border-radius: 0;
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__fab svg {
width: 28px;
height: 28px;
}
.fb-28__fab:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-cy);
}
.fb-28__fab:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__fab:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__fab--pop {
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__fab--pop:hover {
background: oklch(0.75 0.2 22);
}
.fb-28__fab--sticker {
rotate: -4deg;
background: var(--fb-28-cy);
box-shadow: 6px 6px 0 0 var(--fb-28-line),12px 12px 0 0 var(--fb-28-pop);
}
.fb-28__fab--sticker:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line),15px 15px 0 0 var(--fb-28-pop);
background: oklch(0.85 0.14 195);
}
.fb-28__fab--sticker:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line),6px 6px 0 0 var(--fb-28-pop);
}
.fb-28__pill {
display: inline-flex;
align-items: center;
gap: 11px;
block-size: 68px;
padding-inline: 22px 26px;
border: 3px solid var(--fb-28-line);
cursor: pointer;
color: var(--fb-28-line);
background: var(--fb-28-fill);
font-family: inherit;
font-size: 19px;
font-weight: 800;
letter-spacing: -.02em;
text-transform: uppercase;
box-shadow: 6px 6px 0 0 var(--fb-28-line);
transition: translate .1s linear,box-shadow .1s linear,background .1s linear;
}
.fb-28__pill svg {
width: 26px;
height: 26px;
}
.fb-28__pill:hover {
translate: -2px -2px;
box-shadow: 8px 8px 0 0 var(--fb-28-line);
background: var(--fb-28-pop);
color: var(--fb-28-fill);
}
.fb-28__pill:active {
translate: 6px 6px;
box-shadow: 0 0 0 0 var(--fb-28-line);
}
.fb-28__pill:focus-visible {
outline: 3px dashed var(--fb-28-line);
outline-offset: 5px;
}
.fb-28__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
font-weight: 600;
padding: 8px 14px;
border: 2px solid var(--fb-28-line);
background: var(--fb-28-fill);
}
@media (prefers-reduced-motion: reduce) {
.fb-28 * {
transition: none !important;
}
}
```How this works
The entire effect is one identity — translate distance equals shadow offset:
.brut{ border:3px solid #111; box-shadow:6px 6px 0 0 #111;
transition:translate .1s, box-shadow .1s }
.brut:hover { translate:-2px -2px; box-shadow:8px 8px 0 0 #111 }
.brut:active{ translate: 6px 6px; box-shadow:0 0 0 0 #111 } /* lands flat */On press the button moves down-right by exactly the shadow's offset while the shadow shrinks to zero — so the visual mass stays in the same place on screen and only the elevation disappears. Any mismatch between the two numbers and the button appears to slide sideways instead of being pressed.
Timing is deliberately short and linear (100ms, no easing). Brutalism has no springs; a bouncy neo-brutalist button is a contradiction and reads as a mistake.
The rest is discipline: a single flat fill, a 3px border in near-black rather than pure black, no gradient, no blur anywhere in the file, and a display face with real weight. The style survives or dies on whether you resist adding one soft shadow.
Make it yours
- Offset scale: 4px for compact controls, 6px standard, 10px for hero buttons. Border weight scales with it (2 / 3 / 4px).
- Colour shadow: an accent-coloured slab instead of black reads as pop-art; keep the border black so the shape stays legible.
- Sticker variant: add a slight
rotate(-2deg)and a second offset shadow in a contrasting hue for a layered print look. - Halftone fill: a repeating-radial-gradient dot pattern at 8% opacity gives the fill a risograph texture without a single image.
Gotchas — read before shipping
- Do not blur the shadow "just a little". The whole aesthetic depends on a hard edge; 1px of blur reads as a rendering bug.
- Pure black borders on saturated fills vibrate optically. Use oklch(0.18 …) with a hint of the fill's hue instead.
- Brutalist palettes are usually high-chroma; check text contrast properly, because bright yellow with white text fails badly.
- Offset shadows expand the visual footprint but not the layout box. Reserve margin equal to the offset or neighbouring elements overlap it.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
box-shadow with zero blur and the translate property are supported everywhere modern (translate: Chrome 104+, Safari 14.1+, Firefox 72+). Use transform:translate() for older engines.