Skip to main content

Patterns: Quick Reference

Detailed rules live in docs/patterns/*.md. Load the matching context group (patterns-styling, patterns-components, patterns-architecture, patterns-review) from docs/AI_INDEX.md when you need the full rules.

TaskApproach
Add a CSS custom propertyDeclare under :root, :host in src/styles/customVariables.scss, mirror as $var: var(--var) SCSS alias below the :root block, then pass it from JS via the cssVars({ variables: { ... } }) call in src/app.ts
Bridge a runtime JS value to SCSSAdd it to the cssVars({ rootElement, variables }) call in src/app.ts (BUBBLE branch line ~86, EMBEDDED branch line ~170). Do not call element.style.setProperty — the ponyfill is the only token bridge
Add a translation keyAdd the key to every JSON under src/assets/translations/ (28 locales). LocaleManager.translate() throws on missing keys at runtime
Add a new localeAdd <locale>.json to src/assets/translations/ with all existing keys, then add the locale code to SupportedLocalesArr in src/locale/LocaleManager.ts
Add a SCSS fileCreate src/styles/<name>.scss, then add @import "<name>"; to src/styles/app.scss. Do not add import "./foo.scss" to a .tsx file
Add a class-name constantAdd to src/misc/constants.ts with _CLASSNAME suffix, UPPER_SNAKE_CASE, value is the string class itself (e.g. "webchat__end-livechat__button")
Choose a BEM prefixwebchat__* for botframework-webchat overrides, hbf-* / hbf__* for Helvia-internal HTML you own. Do not introduce new flat names (chat-header, btn-sonar etc. are legacy)
Build a conditional classNameTernary string in className, e.g. className={`btn ${isActive ? 'btn--active' : ''}`}. Do not add the classnames library — it is not a dependency
Add an enumAdd to src/interfaces/enums.ts (the central registry). Use TS enum for closed sets shared across files. Use string-literal unions (`type Foo = "a"
Add a notification levelAdd a member to NotificationLevels enum in src/interfaces/CustomNotificationInterfaces.ts, then add a branch in NotificationManager.toToastOptions() (src/notifications/notificationManager.tsx) returning ToastOptions for the new level
Add an activity middlewareAdd a static private method on ActivityMiddlewares in src/middlewares/activityMiddlewares.tsx matching the curry signature () => (next) => (...args) => ..., then push the call into the array returned by ActivityMiddlewares.getAll(widget). Order matters — earlier entries see the activity first
Create a popup managerMirror CsatManager or RatingFormManager in src/csat/csatManager.tsx: private static instances: Map<string, T>, getInstance(botPreferences, widget, props), getInstanceById(botDivId), removeInstance(botDivId), removeAllInstances(). Mount React via createRoot(span).render(<Popup .../>) inside the manager method that creates the container
Add a widget optionExtend IDefaultProperties in src/interfaces/botProperties.ts, set the default in src/defaultBotProperties.ts, and (if a CSS token) add it to the cssVars() block in src/app.ts
Add a class-based React decorator (middleware-rendered)New file src/components/<Name>Decorator.tsx, class <Name>Decorator extends React.Component, props as interface <Name>Props { ... } co-located in the file. Wire from a middleware in src/middlewares/activityMiddlewares.tsx
Type a component propinterface FooProps { ... } co-located in the same .tsx file. No I prefix on prop interfaces. I prefix is reserved for shared domain interfaces in src/interfaces/ (see typescript.md)
Suppress a TS error// @ts-expect-error <reason> for new code. The codebase has historically used @ts-ignore; do not extend that pattern