Techniques
Techniques, tips, and tricks
The patterns that survive release upgrades, plus the debugging moves that find the real problem in minutes instead of hours.
Scope everything, always
Anchor every rule to one wrapper so your CSS cannot leak into Experience Builder chrome or other components:
/* Aura sites */
.siteforceContentArea .slds-button_brand { background: #235789; }
/* LWR sites */
.comm-page .slds-button_brand { background: #235789; }
/* OmniScripts only */
.vlocity-omniscript .slds-input { border-radius: 6px; }
Never write rules against body, html, or the universal selector on a portal. The one exception to broad scoping: the OmniScript stylesheet static resource already loads inside the component context, so its selectors can start from the omniscript classes directly.
Tokens first, CSS second
Set brand colors, radius, and spacing through the Lightning Design System Design Tokens field before writing CSS. Tokens flow into places your selectors cannot reach (LWR shadow DOM, component internals), survive DOM changes between releases, and keep your CSS file small. Then use the stylesheet for the things tokens cannot express: layout, specific component shapes, and Safari coverage.
Precision targeting with your own names
Every OmniScript element renders with a data-omni-key attribute carrying the element name you chose in the designer. FlexCard elements carry data-style-id and data-test-id. These are the most precise stable handles you have:
/* One specific field, by its element name */
[data-omni-key="CustomerFirstName"] .slds-input {
font-weight: 600;
}
/* One FlexCard element */
[data-test-id="NewQuote"] .vlocity-btn {
min-width: 160px;
}
Because the attribute value is your own element name, it changes only when you rename the element. This beats any class-chain guesswork.
Beating inline styles
FlexCard and OmniScript designers write per-element choices as inline styles (icon fills, label colors, element backgrounds). Resolution order:
- First choice: change it in the designer. The style stays with the component definition and deploys with it.
- Second choice: a narrow !important override, scoped to one element via data attributes, never a blanket rule:
[data-test-id="NewQuote"] .btnLabel {
color: #235789 !important;
}
Before reaching for !important at all, try winning on specificity: a scoped selector with two classes beats most stylesheet rules without the maintenance tax.
Debugging workflow
- Inspect the element and read the Computed pane in devtools. For any property, expand it to see every rule that tried to set it and which one won, with the file each came from. If the winner came from a style attribute, it is designer inline styling.
- Check which document you are in. If the inspector shows an iframe (dashboards) your CSS will never reach it.
- Test selectors live before shipping: the OmniStyler Inspector extension's Test Selector injects a visible outline through your exact selector, proving it reaches the element. Apply CSS then previews the real rule.
- Force states: devtools can pin :hover and :focus so you can style them without chasing the mouse.
- Check Safari separately: design tokens are not supported there, so confirm the stylesheet alone carries your brand.
- Measure before optimizing: the Network tab shows your static resource loading; OmniStudio time tracking and the action debugger show where step time actually goes.
Versioning and deployment hygiene
- Upload stylesheet static resources with cache control Public, otherwise guest users may not receive them.
- Keep one stable resource name (the OmniScript references it by name) and let the resource body change; Salesforce serves static resources with versioned URLs, so updates propagate without renaming.
- Keep the source CSS in version control or export it from OmniStyler as JSON, so rollback is a re-upload, not a reconstruction.
- Sandbox first, always. Test the Builder preview, the published page, a guest user, authenticated users, and a phone-width viewport.
- Re-test after each Salesforce release. Three times a year the runtime can shift markup; your scoped, stable-class selectors will usually survive, but verify.
Performance: keep the styling cheap
- Keep the generated CSS lean. Disable component blocks you are not using (OmniStyler's component toggles exist for this) and stay well under 50 KB.
- Avoid universal selectors and long descendant chains inside the scope; they force wide style recalculation on every DOM change, and OmniScripts re-render elements as users type.
- Prefer class and attribute selectors over :nth-child positional logic, which breaks when conditional views insert or remove elements.
Beyond CSS: improving the OmniScript itself
Styling cannot rescue a heavy script. The highest-impact improvements, drawn from Salesforce's own best practices:
- Consolidate server calls. Wrap multiple DataRaptor calls in one Integration Procedure per step boundary. One heavier call beats five light ones. Enable fire and forget on IPs whose response the user does not wait for.
- Trim the JSON. Use send and response transformations so the data tree carries only what the script displays. Bloated JSON slows every step transition.
- Keep scripts small. Salesforce recommends staying under roughly 750 elements per OmniScript and no more than 10 levels of nested blocks. Break big processes into multiple reusable scripts.
- Balance the steps. Avoid both ten-field walls and one-field-per-step fatigue. Validate at the current step, not at the end. Prefill everything you already know from context.
- Mind the details users feel: field-level help on anything ambiguous, visible required markers, Save for later enabled on long scripts, conditional views instead of permanently hidden steps, and element names without spaces (they load faster and make cleaner data-omni-key selectors).
- Accessibility: keep text contrast at 4.5:1 or better, never remove focus outlines, and note Salesforce's own warning that edit blocks in table mode are not screen reader compatible; FlexCards with data tables are the accessible alternative.