EmpireUI
Get Pro
← Blog7 min read#glassmorphism#tag-cloud#tailwind-css

Glassmorphism Tag Cloud: Topic Chips with Blur Background

Build a glassmorphism tag cloud with frosted-glass topic chips, blur backdrops, and Tailwind v4. Real code, zero fluff — just tokens and CSS you'll actually ship.

Abstract blurred colorful light orbs creating a dreamy frosted-glass backdrop for UI components

Why Tag Clouds Deserve the Glassmorphism Treatment

Honestly, tag clouds are one of the most underrated UI patterns. Everyone builds them as a flat list of grey pills and calls it a day. But topic chips are navigational anchors — they guide users into your content taxonomy, and they should look like they belong to the same world as the rest of your design.

Glassmorphism gives you something interesting here. The frosted-glass surface lets the colorful category-background bleed through the chip, which means each tag visually connects to its context instead of floating in a void. It's subtle, but users notice the coherence.

This guide walks through building a real React tag cloud component using backdrop-filter: blur(), rgba(255,255,255,0.12) backgrounds, and Tailwind v4.0.2 utility classes. We're not going for decoration — we're going for a component you'd actually ship.

What Makes a Tag Cloud 'Glassmorphic' vs Just Transparent

The distinction matters. A lot of developers slap opacity: 0.5 on a div and think that's glass. It's not. Real glassmorphism has three non-negotiable properties: a semi-transparent background color (usually rgba(255,255,255,0.12) to rgba(255,255,255,0.20) for light chips), a backdrop-filter: blur() that blurs what's *behind* the element, and a very thin border at roughly 1px solid rgba(255,255,255,0.25).

For tag chips specifically, you also want to think about size. A chip that's 28–32px tall with 10–14px horizontal padding sits in the sweet spot. Too small and the blur effect looks like a bug. Too large and it competes with your primary content. We'll use py-1.5 px-3 in Tailwind, which lands at about 30px height with default line-height.

If you want to understand the theory before writing any code, what is glassmorphism covers the origin, the Apple/Microsoft influence, and the exact CSS properties involved. Worth 5 minutes if you're new to the style.

Setting Up the Blur Background Canvas

Tag clouds don't work in a vacuum. The blur effect only reads correctly when there's something colorful behind it — a gradient, an image, colored blobs. A tag chip over a white background just looks like a low-contrast button. That's it.

The easiest approach is a gradient canvas with a few position: absolute color blobs. Here's the setup in Tailwind v4.0.2:

export function GlassTagCanvas({ children }: { children: React.ReactNode }) {
  return (
    <div className="relative overflow-hidden rounded-2xl min-h-[260px] p-8 bg-gradient-to-br from-indigo-600 via-purple-500 to-pink-500">
      {/* Soft color blobs for depth */}
      <div className="absolute -top-16 -left-16 w-64 h-64 rounded-full bg-blue-400 opacity-40 blur-3xl" />
      <div className="absolute -bottom-12 right-8 w-48 h-48 rounded-full bg-rose-400 opacity-30 blur-3xl" />
      <div className="relative z-10 flex flex-wrap gap-2">
        {children}
      </div>
    </div>
  );
}

The blur-3xl utility (which maps to blur: 64px) on the blobs is what creates that soft bokeh look. The tags themselves sit in a relative z-10 flex container with an 8px gap (gap-2) between chips. Don't skip the overflow-hidden on the outer wrapper or those blobs will escape their container and mess up your layout.

Building the Glassmorphism Tag Chip Component

With the canvas in place, the chip itself is straightforward. The trick is that backdrop-filter has to be paired with a non-opaque background — if you use a fully transparent background, the blur still applies but you lose the frosted tint that makes the glass effect legible.

interface TagChipProps {
  label: string;
  href?: string;
  count?: number;
  size?: 'sm' | 'md' | 'lg';
}

const sizeMap = {
  sm: 'text-xs py-1 px-2.5',
  md: 'text-sm py-1.5 px-3',
  lg: 'text-base py-2 px-4',
};

export function GlassTagChip({ label, href, count, size = 'md' }: TagChipProps) {
  const classes = [
    'inline-flex items-center gap-1.5 rounded-full',
    'bg-white/[0.12] backdrop-blur-md',
    'border border-white/25',
    'text-white font-medium tracking-wide',
    'hover:bg-white/[0.22] hover:border-white/40',
    'transition-all duration-200 cursor-pointer select-none',
    sizeMap[size],
  ].join(' ');

  const content = (
    <>
      {label}
      {count !== undefined && (
        <span className="text-white/60 text-[10px] font-normal">({count})</span>
      )}
    </>
  );

  if (href) {
    return <a href={href} className={classes}>{content}</a>;
  }

  return <span className={classes}>{content}</span>;
}

A few things worth calling out: bg-white/[0.12] is Tailwind's arbitrary opacity syntax — it maps directly to rgba(255,255,255,0.12). The backdrop-blur-md gives you blur(12px), which is enough to see the effect without turning everything into mud. The hover state bumps opacity to 0.22 and strengthens the border, which gives tactile feedback without a color change.

The size prop is optional but it matters for tag clouds where you want to visually rank topics by frequency. Larger chips catch the eye first. You can also drive size from a weight prop and map tag frequency to sm/md/lg automatically — just a count > 50 ? 'lg' : count > 20 ? 'md' : 'sm' ternary gets you 80% of the way there.

Assembling the Full Tag Cloud With Frequency Weighting

Real tag clouds need data. Let's wire up a component that takes an array of tag objects, sorts by count, and renders the glass chips in a randomized wrap layout. The randomization is optional but it breaks the too-regular look that sorted chips produce.

interface Tag {
  label: string;
  slug: string;
  count: number;
}

function getChipSize(count: number, max: number): 'sm' | 'md' | 'lg' {
  const ratio = count / max;
  if (ratio > 0.66) return 'lg';
  if (ratio > 0.33) return 'md';
  return 'sm';
}

export function GlassmorphismTagCloud({ tags }: { tags: Tag[] }) {
  const max = Math.max(...tags.map((t) => t.count));
  // Shuffle for visual variety without affecting semantic order
  const shuffled = [...tags].sort(() => Math.random() - 0.5);

  return (
    <GlassTagCanvas>
      {shuffled.map((tag) => (
        <GlassTagChip
          key={tag.slug}
          label={tag.label}
          href={`/blog/tag/${tag.slug}`}
          count={tag.count}
          size={getChipSize(tag.count, max)}
        />
      ))}
    </GlassTagCanvas>
  );
}

This pattern works well on blog sidebars, category pages, and search results. The Math.random() - 0.5 sort produces a different layout on each mount — if that bothers you (SSR hydration mismatch, for instance), replace it with a stable deterministic shuffle using the tag slug as a seed.

For SSR in Next.js specifically, move the shuffle into a useMemo with an empty dependency array so it only runs client-side, or pre-shuffle the data server-side and pass it as a prop. Hydration mismatches with random values are a known pain point.

CSS-Only Glass Tags Without Tailwind

Not everyone's using Tailwind. If you're on vanilla CSS or CSS Modules, here's the equivalent without any framework dependency.

.glass-tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.12);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.25);
  color: #ffffff;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.025em;
  cursor: pointer;
  transition: background 200ms ease, border-color 200ms ease;
  text-decoration: none;
}

.glass-tag-chip:hover {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.40);
}

.glass-tag-chip--sm { font-size: 0.75rem; padding: 4px 10px; }
.glass-tag-chip--lg { font-size: 1rem; padding: 8px 16px; }

The -webkit-backdrop-filter prefix is still necessary for Safari as of mid-2026. Don't skip it — Safari's market share on Mac and iOS makes it non-optional if you care about your users. Also note backdrop-filter has no effect if the browser can't composite it; always test in a real browser, not just Chrome DevTools device mode.

One common gotcha: backdrop-filter is ignored if the parent has overflow: hidden set *and* will-change: transform isn't on the element. This trips people up constantly. If your blur isn't rendering, that's the first thing to check.

Accessibility and Dark Mode Variants

Glass chips over a colorful background can fail WCAG AA contrast ratios fast. rgba(255,255,255,0.12) with white text on a mid-tone purple background gives you roughly 4.5:1 — barely passing. If your background gets lighter (like a pastel gradient), you'll need to darken the chip or the text color.

For dark mode, the pattern inverts. Instead of rgba(255,255,255,0.12) you'd use rgba(0,0,0,0.25) with lighter text — maybe text-white/90 — over a dark gradient. The theme-toggle-react article covers how to wire a system-aware theme toggle so you can swap the canvas gradient and chip colors automatically.

Also worth knowing: backdrop-filter elements can cause repaints on scroll in some browsers. If you're rendering a tag cloud inside a sticky sidebar or a scroll container, profile it. In most cases it's fine, but with 40+ chips on a slow device it can get choppy. A will-change: backdrop-filter on the chip sometimes helps, sometimes doesn't — measure before committing.

Where Glass Tag Clouds Fit in Real Interfaces

The pattern works best when you have a visually rich background already established. Blog pages with hero images, dashboard headers with gradient bands, and portfolio case-study pages are natural fits. Trying to use glass chips on a white/neutral background misses the point — you'll end up with something that looks broken rather than designed.

Want to push it further? Pair your tag cloud canvas with the particles background react pattern. Animated particle fields behind glass chips create that immersive feel you see in product landing pages. Just keep the chip count under 20 or the canvas gets cluttered.

If you're comparing style systems more broadly, glassmorphism vs neumorphism is a useful read — the two styles solve different problems and sometimes the tag cloud use case actually calls for the soft-shadow neumorphic approach instead, especially on light-mode interfaces. And if you're evaluating the best free glassmorphism components available today, Empire UI's glass chip set is worth benchmarking against — fully typed, zero dependencies beyond React and Tailwind.

FAQ

Why isn't my backdrop-filter blur showing up?

Three common causes: the element's parent has overflow: hidden without isolation: isolate, there's no opaque or semi-transparent background on the chip itself (fully transparent backgrounds skip compositing), or you're viewing in a browser/mode that doesn't support it. Add -webkit-backdrop-filter for Safari and confirm background isn't transparent.

What's the right blur value for tag chips — blur(8px), blur(12px), or more?

blur(12px) is the sweet spot for small chips. At blur(8px) the effect is subtle enough that some users won't notice it. At blur(20px) and above, small chips start to look washed out because there's not enough surface area to show the blurred gradient beneath. Stick to 10–14px for chips, 20–40px for larger card surfaces.

How do I handle the SSR hydration mismatch when using Math.random() to shuffle tags?

Move the shuffle to useEffect or useMemo so it only runs on the client after mount. Alternatively, shuffle server-side with a deterministic algorithm (e.g., using the current date as a seed) and pass the pre-shuffled array as a prop. The mismatch only happens when server-rendered HTML differs from the first client render.

Can I make the glass chips clickable and accessible at the same time?

Yes. Use <a href> for navigation chips — that gives you keyboard focus, enter-to-activate, and correct ARIA semantics for free. For filter chips (toggle behavior), use <button> with aria-pressed to communicate the selected state to screen readers. Don't use <span onClick> for either case.

What background opacity works for dark vs light themes?

Light theme with dark background canvas: rgba(255,255,255,0.12) to rgba(255,255,255,0.20) with white text. Dark theme on a lighter canvas: rgba(0,0,0,0.20) to rgba(0,0,0,0.35) with near-white text. Always verify contrast ratio with the specific gradient you're using — generalized values won't guarantee WCAG compliance.

How many tags is too many for a glassmorphism tag cloud?

Performance-wise, backdrop-filter composites each chip separately, so 50+ chips on a single page can cause scroll jank on mid-range mobile. UX-wise, anything over 25–30 tags becomes hard to scan. If you have a large taxonomy, consider grouping by category and showing top N per group, or paginating the cloud with a 'show more' toggle.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Glassmorphism Sidebar Navigation: Frosted Glass Done RightGlassmorphism Dropdown Menu: Frosted Select ComponentGlassmorphism Card Hover: Blur Depth Change on Mouse EnterGlassmorphism vs Solid Design: Which Converts Better in 2027