webdevpuneet.com

Mobile Chat Screen — Free HTML CSS JS UI Snippet

Mobile Chat Screen · Mobile · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="mc-phone">
  <div class="mc-screen">
    <div class="mc-status"><span>9:41</span><span class="mc-batt"><i></i></span></div>
    <header class="mc-head">
      <button class="mc-back" aria-label="Back">&#8249;</button>
      <div class="mc-avatar">JL<span class="mc-on"></span></div>
      <div class="mc-who">
        <b>Jordan Lee</b>
        <small id="mcPresence">Active now</small>
      </div>
      <button class="mc-call" aria-label="Call">&#9742;</button>
    </header>
    <div class="mc-thread" id="mcThread">
      <div class="mc-day">Today</div>
      <div class="mc-row in"><div class="mc-bubble">Hey! Did you get a chance to look at the mockups?</div></div>
      <div class="mc-row out"><div class="mc-bubble">Just opened them — the new layout looks great 🔥<span class="mc-tick">✓✓</span></div></div>
      <div class="mc-row in"><div class="mc-bubble">Awesome. I tweaked the header spacing too.</div></div>
    </div>
    <div class="mc-typing" id="mcTyping" hidden><span></span><span></span><span></span></div>
    <form class="mc-input" id="mcForm">
      <button type="button" class="mc-plus" aria-label="Attach">+</button>
      <input id="mcText" type="text" placeholder="Message" autocomplete="off">
      <button type="submit" class="mc-send" id="mcSend" aria-label="Send">&#10148;</button>
    </form>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Two-sided bubbles
One class, alignment flipped per row with flex.
CSS bubble tails
Cut corner radius fakes the tail — no SVG.
Animated typing dots
Staggered keyframes with per-dot delays.
Read receipts
Double-tick ticks on outgoing messages.
Smart send button
Disabled while the input is empty.
Auto-scroll
Thread jumps to the newest message on send.
Bubble entrance
Each message fades and lifts into place.
No dependency
Pure HTML, CSS, and vanilla JavaScript.

About this UI Snippet

Mobile Chat Screen — Messaging Thread UI

Screenshot of the Mobile Chat Screen snippet rendered live

A chat screen is the core of every messaging app — a header with the contact and presence, a scrolling thread of left- and right-aligned bubbles, a typing indicator, and a composer bar pinned to the bottom. This snippet builds a complete, interactive one inside a CSS phone frame: you can type a message, watch it appear as an outgoing bubble, see an animated typing indicator, and get a simulated reply — all in HTML, CSS, and vanilla JavaScript with no dependency.

The two-sided bubble layout

Incoming and outgoing messages share one .mc-bubble class but flip alignment through their row: .mc-row.out uses justify-content: flex-end to push the bubble right, while incoming rows stay left. The tail is faked by rounding three corners fully and cutting the fourth to a small radius — border-bottom-left-radius for incoming, border-bottom-right-radius for outgoing — the universal chat-bubble shape without any SVG. Outgoing bubbles get a gradient fill and white text; incoming bubbles are white with a soft shadow.

The typing indicator

Three dots animate with a staggered @keyframes mcBounce, each dot delayed by 150ms so they ripple. The indicator is toggled with the hidden attribute rather than being rebuilt, and the header presence text swaps to "typing…" while it shows — the two cues you get in real apps when the other person is composing.

The composer and send state

The input is a real <form>, so pressing Enter submits. The send button is disabled whenever the trimmed input is empty and re-enables as you type, which prevents sending blank messages and mirrors native behavior. On submit, the message is appended as an outgoing bubble with double-tick read receipts, the thread auto-scrolls to the bottom via scrollTop = scrollHeight, and a timed reply fires after the typing indicator.

Bubble entrance animation

Every new bubble runs a short mcPop keyframe that fades and lifts it into place, so messages feel like they land rather than blink in. Because bubbles are appended to a flex column, the layout reflows naturally and the newest message always sits at the bottom.

Accessibility and performance

The composer is a real <form> with a labelled text input and an aria-label on the send button, so keyboard users can type and press Enter to send, and screen readers announce the control's purpose. The read-receipt ticks are decorative and carried inside the bubble text rather than as separate imagery, so they add no announcement noise. Performance-wise the thread is a plain flex column that appends one node per message and never re-renders the whole list, and the auto-scroll is a single scrollTop assignment rather than a layout-thrashing loop. The typing indicator animates purely in CSS, so it costs nothing on the main thread while it runs, and toggling it with the hidden attribute avoids rebuilding DOM. For very long conversations you would virtualize the thread so only the visible bubbles are mounted, but for a typical screen's worth of messages the direct-append approach stays smooth and keeps the code readable.

Reusing it

Replace the seeded messages and the canned replies array with your real data, and swap the setTimeout for a WebSocket or fetch call that streams responses. Lift the thread out of the phone frame for a responsive web chat, or keep it framed next to a mobile chat conversation list to present a full inbox-to-thread flow.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA chat thread renders with seeded incoming and outgoing bubbles inside a phone frame.
  2. 2
    Type a messageThe send button lights up as soon as the input has text.
  3. 3
    Send itYour message appears as a gradient outgoing bubble with read ticks and the thread scrolls down.
  4. 4
    Watch the typing indicatorThree bouncing dots appear and the header shows "typing…" before a reply lands.
  5. 5
    Keep the conversation goingEach send cycles through the canned replies so the thread grows.
  6. 6
    Wire your backendSwap the timed reply for a WebSocket or fetch call to make it live.

Real-world uses

Common Use Cases

Messaging app threads
Show a conversation with left and right bubbles from one class, beside a chat conversation list that opens it.
Support chat widgets
Prototype a framed version of a floating chat widget, with a working composer and a live typing indicator for agent replies.
AI assistant conversations
Restyle the thread as an AI chat interface, using staggered keyframe dots to show the assistant is thinking.
Typing and read receipts
Reuse the animated dots as a typing indicator elsewhere, and the double ticks to confirm delivery of outgoing messages.
Pitch and portfolio mockups
Drop the chat into a phone mockup, where cut corner radii fake the bubble tails with no SVG required.
Related: Mobile Banking Screen
See the Mobile Banking Screen for a related mobile pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Both use the same .mc-bubble class, but each sits in a row that controls alignment. Incoming rows stay left; outgoing rows use justify-content: flex-end to push the bubble to the right edge. The tail is faked by fully rounding three corners and cutting the fourth — bottom-left for incoming, bottom-right for outgoing.

It is a simulation for the demo: on send, the header swaps to "typing…", the three-dot indicator un-hides, and after a short timeout a canned reply is appended. In production you would show the indicator when your server signals the other user is composing, and append the message when it actually arrives.

The button is disabled whenever the trimmed input value is empty and re-enables on the input event as you type. This prevents sending blank messages and matches how native messaging apps grey out send until there is content.

After each new bubble is appended, the code sets thread.scrollTop = thread.scrollHeight, which jumps the scroll container to the bottom. Because bubbles live in a flex column, the newest message is always the last child, so this keeps the latest message in view.

Render bubbles from a messages array and key them by id. In React append with a state setter and scroll to the bottom in a useEffect that watches the array; in Vue use a ref and nextTick; in Angular use ngAfterViewChecked. Replace the setTimeout reply with your WebSocket or fetch handler. The CSS ports directly, and Tailwind can express the bubble radii and gradient with utilities.

Yes. The thread already includes a centered day divider, and you can insert more by comparing each message's timestamp to the previous one and adding a divider when the date changes. To collapse consecutive messages from the same sender into a group, check whether the previous bubble shares the direction and only render the avatar or tail on the last bubble in the run, which is how most messaging apps tighten long exchanges.