Skip to main content
Customise

Style the widget

The widget ships structure only, no visual CSS — on purpose, so it inherits nothing it shouldn’t and clashes with nothing on your page. You bring the looks. Paste the starter stylesheet below and you get a tidy, branded chat box to build from.

Quick start

Everything is driven by the five variables at the top of the snippet: change the colours and the font and you’re most of the way there. It comes in copadre’s own colours, light by default. For a dark version, add class="copadre-dark" to your container <div>.

Paste this into your page’s <head>:

copadre-widget.css
<style>
/* ============================================================
   copadre embed widget — starter styles
   Paste into your own page's <head>, then change the variables
   below to match your site. Light by default; add
   class="copadre-dark" to the container div for the dark version.
   ============================================================ */

#copadre-container {
	/* ---- Edit these five to rebrand the whole widget ---- */
	--copadre-accent:   #f9e231;  /* buttons & focus  */
	--copadre-on-accent:#1a1a18;  /* text on the button */
	--copadre-bg:       #faf8f1;  /* widget background */
	--copadre-text:     #1a1a18;  /* message text */
	--copadre-font:     'Inter', system-ui, -apple-system, Segoe UI, Roboto, sans-serif;

	/* ---- Derived; usually leave alone ---- */
	--copadre-muted:    #5b5a54;
	--copadre-border:   #e3ddcc;
	--copadre-radius:   14px;
	--copadre-dot-size: 7px;     /* "thinking" dot diameter */
	--copadre-log-height: 320px; /* transcript height; for a widget that grows
	                                with content instead, swap the height/overflow
	                                on .copadre-output below for max-height */

	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	max-width: 560px;
	padding: 1.25rem;
	font-family: var(--copadre-font);
	color: var(--copadre-text);
	background: var(--copadre-bg);
	border: 1px solid var(--copadre-border);
	border-radius: var(--copadre-radius);
	box-sizing: border-box;
}

/* Dark variant — add class="copadre-dark" to the container */
#copadre-container.copadre-dark {
	--copadre-bg:     #1a1a18;
	--copadre-text:   #eceae1;
	--copadre-muted:  #a4a299;
	--copadre-border: #3a3a35;
}

/* Transcript — the scrolling message log. For a widget that grows with its
   content instead of a fixed box, replace height + overflow-y with
   max-height: var(--copadre-log-height); */
.copadre-output {
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
	height: var(--copadre-log-height);
	overflow-y: auto;
}

/* Message bubbles */
.copadre-msg {
	max-width: 85%;
	padding: 0.55rem 0.8rem;
	line-height: 1.5;
	border-radius: 12px;
	overflow-wrap: anywhere;
}
.copadre-msg--user {
	align-self: flex-end;
	color: var(--copadre-on-accent);
	background: var(--copadre-accent);
	border-bottom-right-radius: 3px;
}
.copadre-msg--bot {
	align-self: flex-start;
	background: rgba(127, 127, 127, 0.14);
	border-bottom-left-radius: 3px;
}

/* Markdown inside a reply */
.copadre-msg p { margin: 0 0 0.75em; }
.copadre-msg p:last-child { margin-bottom: 0; }
.copadre-msg a { color: inherit; }
.copadre-msg code {
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	font-size: 0.9em;
	padding: 0.1em 0.35em;
	border-radius: 5px;
	background: rgba(127, 127, 127, 0.18);
}
.copadre-msg ul { margin: 0 0 0.75em; padding-left: 1.25em; }
.copadre-msg li { margin: 0.2em 0; }

/* Replies arrive a word at a time; this fades each one in. Optional —
   without it the words simply appear. */
@media (prefers-reduced-motion: no-preference) {
	.copadre-word { transition: opacity 220ms ease-out; }
}

/* "Thinking" indicator — three jumping dots while the reply loads */
.copadre-dot {
	display: inline-block;
	width: var(--copadre-dot-size);
	height: var(--copadre-dot-size);
	margin-right: 0.3rem;
	border-radius: 50%;
	background: var(--copadre-muted);
	animation: copadre-bounce 1.2s infinite ease-in-out both;
}
.copadre-dot:last-child { margin-right: 0; }
.copadre-dot:nth-child(2) { animation-delay: 0.18s; }
.copadre-dot:nth-child(3) { animation-delay: 0.36s; }
@keyframes copadre-bounce {
	0%, 60%, 100% { transform: translateY(0);    opacity: 0.4; }
	30%           { transform: translateY(-4px); opacity: 1;   }
}

/* Input row */
.copadre-form {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}
.copadre-input {
	flex: 1 1 auto;
	min-width: 0;
	padding: 0.7rem 0.9rem;
	font: inherit;
	color: var(--copadre-text);
	background: transparent;
	border: 1px solid var(--copadre-border);
	border-radius: 9px;
}
.copadre-input::placeholder { color: var(--copadre-muted); }
.copadre-input:focus {
	outline: 2px solid var(--copadre-accent);
	outline-offset: 1px;
	border-color: var(--copadre-accent);
}

/* Send button — round, with a play-triangle. Disables while awaiting a reply. */
.copadre-submit {
	position: relative;
	flex: 0 0 auto;
	width: 40px;
	height: 40px;
	cursor: pointer;
	background: var(--copadre-accent);
	border: none;
	border-radius: 50%;
}
.copadre-submit::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 0;
	height: 0;
	transform: translate(-60%, -50%);
	border-top: 6px solid transparent;
	border-bottom: 6px solid transparent;
	border-left: 10px solid var(--copadre-on-accent);
}
.copadre-submit:hover { filter: brightness(0.95); }
.copadre-submit:disabled { opacity: 0.6; cursor: default; }
</style>

A starting point, not a cage — every class above is part of the widget’s stable styling contract, so anything you can do in CSS, you can do here.

Class definitions

The widget renders the same set of elements every time, with these stable class names. Style any of them; they won’t change underneath you.

Markdown inside a reply — links, paragraphs, lists, inline code — carries no classes of its own. Style it by element within a bubble: .copadre-msg a for links (always relative to your own site, opened in a new tab), and .copadre-msg p, .copadre-msg ul, .copadre-msg code for the rest, as the starter stylesheet above does.

SelectorWhat it is
#copadre-containerThe container you place (or that the widget creates). The styling root — put your variables here.
.copadre-outputThe scrolling transcript log.
.copadre-msgOne message bubble.
.copadre-msg--userA visitor message bubble.
.copadre-msg--botA bot message bubble (replies, errors, the thinking dots).
.copadre-wordOne word of a reply, as it is revealed. Style the fade here — words are fully visible without any rule.
.copadre-thinkingModifier on the bot bubble while it shows the loading dots.
.copadre-dotOne of the three dots inside a thinking bubble.
.copadre-formThe input + send-button wrapper.
.copadre-inputThe text input.
.copadre-submitThe icon-only send button (disabled while awaiting a reply).

Already animated. While the widget waits for a reply it shows the familiar three jumping dots — the snippet above styles and animates them, so you can recolour, resize or restyle them however you like via --copadre-dot-size and the .copadre-dot rules.

Updating from an earlier snippet? Replies are revealed a word at a time, and the .copadre-word rule fades each one in. It’s optional — if your stylesheet predates it the words simply appear instead, and nothing else changes. Both respect a visitor’s reduced-motion setting.

copadre · co-PAH-dray

© 2025–2026 This Company (Digital) Limited. Privacy · Terms