# Frontend Rules & Conventions **Read this file when working on frontend tasks** (HTML, CSS, JS, locales, templates). ## CSS Custom Properties (Variables) Defined in `server/src/wled_controller/static/css/base.css`. **IMPORTANT:** There is NO `--accent` variable. Always use `--primary-color` for accent/brand color. ### Global (`:root`) | Variable | Value | Usage | |---|---|---| | `--primary-color` | `#4CAF50` | **Accent/brand color** — borders, highlights, active states | | `--primary-hover` | `#5cb860` | Hover state for primary elements | | `--primary-contrast` | `#ffffff` | Text on primary background | | `--danger-color` | `#f44336` | Destructive actions, errors | | `--warning-color` | `#ff9800` | Warnings | | `--info-color` | `#2196F3` | Informational highlights | ### Theme-specific (`[data-theme="dark"]` / `[data-theme="light"]`) | Variable | Dark | Light | Usage | |---|---|---|---| | `--bg-color` | `#1a1a1a` | `#f5f5f5` | Page background | | `--bg-secondary` | `#242424` | `#eee` | Secondary background | | `--card-bg` | `#2d2d2d` | `#ffffff` | Card/panel background | | `--text-color` | `#e0e0e0` | `#333333` | Primary text | | `--text-secondary` | `#999` | `#666` | Secondary text | | `--text-muted` | `#777` | `#999` | Muted/disabled text | | `--border-color` | `#404040` | `#e0e0e0` | Borders, dividers | | `--primary-text-color` | `#66bb6a` | `#3d8b40` | Primary-colored text | | `--success-color` | `#28a745` | `#2e7d32` | Success indicators | | `--shadow-color` | `rgba(0,0,0,0.3)` | `rgba(0,0,0,0.12)` | Box shadows | ## UI Conventions for Dialogs ### Hints Every form field in a modal should have a hint. Use the `.label-row` wrapper with a `?` toggle button: ```html
``` Add hint text to both `en.json` and `ru.json` locale files using a `.hint` suffix on the label key. ### Select dropdowns Do **not** add placeholder options like `-- Select something --`. Populate the `` dropdowns wherever appropriate. Plain HTML selects break the visual consistency of the UI. Any selector with a small fixed set of options (types, modes, presets, bands) should use `IconSelect`; any selector referencing dynamic entities should use `EntitySelect`. Plain `` with a visual grid of icon+label+description cells. See `_ensureCSSTypeIconSelect()`, `_ensureEffectTypeIconSelect()`, `_ensureInterpolationIconSelect()` in `color-strips.ts` for examples. - **Entity references** (picture sources, audio sources, devices, templates, clocks) → use `EntitySelect` from `js/core/entity-palette.ts`. This replaces the `` but keep it in the DOM with its value in sync. **The `` value, call `.refresh()` (EntitySelect) or `.setValue(val)` (IconSelect) to update the trigger display. Call `.destroy()` when the modal closes. **Common pitfall:** Using a preset/palette selector (e.g. gradient preset dropdown or effect type picker) that changes the underlying ` ``` Do **not** use a `range-with-value` wrapper div. ### Tutorials The app has an interactive tutorial system (`static/js/features/tutorials.ts`) with a generic engine, spotlight overlay, tooltip positioning, and keyboard navigation. Tutorials exist for: - **Getting started** (header-level walkthrough of all tabs and controls) - **Per-tab tutorials** (Dashboard, Targets, Sources, Profiles) triggered by `?` buttons - **Device card tutorial** and **Calibration tutorial** (context-specific) When adding **new tabs, sections, or major UI elements**, update the corresponding tutorial step array in `tutorials.ts` and add `tour.*` i18n keys to all 3 locale files (`en.json`, `ru.json`, `zh.json`). ## Icons **Always use SVG icons from the icon system, never text/emoji/Unicode symbols for buttons and UI controls.** - Icon SVG paths are defined in `static/js/core/icon-paths.ts` (Lucide icons, 24×24 viewBox) - Icon constants are exported from `static/js/core/icons.ts` (e.g. `ICON_START`, `ICON_TRASH`, `ICON_EDIT`) - Use `_svg(path)` wrapper from `icons.ts` to create new icon constants from paths When you need a new icon: 1. Find the Lucide icon at https://lucide.dev 2. Copy the inner SVG elements (paths, circles, rects) into `icon-paths.js` as a new export 3. Add a corresponding `ICON_*` constant in `icons.ts` using `_svg(P.myIcon)` 4. Import and use the constant in your feature module Common icons: `ICON_START` (play), `ICON_STOP` (power), `ICON_EDIT` (pencil), `ICON_CLONE` (copy), `ICON_TRASH` (trash), `ICON_SETTINGS` (gear), `ICON_TEST` (flask), `ICON_OK` (circle-check), `ICON_WARNING` (triangle-alert), `ICON_HELP` (circle-help), `ICON_LIST_CHECKS` (list-checks), `ICON_CIRCLE_OFF` (circle-off). For icon-only buttons, use `btn btn-icon` CSS classes. The `.icon` class inside buttons auto-sizes to 16×16. ## Localization (i18n) **Every user-facing string must be localized.** Never use hardcoded English strings in `showToast()`, `error.textContent`, modal messages, or any other UI-visible text. Always use `t('key')` from `../core/i18n.ts` and add the corresponding key to **all three** locale files (`en.json`, `ru.json`, `zh.json`). - In JS modules: `import { t } from '../core/i18n.ts';` then `showToast(t('my.key'), 'error')` - In inline `