More Cards Snippets
Neo-Brutalist Card — Free HTML CSS Hard Shadow Snippet
Neo-Brutalist Card · Cards · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Neo-Brutalist Card — Hard Shadows, Thick Borders, Tactile Press

Neo-brutalism is the high-contrast design trend that strips away soft gradients and blurred shadows in favor of thick black outlines, flat saturated color blocks, and chunky offset shadows that look like the element was cut from paper and dropped on the page. This snippet builds a complete neo-brutalist product card in plain HTML, CSS, and a few lines of vanilla JavaScript — no framework, no images, no blur filters.
The defining hard shadow
The whole look hinges on one property: box-shadow: 8px 8px 0 #111. The zero blur radius is what makes it "brutalist" — instead of a soft drop shadow, you get a crisp solid duplicate offset down and to the right, as if a second black card sits directly behind it. Combined with a 3px solid #111 border on every element, the card reads as a bold sticker rather than a floating surface. The same recipe is applied to the thumbnail, the button, and the rotated "NEW" tag so the entire composition feels consistent.
A press that moves in space
On :hover and keyboard :focus-visible the card translates up-left with translate(-4px,-4px) while the shadow grows to 12px 12px — the card appears to lift off the page. On :active it does the opposite: it translates down-right and the shadow shrinks to 4px, so clicking physically pushes the card toward its shadow like a real button. Because the transform and box-shadow both transition over .12s, the motion is snappy rather than floaty, which is exactly the brutalist feel. The button repeats this lift-and-press at a smaller scale so nested controls behave the same way.
Color as structure
Brutalism leans on flat, unapologetic color. The page background is a saturated yellow, the card is pure white, the icon tile is hot pink, the CTA is lime, and the tag is cyan — each separated by the same black border so the palette never turns muddy. There are no opacity tricks or gradients; every surface is a solid fill. This makes the card trivially themeable: swap the five hex values and you have a new variant without touching layout.
The rotated accent tag
The "NEW" badge is absolutely positioned outside the card's top edge and rotated 4deg with transform: rotate(4deg). That tiny tilt is a brutalist signature — perfectly aligned elements feel corporate, while a single rotated sticker feels handmade and energetic. Because it is a normal positioned element, you can move it to any corner or change the angle freely.
What the JavaScript adds
The aesthetic is entirely CSS; JavaScript only provides tactile feedback. Clicking the button swaps its label to "Added ✓" and inverts its colors for 1.1 seconds before reverting, giving a satisfying confirmation without a state library. A keydown handler on the card maps Enter and Space to the button so the whole component is operable from the keyboard, and stopPropagation keeps the button click from bubbling to the card.
Customizing it
Resize the shadow offset to make the card feel heavier or lighter, change the border radius from the rounded 14px to 0 for a stricter brutalist look, or drop the card into a CSS grid to build a whole bento wall of them. Pair it with a pricing card grid or use the same shadow recipe on a shimmer button to keep a consistent design language across a landing page.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA bold yellow stage renders a white card with a hard black offset shadow.
- 2Hover or tab to the cardIt lifts up-left and the solid shadow grows, with no blur.
- 3Press itThe card pushes down-right toward its shadow like a physical button.
- 4Click Get itThe lime CTA flips to a dark Added confirmation for a moment.
- 5Recolor the paletteSwap the five flat hex fills to create a new brutalist variant.
- 6Tile into a gridDrop multiple cards into a CSS grid for a bento-style wall.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The blur radius is zero: box-shadow: 8px 8px 0 #111 paints a solid offset copy of the element rather than a soft fade. Paired with a thick 3px solid black border, the card reads as a flat cut-out sticker instead of a floating material surface, which is the core of the neo-brutalist aesthetic.
On hover it translates up-left while the shadow grows, so it lifts off the page; on active it translates down-right and the shadow shrinks, so a click pushes it toward its shadow. Both the transform and box-shadow transition over 0.12s, giving a snappy mechanical press rather than a floaty animation.
Yes. Every surface is a flat solid fill — the yellow background, white card, pink icon tile, lime button, and cyan tag are independent hex values with no gradients or opacity. Swap any of the five colors to create a new variant without touching the layout or shadow rules.
The card has tabindex="0" and a focus-visible style identical to its hover state, so keyboard users see the same lift. A keydown handler maps Enter and Space to the CTA, and stopPropagation keeps the button from double-firing through the card. Replace the icon SVG with a real label if it conveys meaning.
The markup and CSS port directly — drop the JSX or template in and keep the class names. Move the button click and card keydown listeners into the framework: an onClick handler that toggles an added boolean in state (React), a @click and ref in Vue, or an (click)/(keydown) binding in Angular. In Tailwind, express the look with border-[3px] border-black shadow-[8px_8px_0_#111] and hover:-translate-x-1 hover:-translate-y-1.
Neo-Brutalist Card — Export as HTML, React, Vue, Angular & Tailwind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Neo-Brutalist Card</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#fde047;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:40px 18px}
.nb-stage{width:100%;max-width:320px}
.nb-card{position:relative;background:#fff;border:3px solid #111;border-radius:14px;padding:22px;box-shadow:8px 8px 0 #111;transition:transform .12s ease,box-shadow .12s ease;cursor:pointer;outline:none}
.nb-card:hover,.nb-card:focus-visible{transform:translate(-4px,-4px);box-shadow:12px 12px 0 #111}
.nb-card:active{transform:translate(2px,2px);box-shadow:4px 4px 0 #111}
.nb-tag{position:absolute;top:-14px;right:18px;background:#22d3ee;border:3px solid #111;border-radius:8px;font-size:11px;font-weight:900;letter-spacing:.06em;padding:4px 9px;transform:rotate(4deg)}
.nb-thumb{width:54px;height:54px;border:3px solid #111;border-radius:12px;background:#f472b6;display:flex;align-items:center;justify-content:center;box-shadow:4px 4px 0 #111;margin-bottom:16px}
.nb-thumb svg{width:26px;height:26px}
.nb-title{font-size:23px;font-weight:900;color:#111;letter-spacing:-.02em;margin-bottom:7px}
.nb-text{font-size:13.5px;line-height:1.55;color:#333;font-weight:500;margin-bottom:18px}
.nb-foot{display:flex;align-items:center;justify-content:space-between}
.nb-price{font-size:20px;font-weight:900;color:#111}
.nb-btn{background:#a3e635;border:3px solid #111;border-radius:9px;padding:9px 15px;font-family:inherit;font-size:13px;font-weight:800;color:#111;cursor:pointer;box-shadow:3px 3px 0 #111;transition:transform .1s,box-shadow .1s}
.nb-btn:hover{transform:translate(-2px,-2px);box-shadow:5px 5px 0 #111}
.nb-btn:active{transform:translate(1px,1px);box-shadow:2px 2px 0 #111}
</style>
</head>
<body>
<div class="nb-stage">
<article class="nb-card" tabindex="0">
<div class="nb-tag">NEW</div>
<div class="nb-thumb" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"></path></svg>
</div>
<h3 class="nb-title">Ship faster</h3>
<p class="nb-text">A bold, high-contrast card with a hard offset shadow that snaps on hover. Pure CSS, no blur.</p>
<div class="nb-foot">
<span class="nb-price">$0</span>
<button type="button" class="nb-btn">Get it →</button>
</div>
</article>
</div>
<script>
// Neo-brutalism is a pure-CSS aesthetic — the JS just adds a tactile
// click ripple so the card feels physical on press.
var card = document.querySelector('.nb-card');
var btn = document.querySelector('.nb-btn');
btn.addEventListener('click', function (e) {
e.stopPropagation();
btn.textContent = 'Added ✓';
btn.style.background = '#111';
btn.style.color = '#a3e635';
setTimeout(function () {
btn.textContent = 'Get it →';
btn.style.background = '#a3e635';
btn.style.color = '#111';
}, 1100);
});
// Keyboard activation mirrors the hover press for accessibility.
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
});
</script>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Neo-Brutalist Card</title>
<!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#fde047;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:40px 18px
}
.nb-card:hover,.nb-card:focus-visible {
transform:translate(-4px,-4px);box-shadow:12px 12px 0 #111
}
.nb-thumb svg {
width:26px;height:26px
}
</style>
</head>
<body>
<div class="w-full max-w-xs">
<article class="nb-card relative bg-[#fff] border-[3px] border-[#111] rounded-[14px] p-[22px] shadow-[8px_8px_0_#111] [transition:transform_.12s_ease,box-shadow_.12s_ease] cursor-pointer outline-none active:[transform:translate(2px,2px)] active:shadow-[4px_4px_0_#111]" tabindex="0">
<div class="absolute -top-3.5 right-[18px] bg-[#22d3ee] border-[3px] border-[#111] rounded-lg text-[11px] font-black tracking-[.06em] py-1 px-[9px] [transform:rotate(4deg)]">NEW</div>
<div class="nb-thumb w-[54px] h-[54px] border-[3px] border-[#111] rounded-xl bg-[#f472b6] flex items-center justify-center shadow-[4px_4px_0_#111] mb-4" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"></path></svg>
</div>
<h3 class="text-[23px] font-black text-[#111] tracking-[-.02em] mb-[7px]">Ship faster</h3>
<p class="text-[13.5px] leading-[1.55] text-[#333] font-medium mb-[18px]">A bold, high-contrast card with a hard offset shadow that snaps on hover. Pure CSS, no blur.</p>
<div class="flex items-center justify-between">
<span class="text-xl font-black text-[#111]">$0</span>
<button type="button" class="nb-btn bg-[#a3e635] border-[3px] border-[#111] rounded-[9px] py-[9px] px-[15px] font-[inherit] text-[13px] font-extrabold text-[#111] cursor-pointer shadow-[3px_3px_0_#111] [transition:transform_.1s,box-shadow_.1s] hover:[transform:translate(-2px,-2px)] hover:shadow-[5px_5px_0_#111] active:[transform:translate(1px,1px)] active:shadow-[2px_2px_0_#111]">Get it →</button>
</div>
</article>
</div>
<script>
// Neo-brutalism is a pure-CSS aesthetic — the JS just adds a tactile
// click ripple so the card feels physical on press.
var card = document.querySelector('.nb-card');
var btn = document.querySelector('.nb-btn');
btn.addEventListener('click', function (e) {
e.stopPropagation();
btn.textContent = 'Added ✓';
btn.style.background = '#111';
btn.style.color = '#a3e635';
setTimeout(function () {
btn.textContent = 'Get it →';
btn.style.background = '#a3e635';
btn.style.color = '#111';
}, 1100);
});
// Keyboard activation mirrors the hover press for accessibility.
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
});
</script>
</body>
</html>import React, { useEffect } from 'react';
// CSS — optionally move to NeoBrutalistCard.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#fde047;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:40px 18px}
.nb-stage{width:100%;max-width:320px}
.nb-card{position:relative;background:#fff;border:3px solid #111;border-radius:14px;padding:22px;box-shadow:8px 8px 0 #111;transition:transform .12s ease,box-shadow .12s ease;cursor:pointer;outline:none}
.nb-card:hover,.nb-card:focus-visible{transform:translate(-4px,-4px);box-shadow:12px 12px 0 #111}
.nb-card:active{transform:translate(2px,2px);box-shadow:4px 4px 0 #111}
.nb-tag{position:absolute;top:-14px;right:18px;background:#22d3ee;border:3px solid #111;border-radius:8px;font-size:11px;font-weight:900;letter-spacing:.06em;padding:4px 9px;transform:rotate(4deg)}
.nb-thumb{width:54px;height:54px;border:3px solid #111;border-radius:12px;background:#f472b6;display:flex;align-items:center;justify-content:center;box-shadow:4px 4px 0 #111;margin-bottom:16px}
.nb-thumb svg{width:26px;height:26px}
.nb-title{font-size:23px;font-weight:900;color:#111;letter-spacing:-.02em;margin-bottom:7px}
.nb-text{font-size:13.5px;line-height:1.55;color:#333;font-weight:500;margin-bottom:18px}
.nb-foot{display:flex;align-items:center;justify-content:space-between}
.nb-price{font-size:20px;font-weight:900;color:#111}
.nb-btn{background:#a3e635;border:3px solid #111;border-radius:9px;padding:9px 15px;font-family:inherit;font-size:13px;font-weight:800;color:#111;cursor:pointer;box-shadow:3px 3px 0 #111;transition:transform .1s,box-shadow .1s}
.nb-btn:hover{transform:translate(-2px,-2px);box-shadow:5px 5px 0 #111}
.nb-btn:active{transform:translate(1px,1px);box-shadow:2px 2px 0 #111}
`;
export default function NeoBrutalistCard() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
// Neo-brutalism is a pure-CSS aesthetic — the JS just adds a tactile
// click ripple so the card feels physical on press.
var card = document.querySelector('.nb-card');
var btn = document.querySelector('.nb-btn');
btn.addEventListener('click', function (e) {
e.stopPropagation();
btn.textContent = 'Added ✓';
btn.style.background = '#111';
btn.style.color = '#a3e635';
setTimeout(function () {
btn.textContent = 'Get it →';
btn.style.background = '#a3e635';
btn.style.color = '#111';
}, 1100);
});
// Keyboard activation mirrors the hover press for accessibility.
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
});
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{css}</style>
<div className="nb-stage">
<article className="nb-card" tabindex="0">
<div className="nb-tag">NEW</div>
<div className="nb-thumb" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="#111" strokeWidth="2.4" strokeLinecap="round" stroke-linejoin="round"><path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"></path></svg>
</div>
<h3 className="nb-title">Ship faster</h3>
<p className="nb-text">A bold, high-contrast card with a hard offset shadow that snaps on hover. Pure CSS, no blur.</p>
<div className="nb-foot">
<span className="nb-price">$0</span>
<button type="button" className="nb-btn">Get it →</button>
</div>
</article>
</div>
</>
);
}import React, { useEffect } from 'react';
// Requires Tailwind CSS v3+ — https://tailwindcss.com/docs/installation
// Arbitrary value classes (e.g. bg-[#0f172a]) are valid Tailwind v3+
export default function NeoBrutalistCard() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
// Neo-brutalism is a pure-CSS aesthetic — the JS just adds a tactile
// click ripple so the card feels physical on press.
var card = document.querySelector('.nb-card');
var btn = document.querySelector('.nb-btn');
btn.addEventListener('click', function (e) {
e.stopPropagation();
btn.textContent = 'Added ✓';
btn.style.background = '#111';
btn.style.color = '#a3e635';
setTimeout(function () {
btn.textContent = 'Get it →';
btn.style.background = '#a3e635';
btn.style.color = '#111';
}, 1100);
});
// Keyboard activation mirrors the hover press for accessibility.
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
});
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{`
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#fde047;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:40px 18px
}
.nb-card:hover,.nb-card:focus-visible {
transform:translate(-4px,-4px);box-shadow:12px 12px 0 #111
}
.nb-thumb svg {
width:26px;height:26px
}
`}</style>
<div className="w-full max-w-xs">
<article className="nb-card relative bg-[#fff] border-[3px] border-[#111] rounded-[14px] p-[22px] shadow-[8px_8px_0_#111] [transition:transform_.12s_ease,box-shadow_.12s_ease] cursor-pointer outline-none active:[transform:translate(2px,2px)] active:shadow-[4px_4px_0_#111]" tabindex="0">
<div className="absolute -top-3.5 right-[18px] bg-[#22d3ee] border-[3px] border-[#111] rounded-lg text-[11px] font-black tracking-[.06em] py-1 px-[9px] [transform:rotate(4deg)]">NEW</div>
<div className="nb-thumb w-[54px] h-[54px] border-[3px] border-[#111] rounded-xl bg-[#f472b6] flex items-center justify-center shadow-[4px_4px_0_#111] mb-4" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="#111" strokeWidth="2.4" strokeLinecap="round" stroke-linejoin="round"><path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"></path></svg>
</div>
<h3 className="text-[23px] font-black text-[#111] tracking-[-.02em] mb-[7px]">Ship faster</h3>
<p className="text-[13.5px] leading-[1.55] text-[#333] font-medium mb-[18px]">A bold, high-contrast card with a hard offset shadow that snaps on hover. Pure CSS, no blur.</p>
<div className="flex items-center justify-between">
<span className="text-xl font-black text-[#111]">$0</span>
<button type="button" className="nb-btn bg-[#a3e635] border-[3px] border-[#111] rounded-[9px] py-[9px] px-[15px] font-[inherit] text-[13px] font-extrabold text-[#111] cursor-pointer shadow-[3px_3px_0_#111] [transition:transform_.1s,box-shadow_.1s] hover:[transform:translate(-2px,-2px)] hover:shadow-[5px_5px_0_#111] active:[transform:translate(1px,1px)] active:shadow-[2px_2px_0_#111]">Get it →</button>
</div>
</article>
</div>
</>
);
}<template>
<div class="nb-stage">
<article class="nb-card" tabindex="0">
<div class="nb-tag">NEW</div>
<div class="nb-thumb" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"></path></svg>
</div>
<h3 class="nb-title">Ship faster</h3>
<p class="nb-text">A bold, high-contrast card with a hard offset shadow that snaps on hover. Pure CSS, no blur.</p>
<div class="nb-foot">
<span class="nb-price">$0</span>
<button type="button" class="nb-btn">Get it →</button>
</div>
</article>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
let card;
let btn;
onMounted(() => {
card = document.querySelector('.nb-card');
btn = document.querySelector('.nb-btn');
btn.addEventListener('click', function (e) {
e.stopPropagation();
btn.textContent = 'Added ✓';
btn.style.background = '#111';
btn.style.color = '#a3e635';
setTimeout(function () {
btn.textContent = 'Get it →';
btn.style.background = '#a3e635';
btn.style.color = '#111';
}, 1100);
});
// Keyboard activation mirrors the hover press for accessibility.
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
});
});
</script>
<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#fde047;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:40px 18px}
.nb-stage{width:100%;max-width:320px}
.nb-card{position:relative;background:#fff;border:3px solid #111;border-radius:14px;padding:22px;box-shadow:8px 8px 0 #111;transition:transform .12s ease,box-shadow .12s ease;cursor:pointer;outline:none}
.nb-card:hover,.nb-card:focus-visible{transform:translate(-4px,-4px);box-shadow:12px 12px 0 #111}
.nb-card:active{transform:translate(2px,2px);box-shadow:4px 4px 0 #111}
.nb-tag{position:absolute;top:-14px;right:18px;background:#22d3ee;border:3px solid #111;border-radius:8px;font-size:11px;font-weight:900;letter-spacing:.06em;padding:4px 9px;transform:rotate(4deg)}
.nb-thumb{width:54px;height:54px;border:3px solid #111;border-radius:12px;background:#f472b6;display:flex;align-items:center;justify-content:center;box-shadow:4px 4px 0 #111;margin-bottom:16px}
.nb-thumb svg{width:26px;height:26px}
.nb-title{font-size:23px;font-weight:900;color:#111;letter-spacing:-.02em;margin-bottom:7px}
.nb-text{font-size:13.5px;line-height:1.55;color:#333;font-weight:500;margin-bottom:18px}
.nb-foot{display:flex;align-items:center;justify-content:space-between}
.nb-price{font-size:20px;font-weight:900;color:#111}
.nb-btn{background:#a3e635;border:3px solid #111;border-radius:9px;padding:9px 15px;font-family:inherit;font-size:13px;font-weight:800;color:#111;cursor:pointer;box-shadow:3px 3px 0 #111;transition:transform .1s,box-shadow .1s}
.nb-btn:hover{transform:translate(-2px,-2px);box-shadow:5px 5px 0 #111}
.nb-btn:active{transform:translate(1px,1px);box-shadow:2px 2px 0 #111}
</style>// @ts-nocheck
// Note: vanilla JS DOM manipulation is preserved as-is inside ngAfterViewInit().
// For idiomatic Angular, replace document.getElementById() with @ViewChild() refs
// and move state into component properties with two-way binding.
import { Component, AfterViewInit, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-neo-brutalist-card',
standalone: true,
imports: [CommonModule],
encapsulation: ViewEncapsulation.None,
template: `
<div class="nb-stage">
<article class="nb-card" tabindex="0">
<div class="nb-tag">NEW</div>
<div class="nb-thumb" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"></path></svg>
</div>
<h3 class="nb-title">Ship faster</h3>
<p class="nb-text">A bold, high-contrast card with a hard offset shadow that snaps on hover. Pure CSS, no blur.</p>
<div class="nb-foot">
<span class="nb-price">$0</span>
<button type="button" class="nb-btn">Get it →</button>
</div>
</article>
</div>
`,
styles: [`
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#fde047;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:40px 18px}
.nb-stage{width:100%;max-width:320px}
.nb-card{position:relative;background:#fff;border:3px solid #111;border-radius:14px;padding:22px;box-shadow:8px 8px 0 #111;transition:transform .12s ease,box-shadow .12s ease;cursor:pointer;outline:none}
.nb-card:hover,.nb-card:focus-visible{transform:translate(-4px,-4px);box-shadow:12px 12px 0 #111}
.nb-card:active{transform:translate(2px,2px);box-shadow:4px 4px 0 #111}
.nb-tag{position:absolute;top:-14px;right:18px;background:#22d3ee;border:3px solid #111;border-radius:8px;font-size:11px;font-weight:900;letter-spacing:.06em;padding:4px 9px;transform:rotate(4deg)}
.nb-thumb{width:54px;height:54px;border:3px solid #111;border-radius:12px;background:#f472b6;display:flex;align-items:center;justify-content:center;box-shadow:4px 4px 0 #111;margin-bottom:16px}
.nb-thumb svg{width:26px;height:26px}
.nb-title{font-size:23px;font-weight:900;color:#111;letter-spacing:-.02em;margin-bottom:7px}
.nb-text{font-size:13.5px;line-height:1.55;color:#333;font-weight:500;margin-bottom:18px}
.nb-foot{display:flex;align-items:center;justify-content:space-between}
.nb-price{font-size:20px;font-weight:900;color:#111}
.nb-btn{background:#a3e635;border:3px solid #111;border-radius:9px;padding:9px 15px;font-family:inherit;font-size:13px;font-weight:800;color:#111;cursor:pointer;box-shadow:3px 3px 0 #111;transition:transform .1s,box-shadow .1s}
.nb-btn:hover{transform:translate(-2px,-2px);box-shadow:5px 5px 0 #111}
.nb-btn:active{transform:translate(1px,1px);box-shadow:2px 2px 0 #111}
`]
})
export class NeoBrutalistCardComponent implements AfterViewInit {
ngAfterViewInit(): void {
// Neo-brutalism is a pure-CSS aesthetic — the JS just adds a tactile
// click ripple so the card feels physical on press.
var card = document.querySelector('.nb-card');
var btn = document.querySelector('.nb-btn');
btn.addEventListener('click', function (e) {
e.stopPropagation();
btn.textContent = 'Added ✓';
btn.style.background = '#111';
btn.style.color = '#a3e635';
setTimeout(function () {
btn.textContent = 'Get it →';
btn.style.background = '#a3e635';
btn.style.color = '#111';
}, 1100);
});
// Keyboard activation mirrors the hover press for accessibility.
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
});
}
}