Two shortcuts before you write any CSS: build a theme visually in the Theme Playground, or have the UI Customization Plugin extract the values from your Figma design and write the overrides for you.
Make your CSS reach Velt
Velt can render inside a shadow DOM, which blocks your stylesheets.- CSS variables (
--velt-*) always cross the boundary. Theming with variables alone needs nothing here. - Class and element selectors don’t. Pick one of these:
- React / Next.js
- Other Frameworks
type: "styles" for a CSS string, type: "link" for a stylesheet URL.
- React / Next.js
- Other Frameworks
Wireframes change this. Registering a component’s root wireframe (e.g.
VeltCommentDialogWireframe) removes that component’s shadow DOM automatically, so your class CSS reaches it. A nested-only wireframe doesn’t: set shadowDom={false} yourself. Inline style="" always works either way.Not every component takes shadowDom: the per-component prop lists are in Props.Theme with variables
Put all Velt CSS in one stylesheet and override the tokens you need:CSS variables; don’t invent names. Older surfaces read a few --legacy-velt-* tokens, which are listed there too.
Dark mode
Velt setsdata-velt-theme="dark" on the document root when dark mode is on. You supply the values:
darkMode prop (also dialogDarkMode, pinDarkMode, … for components Velt injects for you), with setDarkMode() app-wide, or by wiring your own prefers-color-scheme listener to it:
- React / Next.js
- Other Frameworks
Fonts
One global token sets the font across every Velt surface:--velt-font-size-* scale. Line-height and weight are per-component.
Override classes
For anything variables don’t cover, target Velt’s classes. Velt’s own styles are high-specificity, so your overrides need!important. That’s the supported way to do class-based Velt CSS, not a hack.
- Run with
shadowDom={false}and inspect the element. - Prefer its
velt-*BEM class over the short legacy twin:velt-comment-dialog--selected, notselected. - Write the rule with
!important:
Unstyled mode
Restyling most of the UI anyway? Strip Velt’s visual styling withsetUnstyledMode() (v6.0.0-beta.10+) and bring your own CSS. Layout and positioning styles are kept so components stay functional. It covers styles in the page head and inside shadow roots, and is reversible.
- React / Next.js
- Other Frameworks
globalStyles: false in your config:
- React / Next.js
- Other Frameworks
Recipes
Specific selector tricks for things variables don’t reach. All of these assumeshadowDom={false} and use !important for the reason above.
Map your brand tokens onto Velt's
Map your brand tokens onto Velt's
Define your brand once as your own variables, then point the
--velt-* tokens at them. One place to change your brand, and Velt stays in sync with your app.Reveal actions only on hover
Reveal actions only on hover
Hide the kebab, options, and reaction icons until hover. Velt already wires this on There is no hover on touch devices. Velt force-shows per-comment actions at mobile widths, so if you build your own hover-reveal add an always-visible fallback under a mobile media query.
.velt-thread-card--container:hover, and you can force it with .velt-thread-card--show-actions.Swap the reaction tool and reaction panel by count
Swap the reaction tool and reaction panel by count
The pin carries
velt-reaction-pin--no-reactions only when the count is 0. Use it, and :has(app-reaction-pin), to position or swap the reaction UI.Style the unread dot
Style the unread dot
Style resolved threads
Style resolved threads
There is no
--resolved class. Detect the unresolve button instead.Hide a container when its data slot is empty
Hide a container when its data slot is empty
Resize the default avatar
Resize the default avatar
Indent threaded replies
Indent threaded replies
Remove default styling a wireframe didn't replace
Remove default styling a wireframe didn't replace
Wireframing a slot doesn’t strip all of Velt’s surrounding defaults: borders, padding, backgrounds, fixed widths, and popover chrome often remain. Inspect, find the Treat “inspect, find class, override” as part of every wireframe pass, not a failure.
velt-* class, override it.Switch a collapsed composer to expanded
Switch a collapsed composer to expanded
Render a collapsed input and an expanded one in the composer slot, and let Velt’s state classes switch between them.The switches are
velt-composer-open, velt-comment-dialog--no-comments, and velt-composer-edit-mode.Fix a dialog or pin rendering in the wrong place
Fix a dialog or pin rendering in the wrong place
Some Velt pieces (dialogs, pins, overlays, reaction pins) render with The most common “why is my dialog floating in the wrong spot?” fix.
position: absolute. If a mounted primitive or wireframe appears top-left or escaping its box, give its parent position: relative so the absolute child anchors to it.What CSS can and can’t do
Writing
display:none to remove parts, or wishing you could move a button? You’ve hit CSS’s ceiling: escalate to wireframes to restructure, or primitives to toggle features.
Troubleshooting
Common CSS symptoms, inDebugging:
- “My CSS does nothing”
- “Dark mode colors are wrong”
- “Default styling is still there even though I wireframed it”
- “My sidebar or list won’t scroll”
- “My dialog, pin, or primitive renders in the wrong place”
Checklist
-
shadowDom={false}on components you style with classes. - All Velt CSS in one stylesheet.
- Only token names that exist in
CSS variables. - Dark values under
:root[data-velt-theme="dark"]. - No
display:noneto remove features: toggle them with a prop.

