You Might Also Like
Shared Document Presence Bar — Free HTML CSS JS Snippet
Shared Document Presence Bar · Navigation · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Shared Document Presence Bar — Who's Viewing, Editing & the Overflow Count

Every collaborative document tool needs a fast answer to "who else is here right now" without opening a separate panel. The presence bar puts that answer directly in the document's top bar: a compact stack of avatar bubbles, a colored status dot showing who's actively editing versus just viewing, and an overflow bubble once the room gets crowded. This snippet builds that bar in plain HTML, CSS, and vanilla JavaScript. Compare it with collaborator presence bar and avatar stack tooltip for related patterns.
Overlapping bubbles from one array
PEOPLE holds everyone currently in the document, each with a name, initials, color, and a status of 'editing' or 'viewing'. render() slices the first MAX_VISIBLE entries and lays them out with a negative left margin so each bubble overlaps the previous one — the same compact-stack technique used in avatar stack, giving a crowd of people a tidy, fixed-width footprint regardless of how many are actually present.
Editing vs viewing at a glance
Rather than a text label per person, each bubble gets a small corner dot: green for actively editing, gray for read-only viewing. This is the same visual grammar used by Google Docs and Notion — a quick color scan tells you whether anyone is actively changing the document right now versus just reading it, which matters far more than an exact headcount when you're deciding whether it's safe to make your own edit.
Overflow that stays honest
When more people are present than MAX_VISIBLE allows, a final "+N" bubble is appended showing exactly how many additional collaborators aren't shown — never silently dropping people from the count. This keeps the bar's width predictable (it never grows past five bubbles) while still being truthful about how many people are actually in the document.
Tooltips confirm identity on demand
Hovering any avatar bubble reveals a small tooltip with the person's full name and their current status in words ("editing" or "viewing"), so the compact initials-only bubbles never leave you guessing who's who — full identity is one hover away, not permanently taking up bar space.
Extending it for real presence
To wire this to real collaboration, replace PEOPLE with live presence data from your backend (a WebSocket channel, Supabase Realtime, Liveblocks, or similar), re-running render() whenever someone joins, leaves, or switches between editing and viewing. Add a click handler on the overflow bubble to open a full presence panel — a natural pairing with team presence list.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't have to work out the stacking and overflow math by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the negative-margin bubble stack achieves a compact overlapping layout and why the overflow count is derived from the array length rather than tracked separately. The same assistant can help you optimize it — ask whether MAX_VISIBLE should adapt responsively to the available bar width on narrow screens instead of staying fixed. It's also useful for extending the bar: ask it to make the overflow bubble open a dropdown with the remaining names, add a subtle pulse animation when someone new joins, or wire status changes to real WebSocket presence events. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a "shared document presence bar" in plain HTML, CSS, and JavaScript for a collaborative document's top toolbar — no frameworks or libraries.
Requirements:
- Render a compact, overlapping stack of circular avatar bubbles (using negative margins so each bubble overlaps the previous one) from a single array of people, each with a name, initials, a background color, and a status of either "editing" or "viewing".
- Each bubble must show a small corner status dot that is a distinct color for "editing" versus "viewing" (e.g. green for actively editing, gray for read-only viewing), so status is scannable without reading text.
- Cap the number of individually rendered bubbles at a configurable maximum; if more people are present than that maximum, append one final "+N" overflow bubble showing exactly how many additional people aren't individually shown, computed from the real data length (never hard-coded).
- On hovering any avatar bubble, show a small tooltip with that person's full name and their status in words ("editing" or "viewing"), positioned so it doesn't get clipped or overlap neighboring bubbles.
- Keep the presence stack's rendering as a single pure function of the people array/state, so it can be re-run cleanly whenever presence data changes (someone joins, leaves, or switches status) without manually patching individual DOM nodes.
- Include a document title area that truncates with an ellipsis instead of pushing the presence stack off-screen on a narrow bar.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA document top bar renders with four overlapping avatar bubbles and a "+2" overflow bubble.
- 2Hover a bubbleA tooltip shows the person's name and whether they're editing or viewing.
- 3Look at the corner dotsGreen dots mean actively editing; gray dots mean read-only viewing.
- 4Check the overflow bubbleIt shows exactly how many additional people aren't shown in the stack.
- 5Change MAX_VISIBLEShow more or fewer bubbles before the overflow count kicks in.
- 6Connect real presenceReplace PEOPLE with live data from your collaboration backend and re-render on change.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
render() slices the PEOPLE array to the first MAX_VISIBLE entries for bubbles, then computes the overflow as PEOPLE.length minus that visible count. Because the overflow number is always derived from the real array length rather than hard-coded, it stays accurate automatically as people join or leave — there's no separate counter to keep in sync.
Each person in the data has a status of "editing" or "viewing", which maps to a green or gray corner dot on their bubble respectively (plus the matching word in the hover tooltip). This mirrors how Google Docs and Notion distinguish someone actively typing changes from someone who merely has the document open in read mode.
Add a click handler to .sdp-overflow that opens a dropdown, popover, or modal listing the remaining PEOPLE entries beyond MAX_VISIBLE — reusing the same bubble/tooltip markup at a larger scale, similar to how team presence list shows a fuller roster.
Replace the static PEOPLE array with data from your realtime backend (WebSocket, Supabase Realtime, Liveblocks, Firebase, etc.), and call render() again whenever a presence event arrives — someone joining, leaving, or switching between editing and viewing. Because render() is a pure function of the array, you never need to manually patch individual bubbles.
Pass PEOPLE as a prop or reactive state, derive the visible slice and overflow count with a computed value (useMemo, a Vue computed, or an Angular pipe), and map the visible array to bubble components with their status class and tooltip. The overlapping-stack CSS ports unchanged.