Neo-Brutalist Card — Free HTML CSS Hard Shadow Snippet

Neo-Brutalist Card · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Zero-blur hard shadow
box-shadow with 0 blur creates the crisp brutalist offset.
Lift on hover and focus
translate plus a larger shadow makes the card rise.
Physical press on active
It moves toward its shadow when clicked.
Flat saturated palette
Solid color blocks separated by 3px black borders.
Rotated accent tag
A tilted NEW sticker gives the handmade brutalist feel.
Keyboard operable
Enter and Space trigger the CTA via a keydown handler.
Tactile click feedback
The button confirms with an inverted Added state.
No images or blur
Pure HTML and CSS, trivially themeable.

About this UI Snippet

Neo-Brutalist Card — Hard Shadows, Thick Borders, Tactile Press

Screenshot of the Neo-Brutalist Card snippet rendered live

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

  1. 1
    Paste HTML, CSS, and JSA bold yellow stage renders a white card with a hard black offset shadow.
  2. 2
    Hover or tab to the cardIt lifts up-left and the solid shadow grows, with no blur.
  3. 3
    Press itThe card pushes down-right toward its shadow like a physical button.
  4. 4
    Click Get itThe lime CTA flips to a dark Added confirmation for a moment.
  5. 5
    Recolor the paletteSwap the five flat hex fills to create a new brutalist variant.
  6. 6
    Tile into a gridDrop multiple cards into a CSS grid for a bento-style wall.

Real-world uses

Common Use Cases

Bold landing pages
Headline a brutalist hero next to an animated gradient CTA.
Pricing grids
Restyle a pricing card wall in the brutalist look.
Feature highlights
Use as the cells of a bento grid layout.
Product cards
Give an e-commerce product card a high-contrast treatment.
Portfolio sites
Match the chunky shadow on a neon glow accent.
Design-system demos
A reference for the hard-shadow brutalist pattern.

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

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>
  <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>
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>
  <!-- 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>
React
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>
    </>
  );
}
React + Tailwind
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>
    </>
  );
}
Vue
<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>
Angular
// @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(); }
    });
  }
}