01. Tailwind CSS No Search Results Empty State (opens in a new tab)
data-state attribute on the root, and group-data-[state=found]/root: swaps the empty block for the result list — the script never touches a class.20 Tailwind CSS empty states for zero-result searches, empty carts and wishlists, inbox-zero inboxes, no-data dashboards and tables, and 404 / 403 / 500 / offline pages — spanning onboarding checklists, error-with-retry panels, skeleton-to-empty transitions, a live-region announcement build, dark-mode and pure-CSS variants, and inline micro empty states. Scoped under .tes-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Related20 CSS Empty States30 CSS Notifications25 Tailwind CSS FAQ Accordion
Premium icon pickPartner: Nucleo
Affiliate linkNeed icons for the components you just copied?
You just browsed 20 Tailwind empty states. Nucleo is a library of 44,000+ SVG icons in five families (Core, UI, Sharp, Pixel and Micro Bold), with web, Mac and Windows apps to manage them. The All Icons Bundle is $149 with lifetime updates.
Nucleo. We may earn a commission, at no extra cost to you.
data-state attribute on the root, and group-data-[state=found]/root: swaps the empty block for the result list — the script never touches a class.conic-gradient, and a template picker as the secondary path. Ticks are aria-pressed buttons, so every row styles itself with has-aria-pressed: — strike-through, colour and check icon included.starting: — the moment of zero is the reward, so it gets the only animation on screen.<table> throughout: the empty message lives in a single colspan cell, layered over ghost rows so the user still sees the shape data will take. Add an invoice and the empty row hides itself with has-[[data-row]]: — the table decides when it's empty, not the script.has-[#id:checked]:[&_[data-x=no]]:hidden on the wrapper, active-filter chips appear beside the title, and a CSS counter reports "Showing N of 5" because counters skip display:none rows.h-(--h), the line draws via stroke-dashoffset, cohort cells fade up — all from a single data-live attribute.[-webkit-text-stroke]; the retry button's three states are data-busy: and data-done: variants.group-has-[#id:checked]/w:.starting:, all without JavaScript. Each tab has its own reason-specific empty copy, because "no approvals" and "no mentions" mean different things.error → loading → ok, all three driven by data-state on the panel and read with group-data-[state=…]/p:, so the grid never jumps.online/offline events, so it reacts to your network too.data-pending attribute.[counter-increment:tes14] before:content-[counter(tes14,decimal-leading-zero)].aria-expanded:) holding a radio segmented control; picking 7, 30 or 90 days rewrites the copy and animates the retention meter in the sidebar.aria-busy tells screen readers when the wait is over. Refetch replays it.focus:not-sr-only, a visually hidden role="status" region that announces "No catalogue entries for…" or "3 entries found", focus moved to the results heading, and suggestion chips that re-run the search.<span> with percentage geometry, so the drawing scales with its container.@custom-variant dark reads them with :has() (and prefers-color-scheme for System), and @theme inline tokens flip in one place — no JavaScript, no flash.@container and @max-2xs:. One icon, one line, one action, every time.aria-label such as Remove status: open. The empty message is driven by the fact that nothing matched, not by the chips, so the copy stays honest: it names the scope that was searched and offers a one-click Clear all filters reset. The detail developers skip is that the reset must actually clear state, not just hide the message — in a real app wire the Clear button to reset your query params or state, then re-run the fetch. Keep the chips inside a real <ul> so each is a list item, mark the result region with role=status so the count change is announced, and give every remove button a visible focus-visible ring so a keyboard user can delete filters one at a time.peer-checked or has-[:checked] variant. For the filter case, checking a sidebar box narrows the list and reveals the empty copy entirely through CSS. The trap is specificity and source order: a peer-checked rule only wins if the peer is a previous sibling in the DOM, so the input has to come before the element it styles. Also remember pure-CSS state does not survive a reload and is not readable by your app logic, so use it for self-contained demos and disclosure UI — not for state your backend or router needs to know about.colspan set to the number of header columns — otherwise the cell collapses under column one and the layout looks broken. Keep the <thead> rendered so the user still sees what the columns would hold, and put the message row inside <tbody>. The common mistake is conditionally hiding the table and rendering a separate empty block, which throws away the header context and causes a layout jump when data arrives. For assistive tech, the message cell reads as a normal table cell; if you want it announced on refresh, wrap the message text in an element with role=status so a fetch that returns zero rows is spoken, not silent.aria-live region wrapping the results area, set to polite, so when a search resolves to nothing the message No records match your query is announced without stealing focus. Three rules matter. First, the live region must already be in the DOM before the text changes — if you insert the whole region at the same moment, many readers miss it. Second, keep the announcement short and specific; do not read the entire illustration. Third, don't trap focus on the empty card — a focus-revealed skip link lets keyboard users jump straight to the search input to try again. Decorative SVG art gets aria-hidden=true so it is skipped entirely, and the heading stays a real <h2> so the empty state appears in the document outline rather than being an orphaned block of text.motion-reduce so it stops for users who ask for reduced motion. Keep the skeleton's row count and spacing close to the real content's so there is no layout jump when either the data or the empty card takes its place.dark: variant — for example bg-white with dark:bg-slate-900, and muted text as text-slate-500 with dark:text-slate-400. The piece developers forget is the illustration: a decorative icon or ghost shape that looks right on white often disappears on a dark panel, so its stroke and fill need dark: overrides too, not just the card background. Demo 19 also ships a three-way System / Light / Dark switch, which means your toggle has to cover the System case — that is driven by the OS prefers-color-scheme media query, while the explicit choices set a class on the root. Decide up front whether dark: keys off the media query or a class and configure Tailwind's dark mode to match, or the manual toggle and the system default will fight each other.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 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 for article pull quotes, testimonial cards, docs callout admonitions, statement banners, and social-post embeds. Covers giant decorative quotation marks, minimalist hairline and classic left borders, animated gradient and glowing neon borders, glassmorphism and aurora backgrounds, brutalist cards, speech bubbles, thick accent rails, highlighted mark text, newspaper multi-column layouts, an expandable read-more disclosure, a scroll-snap testimonial slider, and one-click copy.