Still stacking gray boxes with h-* and w-* for skeletons? That approach has problems:
- Layout shifts when switching from skeleton to actual text
- Duplicate markup for skeleton-specific elements that don't match your real content structure
This package lets you specify skeletons by character count instead. It only renders when the element is empty, so your normal markup stays intact—no layout shifts, no extra skeleton-only markup.
Repo: https://github.com/t4y3/tailwindcss-skeleton-screen
Concept
Keep markup as close to normal as possible.
- Render skeletons only when the element is
:empty. - Use
ss-text-[n]to generate a text skeleton for n characters. - Height follows
font-size/line-height, so it aligns with typography.
{/* Skeleton is shown only while empty */}
<p className="text-base text-gray-600 ss-text-[71]">{text}</p>
The difference from typical width/height rectangles is visually clear:
Installation (Tailwind CSS v4)
npm install -D tailwindcss-skeleton-screen
Load it in your Tailwind v4 entry CSS (no tailwind.config.js needed):
/* tailwind.css (your entry processed by Tailwind v4) */
@import "tailwindcss-skeleton-screen";
Basic Usage
- Just specify the character count; height follows your typography.
<!-- Text skeleton appears only while the element is empty -->
<p class="text-base text-gray-600 ss-text-[71]"></p>
For multiple lines, specify per-line counts with slashes:
<p class="ss-text-[24/24/12]"></p>
Single-line truncation works too:
<p class="truncate ss-text-[40]"></p>
Utilities
-
ss-object: Block skeleton with background color -
ss-text-[n]: Text skeleton for n characters -
ss-text-[a/b]: Per-line counts by slash (multiline)
Notes:
- Skeletons render only when the element is
:empty. - Combine freely with text utilities; it follows your typography.
Customize (v4 @theme)
Colors and radii are exposed as CSS custom properties. Override via Tailwind v4 @theme.
@theme {
--skeleton-color: #e5e7eb; /* default */
--skeleton-radius: .375rem; /* default (~6px) */
}
Global overrides:
@theme {
--skeleton-color: #f3f4f6;
--skeleton-radius: .5rem;
}
Scoped overrides (apply to descendants):
.card {
--skeleton-color: #e2e8f0;
--skeleton-radius: .25rem;
}
React Example (render only when empty)
export function UserBio({ bio }) {
return (
<p className="text-sm leading-6 text-gray-600 ss-text-[72]">
{bio}
</p>
);
}
The skeleton shows while bio is empty and naturally switches to text once populated.
How It Works
- The plugin parses
ss-text-[...]and builds a string of full‑width spaces (U+3000). - It injects that as
--tw-contentinto::beforeonly when the element is:empty. - Background and radius are controlled by CSS variables. It matches your line-height and wrapping.
[class*="ss-text-"]:empty::before {
content: var(--tw-content);
background-color: var(--skeleton-color);
white-space: break-spaces;
word-break: break-all;
border-radius: var(--skeleton-radius);
}
Migration from v3
- No more plugin registration in
tailwind.config.js. - Use v4 CSS directives (
@import/@plugin). - Theme options moved to
@themeCSS custom properties.
Best Practices
- For variable-length content, estimate slightly larger character counts to reduce visual shift.
- For layouts with variable line counts, use
ss-text-[a/b/c]for the maximum expected lines. - Combine with
truncateto match “1 line + ellipsis” from the loading state.
Wrap-up
Designing skeletons by “character count” aligns them with your typography and makes loading states feel natural. Setup is just @import and a utility class. Try it on your existing components.
- npm:
tailwindcss-skeleton-screen - GitHub: https://github.com/t4y3/tailwindcss-skeleton-screen


Top comments (0)