--- name: simulator-styles description: > Simulator.Company Smart Form (CDU) STYLING specialist — authoring complex Less/CSS for Smart Forms: theme tokens, page/form/section layout, component re-skinning, reusable style patterns, responsive and design-system approaches. Use when the user wants to STYLE or RESTYLE an existing Smart Form / CDU app — change its look, build a theme, style a table / sidebar / modal / form, add a design system, fix spacing/colors/fonts, or apply a complex visual design. This skill owns the `style` / `styles/` layer; it reuses the Smart Form tools (pullSmartForm / pushSmartForm / deploySmartForm) but does NOT create form templates — for the data-schema form template use `simulator-forms`, and for page layout / viewModel / backend logic use `simulator-smart-forms` / `simulator-smart-forms-logic`. Activate on: "style a smart form", "CDU styles", "theme the form", "restyle", "custom CSS/Less for the app", "style the table/sidebar/modal/button", "design system for the smart form", "make it look like …", "застилізувати смартформу", "стилі CDU", "тема для форми", "кастомний CSS", "оформити таблицю/ сайдбар/модалку", "застилизовать смартформу", "стили CDU", "тема формы", "кастомный CSS", "оформить таблицу/сайдбар/модалку". --- # Simulator.Company Smart Form STYLING specialist You author and apply **CSS/Less styles** for Smart Forms (CDU / Script apps) on Simulator.Company. Your domain is the **`style` / `styles/` layer** — themes, layout, component re-skinning, reusable patterns, responsive and design-system approaches. This skill is built on patterns reverse-engineered from real production Smart Forms; the recipes below are taken from live Less, not invented. --- ## Scope — what this skill owns (and what it does NOT) | Concern | Skill | |---|---| | **Styling**: `style` / `styles/*`, `pages//style`, `styleClass`, themes, Less | **this skill** | | Form template (data fields / Account Template) | `simulator-forms` | | Page layout JSON (`pages//config`: grid/forms/sections/items), viewModel, locale | `simulator-smart-forms` | | Backend logic (Corezoid `/get` `/send`, dynamic viewModel, `changes[]`) | `simulator-smart-forms-logic` | You **reuse** the Smart Form engine tools (`pullSmartForm`, `pushSmartForm`, `deploySmartForm`, file-history/rollback) — you do not introduce new platform behaviour. When the user needs a *new form/page* first, defer to `simulator-smart-forms`; you come in to make it look right. To attach a `styleClass` to a component you may need a one-line edit to `pages//config` — that's in scope (it's the binding), but designing the layout itself belongs to `simulator-smart-forms`. --- ## How Smart Form styling works (the model) 1. **One Less stylesheet, compiled per save, scoped to `.cdu-page`.** Whatever you write is wrapped in `.cdu-page { … }` at serve time, so `&` = the page root and your styles can't leak out. Less syntax (variables, mixins, `@import`, functions, maps, guards, `each()`) is fully supported. 2. **File organization — and where each rule goes.** Two layouts compile identically; **prefer the modular `styles/` folder** for anything non-trivial: - **Legacy single file** — a root `style` file holds everything (big forms: *Admin Panel*, *CMS*, *LMS*). - **Modular `styles/`** — `styles/index` is the entry point and mainly just `@import`s partials, **in cascade order**: ```less // styles/index — the entry/manifest; imports in cascade order @import "colors_fonts"; // tokens FIRST (everything below + page styles inherit them) @import "init_styles"; // then platform resets + project-wide component defaults // … any other shared partials (mixins, shared components) … ``` - **Page styles** — `pages//style` is **auto-appended after** the main stylesheet (so it **wins the cascade**) and **inherits all root variables/mixins** (no `@import` needed). The platform saves it as `text/css` automatically. It is for **that page's exceptions only**. | File | What belongs here | |---|---| | `styles/index` | Just `@import`s (the entry/manifest), in cascade order. | | `colors_fonts` | Color + font **tokens** (`@color_*`, `@font_*`, `@font-face`). Define them here because everything else — including page styles — inherits them. | | `init_styles` | **(a)** neutralize the platform's default styling (resets); **(b)** project-wide **component defaults** — one base look reused on every page (e.g. a `.button` skin). | | `pages//style` | **Only the page's differences** from the shared styles — page layout + one-off component tweaks. Never define tokens here (they live in `colors_fonts`). | 3. **`styleClass` is the binding contract** between layout JSON and CSS. Every grid / form / section / item in `pages//config` may carry a `styleClass`; your CSS targets that class. - **Static** for structure: `"styleClass":"main_table"`. - **Dynamic** for backend-driven state/theme: `"styleClass":"{{settings_page_text_align}}"` — Corezoid pushes the value (e.g. `text_align_right`, `active_sidebar_btn`) to switch styling. 4. **Validation**: CSS is **not** validated on save; a Less compile error is emitted as a `/* Less Error … */` comment rather than breaking the page. `styleClass` values are never validated — a class with no matching rule is harmless (but dead; clean it up). 5. **Reaching renderer internals**: the public knobs are `styleClass` + documented component classes (`.button`, `.edit`, `.select`, `.table`, `.check`, …). For deeper structure use substring/attribute selectors — `[class*="table__wrap"]`, `[data-class="grid-one-column"]`. **Winning the cascade** — the renderer ships base/inline styles AND per-component CSS-module rules that load *after* your scoped `styles/index`, so an equal-specificity rule of yours **loses the tie by source order**. Beat it by **raising specificity with more stable classes, not ids**: chain your `styleClass` + an ancestor `[class*="…"]` + the element — e.g. `.book-table td.bc-cover-cell .file img` (0,3,2) beats the component's `.file__item__hash img` (0,2,1). Prefer this class chain over `#id` (brittle — it pins the rule to a config `id`, and classes are the house rule) and over a lone `!important` (it only beats non-`!important`; in an `!important` vs `!important` fight specificity still decides). A doubled class (`.x.x`) is a last resort. **See the verified per-component DOM map below for exact hooks (`edit`, `select`, `multiselect`, `radio`, `button`, `row__`).** **The cascade math, with real numbers** (measured against a live renderer build — dump the defaults yourself to confirm, see Workflow §"dump the defaults"): - Your stylesheet is auto-wrapped in `.cdu-page`, so a **bare single-class rule you write is already `0,2,0`** (`.cdu-page .myClass`) and **beats a bare renderer default `.hashed` (`0,1,0`) for free** — most overrides need nothing more. - **BUT the renderer theme-scopes ~⅓ of its rules** (`.theme-light .x` / `.theme-dark .x`, on `#mainRoot`), and those are **also `0,2,0`** — a *tie* with your wrapped single-class rule, and since defaults load **after** you, **the theme default wins**. Symptom: a `background`/`color` override "does nothing" even though your selector clearly matches. Beat it with an extra class (`0,3,0`) or `!important`. - **~1 in 5 default rules already use `!important`** (heavy on `active`/`selected`/`checked` states and theme colors). Matching them with your own `!important` is **normal here, not a smell** — a lone `!important` still loses to a default `!important` of higher specificity, so pair it with a solid class chain. - **Slots own their own background/padding.** Containers like `.section__content`, the sidebar slots (`.sidebar__header/__content/__footer`), and `.page__sidebar` each ship their own bg/padding — painting the *parent* won't show through. Override the **slot**, not its ancestor. > Authoritative component-class + CSS reference: the **"CSS styling"** tag in the CDU swagger and > `$CLAUDE_PLUGIN_ROOT/docs/user-flows/cdu-page-protocol.md`. --- ## Rendered DOM map (verified component internals) Verified against a live `control-cdu` render. The summary below is enough for most work; for the **full per-component tag tree of every component** (incl. all 4 table types, page skeleton, overlays) see `$CLAUDE_PLUGIN_ROOT/docs/user-flows/cdu-dom-tree-reference.md`. Use this to target the right element instead of guessing. **Three rules first:** 1. **`styleClass` lands on the component ROOT.** E.g. `class="label hd-meta label__12ehI"`, `class="edit edit__text txt-input … bordered"`, `class="radio pills … horizontal"`. So a root hook (`.hd-meta`, `.txt-input`, `.pills`) always hits — but only the **outer** element. 2. **Internals use hashed CSS-module classes** (`label__12ehI`, `f-item-0-2-64`, `i-label-0-2-70`, `clickOutside(i)-field-0-2-97`) that change between renderer builds. **Never target the hash.** Reach inside via substring selectors (`[class*="radioItem"]`, `[class*="i-icon"]`, `[class*="chip"]`, `[class*="i-edit"]`), stable state classes (`.checked` `.disabled` `.bordered`), `data-class` attributes, or element selectors (`input`, `textarea`, `label`, `svg`). 3. **`row` / `w` grouping yields a STABLE group class `.row__`.** An item with `"row":"act"` renders inside `
` whose children are `
`. Note `w` is a **relative weight**, not a raw percentage: the rendered width is `w / Σw` across the row (two items at `w:50` each get 50%; at `w:50` + `w:100` they get 33% / 67%). Style the group via `.row__act` (flex container) and items via `[class*="row__item"]` — your reliable hook for multi-column rows, progress bars, etc. **Page skeleton & theme** (stable structural hooks): - **Scope:** all your CSS is wrapped in `.cdu-page` (`&` = page root); per-page hook `.cdu-page-`. - **Dark mode is a CLASS, not a media query:** `.theme-light` / `.theme-dark`. In a live render the class sits on the host wrapper `#mainRoot` (an **ancestor** of `.cdu-page`), which is why `.theme-dark .… {}` works from inside your scoped stylesheet. `control-cdu` itself also stamps the theme class onto `#page` (the `.cdu-page` node) — so **verify the placement on your build**: if the class is only on the `.cdu-page` node (not an ancestor), a wrapped `.theme-dark .foo` won't match and you'd need `&.theme-dark .foo`. Prefer this class approach over `@media (prefers-color-scheme)`. ⚠️ Caveat: the renderer's `light`/`dark` token maps are currently **identical** — the mechanism is wired but dark mode has no distinct palette yet, so a `.theme-dark` override is the only way to make dark actually differ today. - **Grid regions:** `[data-class="grid-one-column"]` / `[data-class="grid-two-column"]` (+`-left`/`-right`); header/footer regions via `[class*="gridtwo__header"]` / `[class*="gridtwo__footer"]`. - **Section slots:** `[data-class="section"]` (+ `[class*="block__"]` for `type:"block"` cards); inner `[class*="section__header"]` / `[class*="section__content"]`. - **Toasts:** two `[class*="notify"]` containers (top inside `#page`, bottom at `#mainRoot` end). **Per-component root + key inner hooks** (`` = your `styleClass`): | Component | Root selector (styleClass here) | Key inner hooks / notes | |---|---|---| | `label` | `.label` / `[data-class="label"]` | `` text; BBCode → real tags; `align`→ `.left__`/`.center__`/`.right__` | | `divider` | `.divider` | empty | | `edit` (all types) | `.edit` (+ `.edit__`) | `.field > input` / `textarea`; states `bordered`(box)/`selected`/`error`; kill box `.field{border:none}`; help/err `[class*="Component-helperText"]` | | `select` | `.select` (outer) | readonly `` in `[class*="i-edit"]` + caret `[class*="endAdornment"]`; **no native `` via `[class*="clickOutside(i)-field"]`; **not** checkbox rows | | `radio` | `.radio` (+ `.horizontal` for row) | options `[class*="radioItem"]`(+`.checked`/`.disabled`); hide svg `[class*="i-icon"]`, style label `[class*="i-label"]` → pills/scales | | `check` | `.check` | `[class*="f-icon"]` svg + `