skills/frontend-design/loading-states/SKILL.md
Design effective loading states, skeleton screens, and empty states that maintain user confidence. Use when content takes time to load, when showing progress, or handling empty data scenarios. Triggers on "loading state", "skeleton screen", "empty state", "spinner", "progress bar", "loading animation", "zero state".
npx skillsauth add sanky369/vibe-building-skills loading-statesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Maintain user confidence when content isn't immediately available.
| Duration | Recommendation | |----------|---------------| | < 100ms | No indicator needed | | 100ms - 1s | Subtle indicator (opacity change) | | 1s - 10s | Skeleton screen or spinner | | > 10s | Progress bar with estimate |
.skeleton {
background: #e5e7eb;
border-radius: 4px;
}
/* Animated shimmer */
.skeleton-animated {
background: linear-gradient(
90deg,
#f3f4f6 25%,
#e5e7eb 50%,
#f3f4f6 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
<!-- Text skeleton -->
<div class="skeleton skeleton-text" style="width: 80%"></div>
<div class="skeleton skeleton-text" style="width: 60%"></div>
<!-- Avatar skeleton -->
<div class="skeleton skeleton-avatar"></div>
<!-- Image skeleton -->
<div class="skeleton skeleton-image"></div>
.skeleton-text {
height: 16px;
margin-bottom: 8px;
}
.skeleton-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
.skeleton-image {
width: 100%;
aspect-ratio: 16/9;
}
<article class="card card-skeleton">
<div class="skeleton skeleton-image"></div>
<div class="card-content">
<div class="skeleton skeleton-text" style="width: 70%"></div>
<div class="skeleton skeleton-text" style="width: 90%"></div>
<div class="skeleton skeleton-text" style="width: 50%"></div>
</div>
</article>
.spinner {
width: 24px;
height: 24px;
border: 3px solid #e5e7eb;
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.button-loading {
position: relative;
color: transparent; /* Hide text */
pointer-events: none;
}
.button-loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
border: 2px solid currentColor;
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
<span class="loading-text">
Loading
<span class="loading-dots">
<span>.</span><span>.</span><span>.</span>
</span>
</span>
<div class="progress">
<div
class="progress-bar"
role="progressbar"
style="width: 65%"
aria-valuenow="65"
aria-valuemin="0"
aria-valuemax="100"
>
65%
</div>
</div>
.progress {
height: 8px;
background: #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: var(--primary);
transition: width 0.3s ease-out;
}
.progress-indeterminate .progress-bar {
width: 30%;
animation: indeterminate 1.5s infinite ease-in-out;
}
@keyframes indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(400%); }
}
<div class="empty-state">
<img src="illustration.svg" alt="" class="empty-illustration">
<h3 class="empty-title">No projects yet</h3>
<p class="empty-description">
Create your first project to get started
</p>
<button class="button-primary">
Create Project
</button>
</div>
<div class="empty-state">
<span class="empty-icon">🔍</span>
<h3 class="empty-title">No results found</h3>
<p class="empty-description">
Try adjusting your search or filters
</p>
<button class="button-secondary">
Clear Filters
</button>
</div>
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 48px 24px;
text-align: center;
}
.empty-illustration {
width: 200px;
max-width: 100%;
margin-bottom: 24px;
}
.empty-icon {
font-size: 48px;
margin-bottom: 16px;
}
.empty-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 8px;
color: var(--text-primary);
}
.empty-description {
font-size: 14px;
color: var(--text-secondary);
max-width: 300px;
margin-bottom: 24px;
}
<div class="empty-state error-state">
<span class="empty-icon">⚠️</span>
<h3 class="empty-title">Something went wrong</h3>
<p class="empty-description">
We couldn't load your data. Please try again.
</p>
<button class="button-primary">
Retry
</button>
</div>
/* Announce loading to screen readers */
.loading-region[aria-busy="true"]::before {
content: "Loading...";
position: absolute;
clip: rect(0, 0, 0, 0);
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.skeleton-animated {
animation: none;
}
.spinner {
animation-duration: 1.5s;
}
}
testing
Diagnose your marketing situation and sequence all 9 other skills strategically. Use when starting a new marketing initiative, auditing your current system, or optimizing your marketing strategy.
development
Use when creating an original visual design language, identity, or art direction for any artifact — infographics, video storyboards, websites, or mobile app UI/UX — or when a design feels generic, derivative, "AI-default," or inconsistent and needs one unifying idea. Triggers on "design language", "art direction", "make it original", "visual identity", "looks generic", "design a world".
development
Write viral, persuasive, engaging tweets and threads. Uses web research to find viral examples in your niche, then models writing based on proven formulas and X algorithm optimization. Use when creating tweets, threads, or X content strategy.
development
Complete DIY SEO strategy based on agency secrets. Covers winnable keyword research, programmatic content at scale, link building, technical SEO, and 90-day action plans. Reference the Complete_SEO_Playbook.md in references folder for deep dives.