The choice, every time: a clean partial (ship what’s supported + a clear comment + a gap entry) beats a complete hack (looks right today, breaks on the next render or SDK update, and misleads the next person). When in doubt, choose the honest gap. (R0.)This complements
Debugging. That page fixes problems; this page decides whether a problem is even fixable, and what to do when it isn’t.
The flow: run it in order, stop at the first “yes”
Before you ever conclude “the SDK can’t do this,” eliminate the fixable causes. Most “blockers” are one of these.1. Is it a styling problem that has a supported fix?
- CSS does nothing? Shadow DOM is on → set
shadowDom={false}orclient.injectCustomCss(...)(R6). Specificity? → add!important(R9b). Wrong class? → inspect and use the realvelt-*class. → Fixable. Not a gap. - Default styling bleeds through a wireframe? Expected: inspect → override the
velt-*class with!important(R9b). → Fixable.
Debugging.)
2. Is it the wrong layer?
Re-run the decision tree for this piece. The most common escalations:- Need your own interactive component / UI library inside the surface, or a wireframe slot’s
onClick/state is being stripped (R4)? → Primitives, not wireframes. - Need to force show/hide a piece on your logic? → a primitive with
defaultCondition={false}(wireframes have no equivalent). - Need to restructure a leaf piece deeper than its slot allows? → use that leaf’s wireframe (mix).
3. Is the piece just hidden by default?
If the design shows something Velt doesn’t render by default (reply avatars, priority, minimap,@here, device badge, comment index/pin number, sidebar-button-on-dialog, …), it’s almost certainly an off-by-default feature: enable it with its prop/method (Feature flags). Enabling a documented feature is not a hack. → Not a gap.
4. Is it custom data, not custom UI?
If the design shows custom statuses / priorities / categories / reactions or app-specific values, that’s a data config (customStatus / customPriority / customCategory / customReactions, or context), not a structural limit (Component config). → Not a gap.
5. Is the design asking Velt to change behavior or data, not presentation?
Velt owns behavior, data, and real-time sync; you own presentation. If the design implies a different behavior (a new action, a different data model, a workflow Velt doesn’t run), that’s out of scope, not a styling gap. Don’t fake the behavior. Note it as out-of-scope (or a feature request), distinct from an SDK customization gap.6. Still no clean path? → it’s a real SDK gap. Stop.
If none of 1-5 apply: no supported style fix, no layer that expresses it, not a flag, not custom data, genuinely presentation, then the goal has no clean supported path today. Do not reach for a DOM/timing hack to force it (R0). Record it (below), ship the best clean partial, and move on.Recording a real gap
Two artifacts, always both.1. A code comment, in place
Leave the blocker visible exactly where the work would have gone: never bury it (R0):2. A gap entry (for the report handed back to Velt)
Capture the same structured fields every time, so the gaps aggregate into one actionable list:| Field | What goes here |
|---|---|
| Surface | The Velt surface (e.g. VeltCommentDialog). |
| Requirement | What the design needed, concretely. |
| Why no clean path | The specific limitation hit, with the rule/reference that confirms it (e.g. “no wireframe slot hosts live inputs; live React in a wireframe is stripped: R4”). |
| Attempted layer(s) | What you tried before concluding it’s a gap (css / wireframe / primitive / headless). |
| Closest clean alternative | What you actually shipped instead (so the surface isn’t broken). |
| Suggested SDK addition | The smallest capability that would make it possible cleanly (e.g. “a dialog side-panel slot that accepts host-rendered content”). |
| Guide ref | The page that documents the limit (Edge cases and limitations, the relevant reference page). |
This is the single most valuable output of a hard customization: an honest, specific map of where the SDK’s customization surface ends. It’s worth more than a fragile hack that hides the same gap.
What is not a gap (quick reference)
Don’t record these as SDK gaps: they have supported answers above:- “My CSS won’t apply.” → shadow DOM / specificity / wrong class (styling fixes).
- “My
onClickin the wireframe does nothing.” → expected (R4); use a slot or escalate to a primitive (wrong layer). - “This feature isn’t showing.” → off-by-default flag (hidden by default).
- “I need custom statuses/reactions.” → custom data config (custom data).
- “I can’t reorder/remove parts with CSS.” → that’s why wireframes exist: escalate (wrong layer).
- “A leaf primitive won’t restructure.” → use that leaf’s wireframe (wrong layer).
See also
Rules: R0 (the prime directive) and the full rule set.Debugging: fix the fixable: symptom → cause → fix.Decision tree: re-pick the layer when you’ve outgrown the current one.Edge cases and limitations: what each layer cannot do (the source of most real gaps).Verifying a customization: the verdict flow that routes an unmet goal here.

