SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/badges

8 CSS Badges

7 Pure CSS1 with JSPublished 2026-10-04Free · no sign-up

Eight badge styles: order status pills, number badges on icon buttons, presence dots on avatars, New and Sale tags on product cards, a solid, soft and outline set, Live and Beta labels, removable filter tags and verified and role badges next to names.

01. Status Badges

BadgeStatus

Pill badges for order and task states: Paid, Pending, Refunded, Failed and Draft. Each has a small dot plus readable text, uses soft background tints with dark text for strong contrast, and never relies on colour alone to carry the meaning.

View code
<div class="bgs">
  <span class="b ok">Paid</span><span class="b wait">Pending</span><span class="b info">Refunded</span><span class="b bad">Failed</span><span class="b neu">Draft</span>
</div>
.bgs { display: flex; flex-wrap: wrap; gap: 8px; font: 600 13px Inter, system-ui, sans-serif; }
.b { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px; }
.b::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.ok { background: #dcfae6; color: #085d3a; }
.wait { background: #fef0c7; color: #93370d; }
.info { background: #d1e9ff; color: #1849a9; }
.bad { background: #fee4e2; color: #912018; }
.neu { background: #eaecf0; color: #344054; }

02. Count Badges on Icons

CounterIcon

Number badges pinned to the corner of icon buttons, such as a cart with 3 items or a message icon with 12 unread. Counts above 99 show 99+, and each button's accessible name includes the number so it is not only conveyed by the badge.

View code
<div class="cnt">
  <button aria-label="Cart, 3 items"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 4h2l2.4 11h11L21 7H6.2M9 20h.01M18 20h.01"/></svg><span aria-hidden="true">3</span></button>
  <button aria-label="Messages, 12 unread"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v11H8l-4 4z"/></svg><span aria-hidden="true">12</span></button>
  <button aria-label="Alerts, more than 99"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 16V11a6 6 0 1112 0v5l2 2H4z"/></svg><span aria-hidden="true">99+</span></button>
</div>
.cnt { display: flex; gap: 22px; }
.cnt button { position: relative; width: 46px; height: 46px; border: 1px solid #343c51; border-radius: 12px; background: #151a26; cursor: pointer; }
.cnt svg { width: 22px; height: 22px; fill: none; stroke: #e7ebf5; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.cnt span { position: absolute; top: -8px; right: -8px; min-width: 20px; height: 20px; padding: 0 5px; box-sizing: border-box; border: 2px solid #0e1118; border-radius: 999px; background: #f43f5e; color: #fff; font: 700 11px/16px Inter, system-ui, sans-serif; }
.cnt button:focus-visible { outline: 2px solid #fda4af; outline-offset: 3px; }

03. Online Status Dot

AvatarPresence

Presence indicators on avatars: online, away, busy and offline. The dot is a pseudo-element with a white ring so it stands out on any photo, and the status is also written in the image's alt text and a visible caption.

View code
<ul class="pres">
  <li><span class="av on" role="img" aria-label="Ana, online">A</span><small>Online</small></li>
  <li><span class="av away" role="img" aria-label="Ben, away">B</span><small>Away</small></li>
  <li><span class="av busy" role="img" aria-label="Chi, busy">C</span><small>Busy</small></li>
  <li><span class="av off" role="img" aria-label="Dev, offline">D</span><small>Offline</small></li>
</ul>
.pres { display: flex; gap: 22px; margin: 0; padding: 0; list-style: none; font: 600 12px Inter, system-ui, sans-serif; color: #475467; text-align: center; }
.pres li { display: grid; justify-items: center; gap: 6px; }
.av { position: relative; display: grid; place-items: center; width: 48px; height: 48px; border-radius: 50%; background: #c7d2fe; color: #312e81; font: 800 18px Inter, sans-serif; }
.av::after { content: ""; position: absolute; right: 0; bottom: 0; width: 13px; height: 13px; border: 3px solid #fff; border-radius: 50%; }
.on::after { background: #17b26a; } .away::after { background: #f79009; } .busy::after { background: #f04438; } .off::after { background: #98a2b3; }

04. New and Sale Ribbon Badges

E-commerceProduct

Product card badges for New, Sale with a percentage and Last few, positioned over the image corner. They are real text inside the card, so screen readers read them with the product, and the sale badge uses an angled clip-path edge.

View code
<div class="pcards">
  <article><div class="img" style="background:linear-gradient(135deg,#334155,#1e293b)"><span class="tag new">New</span></div><h3>Clay planter</h3><p>$36</p></article>
  <article><div class="img" style="background:linear-gradient(135deg,#7c2d12,#431407)"><span class="tag sale">Sale -25%</span></div><h3>Wool throw</h3><p><s>$120</s> $90</p></article>
  <article><div class="img" style="background:linear-gradient(135deg,#14532d,#052e16)"><span class="tag few">Last 2</span></div><h3>Glass carafe</h3><p>$28</p></article>
</div>
.pcards { display: flex; gap: 14px; font-family: Inter, system-ui, sans-serif; }
.pcards article { width: 150px; color: #c9d1e2; }
.img { position: relative; aspect-ratio: 1; border-radius: 12px; }
.tag { position: absolute; top: 10px; left: 10px; padding: 4px 9px; border-radius: 6px; font: 800 11.5px Inter, sans-serif; letter-spacing: .03em; text-transform: uppercase; }
.new { background: #fff; color: #0e1118; }
.sale { left: 0; padding-right: 16px; border-radius: 0 4px 4px 0; background: #ef4444; color: #fff; clip-path: polygon(0 0, 100% 0, 88% 50%, 100% 100%, 0 100%); }
.few { background: #fbbf24; color: #1a1205; }
.pcards h3 { margin: 8px 0 2px; color: #fff; font-size: 14px; }
.pcards p { margin: 0; font-size: 14px; }
.pcards s { color: #8b95a9; }
@media (max-width: 520px) { .pcards { flex-wrap: wrap; } }

05. Outline and Soft Badge Set

Design systemVariants

One badge component in three visual weights: solid, soft and outline, across three sizes. The variants share one base class and change only background, border and padding through modifier classes, which keeps a design system tidy.

View code
<div class="set">
  <div><span class="bd solid sm">Solid</span><span class="bd solid">Solid</span><span class="bd solid lg">Solid</span></div>
  <div><span class="bd soft sm">Soft</span><span class="bd soft">Soft</span><span class="bd soft lg">Soft</span></div>
  <div><span class="bd line sm">Outline</span><span class="bd line">Outline</span><span class="bd line lg">Outline</span></div>
</div>
.set { display: grid; gap: 12px; font-family: Inter, system-ui, sans-serif; }
.set div { display: flex; align-items: center; gap: 10px; }
.bd { display: inline-block; padding: 3px 10px; border: 1.5px solid transparent; border-radius: 999px; font-weight: 700; font-size: 12.5px; }
.bd.sm { padding: 1px 7px; font-size: 11px; }
.bd.lg { padding: 5px 14px; font-size: 14px; }
.solid { background: #6941c6; color: #fff; }
.soft { background: #f4ebff; color: #53389e; }
.line { border-color: #7f56d9; color: #53389e; }

06. Live and Beta Badges

AnimationDark

Badges that draw attention for live streams and beta features: a red Live badge with a pulsing dot, and a gradient Beta badge with a soft glow. The pulse is a scaling ring that stops for reduced motion, while the text stays perfectly still.

View code
<div class="gl">
  <span class="live"><i aria-hidden="true"></i>Live</span>
  <span class="beta">Beta</span>
  <span class="pro">Pro</span>
</div>
.gl { display: flex; gap: 12px; font: 800 12px Inter, system-ui, sans-serif; letter-spacing: .06em; text-transform: uppercase; }
.live { display: inline-flex; align-items: center; gap: 7px; padding: 5px 11px; border-radius: 6px; background: #dc2626; color: #fff; }
.live i { position: relative; width: 7px; height: 7px; border-radius: 50%; background: #fff; }
.live i::after { content: ""; position: absolute; inset: -4px; border: 2px solid #fff; border-radius: 50%; animation: lv 1.4s ease-out infinite; }
@keyframes lv { from { transform: scale(.4); opacity: 1; } to { transform: scale(1.5); opacity: 0; } }
.beta { padding: 5px 11px; border-radius: 6px; background: linear-gradient(90deg, #22d3ee, #a855f7); color: #0b0716; box-shadow: 0 0 16px rgba(168,85,247,.5); }
.pro { padding: 4px 10px; border: 1.5px solid #fbbf24; border-radius: 6px; color: #fbbf24; }
@media (prefers-reduced-motion: reduce) { .live i::after { animation: none; display: none; } }

07. Removable Tag Badges

TagsFilter

Applied filter tags with a remove button on each, plus a Clear all link. Each remove button is named Remove filter: Cotton and so on, and focus moves to the next tag after one is removed so keyboard users do not lose their place.

View code
<div class="rm">
  <p>Filters:</p>
  <ul>
    <li>Cotton <button aria-label="Remove filter: Cotton">&times;</button></li>
    <li>Under $50 <button aria-label="Remove filter: Under $50">&times;</button></li>
    <li>In stock <button aria-label="Remove filter: In stock">&times;</button></li>
  </ul>
  <button class="clr">Clear all</button>
</div>
.rm { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; width: min(460px, 94vw); font: 600 13.5px Inter, system-ui, sans-serif; color: #344054; }
.rm p { margin: 0; }
.rm ul { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.rm li { display: inline-flex; align-items: center; gap: 4px; padding: 4px 4px 4px 11px; border: 1px solid #d0d5dd; border-radius: 999px; background: #fff; color: #101828; }
.rm li button { width: 22px; height: 22px; border: 0; border-radius: 50%; background: #f2f4f7; color: #475467; cursor: pointer; }
.rm li button:hover { background: #e4e7ec; }
.clr { border: 0; background: none; color: #1d4ed8; font: 600 13.5px Inter, sans-serif; text-decoration: underline; cursor: pointer; }
.rm button:focus-visible { outline: 2px solid #2563eb; outline-offset: 1px; }
const ul = document.querySelector('.rm ul');
ul.addEventListener('click', e => {
  const b = e.target.closest('button'); if (!b) return;
  const li = b.parentElement, next = li.nextElementSibling || li.previousElementSibling;
  li.remove();
  (next ? next.querySelector('button') : document.querySelector('.clr')).focus();
});
document.querySelector('.clr').addEventListener('click', () => { ul.innerHTML = ''; });

08. Verified and Role Badges

ProfileIcon

Inline badges that sit next to a name: a verified check, an Admin role and a Top seller label with a star. The verified check is an SVG with an accessible title, so the verification is announced rather than only shown as a blue tick.

View code
<ul class="vb">
  <li><b>Clayworks Studio</b><svg viewBox="0 0 24 24" role="img" aria-label="Verified"><path d="M12 2l2.4 2.2 3.2-.4.9 3.1 2.8 1.6-1 3 1 3-2.8 1.6-.9 3.1-3.2-.4L12 22l-2.4-2.2-3.2.4-.9-3.1L2.7 15.5l1-3-1-3 2.8-1.6.9-3.1 3.2.4z"/><path d="M8 12.3l2.6 2.6L16.3 9" class="ck"/></svg></li>
  <li><b>Jordan Lee</b><span class="role">Admin</span></li>
  <li><b>Fern &amp; Co</b><span class="top">&#9733; Top seller</span></li>
</ul>
.vb { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; font: 15px Inter, system-ui, sans-serif; color: #fff; }
.vb li { display: flex; align-items: center; gap: 8px; }
.vb svg { width: 20px; height: 20px; fill: #3b82f6; }
.vb .ck { fill: none; stroke: #fff; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.role { padding: 2px 8px; border-radius: 6px; background: #1e2433; color: #c9d1e2; font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
.top { padding: 2px 9px; border-radius: 999px; background: rgba(251,191,36,.15); color: #fcd34d; font-size: 12px; font-weight: 700; }

About these CSS Badges

Badges are text

A badge should be real text that makes sense on its own, such as Paid or 3 items. Colour helps scanning, but the word carries the meaning for screen readers and people with colour vision deficiency.

Counts on buttons

When a badge shows a number on an icon button, include the number in the button's accessible name, for example Cart, 3 items, and hide the visual badge from screen readers so it is not read twice.

Contrast in small text

Badges use small text, so contrast matters more. Soft badges here pair a light tint with a dark text colour of the same hue, which keeps contrast high without heavy blocks of colour.

Removable tags

A remove button inside a tag needs a full name such as Remove filter: Cotton. After removing a tag, move focus to the next one so keyboard users stay in place.

Frequently asked questions

Do badges need JavaScript?

Only the removable tags demo.

How do I show 99+?

Cap the number in your template and say more than 99 in the accessible name, as the count demo does.

Can badges animate?

Keep animation small, like the Live pulse, and stop it with prefers-reduced-motion.