Patterns: Quick Reference
Detailed rules live in
docs/patterns/*.md. Load the matching context group (patterns-styling,patterns-components,patterns-architecture,patterns-review) fromdocs/AI_INDEX.mdwhen you need the full rules.
| Task | Approach |
|---|---|
| Add a CSS custom property | Declare 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 SCSS | Add 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 key | Add the key to every JSON under src/assets/translations/ (28 locales). LocaleManager.translate() throws on missing keys at runtime |
| Add a new locale | Add <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 file | Create 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 constant | Add 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 prefix | webchat__* 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 className | Ternary string in className, e.g. className={`btn ${isActive ? 'btn--active' : ''}`}. Do not add the classnames library — it is not a dependency |
| Add an enum | Add 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 level | Add 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 middleware | Add 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 manager | Mirror 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 option | Extend 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 prop | interface 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 |