Free tool · no signup · runs in your browser

Chat Widget Designer

Design the chatbot window for your website - launcher, header, bubbles and composer - in a preview you can click and type into. Contrast is checked as you go, and you leave with working widget code, free.

Pick a starting theme

Brand and colour

Your messages 6.3:1Bot messages 16.1:1Header 6.3:1

Shape and layout

Header style
Launcher
Position
18px
16px
Font
Density

Words

The first message. One friendly line that says what you can help with.

<!-- Chat widget: paste before </body> -->
<style>
.cw { --cw-accent: #4F46E5; --cw-on-accent: #ffffff; --cw-bg: #FFFFFF; --cw-bot: #F3F4F6;
  --cw-text: #111827; --cw-muted: #4B5563; --cw-border: rgba(17,24,39,.08); --cw-radius: 18px; --cw-bubble: 16px;
  --cw-pad: 14px; font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; position: fixed; right: 20px; bottom: 20px; z-index: 2147483000; }
.cw-launch { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 56px; min-width: 56px;
  padding: 0; border: 0; border-radius: 50%; background: var(--cw-accent); color: var(--cw-on-accent);
  font-family: inherit; font-size: 14px; font-weight: 700; line-height: 1; cursor: pointer; box-shadow: 0 10px 30px -8px rgba(0,0,0,.35); transition: transform .15s; }
.cw-launch:hover { transform: translateY(-2px); }
.cw-launch:focus-visible, .cw button:focus-visible, .cw input:focus-visible { outline: 2px solid var(--cw-accent); outline-offset: 2px; }
.cw-win { position: absolute; right: 0; bottom: 72px; width: min(370px, calc(100vw - 40px)); height: min(560px, calc(100vh - 120px));
  display: flex; flex-direction: column; background: var(--cw-bg); color: var(--cw-text); border: 1px solid var(--cw-border);
  border-radius: var(--cw-radius); overflow: hidden; box-shadow: 0 24px 60px -18px rgba(0,0,0,.35);
  transform-origin: bottom right; animation: cw-in .18s ease-out; }
.cw-win[hidden] { display: none; }
@keyframes cw-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }
.cw-head { display: flex; align-items: center; gap: 10px; padding: calc(var(--cw-pad) + 2px); background: #4F46E5;
  color: #ffffff; }
.cw-av { width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; font-weight: 700;
  background: rgba(255,255,255,.22); color: inherit; }
.cw-head strong { display: block; font-size: 15px; }
.cw-head small { font-size: 12px; opacity: .85; }
.cw-close { margin-left: auto; width: 32px; height: 32px; border: 0; border-radius: 8px; background: transparent; color: inherit; cursor: pointer; font-size: 18px; }
.cw-list { flex: 1; overflow-y: auto; padding: var(--cw-pad); display: flex; flex-direction: column; gap: 9px; }
.cw-msg { max-width: 82%; padding: 9px 13px; font-size: 14px; line-height: 1.45; overflow-wrap: anywhere; }
.cw-msg--bot { align-self: flex-start; background: var(--cw-bot); border-radius: var(--cw-bubble) var(--cw-bubble) var(--cw-bubble) 4px; }
.cw-msg--me { align-self: flex-end; background: var(--cw-accent); color: var(--cw-on-accent); border-radius: var(--cw-bubble) var(--cw-bubble) 4px var(--cw-bubble); }
.cw-form { display: flex; gap: 8px; padding: calc(var(--cw-pad) - 4px); border-top: 1px solid var(--cw-border); }
.cw-form input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: inherit; font-family: inherit; font-size: 14px; padding: 8px; }
.cw-form input::placeholder { color: var(--cw-muted); opacity: 1; }
.cw-form button { display: grid; place-items: center; width: 40px; height: 40px; border: 0; border-radius: 12px; background: var(--cw-accent); color: var(--cw-on-accent); cursor: pointer; }
.cw-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .cw-win { animation: none; } .cw-launch { transition: none; } }
</style>

<div class="cw" id="chat-widget">
  <div class="cw-win" id="cw-win" role="dialog" aria-label="Ava from Acme chat" hidden>
    <div class="cw-head">
      <span class="cw-av" aria-hidden="true">A</span>
      <div><strong>Ava from Acme</strong><small>Typically replies in a minute</small></div>
      <button type="button" class="cw-close" aria-label="Close chat">&times;</button>
    </div>
    <div class="cw-list" role="log" aria-live="polite"></div>
    <form class="cw-form">
      <label class="cw-sr" for="cw-input">Message</label>
      <input id="cw-input" autocomplete="off" placeholder="Type your message..." />
      <button type="submit" aria-label="Send"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M2.5 8h10M8.5 3.5L13 8l-4.5 4.5" stroke="currentColor" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
    </form>
  </div>
  <button type="button" class="cw-launch" aria-controls="cw-win" aria-expanded="false" aria-label="Open chat"><svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5.5A2.5 2.5 0 016.5 3h11A2.5 2.5 0 0120 5.5v8a2.5 2.5 0 01-2.5 2.5H10l-4.5 4v-4A1.5 1.5 0 014 14.5z" fill="currentColor"/></svg></button>
</div>

<script>
(function () {
  var root = document.getElementById("chat-widget");
  var win = root.querySelector(".cw-win"), launch = root.querySelector(".cw-launch");
  var list = root.querySelector(".cw-list"), form = root.querySelector(".cw-form"), input = root.querySelector("input");

  function add(from, text) {
    var el = document.createElement("div");
    el.className = "cw-msg cw-msg--" + from;
    el.textContent = text; // textContent, never innerHTML: messages are untrusted
    list.appendChild(el);
    list.scrollTop = list.scrollHeight;
  }
  function toggle(open) {
    win.hidden = !open;
    launch.setAttribute("aria-expanded", String(open));
    launch.setAttribute("aria-label", open ? "Close chat" : "Open chat");
    if (open) input.focus(); else launch.focus();
  }

  // Replace this with a call to your backend. Return a Promise of the reply text.
  function onSend(text) {
    return new Promise(function (resolve) {
      setTimeout(function () { resolve("Thanks! We will get back to you shortly."); }, 600);
    });
  }

  add("bot", "Hi there! How can I help you today?");
  launch.addEventListener("click", function () { toggle(win.hidden); });
  root.querySelector(".cw-close").addEventListener("click", function () { toggle(false); });
  root.addEventListener("keydown", function (e) { if (e.key === "Escape" && !win.hidden) toggle(false); });
  form.addEventListener("submit", function (e) {
    e.preventDefault();
    var text = input.value.trim();
    if (!text) return;
    add("me", text);
    input.value = "";
    onSend(text).then(function (reply) { add("bot", reply); });
  });
})();
</script>

The anatomy of a chat window

Every chat widget is the same five parts: a launcher that sits in a corner, a header that says who is on the other end, a scrolling list of messages, a composer, and the small states between them - the greeting, the typing indicator, an offline notice. Get those right and the widget feels like part of your site. Get them wrong and it feels like an ad.

The header carries trust: a name, an avatar and an honest status line ("Typically replies in a minute", not "Online 24/7" if nobody is). The bubbles carry the conversation, so they need the strongest contrast on the page. The composer must never scroll away. And the launcher should not cover your own calls to action - check it against your cookie banner and any floating buttons before you ship.

Design choices that hold up

  • One accent colour. Use your brand colour for the launcher, your messages and the send button, and keep everything else neutral. This designer picks white or near-black text on the accent automatically, whichever passes contrast.
  • Match your corners. If your buttons are rounded, round the window and bubbles to match; a square site with a bubbly widget looks bolted on.
  • Comfortable on phones. The generated window caps itself to the viewport and the launcher is 56 pixels, comfortably above the 24 pixel target-size minimum.
  • Dark mode on purpose. A dark window on a dark site, a light one on a light site. Preview both above.

More detail, with examples from real products, is in our guides to chatbot window UI design and chatbot UI best practices.

Using the code

Copy the all-in-one tab and paste it just before </body>. You get a real, keyboard-friendly widget: the launcher is a button with aria-expanded, Escape closes the window and returns focus, messages land in a polite live region so screen readers announce replies, and messages are inserted as text, never as HTML, so a visitor cannot inject markup. Replace onSend with a call to your backend and you are live.

If you would rather skip the backend, a hosted website chatbot gives you the same window with an AI agent, conversation history and handoff to your team already connected. If a widget you have installed does not appear, the widget troubleshooting guide covers the usual causes.

Frequently asked questions

What makes a good chatbot window UI?

A good chat window is quiet until it is needed, obvious when it is, and readable at a glance: a launcher in a corner that does not cover your own buttons, a header that says who is answering, bubbles with clear contrast, and a composer that is always visible. Everything else - colour, radius, font - should match the site so it feels native.

Where should a chat widget sit on a website?

Bottom right is the convention most visitors expect, so start there. Move it to the bottom left only when the right corner is taken - a cookie banner, a back-to-top button, a cart drawer. On phones keep the launcher at least 56 pixels and away from the browser's own controls, and make the open window fill most of the screen rather than float.

How do I check my chat widget colours are accessible?

Measure the contrast between text and its background. WCAG asks for at least 4.5 to 1 for normal text, which this designer checks live for your sent bubbles, the bot's bubbles and the header. It also picks white or near-black text on your accent colour automatically, whichever reads better, so a light brand colour never ends up with white text on it.

Can I use the generated widget code on my site?

Yes. The HTML, CSS and script are plain, dependency-free and free to use commercially. Paste them before the closing body tag and the window opens, closes, takes messages and shows replies. The one thing you supply is the onSend function, which sends the visitor's text to your backend and returns the reply.

How do I connect the widget to an AI chatbot?

Point onSend at an endpoint that passes the message to your model or bot platform and returns its answer as text. If you would rather not build and host that, a hosted website chatbot gives you the same window with the AI, conversation history, human handoff and analytics already wired up, and you only paste one script tag.

Does this designer store anything I type?

No. The preview, the contrast checks and the generated code are all produced in your browser. Nothing you enter - names, greetings, colours or the test messages you send in the preview - is sent to Conferbot or saved anywhere, and it is gone when you close the tab.

Related

Like the window? Put an AI behind it

Conferbot gives you this window with an AI agent that answers from your website, plus human handoff. One script tag, free plan, no card.