Stacked faces in a members column. Notification cards that peek under each other. A “+” that fans into actions. That look is usually a pile of one-off CSS—or a JS component library.
overlapui.fscss turns it into a small architecture: shared tokens, four stack patterns, hover/focus open states, no JS for the motion.
This article is about how the pieces fit, not only how to import the module.
The idea: cascade by design
“Overlap UI” is a layout contract:
- Rest — children share space (negative margin, peek, or stacked under a center).
-
Open — hover or
:focus-withinrestores gap / orbit / full card height. -
Tokens — size, gap, ring, timing live in
--ou-*, not scattered magic numbers. -
Plain CSS out — FSCSS expands
@defines; the browser runs normal CSS.
@ou-root() → design tokens
@avatar-overlap() → horizontal faces
@card-overlap() → vertical cards
@img-overlap() → tilted photos
@circle-overlap() → radial menu
One module, four cascades.
Install
FSCSS 1.2.5+.
Vscode: FSCSS Support
Example with browser runtime:
<script src="https://cdn.jsdelivr.net/npm/fscss@1.2.5/runtime.min.js" defer></script>
@import((*) from overlapui)
@ou-root()
@avatar-overlap(.avatar-overlap)
@card-overlap(.card-overlap)
@img-overlap(.img-overlap)
@circle-overlap(.circle-overlap)
@ou-reduced-motion()
Architecture layer 1 — tokens (--ou-*)
Everything visual should read from the token sheet:
| Concern | Examples |
|---|---|
| Motion |
--ou-time, --ou-ease
|
| Chrome |
--ou-ring, --ou-ring-width
|
| Avatar |
--ou-size, --ou-overlap, --ou-gap
|
| Card |
--ou-peek, --ou-card-h, --ou-card-gap
|
| Image |
--ou-img, --ou-img-overlap, --ou-img-tilt
|
| Circle |
--ou-item, --ou-r, --ou-center-bg, --n
|
@ou-root()
:root {
--ou-ring: #0f172a;
--ou-center-bg: #6366f1;
@ou-avatar-size(40px, -12px)
@ou-circle-size(48px, 80px)
}
Fit: product theme = override tokens once; stack mixins stay unchanged. Same pattern as st-core’s --st-* or circle-progress’s --progress-*.
Architecture layer 2 — rest vs open
Each mixin encodes the same state machine in CSS:
| Pattern | Rest | Open (:hover / :focus-within) |
|---|---|---|
| Avatar | margin-left: var(--ou-overlap) |
siblings get var(--ou-gap)
|
| Card | margin-top: peek − height |
siblings get var(--ou-card-gap)
|
| Image | overlap + tilt | gap + rotate(0)
|
| Circle | satellites opacity: 0 under center |
polar rotate + translateY(−r)
|
Fit: designers describe “collapsed density” and “expanded density”; you map those to tokens, not new class names per page.
Use tabindex="0" on the stack so keyboard users get the same open state.
Avatar cascade — members & presence
<ul class="avatar-overlap" tabindex="0">
<li data-alph="g">Gina</li>
<li data-alph="h"><img src="…" alt="">Hank</li>
<li data-alph="5">Ops</li>
</ul>
- Letter from
data-alph(::before+font-size: 0on the face so names stay in the DOM for a11y). - Optional img covers the letter.
- FSCSS arrays map A–Z / 0–9 → palette at compile time.
Where it fits
- Dashboard Members column
- Comment “also notified” rows
- Workspace switcher
@avatar-overlap(.members)
:root {
@ou-avatar-size(28px, -10px) /* denser in tables */
}
Card cascade — notifications without a drawer
<div class="card-overlap" tabindex="0">
<div>
<img src="…" alt="">
<h3>Invoice paid</h3>
Acme · $2,400
</div>
<div>…</div>
</div>
Peek amount is --ou-peek vs --ou-card-h. Open state is margin, not display toggles—cheap and animatable.
Fit: inbox previews, activity feeds, stacked tickets.
Image cascade — editorial strips
<div class="img-overlap" tabindex="0">
<img src="…" alt="">
<img src="…" alt="">
<img src="…" alt="">
</div>
Rest: overlap + alternating tilt. Open: align and gap.
Fit: case-study heroes, “recent shots,” gallery teasers.
Circle cascade — radial settings / FAB
<div class="circle-overlap" style="--n:5" tabindex="0">
<button type="button" aria-label="Settings">+</button>
<a href="#profile" aria-label="Profile">P</a>
<a href="#billing" aria-label="Billing">B</a>
<a href="#team" aria-label="Team">T</a>
<a href="#api" aria-label="API">K</a>
<a href="#out" aria-label="Sign out">×</a>
</div>
-
First child = center (
--ou-center-bg). -
--n= number of satellites (not counting the center). - Position via
--iand360deg / var(--n).
Fit: topbar settings, mobile FAB, reaction pickers. Label the center and each action for screen readers.
Composing with the rest of the stack
overlapui does not own the page shell. It plugs into larger systems:
Same --ou-ring can align with a dark glass sidebar border so stacks feel native, not pasted on.
Responsive architecture
Scale tokens at breakpoints instead of rewriting markup:
@media (max-width: 520px) {
:root {
@ou-avatar-size(36px, -11px)
@ou-circle-size(40px, 64px)
}
}
Horizontal stacks: allow scroll or smaller --ou-size. Tables: overflow-x: auto on a wrapper; keep avatars in the first column.
Accessibility (part of the architecture)
- Real names in the DOM for avatars.
-
aria-labelon circle center and actions. -
tabindex="0"on stacks for focus-within. -
@ou-reduced-motion()kills springy transitions when preferred.
Mental model checklist
- Pick a cascade type (avatar / card / image / circle).
- Set density tokens (overlap vs gap, size, radius).
- Mark the root with
tabindex="0"if it should open from keyboard. - Theme with
--ou-*, not one-off overrides on every child. - Compile with FSCSS ship CSS.
module
fscss-ttr
/
overlapui.fscss
CSS-first overlapping UI stack: avatars, cards, and images that overlap at rest and spread on hover/focus in FSCSS
overlapui.fscss
CSS-first overlapping UI stacks for the FSCSS ecosystem.
Avatars, cards, images, and radial circle menus that overlap at rest and open on hover/focus — no JS required for the interaction.
MIT Licensed · github.com/fscss-ttr/overlapui.fscss
Requires FSCSS v1.2.3+. Recommended: FSCSS 1.2.5 or later (stable shorthands + array attribute maps).
Table of Contents
- What is overlapui?
- Installation
- Design tokens —
@ou-root - Mixins
- Markup patterns
- Full examples
- Token reference
- Accessibility & motion
- License
1. What is overlapui?
overlapui.fscss is an FSCSS module for stacked UI:
Component
Behavior
Avatar overlap
Circles pull together; hover/focus spreads them;
data-alph sets letter + color
Card overlap
Cards peek under each other; expand vertically on hover/focus
Image overlap
Tilted photos overlap; straighten and gap on hover/focus
Circle overlap
Center trigger; other children fan out on a ring on hover/focus (
--n = satellite count)
Everything is driven by --ou-* custom properties. Compile with the CLI or…
Top comments (0)