Pure CSS Loading Skeleton Screens

Pure CSS Loading Skeleton Screens

We've all been there. Clicking a button and watching a blank screen while wondering if anything is actually happening. Traditional loading spinners work, but they don't tell users what's coming next. Enter skeleton screens: the elegant solution that's become the standard for modern web applications.

A skeleton screen is a placeholder that mimics the layout and structure of your content while it loads. Instead of showing users an empty void, you're giving them a preview of what's about to appear.

Why Skeleton Screens Work

The magic of skeleton screens isn't just visual, it's psychological. When users see a structured placeholder, their brains can mentally prepare for the content that's coming. This makes the actual loading time feel shorter, even when it isn't.

Research shows that skeleton screens can improve perceived performance by up to 50%. Users report feeling less frustrated during loading states because they have context about what they're waiting for. Instead of anxiety-inducing uncertainty, they get a roadmap of the content structure.

Plus, skeleton screens scale beautifully across devices and layouts. A well-designed skeleton adapts to different screen sizes just like your real content, maintaining consistency across your entire user experience.

The Foundation: Basic Skeleton CSS

Before diving into specific components, let's establish the core CSS that powers all skeleton screens. Every skeleton shares these fundamental properties:

.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: loading 1.5s infinite;
}

@keyframes loading {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

This creates the signature "shimmer" effect that travels across your placeholder elements. The gradient background is twice the width of the container (200% 100%), and the animation slides it back and forth to create that polished loading appearance.

Profile Card Skeletons

Let's start with one of the most common patterns: user profile cards. These typically contain an avatar, name, and some additional information.

HTML

<div class="profile-card">
  <div class="skeleton skeleton-avatar"></div>
  <div class="skeleton-content">
    <div class="skeleton skeleton-name"></div>
    <div class="skeleton skeleton-title"></div>
  </div>
</div>

CSS

.profile-card {
  display: flex;
  align-items: center;
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
  background: white;
  max-width: 400px;
}

.skeleton-avatar {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  margin-right: 16px;
  flex-shrink: 0;
}

.skeleton-content {
  flex: 1;
}

.skeleton-name {
  height: 18px;
  width: 120px;
  margin-bottom: 8px;
  border-radius: 4px;
}

.skeleton-title {
  height: 14px;
  width: 90px;
  border-radius: 4px;
}

The key here is matching the proportions of your real content. The avatar gets a circular shape with border-radius: 50%, while text elements use smaller border-radius values to look like text lines. Different widths for the name and title create a more natural, less uniform appearance.

Article Card Skeletons

Content cards are everywhere in modern web design. They typically feature an image, headline, and description text. Here's how to skeleton them effectively:

HTML

<div class="article-card">
  <div class="skeleton skeleton-image"></div>
  <div class="skeleton skeleton-headline"></div>
  <div class="skeleton skeleton-text"></div>
  <div class="skeleton skeleton-text"></div>
  <div class="skeleton skeleton-text short"></div>
</div>

CSS

.article-card {
  padding: 20px;
  border-radius: 8px;
  background: white;
  border: 1px solid #eee;
  max-width: 350px;
}

.skeleton-image {
  width: 100%;
  height: 200px;
  margin-bottom: 16px;
  border-radius: 6px;
}

.skeleton-headline {
  height: 24px;
  width: 85%;
  margin-bottom: 16px;
  border-radius: 4px;
}

.skeleton-text {
  height: 16px;
  width: 100%;
  margin-bottom: 8px;
  border-radius: 4px;
}

.skeleton-text.short {
  width: 65%;
}

The image placeholder uses a larger border-radius to match typical image styling. Multiple text lines with varying widths create the illusion of paragraph text, with the last line being shorter to mimic how real paragraphs often end.

Data Table Skeletons

Tables present unique challenges because you need to maintain column alignment and proportions. The skeleton should respect your table's structure while providing clear visual hierarchy.

HTML

<table class="skeleton-table">
  <tbody>
    <tr class="skeleton-row">
      <td class="skeleton skeleton-cell"></td>
      <td class="skeleton skeleton-cell"></td>
      <td class="skeleton skeleton-cell"></td>
    </tr>
    <tr class="skeleton-row">
      <td class="skeleton skeleton-cell"></td>
      <td class="skeleton skeleton-cell"></td>
      <td class="skeleton skeleton-cell"></td>
    </tr>
    <tr class="skeleton-row">
      <td class="skeleton skeleton-cell"></td>
      <td class="skeleton skeleton-cell"></td>
      <td class="skeleton skeleton-cell"></td>
    </tr>
  </tbody>
</table>

CSS

.skeleton-table {
  width: 100%;
  border-collapse: collapse;
}

.skeleton-row {
  border-bottom: 1px solid #eee;
}

.skeleton-cell {
  padding: 12px;
  height: 20px;
  border-radius: 4px;
}

/* Adjust column widths to match your data */
.skeleton-cell:nth-child(1) { width: 30%; }
.skeleton-cell:nth-child(2) { width: 50%; }
.skeleton-cell:nth-child(3) { width: 20%; }

The magic is in the column width specifications. Analyze your actual table data and set skeleton widths accordingly. If your first column typically contains names, make it wider. If the last column is for status badges, make it narrow.

List Item Skeletons

Lists are perfect for skeletons because they have predictable, repeating structures. This pattern works great for notifications, messages, or any feed-style content.

HTML

<div class="skeleton-list">
  <div class="skeleton-list-item">
    <div class="skeleton skeleton-icon"></div>
    <div class="skeleton-item-content">
      <div class="skeleton skeleton-list-title"></div>
      <div class="skeleton skeleton-list-subtitle"></div>
    </div>
  </div>
  <div class="skeleton-list-item">
    <div class="skeleton skeleton-icon"></div>
    <div class="skeleton-item-content">
      <div class="skeleton skeleton-list-title"></div>
      <div class="skeleton skeleton-list-subtitle"></div>
    </div>
  </div>
</div>

CSS

.skeleton-list-item {
  display: flex;
  align-items: center;
  padding: 16px 0;
  border-bottom: 1px solid #eee;
}

.skeleton-icon {
  width: 40px;
  height: 40px;
  border-radius: 6px;
  margin-right: 12px;
  flex-shrink: 0;
}

.skeleton-item-content {
  flex: 1;
}

.skeleton-list-title {
  height: 16px;
  width: 70%;
  margin-bottom: 6px;
  border-radius: 4px;
}

.skeleton-list-subtitle {
  height: 14px;
  width: 50%;
  border-radius: 4px;
}

The two-line structure with different widths creates a natural hierarchy. The icon placeholder uses a subtle border-radius rather than a circle, making it suitable for various icon styles or small images.

Grid Layout Skeletons

Modern web design often uses grid layouts for displaying multiple items. Your skeleton should maintain the same grid structure to provide accurate expectations.

HTML

<div class="skeleton-grid">
  <div class="skeleton-card">
    <div class="skeleton skeleton-card-image"></div>
    <div class="skeleton skeleton-card-title"></div>
    <div class="skeleton skeleton-card-text"></div>
  </div>
  <div class="skeleton-card">
    <div class="skeleton skeleton-card-image"></div>
    <div class="skeleton skeleton-card-title"></div>
    <div class="skeleton skeleton-card-text"></div>
  </div>
  <div class="skeleton-card">
    <div class="skeleton skeleton-card-image"></div>
    <div class="skeleton skeleton-card-title"></div>
    <div class="skeleton skeleton-card-text"></div>
  </div>
</div>

CSS

.skeleton-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}

.skeleton-card {
  padding: 16px;
  border: 1px solid #eee;
  border-radius: 8px;
  background: white;
}

.skeleton-card-image {
  width: 100%;
  height: 120px;
  margin-bottom: 12px;
  border-radius: 4px;
}

.skeleton-card-title {
  height: 18px;
  width: 80%;
  margin-bottom: 8px;
  border-radius: 4px;
}

.skeleton-card-text {
  height: 14px;
  width: 60%;
  border-radius: 4px;
}

The auto-fit and minmax() functions ensure your skeleton grid behaves exactly like your real content grid, maintaining consistent spacing and breakpoints across all screen sizes.

Alternative Animation Styles

While the shimmer effect is most common, you can create different moods with alternative animations:

Pulse Animation:

.skeleton-pulse {
  background: #f0f0f0;
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

Wave Animation:

.skeleton-wave {
  background: linear-gradient(45deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 40px 40px;
  animation: wave 2s infinite linear;
}

@keyframes wave {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 40px 40px;
  }
}

Pulse animations feel more subtle and work well for content-heavy applications. Wave animations create diagonal movement patterns that can feel more dynamic for creative or interactive applications.

Making Skeletons Responsive

Your skeleton screens should adapt to different screen sizes just like your real content. Use the same responsive techniques you'd use for any other component:

@media (max-width: 768px) {
  .skeleton-grid {
    grid-template-columns: 1fr;
  }
  
  .profile-card {
    flex-direction: column;
    text-align: center;
  }
  
  .skeleton-avatar {
    margin: 0 0 16px 0;
  }
  
  .skeleton-name,
  .skeleton-title {
    width: 100%;
  }
}

Dark Mode Support

Modern applications need to support both light and dark themes. Adjust your skeleton colors accordingly:

@media (prefers-color-scheme: dark) {
  .skeleton {
    background: linear-gradient(90deg, #2a2a2a 25%, #3a3a3a 50%, #2a2a2a 75%);
  }
  
  .skeleton-pulse {
    background: #2a2a2a;
  }
}

When and How to Show Skeletons

Timing is crucial for skeleton screen effectiveness. Show them immediately when a user action triggers loading, but avoid showing them for very fast requests (under 300ms) as this creates unnecessary visual noise.

Consider progressive loading where you show basic skeletons first, then replace them with more detailed ones as different data loads. This layered approach keeps users engaged throughout longer loading processes.

The goal is always to match your skeleton structure as closely as possible to your real content. Users should feel like they're watching your content materialize rather than seeing a generic placeholder transform into something completely different.

Walt is a computer scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.

Community Comments

No comments posted yet

Code Your Own Classic Snake Game – The Right Way

Master the fundamentals of game development and JavaScript with a step-by-step guide that skips the fluff and gets straight to the real code.

"Heavy scripts slowing down your site? I use Fathom Analytics because it’s lightweight, fast, and doesn’t invade my users privacy."
Ad Unit

Current Poll

Help us and the community figure out what the latest trends in coding are.

Total Votes:
Q:
Submit

Add a comment