About this set
A complete two-pane messenger: a conversation list on the left and the open thread with its composer on the right. The list shows initials avatars, an online dot backed by hidden text, the last message, its time and an unread count badge, and the header totals the unread messages. Selecting a conversation marks it with aria-current, shows its thread, removes its badge and updates the total. Below 768px the layout collapses to one pane: the list first, then the thread with a back button that returns focus to the conversation you came from. The composer is a textarea that grows with its text, sends on Enter, adds a line on Shift+Enter and keeps the Send button disabled while it is empty. Sending appends the message locally with a Sent status and updates the preview in the list; nothing is sent to a server. The script is about 60 lines of vanilla JavaScript driven by data attributes, so the same script runs the plain CSS, Bootstrap and Tailwind files. The Bootstrap version uses .list-group-item-action buttons, .badge, .form-control and .btn.
What’s included
- Conversation list, thread and composer in one layout
- Collapses to one pane under 768px with a back button
- Unread badges with hidden text and a running total
- Enter to send, Shift+Enter for a new line, growing textarea
- Bootstrap .list-group, .badge, .form-control and .btn
Accessibility
Each thread is a focusable role="log" region with aria-live="polite", named by the contact's heading. Conversations are buttons with aria-current on the open one, and online status and unread counts include hidden text. On small screens focus moves to the thread heading when a conversation opens and back to the conversation when you press Back. Every control shows a 2px teal focus outline, the textarea has a visually hidden label, and the hover transitions are removed under prefers-reduced-motion.
Customise it
Set --mg-accent, --mg-soft and --mg-in on .mg for colours, and change the 767.98px media query (max-md: in Tailwind) to move the one-pane breakpoint. Bootstrap uses the same --mg-* variables plus --bs-list-group-* overrides on .mg-convos.






