Accessibility
Velt’s default UI and its slot components ship with roles, labels, keyboard handling, and focus management. How much of that you keep depends on your approach:
When you’re writing your own markup:
- Use semantic elements (
<button>,<nav>,<ul>), and addaria-*,role, andaria-labelwhere your markup needs them. - Preserve a sensible focus order and visible focus styles (
:focus-visible). Don’toutline: nonewithout a replacement. - Keep interactive behavior in Velt slot components. A custom
<div onClick>in a wireframe doesn’t run at all (the interactivity rule), and a bare div isn’t keyboard-accessible anyway. - Don’t rely on color alone for state (resolved, unread, priority): pair it with an icon or label.
- Keep contrast at WCAG AA (4.5:1 for text) when you override
--velt-*colors. Brand palettes break this easily, so check light and dark. - Respect
prefers-reduced-motionin any transitions you add.
Localization
Four levers, cheapest first: 1. Placeholder props. Pass already-translated values tocommentPlaceholder, replyPlaceholder, editPlaceholder, editCommentPlaceholder, editReplyPlaceholder, and the slot-level placeholder props.
2. Your own text. Anything in your wireframe markup or headless components is yours: render it through your app’s i18n library.
3. Automatic translation. enableAutoTranslation() translates text in Velt components based on the user’s language preference, with no string map to maintain.
4. Your own translations. The strongest lever: supply the strings yourself and switch languages at runtime. Use it to reword Velt’s UI text, not only to translate it.
- React / Next.js
- Other Frameworks
Dates and relative times are rendered by Velt’s own UI. If you render your own timestamps in a headless build, localize them yourself.
Right-to-left (RTL)
- Set
dir="rtl"on the container wrapping your Velt UI. Velt’s UI inherits direction from the DOM. - In your CSS and wireframe markup, use logical properties (
margin-inline-start,padding-inline,inset-inline) instead of hardleft/right, so they mirror automatically. - Mirror directional glyphs you supply (chevrons, arrows, reply icons) with
transform: scaleX(-1)under[dir="rtl"]. - Check anything absolutely positioned (pins, dialogs, dropdown popovers) and your
position: relativewrappers anchor on the correct side. - Fix mirroring in your own CSS, never by hacking Velt internals.
Responsive and mobile
- Use the layout props first, they’re the cheapest fix:
filterPanelLayout="bottomSheet"andfilterOptionLayouton the sidebar,panelOpenModeon notifications, the dialog’s bottom-sheet mode, andembedMode/floatingMode/position. SeeLayout config. - Your media queries apply to your wrappers and your wireframe markup. Velt also flips some internal layouts at mobile breakpoints on its own.
- Touch targets of roughly 44px or more, especially for custom buttons in wireframe slots.
- Hover doesn’t exist on touch. If you build a hover-reveal, add an always-visible fallback under a mobile media query (recipe).
- Re-check the scroll and height chain at mobile sizes: collapsed viewports expose missing
min-height:0links.
Verify before you ship
Run this on every surface you gave custom markup, after customizing it. Manual:- States: empty, loading/skeleton, unread, resolved, private, filtered-to-zero, and long content (truncation).
- Dark mode on and off, RTL on and off, mobile width.
- Keyboard-only navigation through the whole surface, with visible focus. Then a screen-reader pass (VoiceOver or NVDA) and zoom to 200%.
- Scroll actually works; nothing overflows or clips.
- Compare against default: temporarily remove your customization to confirm a problem is yours and not Velt’s.
- Assert on Velt’s stateful classes from
CSS classes, e.g. expect.velt-comment-pin-unread-commentpresent or absent. - Read live data via
Hooksin component tests, e.g. assertuseUnreadCommentCountOnCurrentDocument()?.count. - Put
data-testidon your own wrapper markup, not on Velt internals whose class and structure can change between versions, and target those in e2e. - Visual regression snapshots per surface × (light/dark) × (LTR/RTL) catch CSS-override drift after SDK upgrades, the main risk for class-based overrides.

