# Design Conventions UI and visual conventions for the admin and members portals. Read this when working on ActiveAdmin pages, members views, or CSS. ## CSS Native CSS. Propshaft serves `admin.css` and `member.css`, which `@import` sheets under `shared/`, `admin/`, and `members/`. Save and refresh. **New widget.** Add a file next to its siblings, `@import` it from the portal entry, name the thing (`.panel-action-row`). Do not attach a long class string to a one-off `div`. - Colors and space live in `shared/tokens.css`. Change them there. Numbered greys/greens stay put. Semantic names flip on `html.dark` / `.dark`. Do not invent a token for a 1–2× pair, a hover-only green, a status inversion, or CodeJar hex. - Stored preference is `system|light|dark` on `html[data-theme]`. Paint via `html.dark` / `.dark`. Do not switch to `@media (prefers-color-scheme)` or `light-dark()`. - Markup we own uses semantic classes (`.btn`, `.status-tag`, `.panel`). Name the thing, not the paint. - Prefix members layout chrome (`member-app`, `member-nav`) and Stimulus maps (`member-menu-open`). Admin chrome uses `admin-` the same way. Page widgets do not (`shop-cart`, `billing-card`). - Tiny utilities live in `shared/utilities.css` (`is-*`, `cluster` / `stack`, `icon-*`, type helpers). Read that file. Do not grow a spacing scale in HTML. Tooltip/menu visibility is nested (`.tooltip.is-visible`). - ActiveAdmin gem HTML is styled with descendant selectors. Do not fork gem templates to rename their classes. - Members page gutter is `--inline-space` (`1rem` on mobile, `1ch` from `48rem`) on rewritten chrome, with matching bleeds. Formtastic and tables stay `rem`. Do not find-replace rem. - `@media` repeats rem (`48rem` / `64rem` / `40rem`). `var()` is invalid in queries. `@custom-media` waits for a later Safari floor (today 16.6). - Icons: Lucide SVGs via `icon("name")`. Size and mute them in CSS. ## Icons in Panels and Form Fieldsets Both `panel` and `f.inputs` support the `icon:` option: ```ruby panel "Title", icon: "icon-name" do # ... end f.inputs "Title", icon: "icon-name" do f.input :field end ``` Icons render at half opacity at `1.25rem` (`.icon-5` / `.panel-title-icon`) — a muted visual cue, not a distraction. **Only titled fieldsets get icons** — bare `f.inputs do` blocks (no title string) should never receive an icon. When a fieldset has extra options, place `icon:` before them: ```ruby f.inputs "Title", icon: "icon-name", data: { controller: "..." } do f.inputs "Title", icon: "icon-name", "data-controller" => "..." do ``` Use the same icon for the same concept everywhere (panels, fieldsets, nav). ### Icon Mapping All icons are sourced from [Lucide](https://lucide.dev), stored as SVGs in `app/assets/images/icons/`. Two custom exceptions are noted below. The panel icon for a concept **must** match the nav icon when one exists. #### Resource / Model Icons | Concept | Icon Name | Nav | Notes | | ------------------ | ------------------ | :-: | ------------------------------------- | | Member | `users` | ✓ | Plural for collections | | Producer | `user-round-group` | | Shared shop and basket content source | | Membership | `calendar-range` | ✓ | | | Delivery | `calendar` | | | | Basket | `shopping-bag` | ✓ | Paniers nav (basket_content.rb) | | Shop / Shop Orders | `shopping-basket` | ✓ | Shop nav (active_admin.rb) | | Activity | `handshake` | ✓ | Nav uses same icon | | Invoice / Billing | `banknotes` | ✓ | ⚠️ Custom Heroicon (no Lucide plural) | | Payment | `banknotes` | ✓ | Same as above | | Absence | `tent` | | | | Temporary address | `clock-fading` | | Overlay row; CTA uses `map-pin-house` | | Email / Mails | `mails` | ✓ | Plural; nav uses `mail` | | Newsletter | `megaphone` | | | | Mail Template | `clipboard` | | | | Announcement | `megaphone` | | Same as newsletter | | Bidding Round | `scale` | | | | Shares | `receipt-text` | | | | Basket Content | `sprout` | | | | Price reduction | `ticket-percent` | | Not the shop tag icon | #### Non-Model / Generic Panel Icons | Concept | Icon Name | Notes | | ------------------- | ------------------------ | --------------------------- | | Details | `notebook-text` | ID, dates, validation info | | Contact | `contact-round` | Name, email, phone, address | | Billing (panel) | `banknotes` | ⚠️ Custom Heroicon | | Amount / Pricing | `receipt-text` | Monetary breakdown | | Notes | `notepad-text` | Free-text notes | | Comments | `message-square-text` | ActiveAdmin comments | | Waiting / Pending | `clock` | Waiting membership | | Config / Settings | `sliders-horizontal` | Configuration options | | Renewal | `refresh-cw` | Membership renewal | | Sheets / PDF | `file-spreadsheet` | PDF sheet documents | | Attachments | `paperclip` | File attachments | | Notifications | `mail-check` | Email notifications | | Registration / Form | `form` | Member registration form | | Recipients | `users` | Mail recipients list | | Preview | `eye` | Content preview | | Carpooling | `car` | Carpooling info | | State / Status | `circle-check-big` | State/validation info | | Address / Location | `map` | Physical address | | Overdue Notices | `mail-warning` | Overdue/reminder notices | | Periods | `calendar-days` | Delivery cycle periods | | Information | `info` | Info/help text | | Missing Deliveries | `triangle-alert` | Warning/missing data | | Support | `life-buoy` | Help / support | | Updates | `gift` | What's new | | Analytics | `chart-no-axes-combined` | Historical year-over-year | ### Custom Icons Two icons in `app/assets/images/icons/` are **not** from Lucide: - **`banknotes`** — Custom Heroicon (stacked bills). Lucide only has singular `banknote`. Used for billing/payment panels and nav. - **`redo-off`** — Custom icon (redo arrow with slash). Hand-made for "no renewal" state. ### Adding New Icons 1. Find the icon on [Lucide](https://lucide.dev) 2. Download the SVG and save to `app/assets/images/icons/{name}.svg` 3. Set `stroke-width="1.5"` and remove the `class` attribute 4. Use via `icon("name")`, `panel "Title", icon: "name"`, or `f.inputs "Title", icon: "name"` ### Style Guidelines - **Lucide only** — all icons come from Lucide (two custom exceptions noted above) - **24px / stroke 1.5** — all SVGs use `width="24" height="24"` and `stroke-width="1.5"` - **Match nav icons** — if a concept has a nav icon, the panel must use the same one - **One icon per concept** — same concept = same icon everywhere