Most card bugs I’ve run into had nothing to do with shadows or border radius. The markup looked fine with one card on the page and fell apart at twelve.
Product listings, blog indexes, profile grids and pricing tables all lean on the same pattern. Underneath it’s plain HTML and CSS putting an image, title, text and action into one container that gets styled and resized as a single unit. A grid of cards built with Flexbox or CSS Grid reflows from one column to many without a separate layout per screen.
Container queries became Baseline Widely available in August 2025, according to the web.dev Baseline digest (2025). That changed how cards get built, because a card can now restyle itself by the width of its parent instead of the width of the viewport.
What Is a CSS Card and What Parts Does It Have?
See the Pen
Modern css cards design by FlorinCornea (@FlorinCornea)
on CodePen.
Take away the shadow and the rounded corners and you still have a card, as long as the related content sits in one bounded container. Styled, aligned and sized together, that container is what separates a card from a loose stack of elements.
Cards repeat. A page rarely shows just one, so every decision about the container (width, spacing, shadow) has to survive being copied a dozen times inside a card-based layout.
Parts of a card
Each part of a full card maps to a specific element.
- The media goes in an
imgorpictureelement, usually shown first - The title is a heading, and whether it’s an
h2,h3orh4depends on the page outline - A
pelement holds the summary text - The footer carries metadata like date, author or price
- The action is an
aelement (or abutton) that leads somewhere
Source order matters more than it looks. Nomensa’s guidance on accessible cards puts the heading first in the HTML and uses Flexbox to show the image above it, so the heading still introduces the group for screen readers.
A minimal card looks like this:
<article class="card">
<img class="card-img" src="cable-car.jpg" alt="Cable car over a valley">
<div class="card-body">
<h3 class="card-title">Cable car routes</h3>
<p class="card-text">Three scenic lines, ranked by view and queue time.</p>
<a class="card-action" href="/cable-cars">See routes</a>
</div>
</article>Which HTML element should wrap a card?
For a self-contained item like a blog post or a product, article fits. It makes sense on its own, which is the whole test.
Cards that form a list are better as an li inside a ul, since screen readers then announce how many items the list holds. The two combine well: the li becomes the grid item and the article sits inside it.
A plain div is the fallback for purely visual groupings like stat tiles and promo boxes.
Card variants and the parts each uses
- A blog post card usually carries an image, title, excerpt, date and a “Read more” link
- Product cards swap in a name, price, rating and buy button
- Profile cards hold an avatar, name, role and social links
- Pricing cards need a plan name, price, feature list and call to action
- A horizontal card can be any of the above, with the image beside the text instead of above it
Product cards carry the most conversion pressure of the lot, and the details of designing a product card deserve their own guide.
How to Create a CSS Card Step by Step
See the Pen
Material Design – Responsive card by David Foliti (@marlenesco)
on CodePen.
A basic card goes together quickly once the markup is in place.
- Write the markup with the structure shown above.
- Style the container with a
background, a 1pxborder, aborder-radiusand abox-shadow. - Put padding on
.card-body, not on.card, so the image runs edge to edge. - Add
overflow: hiddento the card so the image follows the rounded corners. - Give the link or button a visible background, its own padding and a hover state.
.card {
max-width: 22rem;
background: #fff;
border: 1px solid #e3e6ea;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
overflow: hidden;
}
.card-body {
padding: 1rem 1.25rem;
}Shadow, radius and border values
See the Pen
CSS cards with drop-shadow effect by Jaroslaw Hubert (@jhkepa)
on CodePen.
The box-shadow shorthand takes horizontal offset, vertical offset, blur, spread and color, in that order.
Keep the color low in opacity. A box shadow generator lets you preview offset and blur before you commit to a value.
Radius follows the same logic. Pick one value and match it to your buttons and inputs. Try a few options in a border radius generator before you fix the number across every card.
Custom properties for card variants
Store the values that change between variants in custom properties. A featured card then needs one line of CSS instead of a copy of the whole rule.
.card {
--card-bg: #fff;
--card-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
background: var(--card-bg);
box-shadow: var(--card-shadow);
}
.card--featured {
--card-bg: #fff8e6;
}A fixed width on .card is the mistake that shows up most. It fights the grid track and breaks reflow on narrow screens, so use max-width and let the layout size the card.
The Computed tab in Chrome DevTools shows the box model for any card. Shadows take no layout space, so a large spread never pushes neighboring cards around.
Flexbox vs CSS Grid for Card Layouts
See the Pen
CSS Grid: Makeup Palettes by Olivia Ng (@oliviale)
on CodePen.
Use CSS Grid for the grid of cards and Flexbox for the inside of each card.
Grid controls rows and columns together, so cards line up in both directions. Flexbox runs on one axis, which suits the stack of image, text and button inside a single card.
| Method | Best for | Where it breaks |
|---|---|---|
| Flexbox with flex-wrap | Small sets, centered rows | With flex-grow, the last row stretches |
| Grid with auto-fit | Fluid grids with many cards | A lone card stretches across the row |
| Grid with auto-fill | Consistent card width at any count | Empty tracks keep their space |
| Grid with subgrid | Aligned image, title and button rows | Needs a row span that matches the children |
The difference between auto-fit and auto-fill only shows when a row holds fewer cards than the container could fit. MDN describes auto-fit as identical to auto-fill, except that empty tracks collapse once the items are placed.
That makes auto-fill the better default for card grids, which goes against the usual advice. Two cards in a wide container keep their normal width instead of stretching across the row.
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
gap: 1.5rem;
}Wrapping the minimum in min() stops the column from overflowing a narrow screen. The result is responsive design with zero breakpoints.
How to get equal-height cards
Cards in one grid row share the same height by default, because align-items defaults to stretch.
The visible gap is inside the card. Short text leaves the button floating mid-card.
- Set the card to
display: flex; flex-direction: column, then give the bodyflex: 1 - Or skip the growing body and push the action down with
margin-top: auto
How subgrid aligns inner card rows
See the Pen
Layout practice, using grid by Bart Veneman (@bartveneman)
on CodePen.
Subgrid became Baseline Newly available on 15 September 2023 and Baseline Widely available on 15 March 2026, according to Web Platform DX data. Firefox 71 shipped it in December 2019, Safari 16 followed in September 2022, and Chrome 117 and Edge 117 arrived in September 2023.
Without subgrid, a long title in one card pushes its button down while a neighbor’s button stays high. With subgrid, each card inherits the parent’s row tracks, so titles, text and buttons line up across the whole row.
Each card spans a fixed number of rows, set with grid-row: span 3, and grid-template-rows: subgrid on the card does the inheriting. The parent’s gap then controls the space between card sections.
SitePoint’s layout tutorial flags a catch. Putting container-type on the card itself breaks track inheritance, so the declaration moves to an inner wrapper.
Survey comments in the State of CSS 2024, as analyzed by Patrick Brosset, also mention bugs and performance problems with subgrid. Test it in every browser you support.
Media Queries or Container Queries for Responsive Cards?
See the Pen
Responsive News Card Slider by Muhammed Erdem (@JavaScriptJunkie)
on CodePen.
Use container queries for the card itself and media queries for page-level layout.
Media queries read the viewport width. A container query reads the width of the card’s parent, so one card design works in a sidebar, a three-column grid and a full-width hero without separate overrides.
.card-slot {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 480px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}A card cannot query its own width. The container is always an ancestor, so the grid cell or a wrapper element gets container-type: inline-size, and the card’s rules sit inside @container.
The page shell, navigation and number of columns stay with media queries. A card that switches between stacked and horizontal belongs to a container query. User preferences like reduced motion go back to media queries.
Chrome’s container query case studies describe Tokopedia finding its use case by looking for truncated text (ellipsis) on its site. Cards whose titles get cut off in narrow slots are the same signal.
As of the May 2026 Baseline digest, the support picture looks like this.
- Container queries landed in all major browser engines in 2023 (web.dev)
- Container queries became Baseline Widely available in August 2025 (web.dev Baseline digest, September 2025)
- Name-only container queries became Baseline Newly available with Chrome 148 in May 2026 (web.dev, 2026)
- Container style queries for custom properties became Baseline Newly available with Firefox 151 in May 2026 (web.dev, 2026)
Older tutorials still describe style queries as a Chrome and Edge feature. The May 2026 status replaces that claim, though Drupal core still waits for Firefox ESR before adopting them broadly, as The Droptimes reported in 2026.
Container queries have a cost too. Brosset’s read of the State of CSS 2024 comments found respondents calling them too complex.
Keep the non-queried layout usable wherever style queries are unsupported. MDN’s @container table and Can I use show current support before you ship.
How to Size and Crop Card Images
Give every card image the same aspect-ratio and crop it with object-fit: cover. The image box then stays identical across cards, whatever proportions the source files have.
.card-img {
display: block;
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}An aspect ratio is the width divided by the height, so 16 / 9 gives a wide box and 1 / 1 a square.
With cover the image fills the box and the overflow gets cropped. contain shows the whole image but leaves empty bands where proportions differ. object-position moves the focal point, for example center top for portraits.
Cropping has a cost. cover can slice off faces or product edges, so test the worst image in the set, not the best one.
A Cumulative Layout Shift score of 0.1 or less counts as good, according to web.dev, and images without dimensions are one of the causes it names.
Width and height attributes on the img, or the aspect-ratio above, reserve the space before the file arrives.
Add loading="lazy" to card images below the fold. Nomensa lists it next to file-size optimization as the basic image step for cards.
Keep it off the first visible row. Shopify’s theme documentation names lazy-loading the largest contentful paint image among the most common and damaging performance mistakes, and a first-row card image is often that element.
Cards without an image need a different top edge.
:has() lets the card style itself by its content: .card:not(:has(img)) { padding-top: 1.25rem; }
web.dev’s State of CSS 2024 recap listed :has() as Baseline Newly available. That label is out of date, since it reached Baseline Widely available in June 2026.
Hover, Focus and Transition States on Cards
See the Pen
Card Animation by Jon Yablonski (@jonyablonski)
on CodePen.
Animate transform and box-shadow with a short transition, and give keyboard focus the same visual change as mouse hover.
.card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
@media (hover: hover) {
.card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
}
}
.card:focus-within {
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
}
.card-action:focus-visible {
outline: 3px solid #1a5fb4;
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
}Using transform lifts the card without shifting its neighbors. Anything much longer than 0.2s to 0.3s feels sluggish on repeated hovers, so keep the transition short. :focus-within fires when an inner link has keyboard focus, and prefers-reduced-motion removes movement for people who ask for less of it.
:focus-visible reached Baseline Widely available on 14 September 2024, according to Web Platform DX. It draws the ring for keyboard focus and follows the browser’s own rules about when mouse focus needs one.
Draw that ring with outline, not with a shadow. Forced-colors mode drops shadows but keeps outlines.
Touch screens have no hover, so a hover-only cue does nothing there. The (hover: hover) query above keeps the lift on devices that can use it.
Flashier card hover effects such as image zoom or tilt follow the same pattern with different values.
A CSS transition generator helps compare easing curves before you settle on one.
How to Make a Whole Card Clickable Without Breaking Accessibility
Make the title the link and stretch it over the card with an ::after pseudo-element. The card gets a short link name and a full-size click area.
.card {
position: relative;
}
.card-title a::after {
content: "";
position: absolute;
inset: 0;
}
.card-meta a {
position: relative;
z-index: 1;
}You can wrap the card in an anchor, stretch a title link, or add a JavaScript click handler. CSS-Tricks walks through all three.
Sources disagree on the first one. The hyring.com card tutorial recommends wrapping the card in an anchor when it has a single destination, while CSS-Tricks, Nomensa and DEV Community posts warn that the link name becomes the full card text and that nested links break HTML semantics.
The gap comes down to card content. A card with one destination tolerates the long name, and a card with author and tag links does not.
Wrapping the card in an anchor
This has the simplest markup. No JavaScript, no z-index, and one link means one focus stop.
The downside is that screen readers announce the entire card as the link name. Any second link inside the card also becomes invalid nested interactive content.
Stretching the title link
Pure CSS, and the link name is just the title. The title link keeps its normal focus and hover behavior, and secondary links stay clickable when you lift them above the overlay with z-index.
The overlay blocks text selection, though, and the stacking order needs care as the card grows.
Adding a JavaScript click handler
The markup stays semantic, with a real title link and no pseudo-element stacking.
But you’ve added a script dependency for what is really a layout problem. The handler also has to ignore clicks that land on inner links, and a call to action must stay styled text instead of a second link, which is Nomensa’s suggestion for this pattern.
Pick the stretched link by default. Restaumatic’s accessible card write-up covers the selection problem: raising the text above the overlay with z-index restores selecting and copying, but clicks on that text then stop following the link.
Pair the pattern with the :focus-within rule from the previous section so keyboard users see which card is active.
Accessibility Checks for CSS Cards
Before a card ships, check heading level, color contrast, link text and keyboard order.
- Heading level follows the page outline, so cards under an h2 section use h3
- “Read more” repeated on every card tells a screen reader user nothing, so link the title or add visually hidden context
- Tab order should match reading order
- Informative images get descriptive alt text, and decorative ones get
alt=""so the title is not announced twice
WCAG 2.2 requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text, which W3C defines as 18 point or 14 point bold. Under success criterion 1.4.11, non-text parts such as a card border or focus ring need 3:1 against adjacent colors.
Test color contrast on every card variant, because a theme override can turn a passing card into a failing one. Text laid over an image needs a gradient overlay, since the ratio changes with every photo.
WebAIM’s 2026 Million report found low contrast text on 83.9% of the home pages it tested, up from 79.1% in 2025 and the most common failure it recorded.
Reordering focusable elements with Flexbox order splits the visual sequence from the tab sequence. Reorder only non-focusable parts such as the image, and leave links and buttons in source order.
The broader web accessibility checklist covers forms, tables and media beyond cards.
CSS cards examples
Color Palette Trading Cards
See the Pen
Color Palette Trading Cards by Mike Mangialardi (@mikemang)
on CodePen.
F1 Player Hexagon Card Responsive
See the Pen
F1 Player Hexagon Card Responsive by Shireen Taj (@TajShireen)
on CodePen.
Sneaker Product Cards
See the Pen
Sneaker Product Cards by ayush gupta (@ayushgptaa)
on CodePen.
Recipe Card
See the Pen
Recipe Card by Ahmad Nasr (@ahmadnasr)
on CodePen.
Fairly Colourful Profile Card
See the Pen
Fairly Colourful Profile Card by Takane Ichinose (@takaneichinose)
on CodePen.
Holiday Feature Folding Cards
See the Pen
Holiday Feature Folding Cards [Pure CSS] by Madalena (@Maza-designDev)
on CodePen.
Skewed eCommerce Cards
See the Pen
Responsive skewed eCommerce CSS Grid by Andy Barefoot (@andybarefoot)
on CodePen.
Parallax Card
See the Pen
Parallax Card by Hakkam Abdullah (@hkmDesigner)
on CodePen.
Comment Card Animation
See the Pen
Comment Card Animation by Shaw (@shshaw)
on CodePen.
Keyboard Love
See the Pen
Keyboard accessible animated card by Cristian Diaz (@ItsCrisDiaz)
on CodePen.
Comic Nerd Alert!
See the Pen
Marvel Corner Box Art by Aryan Tayal (@aryancodeworm)
on CodePen.
Flip Out
See the Pen
UI – Flip Card (using :focus-within for a11y) by Abubaker Saeed (@AbubakerSaeed)
on CodePen.
Glassy Dreams
See the Pen
Glass Card by Marcello Lopes (@marcell0lopes)
on CodePen.
Stretch it Out
See the Pen
Expanding Card Grid With Flexbox by Naila Ahmad (@nailaahmad)
on CodePen.
Nostalgic Pixels
See the Pen
Polaroid Memories – CSS only by Nico (@nicokoenig)
on CodePen.
Get Inspired
See the Pen
Inspiration Card by Dannie Vinther (@dannievinther)
on CodePen.
Go Digital, Go Bold
See the Pen
Digital Business Card by Will Johnson (@willalanjohnson)
on CodePen.
Dance of the Cards
See the Pen
Card Stack by Lane Olson (@Lane)
on CodePen.
Peek-a-Boo Magic
See the Pen
Simple Animated Card by Josh (@jepooley)
on CodePen.
Chic Grid Vibes
See the Pen
Isometric Card Grid by Jon Kantner (@jkantner)
on CodePen.
Bones of Design
See the Pen
Card UI Skeleton Screen by Max Böck (@mxbck)
on CodePen.
Becoming One with the Background
See the Pen
Expanding card page transition effect by Rachel Smith (@rachsmith)
on CodePen.
Meet the Two-Faced Card
See the Pen
Pure CSS Card Flip by Eddy Sims (@edeesims)
on CodePen.
The Shopper’s Delight
See the Pen
Playing card CSS Grid eCommerce layout by Andy Barefoot (@andybarefoot)
on CodePen.
Taking Flight in 3D
See the Pen
3D Perspective cards with depth [CSS] by Fernando Cohen (@designfenix)
on CodePen.
Figure it Out
See the Pen
A <figure> with a <figcaption>… and .date that all:hovers by Joshua Ward (@joshuaward)
on CodePen.
Eco Warrior Alert!
See the Pen
Environment Impact Cards (GSAP) by Anton Mudrenok (@mudrenok)
on CodePen.
Reality Flip
See the Pen
Realistic 3D Image Flip Box, Flip Card, 3d Image hover, Kallyas WordPress Theme by Nicola Mihaita (@nicolamihaita)
on CodePen.
Cut it Out, But in 3D!
See the Pen
3D Cutout Card by Dannie Vinther (@dannievinther)
on CodePen.
The Ultimate Caption Move
See the Pen
CSS-only Slide-up Caption Hover Effect by Mojtaba Seyedi (@seyedi)
on CodePen.
Black & White to Life
See the Pen
Profile Card hover over it once by Atul Prajapati (@atulcodex)
on CodePen.
Draggin’ Feels Real
See the Pen
Hearthstone Card CSS 3D Click/Drag by Jack Rugile (@jackrugile)
on CodePen.
Doorbell Ringer
See the Pen
Delivery Card Animation by Nikolay Talanov (@suez)
on CodePen.
CSS Cards FAQ
Should I use Bootstrap, Tailwind CSS or custom CSS cards?
Custom CSS fits a project with a few card types and its own design system. Bootstrap or Tailwind CSS fits a project that already loads the framework, since loading one only for cards adds code the project never uses.
Bootstrap ships a ready-made card component with predefined classes, while Tailwind CSS builds the same card from utility classes in the markup.
Does Material Design elevation apply to custom cards?
See the Pen
Web Material Card Concept by Mason Fox (@masonfox)
on CodePen.
Only as a visual convention. Material Design uses shadow depth to show how far a card sits above the page, and you can copy that with layered box-shadow values.
Nothing enforces it, because elevation is a design guideline and not a CSS feature.
How do I build a flip or 3D card?
See the Pen
flippyCards by Kayleigh Foley (@kayfo23)
on CodePen.
Put a front face and a back face inside one wrapper, set perspective on the card, and rotate the wrapper 180 degrees with rotateY. Add transform-style: preserve-3d to the wrapper and backface-visibility: hidden to both faces.
Hover does nothing on touch screens, so trigger the flip from :focus-within or a button as well. A dedicated CSS flip card guide covers the full markup.
How do I center a single card or a row of cards?
Center a single card with a max-width and margin-inline: auto. Center a row with justify-content: center on the flex or grid container.
In a grid, use fixed-width tracks such as repeat(auto-fill, 240px). Tracks sized with 1fr already fill the row, so there is nothing left to center.
What to Build and Test First in a CSS Card Component
Start with the markup and keyboard behavior before any visual layer. Semantics are the hardest part to retrofit across a finished set of cards, while shadows and radii change in one rule.
After that comes the grid layout with subgrid rows, and the container query for the stacked-to-horizontal switch goes last.
Test by resizing the grid cell instead of the window, then tab through every card variant. The cost of this order is slower visible progress, since stakeholders see a plain card for longer.
As of 15 October 2026, style queries on custom properties are recent enough to remain an enhancement with a fallback, not a dependency. Once the card passes these checks, it belongs in a shared library of design system components.
- What is Backend in Web Development? - September 13, 2026
- What is Frontend Development? - September 11, 2026
- How to Make a Button in Figma: Design Best Practices - September 10, 2026


