CSS Framework Reference
Complete reference for the CSS framework powering ChatStyle chat overlays. For expert mode users writing custom CSS and HTML templates/skins.
How It Works
ChatStyle uses a three-layer system to style chat bubbles in the overlay:
- CSS Variables — named
--cs-{element}-{property}, control all visual properties (colors, sizes, fonts, animations) - HTML Template — defines the DOM structure with
.cs-*class names and placeholder slots for chat data - Framework CSS — binds variables to CSS properties with neutral defaults and provides structural toggle classes
HTML Template Contract
Every chat bubble follows a defined HTML structure with required CSS classes and placeholder slots. The framework CSS targets these classes to apply styling.
Example Structure
<div class="cs-bubble">
<div class="cs-avatar">{{avatar}}</div>
<div class="cs-user" style="--cs-usercolor: {{usercolor}};">
<span class="cs-badges">{{badges}}</span>
<span class="cs-username">{{username}}</span>
<span class="cs-pronouns">{{pronouns}}</span>
</div>
<div class="cs-content">
<span class="cs-user--inline">
<span class="cs-badges cs-badges--inline">{{badges}}</span>
<span class="cs-username--inline" style="--cs-usercolor: {{usercolor}};">{{username}}</span>
<span class="cs-pronouns cs-pronouns--inline">{{pronouns}}</span>
</span>
<div class="cs-message">{{message}}</div>
</div>
</div>
Placeholders
| Placeholder | Replaced With |
|---|---|
{{avatar}} | <img> tag with the user's profile picture, or empty string |
{{badges}} | <img> tags for Twitch badges (subscriber, mod, VIP, etc.), or empty string |
{{timestamp}} | Formatted local time string from the overlay browser (for example 12:34 or 12:34:56) |
{{pronouns}} | Optional chatter pronouns string resolved in the overlay browser (for example she/her), or empty string |
{{broadcaster}} | Broadcaster channel login for current overlay (for example propz_tv) |
{{username}} | The chatter's display name |
{{usercolor}} | Hex color string from Twitch (e.g. #ff4500) |
{{message}} | Message HTML containing text, emote images, and Twitch GIF images |
Required CSS Classes
| Class | Element | Purpose |
|---|---|---|
.cs-bubble | Outer container | Root element, receives toggle and position classes |
.cs-avatar | Avatar container | Contains the user's profile image |
.cs-user | Header row | Username header above the message (badges + username) |
.cs-badges | Badges container | Contains Twitch badge images |
.cs-username | Username text | Display name text inside the header row |
.cs-pronouns | Pronouns text | Optional pronouns element; hidden when empty or when pronoun visibility is off |
.cs-content | Content wrapper | Contains the message area and inline username |
.cs-user--inline | Inline header | Inline username + badges wrapper, shown when username position is inline |
.cs-username--inline | Inline username | Username text when displayed inline with the message |
.cs-pronouns--inline | Inline pronouns | Optional inline pronouns copy, hidden with the standard pronouns element |
.cs-message | Message text | Contains the chat message text and emotes |
.cs-user is the header-style username (above the message). .cs-user--inline is the inline variant (displayed on the same line as the message). The framework toggles between them using the cs-username-position-inline class.
Twitch GIF images use .cs-gif inside the message. They preserve their aspect ratio without upscaling, with a maximum height of 140px and a maximum width of the available bubble width. The global Show GIFs setting controls new messages; disabled or failed GIFs show their description text.
Bubble Modifier Classes
The overlay and previews add public modifier classes to .cs-bubble for platform, sender roles, Twitch Turbo status, and emote-only messages. Use these hooks for platform-specific, role-specific, or message-specific styling.
| Class | Applied When |
|---|---|
.cs-twitch | Message is from Twitch, or platform is missing/unknown |
.cs-kick | Message is from Kick |
.cs-youtube | Message is from YouTube |
.cs-broadcaster | Sender has the broadcaster badge |
.cs-mod | Sender is a moderator |
.cs-vip | Sender is a VIP |
.cs-sub | Sender is a subscriber |
.cs-turbo | Sender has Twitch Turbo enabled for this message |
.cs-sub-tier-1 | Sender's subscription tier is known as Tier 1 (data-cs-sub-tier="1000") |
.cs-sub-tier-2 | Sender's subscription tier is known as Tier 2 (data-cs-sub-tier="2000") |
.cs-sub-tier-3 | Sender's subscription tier is known as Tier 3 (data-cs-sub-tier="3000") |
.cs-odd | First bubble rendered by the overlay, then every other bubble after that |
.cs-even | Second bubble rendered by the overlay, then every other bubble after that |
.cs-follow-up | Sender matches the previous rendered overlay message; the first message in a sequence does not receive this class |
.cs-twitch-gif | Message displays at least one Twitch GIF; GIFs do not count as emotes |
.cs-emote-only | Message contains only emotes and whitespace |
.cs-ends-bang | Displayed message text ends with ! |
.cs-ends-question | Displayed message text ends with ? |
.cs-ends-bang-question | Displayed message text ends with !? |
.cs-ends-question-bang | Displayed message text ends with ?! |
Known subscriber tenure is exposed as data-cs-sub-months="N" on .cs-bubble. Missing data-cs-sub-months or data-cs-sub-tier means unknown or not subscribed.
CSS Variables Reference
All CSS variables follow the naming pattern --cs-{element}-{property}. The framework binds these variables to actual CSS properties with neutral defaults (0px, transparent, none). Styles override these defaults through settings values.
Variable Scoping
All CSS variables are set as inline styles on .cs-bubble — not on their respective elements. Variables like --cs-avatar-size, --cs-username-font-size, and --cs-message-font-color all live on the bubble root element. Child elements (.cs-avatar img, .cs-user, .cs-message) consume these variables via CSS inheritance.
Container-scoped variables live on .cs-chat instead. Today that includes --cs-bubble-gap (spacing between bubbles) and --cs-chat-padding* (inner spacing around the whole chat stack).
.cs-bubble to match how the system applies them. For example, .cs-bubble { --cs-message-font-size: 20px; } is the correct approach. Setting variables on child elements also works (via inheritance), but .cs-bubble is the canonical scope.
Helper Color Tokens
The framework exposes shared Twitch color constants on .cs-chat. They are inherited into every bubble, useful with role modifier classes such as .cs-sub, .cs-mod, .cs-vip, and .cs-broadcaster, and are not customizer-managed settings.
| Variable | Default | Description |
|---|---|---|
--cs-color-twitch | #9146ff | Twitch brand purple helper color |
--cs-color-twitch-subscriber | #8205b4 | Subscriber role helper color for .cs-sub styles |
--cs-color-twitch-moderator | #05af08 | Moderator role helper color for .cs-mod styles |
--cs-color-twitch-vip | #e006b9 | VIP role helper color for .cs-vip styles |
--cs-color-twitch-broadcaster | #e91916 | Broadcaster role helper color for .cs-broadcaster styles |
Pronoun Vars
The framework reads these plain CSS variables for pronoun elements. They are not part of the customizer registry, but styles and skins may override them.
| Variable | Default | Description |
|---|---|---|
--cs-pronouns-font-color | inherit | Optional text color hook for the pronoun chip, overridden from expert CSS or skin CSS |
--cs-pronouns-background-color | transparent | Optional background hook for the pronoun chip, overridden from expert CSS or skin CSS |
pronouns.show structural setting adds .cs-pronouns-hidden, which hides both .cs-pronouns and .cs-pronouns--inline.
Chat Container (--cs-chat-*)
The chat stack container. These variables affect the whole message column, not an individual bubble. In the designer, this control is shown as Space Inside Chat Area.
Spacing
| Variable | Default | Description |
|---|---|---|
--cs-chat-padding | 0px | Uniform inner spacing around the full chat container |
--cs-chat-padding-top | var(--cs-chat-padding) | Top inner spacing (when advanced spacing is on) |
--cs-chat-padding-right | var(--cs-chat-padding) | Right inner spacing (when advanced spacing is on) |
--cs-chat-padding-bottom | var(--cs-chat-padding) | Bottom inner spacing (when advanced spacing is on) |
--cs-chat-padding-left | var(--cs-chat-padding) | Left inner spacing (when advanced spacing is on) |
Bubble (--cs-bubble-*)
The main chat bubble container. Controls overall bubble appearance including padding, background, border, shadow, animation, width, and gap between bubbles.
Spacing
| Variable | Default | Description |
|---|---|---|
--cs-bubble-padding | 0px | Uniform inner padding (all sides) |
--cs-bubble-padding-top | var(--cs-bubble-padding) | Top padding (when advanced padding is on) |
--cs-bubble-padding-right | var(--cs-bubble-padding) | Right padding (when advanced padding is on) |
--cs-bubble-padding-bottom | var(--cs-bubble-padding) | Bottom padding (when advanced padding is on) |
--cs-bubble-padding-left | var(--cs-bubble-padding) | Left padding (when advanced padding is on) |
--cs-bubble-gap | 10px | Gap between chat bubbles |
--cs-bubble-width | 400px | Maximum bubble width |
Background
| Variable | Default | Description |
|---|---|---|
--cs-bubble-background-color | transparent | Background color |
--cs-bubble-background-gradient | none | Complete CSS gradient used as background-image |
Border
| Variable | Default | Description |
|---|---|---|
--cs-bubble-border-width | 0px | Border width |
--cs-bubble-border-style | solid | Border line style |
--cs-bubble-border-color | transparent | Border color |
--cs-bubble-border-radius | 0px | Uniform border radius (all corners) |
--cs-bubble-border-radius-top-left | var(--cs-bubble-border-radius) | Top-left corner radius |
--cs-bubble-border-radius-top-right | var(--cs-bubble-border-radius) | Top-right corner radius |
--cs-bubble-border-radius-bottom-right | var(--cs-bubble-border-radius) | Bottom-right corner radius |
--cs-bubble-border-radius-bottom-left | var(--cs-bubble-border-radius) | Bottom-left corner radius |
Shadow
| Variable | Default | Description |
|---|---|---|
--cs-bubble-box-shadow-offset-x | 0px | Horizontal shadow offset |
--cs-bubble-box-shadow-offset-y | 0px | Vertical shadow offset |
--cs-bubble-box-shadow-blur-radius | 0px | Shadow blur radius |
--cs-bubble-box-shadow-spread-radius | 0px | Shadow spread radius |
--cs-bubble-box-shadow-color | transparent | Shadow color |
--cs-bubble-box-shadow-inset | — | Inset shadow toggle (empty = outset, inset = inset) |
Animation
| Variable | Default | Description |
|---|---|---|
--cs-bubble-animation | bp-slide-in-up | Entrance animation keyframe name |
--cs-bubble-animation-duration | 750ms | Animation duration |
--cs-bubble-animation-delay | 0ms | Delay before animation starts |
--cs-bubble-animation-timing-function | ease-out | Animation timing function |
Avatar (--cs-avatar-*)
The user's profile image. Applied to the img element inside .cs-avatar. Has the same box properties (padding, background, border, shadow) plus a size property.
| Variable | Default | Description |
|---|---|---|
--cs-avatar-size | 60px | Avatar width and height |
--cs-avatar-padding | 0px | Uniform inner padding |
--cs-avatar-background-color | transparent | Background color |
--cs-avatar-background-gradient | none | Complete CSS gradient used as background-image |
--cs-avatar-border-width | 0px | Border width |
--cs-avatar-border-style | solid | Border style |
--cs-avatar-border-color | transparent | Border color |
--cs-avatar-border-radius | 0px | Border radius |
--cs-avatar-box-shadow-offset-x | 0px | Horizontal shadow offset |
--cs-avatar-box-shadow-offset-y | 0px | Vertical shadow offset |
--cs-avatar-box-shadow-blur-radius | 0px | Shadow blur |
--cs-avatar-box-shadow-spread-radius | 0px | Shadow spread |
--cs-avatar-box-shadow-color | transparent | Shadow color |
--cs-avatar-animation | none | Entrance animation |
--cs-avatar-animation-duration | 750ms | Animation duration |
--cs-avatar-animation-delay | 0ms | Animation delay |
--cs-avatar-animation-timing-function | ease-out | Animation easing |
Username (--cs-username-*)
The username text in the header row. Controls font properties, text effects, and has the same box properties (padding, background, border, shadow) as the bubble.
Typography
| Variable | Default | Description |
|---|---|---|
--cs-username-font-family | 'Space Grotesk' | Font family |
--cs-username-font-size | 16px | Font size |
--cs-username-font-weight | 700 | Font weight |
--cs-username-font-color | var(--cs-usercolor, #ffffff) | Text color (defaults to chatter's Twitch color) |
--cs-username-text-stroke-width | 0px | Text stroke (outline) width |
--cs-username-text-stroke-color | #000000 | Text stroke color |
--cs-username-text-shadow-offset-x | 0px | Horizontal text shadow offset |
--cs-username-text-shadow-offset-y | 0px | Vertical text shadow offset |
--cs-username-text-shadow-blur-radius | 0px | Text shadow blur |
--cs-username-text-shadow-color | transparent | Text shadow color |
Box Properties
The username element also supports padding, background, border, border-radius, box-shadow, and animation — following the same variable pattern as the bubble element (--cs-username-padding, --cs-username-background-color, etc.).
Message (--cs-message-*)
The chat message text. Controls font appearance and has the same box properties as the username element.
Typography
| Variable | Default | Description |
|---|---|---|
--cs-message-font-family | 'Inter' | Font family |
--cs-message-font-size | 16px | Font size |
--cs-message-font-weight | 400 | Font weight |
--cs-message-font-color | #ffffff | Text color |
--cs-message-text-stroke-width | 0px | Text stroke width |
--cs-message-text-stroke-color | #000000 | Text stroke color |
--cs-message-text-shadow-offset-x | 0px | Horizontal text shadow |
--cs-message-text-shadow-offset-y | 0px | Vertical text shadow |
--cs-message-text-shadow-blur-radius | 0px | Text shadow blur |
--cs-message-text-shadow-color | transparent | Text shadow color |
Box Properties
The message element also supports padding, background, border, border-radius, box-shadow, and animation — following the same variable pattern as the bubble element (--cs-message-padding, --cs-message-background-color, etc.).
Events (--cs-events-*)
The events section inside a bubble (e.g. sub gifts, raids). Controls entrance animation independently from the bubble animation.
| Variable | Default | Description |
|---|---|---|
--cs-events-animation | none | Entrance animation keyframe name |
--cs-events-animation-duration | 750ms | Animation duration |
Structural Toggle Classes
These CSS classes are applied to bubble or container elements based on structural settings. They control layout, visibility, and positioning without CSS variables.
Position Classes
| Class | Applied To | Effect |
|---|---|---|
.cs-avatar-position-left | .cs-bubble | Position avatar at start (left in LTR) |
.cs-avatar-position-center | .cs-bubble | Position avatar at center |
.cs-avatar-position-right | .cs-bubble | Position avatar at end (right in LTR) |
.cs-username-position-inline | .cs-bubble | Switch from header username to inline (same line as message) |
.cs-badges-position-after-name | .cs-bubble | Move badges after the username (CSS order: 1) |
.cs-direction-rtl | .cs-bubble | Set text direction to right-to-left |
Visibility Classes
| Class | Applied To | Effect |
|---|---|---|
.cs-avatar-hidden | .cs-bubble | Hide the avatar element |
.cs-badges-hidden | .cs-bubble | Hide all badge images |
.cs-pronouns-hidden | .cs-bubble | Hide both .cs-pronouns and .cs-pronouns--inline |
.cs-events-hidden | .cs-bubble | Hide the events element |
Container Classes
| Class | Applied To | Effect |
|---|---|---|
.cs-full-width | .cs-chat | Bubbles stretch to 100% width with no max-width |
.cs-chat--bottom-up | .cs-chat | Flow messages upward from the bottom |
.cs-chat--top-down | .cs-chat | Flow messages downward from the top |
.cs-chat--left-to-right | .cs-chat | Flow messages horizontally left to right |
.cs-chat--right-to-left | .cs-chat | Flow messages horizontally right to left |
.cs-chat--horizontal | .cs-chat | Added with left-to-right and right-to-left flows for shared horizontal layout rules |
.cs-align-left | .cs-chat | Align bubbles to the start |
.cs-align-center | .cs-chat | Align bubbles to center |
.cs-align-right | .cs-chat | Align bubbles to the end |
.cs-align-top | .cs-chat | Align bubbles to the top (for horizontal message directions) |
.cs-align-bottom | .cs-chat | Align bubbles to the bottom (for horizontal message directions) |
Event Classes
When a chat message is an event (not a regular chat message), event classes are added to .cs-bubble: the shared .cs-event marker and one or more specific .cs-event-* classes for the event type or provider. Use these to style events independently.
| Class | Source | Description |
|---|---|---|
.cs-event | All events | Present on every event bubble — use as a shared event hook |
| PRIVMSG events | ||
.cs-event-action | /me message | Italic action message (/me does a thing) |
.cs-event-cheer | Bits cheer | Message containing a Bits cheer |
.cs-event-highlight | Channel Points | Highlighted message redeemed with Channel Points |
.cs-event-channel-points | Channel Points | Message sent with a channel points reward (skip subs mode) |
.cs-event-announcement | Announcement | Moderator announcement |
.cs-event-gigantified | Channel Points | Gigantified emote message |
| USERNOTICE events (rendered as system message bubbles) | ||
.cs-event-sub | USERNOTICE | New subscriber |
.cs-event-resub | USERNOTICE | Resubscriber (may include a personal message) |
.cs-event-subgift | USERNOTICE | Gifted subscription to another viewer |
.cs-event-mystery-gift | USERNOTICE | Mystery gift sub (random recipient) |
.cs-event-gift-upgrade | USERNOTICE | Viewer upgraded a gifted sub to paid |
.cs-event-reward-gift | USERNOTICE | Reward gift event |
.cs-event-anon-gift-upgrade | USERNOTICE | Viewer upgraded an anonymous gifted sub to paid |
.cs-event-raid | USERNOTICE | Incoming raid from another channel |
.cs-event-unraid | USERNOTICE | Raid was cancelled |
.cs-event-bits-badge | USERNOTICE | Viewer unlocked a Bits badge tier |
.cs-event-shared-chat | USERNOTICE | Shared chat notice |
| External webhook events | ||
.cs-event-external | Webhook | Provider-neutral hook for all external webhook events |
.cs-event-kofi | Ko-fi webhook | Provider-specific hook. Future services use .cs-event-{provider}. |
.cs-bubble.cs-event-raid { background: gold; } — style only raid bubbles..cs-bubble.cs-event { opacity: 0.8; } — dim all event bubbles uniformly.
Font Modifier Classes
| Class | Effect |
|---|---|
.cs-username-italic | Apply font-style: italic to username |
.cs-username-underline | Apply text-decoration: underline to username |
.cs-username-uppercase | Apply text-transform: uppercase to username |
.cs-username-lowercase | Apply text-transform: lowercase to username |
.cs-username-capitalize | Apply text-transform: capitalize to username |
.cs-username-align-center | Apply text-align: center to username |
.cs-username-align-end | Apply text-align: end to username |
.cs-message-italic | Apply font-style: italic to message |
.cs-message-underline | Apply text-decoration: underline to message |
.cs-message-uppercase | Apply text-transform: uppercase to message |
.cs-message-lowercase | Apply text-transform: lowercase to message |
.cs-message-capitalize | Apply text-transform: capitalize to message |
.cs-message-align-center | Apply text-align: center to message |
.cs-message-align-end | Apply text-align: end to message |
Animations
Entrance animations control how chat bubbles appear in the overlay. Each element (bubble, avatar, username, message) can have its own entrance animation.
slide-in-up) but compiled with a bp- prefix for CSS (e.g. bp-slide-in-up). Use the bp- prefixed name when referencing animations in custom CSS.
| Setting Value | CSS @keyframes Name |
|---|---|
none | none |
fade-in | bp-fade-in |
fade-in-up | bp-fade-in-up |
fade-in-down | bp-fade-in-down |
fade-in-left | bp-fade-in-left |
fade-in-right | bp-fade-in-right |
slide-in-up | bp-slide-in-up |
slide-in-down | bp-slide-in-down |
slide-in-left | bp-slide-in-left |
slide-in-right | bp-slide-in-right |
bounce-in | bp-bounce-in |
bounce-in-up | bp-bounce-in-up |
bounce-in-down | bp-bounce-in-down |
zoom-in | bp-zoom-in |
zoom-in-up | bp-zoom-in-up |
flip-in-x | bp-flip-in-x |
flip-in-y | bp-flip-in-y |
rotate-in | bp-rotate-in |
jack-in-the-box | bp-jack-in-the-box |
roll-in | bp-roll-in |
light-speed-in-right | bp-light-speed-in-right |
Special Values
User Color (--cs-usercolor)
The special value __usercolor__ can be used in any color property. It compiles to var(--cs-usercolor), which resolves to each chatter's Twitch chat color at render time. The --cs-usercolor variable is set per-bubble via inline style on the .cs-user element. The username font color defaults to this value.
.cs-username {
color: var(--cs-usercolor, #ffffff);
}
Gradient Values
The background-gradient setting stores a complete normalized CSS gradient string. Supported families are linear-gradient(...), radial-gradient(...), and conic-gradient(...).
Advanced Toggles
These designer-only toggles control how CSS variables are emitted. They are not CSS variables themselves:
- border-radius-advanced — When OFF, uniform
--cs-{el}-border-radiussynced to all corners. When ON, individual per-corner radius values. - padding-advanced — When OFF, uniform
--cs-{el}-paddingsynced to all sides. When ON, individual per-side padding values.
Expert Mode CSS
Expert Mode lets you write custom CSS targeting any element in the chat bubble DOM. Your CSS loads after the framework and style template, so it can override any property.
Targeting Elements
Target any .cs-* class and their children. You can also use CSS custom properties (var()) and reference the --cs-usercolor variable for the chatter's Twitch color.
.cs-bubble {
background: linear-gradient(135deg, #1a0030, #2a0050);
border: 2px solid #ff00aa;
border-radius: 12px;
}
.cs-message {
font-size: 18px;
color: #ffccee;
text-shadow: 0 0 8px rgba(255, 0, 170, 0.5);
}
.cs-username {
color: var(--cs-usercolor);
font-weight: 900;
text-transform: uppercase;
}
Restrictions
Maximum 10,000 characters. CSS comments are stripped during sanitization. Premium streamer and admin expert CSS may load trusted font stylesheets with @import url("https://fonts.googleapis.com/css2?...");, @import "https://fonts.googleapis.com/css2?...";, or @import url("https://fonts.cdnfonts.com/css/...");. All other imports are blocked.
| Blocked | Reason |
|---|---|
@import | Only strict Google Fonts /css2 and CDNFonts /css/... imports are allowed for premium/admin expert CSS |
@font-face | User-defined font files are blocked; use trusted font @import instead |
@charset | Encoding interference |
url() | 정책에 따라 제어됩니다. 엄격한 컨텍스트 블록 URL 로딩; 프리미엄 전문가 컨텍스트는 자체 호스팅 이미지, i.ibb.co 이미지 URL 및 삭제된 비 base64 SVG 데이터 URL을 허용합니다. |
data:image/*;base64 | Raster data images and base64 SVG data URLs are blocked. Use ImgBB direct CDN URLs from i.ibb.co for external images. |
expression() | Legacy IE JavaScript execution |
javascript: | Script injection prevention |
-moz-binding | XUL binding attack prevention |
behavior: | IE HTC component attack prevention |
What's Allowed
- All standard CSS properties (color, background, border, etc.)
- CSS animations and transitions
- CSS transforms (translate, rotate, scale, etc.)
- CSS custom properties (
var(--my-prop)) @keyframesdefinitions@mediaqueries- Premium/admin only: image URLs from ChatStyle domains or direct ImgBB CDN URLs on
i.ibb.co. Use imgbb.com to upload, then copy the directi.ibb.coimage URL.
Expert Mode HTML
Expert Mode also lets you customize the HTML structure of chat bubbles. You can rearrange elements, add wrapper divs, or create entirely new layouts.
Requirements
- Template must contain an element with the
cs-bubbleclass - Must include the core placeholders your layout depends on:
{{username}},{{message}},{{badges}},{{avatar}},{{usercolor}} - Optional placeholders:
{{timestamp}},{{pronouns}},{{broadcaster}} - Maximum 10,000 characters
Blocked Elements
| Blocked | Reason |
|---|---|
<script>, <style>, <iframe>, <object> | Script execution and embedding prevention |
<svg>, <math>, <template> | Complex embedded content prevention |
<form>, <link>, <embed>, <base> | External resource and form injection prevention |
on* attributes | JavaScript event handler prevention |
javascript:, vbscript:, data: | Dangerous protocol prevention |
Framework CSS
The complete framework.css file that provides structural layout, CSS variable bindings, and toggle classes. This file is loaded before each style's CSS template.
/* ==========================================================
ChatStyle — Framework CSS
https://chatstyle.gg
Loaded before each style's CSS template. Provides structural
layout, animation defaults, toggle classes, and ALL standard
CSS variable bindings for system elements.
All defaults are neutral/zero (0px, transparent, no shadow).
Styles become visible through settings_values overrides.
========================================================== */
/* ====================
Chat Container
==================== */
.cs-chat {
display: flex;
flex-direction: column;
align-items: flex-start;
flex: 1;
gap: var(--cs-bubble-gap, 10px);
inline-size: 100%;
/* Needed for proper chat flow */
overflow: hidden;
/* Safe area for shadows */
padding-block-start: var(--cs-chat-padding-top, var(--cs-chat-padding, 0px));
padding-block-end: var(--cs-chat-padding-bottom, var(--cs-chat-padding, 0px));
padding-inline-start: var(--cs-chat-padding-left, var(--cs-chat-padding, 0px));
padding-inline-end: var(--cs-chat-padding-right, var(--cs-chat-padding, 0px));
/* Twitch helper colors */
--cs-color-twitch: #9146ff;
--cs-color-twitch-subscriber: #8205b4;
--cs-color-twitch-moderator: #05af08;
--cs-color-twitch-vip: #e006b9;
--cs-color-twitch-broadcaster: #e91916;
}
/* Chat Flow */
.cs-chat.cs-chat--bottom-up {
justify-content: flex-end;
}
.cs-chat.cs-chat--top-down {
justify-content: flex-start;
}
.cs-chat.cs-chat--left-to-right {
flex-direction: row-reverse;
flex-wrap: nowrap;
justify-content: flex-end;
align-items: flex-end;
}
.cs-chat.cs-chat--right-to-left {
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-end;
align-items: flex-end;
}
.cs-chat.cs-chat--horizontal > * {
flex-shrink: 0;
max-inline-size: 300px;
margin-block-start: 0;
margin-inline-start: var(--cs-bubble-gap, 20px);
}
/* ====================
Bubble Base
==================== */
.cs-bubble {
position: relative;
display: flex;
flex-direction: column;
line-height: 1.5;
color: #fff;
max-inline-size: var(--cs-bubble-width, 400px);
/* We need this, so bubbles never shrink. Super mega weird conflict with overflow:hidden on .cs-bubble element */
flex-shrink: 0;
paint-order: stroke fill;
padding-block-start: var(--cs-bubble-padding-top, var(--cs-bubble-padding, 0px));
padding-inline-end: var(--cs-bubble-padding-right, var(--cs-bubble-padding, 0px));
padding-block-end: var(--cs-bubble-padding-bottom, var(--cs-bubble-padding, 0px));
padding-inline-start: var(--cs-bubble-padding-left, var(--cs-bubble-padding, 0px));
background-color: var(--cs-bubble-background-color, transparent);
background-image: var(--cs-bubble-background-gradient, none);
background-repeat: no-repeat;
border-width: var(--cs-bubble-border-width, 0px);
border-style: var(--cs-bubble-border-style, solid);
border-color: var(--cs-bubble-border-color, transparent);
border-start-start-radius: var(--cs-bubble-border-radius-top-left, var(--cs-bubble-border-radius, 0px));
border-start-end-radius: var(--cs-bubble-border-radius-top-right, var(--cs-bubble-border-radius, 0px));
border-end-end-radius: var(--cs-bubble-border-radius-bottom-right, var(--cs-bubble-border-radius, 0px));
border-end-start-radius: var(--cs-bubble-border-radius-bottom-left, var(--cs-bubble-border-radius, 0px));
box-shadow: var(--cs-bubble-box-shadow-inset,) var(--cs-bubble-box-shadow-offset-x, 0px) var(--cs-bubble-box-shadow-offset-y, 0px) var(--cs-bubble-box-shadow-blur-radius, 0px) var(--cs-bubble-box-shadow-spread-radius, 0px) var(--cs-bubble-box-shadow-color, transparent);
/* Animation */
animation-name: var(--cs-bubble-animation, bp-slide-in-up);
animation-duration: var(--cs-bubble-animation-duration, 750ms);
animation-delay: var(--cs-bubble-animation-delay, 0ms);
animation-timing-function: var(--cs-bubble-animation-timing-function, ease-out);
animation-fill-mode: both;
}
/* Full Width */
.cs-chat.cs-full-width {
align-items: stretch !important;
}
.cs-chat.cs-full-width .cs-bubble {
inline-size: 100%;
max-inline-size: none;
}
/* Direction */
.cs-bubble.cs-direction-rtl {
direction: rtl;
}
/* Alignment (applied to .cs-chat container) */
.cs-chat.cs-align-left {
align-items: flex-start !important;
}
.cs-chat.cs-align-center {
align-items: center !important;
}
.cs-chat.cs-align-right {
align-items: flex-end !important;
}
.cs-chat.cs-align-top {
align-items: flex-start !important;
}
.cs-chat.cs-align-bottom {
align-items: flex-end !important;
}
/* ====================
Avatar
==================== */
.cs-avatar {
display: flex;
align-items: center;
flex-shrink: 0;
position: relative;
z-index: 1;
&:empty {
display: none;
}
img {
inline-size: var(--cs-avatar-size, 60px);
block-size: var(--cs-avatar-size, 60px);
object-fit: cover;
/* Box Properties */
padding-block-start: var(--cs-avatar-padding-top, var(--cs-avatar-padding, 0px));
padding-inline-end: var(--cs-avatar-padding-right, var(--cs-avatar-padding, 0px));
padding-block-end: var(--cs-avatar-padding-bottom, var(--cs-avatar-padding, 0px));
padding-inline-start: var(--cs-avatar-padding-left, var(--cs-avatar-padding, 0px));
background-color: var(--cs-avatar-background-color, transparent);
background-image: var(--cs-avatar-background-gradient, none);
background-repeat: no-repeat;
border-width: var(--cs-avatar-border-width, 0px);
border-style: var(--cs-avatar-border-style, solid);
border-color: var(--cs-avatar-border-color, transparent);
border-start-start-radius: var(--cs-avatar-border-radius-top-left, var(--cs-avatar-border-radius, 0px));
border-start-end-radius: var(--cs-avatar-border-radius-top-right, var(--cs-avatar-border-radius, 0px));
border-end-end-radius: var(--cs-avatar-border-radius-bottom-right, var(--cs-avatar-border-radius, 0px));
border-end-start-radius: var(--cs-avatar-border-radius-bottom-left, var(--cs-avatar-border-radius, 0px));
box-shadow: var(--cs-avatar-box-shadow-inset,) var(--cs-avatar-box-shadow-offset-x, 0px) var(--cs-avatar-box-shadow-offset-y, 0px) var(--cs-avatar-box-shadow-blur-radius, 0px) var(--cs-avatar-box-shadow-spread-radius, 0px) var(--cs-avatar-box-shadow-color, transparent);
/* Animation */
animation-name: var(--cs-avatar-animation, none);
animation-duration: var(--cs-avatar-animation-duration, 750ms);
animation-delay: var(--cs-avatar-animation-delay, 0ms);
animation-timing-function: var(--cs-avatar-animation-timing-function, ease-out);
animation-fill-mode: both;
}
}
.cs-bubble.cs-avatar-hidden .cs-avatar {
display: none;
}
/* ====================
User (Username Header Container)
==================== */
.cs-user {
display: flex;
align-items: center;
gap: 4px;
line-height: 1;
z-index: 1;
padding-block-start: var(--cs-username-padding-top, var(--cs-username-padding, 0px));
padding-inline-end: var(--cs-username-padding-right, var(--cs-username-padding, 0px));
padding-block-end: var(--cs-username-padding-bottom, var(--cs-username-padding, 0px));
padding-inline-start: var(--cs-username-padding-left, var(--cs-username-padding, 0px));
background-color: var(--cs-username-background-color, transparent);
background-image: var(--cs-username-background-gradient, none);
background-repeat: no-repeat;
border-width: var(--cs-username-border-width, 0px);
border-style: var(--cs-username-border-style, solid);
border-color: var(--cs-username-border-color, transparent);
border-start-start-radius: var(--cs-username-border-radius-top-left, var(--cs-username-border-radius, 0px));
border-start-end-radius: var(--cs-username-border-radius-top-right, var(--cs-username-border-radius, 0px));
border-end-end-radius: var(--cs-username-border-radius-bottom-right, var(--cs-username-border-radius, 0px));
border-end-start-radius: var(--cs-username-border-radius-bottom-left, var(--cs-username-border-radius, 0px));
box-shadow: var(--cs-username-box-shadow-inset,) var(--cs-username-box-shadow-offset-x, 0px) var(--cs-username-box-shadow-offset-y, 0px) var(--cs-username-box-shadow-blur-radius, 0px) var(--cs-username-box-shadow-spread-radius, 0px) var(--cs-username-box-shadow-color, transparent);
animation-name: var(--cs-username-animation, none);
animation-duration: var(--cs-username-animation-duration, 750ms);
animation-delay: var(--cs-username-animation-delay, 0ms);
animation-timing-function: var(--cs-username-animation-timing-function, ease-out);
animation-fill-mode: both;
}
/* Inside .cs-content */
.cs-user--inline {
display: none;
align-items: baseline;
gap: 4px;
}
.cs-username,
.cs-username--inline {
font-family: var(--cs-username-font-family, 'Space Grotesk'), sans-serif;
font-size: var(--cs-username-font-size, 16px);
font-weight: var(--cs-username-font-weight, 700);
color: var(--cs-username-font-color, var(--cs-usercolor, #ffffff));
-webkit-text-stroke: var(--cs-username-text-stroke-width, 0px) var(--cs-username-text-stroke-color, #000000);
text-shadow: var(--cs-username-text-shadow-offset-x, 0px) var(--cs-username-text-shadow-offset-y, 0px) var(--cs-username-text-shadow-blur-radius, 0px) var(--cs-username-text-shadow-color, transparent);
text-align: start;
white-space: nowrap;
}
.cs-pronouns {
text-align: start;
}
.cs-bubble.cs-username-position-inline .cs-user {
display: none;
}
.cs-bubble.cs-username-position-inline .cs-user--inline {
display: inline-flex;
}
.cs-bubble.cs-username-position-inline .cs-badges--inline {
display: inline-flex;
}
.cs-bubble.cs-username-position-inline .cs-username--inline {
display: inline;
}
.cs-bubble.cs-username-position-inline .cs-message {
display: inline;
}
/* ====================
Pronouns
==================== */
.cs-pronouns:empty,
.cs-pronouns:has(>*:empty),
.cs-pronouns > *:empty{display:none}
.cs-pronouns {
color: var(--cs-pronouns-font-color, currentColor);
background-color: var(--cs-pronouns-background-color, transparent);
}
/* ====================
Timestamp
==================== */
.cs-timestamp:empty {display:none}
/* ====================
Content Wrapper - Inline Elements Wrapper
==================== */
.cs-content {
flex: 1;
min-inline-size: 0;
}
/* ====================
Badges
==================== */
.cs-badges {
display: inline-flex;
align-items: center;
gap: 4px;
&:empty {
display: none !important;
}
img {
inline-size: calc(var(--cs-username-font-size, 14px) + 2px);
aspect-ratio: 1;
border-radius: 1px;
}
}
.cs-badges--inline {
display: none;
align-self: stretch;
align-items: center;
gap: 3px;
margin-inline-end: 3px;
}
.cs-bubble.cs-badges-hidden {
.cs-badges,
.cs-badges--inline {
display: none;
}
}
.cs-bubble.cs-pronouns-hidden {
.cs-pronouns,
.cs-pronouns--inline {
display: none;
}
}
/* Badges Position */
/* Header: after-name → badges come after username */
.cs-bubble.cs-badges-position-after-name .cs-user .cs-badges {
order: 1;
}
/* Inline: after-name → badges after username inside inline wrapper */
.cs-bubble.cs-badges-position-after-name .cs-user--inline .cs-badges--inline {
order: 1;
}
/* ====================
Message
==================== */
.cs-message {
position: relative;
word-break: break-word;
display: block;
padding-block-start: var(--cs-message-padding-top, var(--cs-message-padding, 0px));
padding-inline-end: var(--cs-message-padding-right, var(--cs-message-padding, 0px));
padding-block-end: var(--cs-message-padding-bottom, var(--cs-message-padding, 0px));
padding-inline-start: var(--cs-message-padding-left, var(--cs-message-padding, 0px));
background-color: var(--cs-message-background-color, transparent);
background-image: var(--cs-message-background-gradient, none);
background-repeat: no-repeat;
border-width: var(--cs-message-border-width, 0px);
border-style: var(--cs-message-border-style, solid);
border-color: var(--cs-message-border-color, transparent);
border-start-start-radius: var(--cs-message-border-radius-top-left, var(--cs-message-border-radius, 0px));
border-start-end-radius: var(--cs-message-border-radius-top-right, var(--cs-message-border-radius, 0px));
border-end-end-radius: var(--cs-message-border-radius-bottom-right, var(--cs-message-border-radius, 0px));
border-end-start-radius: var(--cs-message-border-radius-bottom-left, var(--cs-message-border-radius, 0px));
box-shadow: var(--cs-message-box-shadow-inset,) var(--cs-message-box-shadow-offset-x, 0px) var(--cs-message-box-shadow-offset-y, 0px) var(--cs-message-box-shadow-blur-radius, 0px) var(--cs-message-box-shadow-spread-radius, 0px) var(--cs-message-box-shadow-color, transparent);
font-family: var(--cs-message-font-family, 'Inter'), sans-serif;
font-size: var(--cs-message-font-size, 16px);
font-weight: var(--cs-message-font-weight, 400);
color: var(--cs-message-font-color, #ffffff);
-webkit-text-stroke: var(--cs-message-text-stroke-width, 0px) var(--cs-message-text-stroke-color, #000000);
text-shadow: var(--cs-message-text-shadow-offset-x, 0px) var(--cs-message-text-shadow-offset-y, 0px) var(--cs-message-text-shadow-blur-radius, 0px) var(--cs-message-text-shadow-color, transparent);
text-align: start;
animation-name: var(--cs-message-animation, none);
animation-duration: var(--cs-message-animation-duration, 750ms);
animation-delay: var(--cs-message-animation-delay, 0ms);
animation-timing-function: var(--cs-message-animation-timing-function, ease-out);
animation-fill-mode: both;
.cs-gif {
inline-size: auto;
block-size: auto;
max-block-size: 140px;
max-inline-size: 100%;
vertical-align: middle;
}
.emote {
inline-size: calc(var(--cs-message-font-size, 14px) + 16px);
margin-inline: 2px;
vertical-align: middle;
}
img {
max-inline-size: 100%;
}
}
/* ====================
Events
==================== */
.cs-events {
animation-name: var(--cs-events-animation, none);
animation-duration: var(--cs-events-animation-duration, 750ms);
animation-timing-function: ease-out;
animation-fill-mode: both;
}
.cs-bubble.cs-events-hidden .cs-events {
display: none;
}
/* ====================
Font Modifiers
==================== */
/* Username */
.cs-bubble.cs-username-italic .cs-username,
.cs-bubble.cs-username-italic .cs-username--inline,
.cs-bubble.cs-username-italic .cs-pronouns {
font-style: italic;
}
.cs-bubble.cs-username-underline .cs-username,
.cs-bubble.cs-username-underline .cs-username--inline,
.cs-bubble.cs-username-underline .cs-pronouns {
text-decoration: underline;
}
.cs-bubble.cs-username-uppercase .cs-username,
.cs-bubble.cs-username-uppercase .cs-username--inline,
.cs-bubble.cs-username-uppercase .cs-pronouns {
text-transform: uppercase;
}
.cs-bubble.cs-username-lowercase .cs-username,
.cs-bubble.cs-username-lowercase .cs-username--inline,
.cs-bubble.cs-username-lowercase .cs-pronouns {
text-transform: lowercase;
}
.cs-bubble.cs-username-capitalize .cs-username,
.cs-bubble.cs-username-capitalize .cs-username--inline,
.cs-bubble.cs-username-capitalize .cs-pronouns {
text-transform: capitalize;
}
.cs-bubble.cs-username-align-center .cs-username,
.cs-bubble.cs-username-align-center .cs-username--inline,
.cs-bubble.cs-username-align-center .cs-pronouns {
text-align: center;
}
.cs-bubble.cs-username-align-end .cs-username,
.cs-bubble.cs-username-align-end .cs-username--inline,
.cs-bubble.cs-username-align-end .cs-pronouns {
text-align: end;
}
/* Message */
.cs-bubble.cs-message-italic .cs-message {
font-style: italic;
}
.cs-bubble.cs-message-underline .cs-message {
text-decoration: underline;
}
.cs-bubble.cs-message-uppercase .cs-message {
text-transform: uppercase;
}
.cs-bubble.cs-message-lowercase .cs-message {
text-transform: lowercase;
}
.cs-bubble.cs-message-capitalize .cs-message {
text-transform: capitalize;
}
.cs-bubble.cs-message-align-center .cs-message {
text-align: center;
}
.cs-bubble.cs-message-align-end .cs-message {
text-align: end;
}
CSS Load Order
The overlay loads CSS in this order. Later sources override earlier ones:
- animations.css — Keyframe definitions for all entrance animations (
bp-fade-in,bp-slide-in-up, etc.) - framework.css — Structural layout, all CSS variable bindings with neutral defaults, toggle classes
- Style CSS Template — Style-specific CSS rules, custom layout, and decorative styling
- Skin CSS Variables — Per-bubble inline CSS variable overrides from skin settings
- Expert Mode CSS — Your custom CSS rules (highest priority)
Default Cascade
CSS property values are resolved in this priority order (lowest to highest):
- Framework Defaults — Neutral zero/transparent defaults from
var()fallbacks inframework.css - Style Template — CSS template defaults from the style's
var()fallback values - Style Settings — The style author's intended base values, compiled to CSS variables
- Skin Overrides — Skin-specific CSS variable overrides (delta only — only changed values)
- Viewer Skin — Personal viewer skin overrides applied per-bubble
- Expert Mode CSS — Your custom CSS rules (highest priority, can override everything)