20 CSS Ribbons

20 hand-coded CSS ribbons for e-commerce product cards, SaaS pricing tables, blog thumbnails, open-source project pages, and coupon campaigns. Covers corner sale and sold-out bands with wrapped tails, the GitHub fork-me ribbon rebuilt without an image, swallowtail V-cut banners, most-popular pricing bands, award rosettes, certificate seals, coupon tear-off edges, and a single-element clip-path build. 16 are pure CSS with no JavaScript. Scoped under .rib-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

16 pure CSS4 light JSPublished

Related30 CSS Badges24 CSS Tags & Chips18 CSS Product Cards21 CSS Pricing Tables

Corner Sale Ribbon for Product Cards — preview
01 / 20Pure CSS

Corner Sale Ribbon for Product Cards

The corner ribbon every storefront ships: a 45° discount band flush across the top-right of a product card, clipped perfectly to the card's rounded corner. The geometry is parametric rather than hardcoded — one --rib-01-size value drives the corner box, the band length and the offset, so the same ribbon works on a 200px thumbnail and a 600px hero. A one-shot sheen sweeps the band on load and replays on hover.

Published

Sold Out Corner Ribbon Overlay — preview
02 / 20Pure CSS

Sold Out Corner Ribbon Overlay

Sold out is a state, not a sticker. Toggle the checkbox and one :has() selector re-styles the entire card at once: the photo desaturates and dims, the price greys, the button disables, and a frosted translucent band slides across the corner with backdrop-filter so the product stays visible underneath — the shopper can still see what they missed. Zero JavaScript; the checkbox is the state machine.

Published

Bestseller Corner Ribbon — preview
03 / 20Pure CSS

Bestseller Corner Ribbon

A corner ribbon that is tied on rather than printed on. Demo 01 clips its band flush to the card; this one crosses the top-left corner at 45° and keeps going, overhanging both card edges, with two darker triangles folding under the card where a real ribbon would disappear. Overhang plus fold is what makes it read as a physical band. Gold-foil gradient, slow animated shine, and a tilt that deepens on hover — all from two pseudo-elements and one rotate.

Published

Fork Me on GitHub Corner Ribbon — preview
04 / 20Pure CSS

Fork Me on GitHub Corner Ribbon

The developer-portfolio classic, rebuilt for 2027: no 6 KB PNG, no SVG octocat, no position: fixed that breaks in a scroll container. A single anchor, two dashed stitch lines drawn with border, an inline SVG glyph, and a hover state that pushes the whole band a couple of pixels further out of the corner. Left and right variants share one rule set — the side is a custom property.

Published

Notched Corner Badge for Blog Cards — preview
05 / 20Pure CSS

Notched Corner Badge for Blog Cards

Most freshness badges are stickers laid on top of the artwork. This one is a hole. The thumbnail's corner is chamfered away with clip-path, and the label sits in the negative space as a separate tab with a real gap around it, so the badge reads as slotted through the card rather than printed on it. One registered @property length drives the notch and the tab together, which makes a clip-path chamfer animatable — hover a card and the corner opens. Underneath, freshness is a quantity, not a sticker: a hairline meter whose length and colour both interpolate from one number.

Published

Swallowtail V-Cut Ribbon Banner — preview
06 / 20Pure CSS

Swallowtail V-Cut Ribbon Banner

The swallowtail — a section header with a V notched into each end, like a pennant. One clip-path polygon cuts both ends, and because every point is written against a single --rib-06-v property, notch depth is one number you can theme, animate, or shrink on mobile. The band genuinely overhangs the sheet and its fold tails attach flush to the outer ends, so it reads as tied around the page — the detail that separates a wrapped ribbon from a coloured rectangle with decorations near it. Flat ink, no gloss: the only shading is a 3px darker edge that the clip follows.

Published

Skewed 3D Ribbon Header Banner — preview
07 / 20Pure CSS

Skewed 3D Ribbon Header Banner

Match-day energy: three skewed bands stacked in real 3D space with perspective, animated hazard stripes scrolling behind the headline, and a neon edge that bleeds through the layers. The key move is the counter-skew — the band is skewed, the text inside is skewed back by the same angle, so the type stays upright and readable while the shape stays aggressive.

Published

Most Popular Pricing Card Ribbon — preview
08 / 20Pure CSS

Most Popular Pricing Card Ribbon

The conversion workhorse: a horizontal ribbon wrapped across the top of the recommended plan, overhanging both edges with folded ends tucked behind the card. Modern touches throughout — :has() lifts the whole row's shadow when any card is hovered, the featured card is elevated in the grid with a negative block margin, and the ribbon text is bound to the plan with aria-describedby so it is announced, not just seen.

Published

Annual Savings Ribbon Badge — preview
09 / 20Pure CSS

Annual Savings Ribbon Badge

A monthly/annual switch where the savings flag is part of the interaction, not a static sticker: choose Annual and the ribbon pops with a spring, the price rolls over, and the flag's little pointer stays anchored to the tab it belongs to. Built from two radio inputs and :has() — the entire state machine is the checked radio, so it works with no JavaScript at all and is keyboard-navigable by default.

Published

Bookmark Flag Tag with Notched Edge — preview
10 / 20Pure CSS

Bookmark Flag Tag with Notched Edge

The vertical bookmark: a flag that hangs from a rail with a V notched out of its bottom edge. Five points of clip-path make the shape, one --rib-10-notch variable controls the bite, and a companion price tag shows the other half of the technique — a punched hole made by subtracting a circle with mask-composite. Selecting a flag is pure CSS: hidden checkboxes and :has().

Published

Hanging Ribbon Bookmark — preview
11 / 20Light JS

Hanging Ribbon Bookmark

A cloth bookmark that drops over the top edge of the card when you save the article — it falls, overshoots, and settles with a two-beat pendulum swing, exactly like a real ribbon marker in a book. One toggle button drives it, the state is aria-pressed (not a class), and the whole animation is transform-origin: top center plus five keyframes.

Published

Award Rosette Ribbon with Tails — preview
12 / 20Pure CSS

Award Rosette Ribbon with Tails

A proper rosette: a pleated scalloped disc built from a 24-point clip-path and a repeating-conic-gradient, with two ribbon tails dangling beneath and a slow specular sweep across the metal. Every value is derived — the scallop polygon comes from trigonometry, the pleats from one repeating gradient — so it stays sharp at any size and weighs nothing.

Published

Certificate Seal Ribbon Rosette — preview
13 / 20Pure CSS

Certificate Seal Ribbon Rosette

The foil seal that makes a certificate feel issued rather than printed. A registered @property <angle> rotates a multi-stop conic gradient so the gold genuinely catches light as it turns — an effect that is impossible with an unregistered custom property, because gradients cannot be transitioned but their angle variable can. Sits on a real certificate layout with an engraved guilloche border.

Published

3D Folded Ribbon Banner — preview
14 / 20Pure CSS

3D Folded Ribbon Banner

Paper physics, built rather than drawn. The ribbon is five real facets in one preserve-3d space — a flat face, two wings creased back on the Y axis, and two tails folded down on the X axis — so the ends genuinely turn away from the camera instead of being triangles pointing at it. Each facet is lit by the same light source, its value derived from the accent with color-mix(), and a blurred contact shadow plants the whole strip on the card. It unfolds on load from a hinge at its pinned edge and settles at three degrees, never quite flat.

Published

Coupon Ribbon with Tear-Off Edge — preview
15 / 20Light JS

Coupon Ribbon with Tear-Off Edge

A voucher with a real perforation: semicircular bites punched out of both edges and a dashed tear line between the stub and the body. The notches are carved with mask layers rather than covered with background-coloured circles, so the coupon sits correctly on any page colour, gradient or photograph. Copying the code is one button with proper success feedback.

Published

Stitched Edge Ribbon — preview
16 / 20Pure CSS

Stitched Edge Ribbon

Craft and handmade brands live or die on texture. This ribbon is stitched — a dashed thread line set inside the band with outline-offset, a woven twill pattern from two crossed repeating gradients, real paper grain from an inline SVG turbulence filter, and a slightly tilted seat so it looks pinned rather than pasted.

Published

Hover Unfold Animated Ribbon — preview
17 / 20Pure CSS

Hover Unfold Animated Ribbon

A compact corner tab that unfurls into a full ribbon when the card is hovered or focused — and it genuinely animates the shape, not just an opacity fade. A registered @property percentage lives inside the clip-path, which is the only way to transition a polygon smoothly. Keyboard users get the same reveal through :focus-within.

Published

Slide-In Ribbon Notification — preview
18 / 20Light JS

Slide-In Ribbon Notification

The announcement ribbon that drops from the top of the viewport — built on the platform's own popover API, so it lives in the top layer above everything without a z-index war. The entry animation uses @starting-style and transition-behavior: allow-discrete, the pair that finally makes it possible to animate an element in and out of display: none with pure CSS.

Published

Waving Flag Ribbon Animation — preview
19 / 20Pure CSS

Waving Flag Ribbon Animation

A flag that actually ripples: fourteen vertical slices, each running the same keyframe with a progressively offset animation-delay, so a wave travels along the cloth. Shading is part of the animation — every slice brightens as it rises and darkens as it falls — which is what turns a row of moving rectangles into fabric catching light.

Published

Clip-Path Polygon Ribbon — preview
20 / 20Light JS

Clip-Path Polygon Ribbon

The finale and the reference implementation: three ribbon archetypes — banner, corner, bookmark — each drawn by a single element with a single clip-path, no pseudo-elements, no wrappers, no overflow tricks. Every coordinate is a registered @property, so you can drag the sliders and watch the polygon interpolate live, then copy the exact CSS the playground produces.

Published

FAQ

Frequently asked questions

How do I make a corner ribbon that wraps around a card?
The convincing version has three parts, and most snippets ship only the first. The band itself is a rotated element pinned to the corner: give the card position: relative and overflow: hidden, then absolutely position the ribbon and rotate it 45 degrees. Make it roughly 1.5× wider than the corner it crosses so the ends run past the card edge and get clipped, rather than stopping short and revealing that it is a rectangle. Second, the tails — the small darker triangles that appear to fold behind the card — are what sell the wrap. Build them with ::before and ::after using border-color: transparent triangles or a small clip-path, filled with a darker shade of the ribbon colour derived via color-mix(in oklch, var(--ribbon) 70%, black). Third, the shadow: a tight filter: drop-shadow() follows the actual ribbon shape including its clipped edges, whereas box-shadow draws a rectangle around the unrotated box and looks wrong. One trap worth knowing: overflow: hidden on the card is what clips the ribbon ends, but it also clips any tooltip or dropdown inside that card — if you need both, use overflow: clip on the axis you actually want clipped.
How do I build the GitHub 'Fork me on GitHub' ribbon in pure CSS?
The original was a 10KB PNG served from GitHub's own CDN, and it is still widely copied that way. The CSS version is under 40 lines, scales perfectly on any display, retheme-able in one line, and costs no network request. The structure is an <a> positioned fixed (or absolute within a relatively positioned hero) at the corner, rotated 45 degrees, given generous horizontal padding so the band extends past both card edges, with the text centred inside. Two details make it read as authentic: the thin light border inset from each long edge — a single box-shadow: inset 0 1px 0 rgba(255,255,255,.3), inset 0 -1px 0 rgba(0,0,0,.2) handles both — and a slight letter-spacing on uppercase text, which is what the original bitmap had. For accessibility, this is a real link, so it needs discernible text: the visible label "Fork me on GitHub" is usually enough, but if you shorten it to an icon, add an aria-label. Also keep the tap target at least 44 by 44 CSS pixels — a thin rotated band is easy to make too small to hit on mobile.
What is the modern single-element way to build a ribbon?
clip-path: polygon() lets you cut the entire ribbon silhouette — including the V-notch or swallowtail ends — from one element, with no pseudo-elements at all. A basic banner with notched ends is clip-path: polygon(0 0, 100% 0, calc(100% - 18px) 50%, 100% 100%, 0 100%, 18px 50%): the two mid-edge points pull each end inward to a point. That is the whole shape. The advantages over the classic pseudo-element build are real — one element instead of three, no z-index juggling for the tails, and the shape animates smoothly because clip-path is interpolatable between polygons with the same number of points. Two caveats decide when to use it. First, clip-path clips everything in the element including its own ::before and ::after, so decorative extras have to live on a wrapper — this catches people out constantly. Second, a clipped element cannot cast a box-shadow, because there is no box left to shadow; use filter: drop-shadow() on the parent instead, which follows the clipped silhouette. Demo 20 is built this way as the reference.
How do I add a 'Most Popular' ribbon to a pricing card?
This is the highest-converting ribbon on the list, and the mistakes are usually structural rather than visual. Place the band across the top of the featured card, either full-width flush with the card's top edge or as a floating pill overlapping the boundary. If it overlaps, the card needs overflow: visible and enough padding-block-start that the ribbon does not collide with the plan name — a common bug when the card is inside a grid with align-items: stretch. Give the featured card a slight scale(1.03) and a stronger shadow so the ribbon reinforces a hierarchy the layout already establishes rather than doing the work alone. On semantics: the ribbon is real information, not decoration, so it belongs in the document as text — not as a ::before with content: "Most Popular", which screen readers may skip and which no user can select or translate. Generated content is fine for the ribbon's tails; it is wrong for its label. If the card is a radio-selectable plan, wire the highlight from state with .card:has(:checked) so the ribbon follows the actual selection instead of being hardcoded to one plan.
How do I create a coupon ribbon with a tear-off dashed edge?
The notched ticket edge comes from a repeating radial gradient used as a mask, not from drawing circles. Apply mask-image: radial-gradient(circle 8px at 8px 50%, transparent 98%, black 100%) with mask-size set to the notch spacing and mask-repeat: repeat-y — each repeat punches a semicircular bite out of the edge. Because it is a mask rather than an overlay, the notches are genuinely transparent, so whatever sits behind the coupon shows through; an overlay of background-coloured circles looks identical on a flat background and breaks instantly on a gradient or photo. Pair it with a border-left: 2px dashed on the tear line. Two production notes. Safari still wants the -webkit-mask-image prefix, and it is one extra line to keep the effect from vanishing entirely there. And if the coupon is interactive — a copy-code button, a claim action — that control must be a real <button>, not the whole card, so keyboard users get a single predictable target rather than a large ambiguous one.
Are CSS ribbons accessible, and how should they be marked up?
The rule that resolves most cases: if the ribbon carries information, it is content; if it is ornament, it is decoration. A "Sale", "Sold Out" or "Most Popular" ribbon is content — put the words in the DOM as real text so they are announced, selectable and translatable. Only the folded tails and shading belong in ::before / ::after. Three further requirements. Never rely on colour alone: a greyed-out card means nothing to a colour-blind user, so "Sold Out" must appear as text, and the underlying control should be genuinely disabled or replaced with an enabled alternative like "Notify me" — a dimmed-but-clickable button is worse than no signal at all. Contrast still applies to rotated text: a 45-degree band does not exempt it from the 4.5:1 requirement, and light-on-bright-red is the usual failure. Finally, rotation must not be the only cue for meaning, and any animated ribbon — the unfold, the waving flag, the slide-in notification — needs a prefers-reduced-motion: reduce guard that settles it into its final visible state rather than removing it.
Which ribbon style fits which kind of page?
Match the ribbon to the commercial intent of the surface. E-commerce product grids want corner ribbons — sale, bestseller, sold out — because they read at thumbnail size and survive being one card among twenty; that is why Shopify, WooCommerce and BigCommerce themes all ship variations of the same corner band. SaaS pricing tables want the horizontal top band, since "Most Popular" needs to be read before the price and a corner ribbon competes with the plan name. Blog and content cards suit the smaller notched corner badge, which marks New or Updated without overpowering a headline. Open-source project pages have their own convention in the fork-me corner ribbon, and deviating from it costs recognition. Award rosettes and certificate seals belong on trust surfaces — testimonial sections, comparison pages, about pages — where the visual weight is justified. Coupon ribbons carry the dashed tear-off edge because that shape is a learned signal for redeemable value. The general rule: the ribbon should be the second thing seen, after the product or the price. If it is the first, it is too heavy for its job.
How do these ribbons port to React, Vue, Svelte, Next.js and Tailwind?
The 16 pure-CSS ribbons port with no work — copy the markup and the scoped .rib-NN rules and they behave identically anywhere, because there is no JavaScript to place in a lifecycle. In component terms, the natural shape is a small presentational component taking a label and a variant: <Ribbon label="Sale" variant="corner" />, with the colour driven by a CSS custom property so themes stay in CSS rather than in props. The four demos with JavaScript — the hanging bookmark toggle, the coupon copy action, the slide-in notification, and one hover enhancement — wire into the usual hooks: useEffect with a returned cleanup in React, onMounted plus onBeforeUnmount in Vue, onMount returning cleanup in Svelte, and 'use client' for those four in the Next.js App Router while the other 16 render fine as server components. For Tailwind v4, padding, colour and positioning map to utilities directly; the parts with no utility equivalent — clip-path: polygon(), mask gradients, keyframes — belong in a @theme block or an arbitrary-value class. Each ribbon is roughly 1-3KB of CSS.

Related collections

30 CSS Badges preview

30 CSS Badges

30 hand-coded CSS badges for status indicators, notifications, membership tiers, live-data displays, and SEO / DevOps / financial dashboards — upload progress, typing indicator, transit line status, Core Web Vitals, ECG heartbeat, CI/CD build pipeline, countdown ring, live price ticker, keycap shortcut, wax seal, conference lanyard, and holographic collectibles. Copy-paste HTML and CSS, WCAG 2.2 accessible, MIT licensed.

20 CSS Banners & Alerts preview

20 CSS Banners & Alerts

20 hand-coded CSS banner and alert bars for e-commerce storefronts, SaaS dashboards, marketing sites, and compliance-bound products. Covers GDPR cookie consent with a real preferences panel, promo bars with countdown timers, free-shipping threshold progress, app-install smart banners, maintenance and incident status bars, newsletter signup bars, age verification gates, limited-stock urgency banners, live event announcements, browser upgrade notices, geo and currency switchers, sticky top announcement bars, bottom cookie bars, inline form validation alerts, icon-aligned alert banners, left-border accent alerts, diagonal stripe promos, full-width hero banners, animated gradient border alerts, and a text-wrapping laboratory that shows five strategies for the unbreakable string that breaks more banners than anything else. Every bar is scoped under a .ba-NN prefix for no-collision pasting, guards prefers-reduced-motion, carries the correct aria-live and role semantics, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

25 CSS Blockquotes preview

25 CSS Blockquotes

25 hand-coded CSS blockquote designs for SaaS testimonials, editorial pull quotes, documentation callouts, and developer engineering blogs: large decorative quotation marks with ::before / ::after glyphs, Tailwind CSS blockquote component, pure CSS callout / admonition boxes for Docusaurus / Mintlify / Nextra documentation, responsive center-aligned quote banners, modern minimalist left-border editorial style, animated CSS gradient border, classic left border, brutalist high-contrast, glassmorphism frosted card, glowing neon border, article pull quote with text-wrap, speech bubble chat-style, thick vertical accent rail, customer testimonial cards with avatar and star rating, inline highlighted text, marker highlight underline, floating drop-shadow, aurora gradient background, Twitter / X card style, multi-column newspaper editorial, IDE code-comment style, Markdown-compatible defaults, JavaScript testimonial quote slider, expandable read-more, and one-click copy button. 22 Pure CSS + 3 Light JS (slider, expand, copy). Every design scoped under .bq-NN prefix for no-collision pasting, prefers-reduced-motion guarded per WCAG 2.3.3, WCAG 2.2 AA accessible, framework-agnostic (React, Vue, Svelte, Astro, Next.js, Nuxt, Remix, SvelteKit).

Search CodeFronts

Loading…