plans/widget-css-class.md
Source:
.scratch/prd-widget-css-class.md· Context:frontend/CONTEXT.md· ADR:frontend/docs/adr/0001-widget-css-class-target-node.md
Durable decisions that apply across all phases:
server/ee, frontend/ee), no Cloud-specific gating.frontend/src/AppBuilder/...) and server (server/src/modules/apps/services/widget-config/...).lodash.merge(componentMeta.definition.generalStyles, saved.generalStyles) at appUtils.js:211 injects the new cssClass default into existing component records at load time (same path boxShadow used).universalProps; combineProperties() propagates it to all 81 widgets. No per-widget schema edits.cssClass goes in universalProps.**styles**, NOT generalStyles. (Grill correction — generalStyles ignores the accordian key and isn't rendered for revamped widgets; styles rides RenderStyleOptions which honors accordian.)canvas-component _tooljet-{name} node (RenderWidget.jsx:302), not the outer WidgetWrapper. See ADR 0001.universalProps exists independently in frontend componentTypes.js AND server widget-config/index.js (server bucket styles: {} + definition.styles: {} confirmed present) — both must be kept in sync.customStyling): the feature is gated by the customStyling license flag (same flag as the app/workspace Custom CSS panel it depends on), read via useStore((s) => s.license.featureAccess?.customStyling). Gate at the ends only — hide the field in the inspector and skip DOM application when false; never erase the stored value so it returns on re-enable. No schema/migration change (the field stays in the schema regardless of license). (PM correction, 2026-06-18)User stories: As an app builder, I can tag a widget with custom class(es) so my app/workspace Custom CSS can target that widget reliably (in both the editor and the published app). Type: AFK Blocked by: — Repos: root (frontend + server)
A universal cssClass field on every widget. The builder enters one or more
space-separated class names (with optional {{}} bindings) in the widget's
Styles → Advanced section. The resolved value is applied to the widget's inner
root DOM node in both the editor canvas and the viewer/public app, so CSS
authored in the existing Custom CSS panel can target it. No new CSS authoring
surface is added.
cssClass automatically.cssClass (type: code, label "CSS class", accordian: 'Advanced') to universalProps.styles and its default ({ value: '' }) to universalProps.definition.styles in widget-config/index.js. Schema mirror only — no service/controller/endpoint changes.cssClass additions in componentTypes.js universalProps.styles + definition.styles.canvas-component node at RenderWidget.jsx:302, trimmed + whitespace-collapsed ((resolvedStyles?.cssClass ?? '').trim().replace(/\s+/g, ' ')).RenderStyleOptions (revamped path), pin the Advanced accordion last in the group order (universal styles are spread first in combineProperties, so without this it would render at the top).cssClass as a top-level style control (not grouped) — no extra wiring on that sunsetting path.{{}}-bound class expression resolves to the correct class at render.canvas-component _tooljet-{name}) in the editor.{{}} bindings in the field resolve dynamically (e.g. conditional class from a component value).customStyling license flag false: the "CSS class" field / Advanced group is hidden in the inspector, and any previously-saved class is NOT applied to the DOM. With the flag true again: the saved class re-appears in the field and re-applies — value was never erased.cssClass.setCustomClass CSA (v2) — future / planned separatelyUser stories: As an app builder, I can change a widget's class imperatively at runtime from RunJS or an event handler. Type: AFK Blocked by: Phase 1 Repos: root (frontend)
Deferred. Dynamic
{{}}binding in Phase 1 covers reactive class changes; this phase adds imperative control. Scope and acceptance criteria to be detailed when v2 work starts.
A component-specific action (CSA) — e.g. setCustomClass (and possibly
addClass / removeClass) — that lets builders set the widget's custom class
from event handlers and RunJS.
cssClass. No server changes expected.