# Frontend Development Standards

## Naming Convention (LOCKED)

The shopdb-flask naming convention lives in the root `CONTRIBUTING.md`. Read it before naming any variable, component, API param, or CSS class.

Frontend-specific reminders pulled from the convention:

- Variables holding API field values: match the API field name exactly. Do NOT convert to camelCase. (`response.machineid`, NOT `response.machineId`)
- Pure JS variables: camelCase (`currentUser`, `isLoading`)
- Vue components: PascalCase, spelled out (`AssetDetail.vue`, `MachineForm.vue`)
- CSS classes: lowercase with dashes (`asset-detail`, `machine-form`)
- API params sent to backend: match DB column names without underscores (`params.locationid = 5`, NOT `params.location_id`)
- No emojis, em-dashes, smart quotes, or Unicode arrows anywhere. Plain ASCII only.
- Banned shorthand as standalone variables: `cfg`, `ctx`, `mgr`, `req`, `res`, `env`, `util`, `helper`. Spell them out (`canvasContext`, `response`, `manager`, etc.). Suffix usage like `printers_bp` is allowed.

Pre-commit hook at `scripts/check-naming-and-style.sh` enforces these rules.

## CSS Styling Standards

### Use CSS Variables for ALL Colors

**NEVER hardcode colors in component styles.** Always use CSS variables defined in `src/assets/style.css`.

Available variables:
```css
--primary       /* Primary brand color */
--primary-dark  /* Darker primary for hover states */
--secondary     /* Secondary/muted color */
--success       /* Success states (green) */
--warning       /* Warning states (orange) */
--danger        /* Error/danger states (red) */
--bg            /* Page background */
--bg-card       /* Card/panel background */
--text          /* Primary text color */
--text-light    /* Secondary/muted text */
--border        /* Border color */
--link          /* Link color (bright blue in dark mode) */
```

**Bad:**
```css
.my-card {
  background: white;
  color: #1a1a1a;
}
```

**Good:**
```css
.my-card {
  background: var(--bg-card);
  color: var(--text);
}
```

### Detail Pages - Use Global Styles

All detail pages (MachineDetail, PCDetail, PrinterDetail, ApplicationDetail) should use the **unified global styles** from `style.css`:

- `.detail-page` - Container wrapper
- `.hero-card` - Main hero section with image and info
- `.hero-image`, `.hero-content`, `.hero-title`, `.hero-meta`, `.hero-details`
- `.section-card` - Info sections
- `.section-title` - Section headers
- `.info-list`, `.info-row`, `.info-label`, `.info-value`
- `.content-grid`, `.content-column` - Two-column layout
- `.audit-footer` - Created/modified timestamps

**Only add scoped styles for page-specific elements** (e.g., supplies grid for printers, version list for applications).

### PrinterDetail.vue is the Master Template for Detail Pages

Use `PrinterDetail.vue` as the reference for new detail pages. Follow its structure and styling patterns.

### List Pages - Use Global Styles

All list pages should use the **unified global styles** from `style.css`:

- `.page-header` - Header with title and action button
- `.filters` - Search and filter controls
- `.card` - Main content container
- `.table-container` - Scrollable table wrapper
- `table`, `th`, `td` - Table styling
- `.pagination` - Page navigation
- `.badge`, `.badge-success`, etc. - Status badges
- `.actions` - Action button column

**PrintersList.vue is the Master Template for List Pages**

Use `PrintersList.vue` as the reference for new list pages. It has NO scoped styles - everything uses global CSS.

**Only add scoped styles for page-specific elements** (e.g., icon cells for applications, stats badge for knowledge base).

### Dark Mode Support

Dark mode is automatic via `@media (prefers-color-scheme: dark)`. Using CSS variables ensures colors adapt automatically - no extra work needed per page.

## Data Entry: Page or Modal

Both patterns are in use on purpose. Pick by what the record IS, not by how big the form feels today.

**Routed form page** (`views/<Thing>Form.vue` + a route) when the record has its own detail page and its own URL: machines, PCs, printers, network devices, measuring tools, USB devices, applications, knowledge-base articles, notifications, printed items. These forms are long, often carry an image upload, a map-position picker or relationship editing, and someone will want to link straight to one.

**Modal over its list** when the record is reference data that only exists inside the list it belongs to: every `*TypesList`, plus locations, vendors, models, VLANs, subnets, support teams, custom fields, API tokens, printer drivers, dashboard defaults. The form is a handful of fields, and keeping the list visible behind the dialog is the point.

Rule of thumb: **a thing with a detail page gets a form page; a lookup row gets a modal.**

Modal rules (from the overlay-close fix, commit d8fe0a4):

- A modal holding typed input must NOT close on overlay click or Escape. Losing a part-filled form to a stray click is not an acceptable failure.
- A confirmation dialog MAY close on overlay click (`@click.self`), since it holds nothing to lose.
- The panel background is `var(--bg-card-solid)`, never `var(--bg-card)`: the card variable is translucent in dark mode, which leaves a dialog see-through over the overlay. The shared `.modal` in `style.css` already does this; hand-rolled `.modal-panel` rules must too.

## Component Organization

- **Global styles**: `src/assets/style.css`
- **Page-specific styles**: Scoped `<style scoped>` block, only for unique elements
- **Font sizes**: Use `rem` units, base is 18px for readability on 1080p

## File Structure

```
src/
  assets/
    style.css          # Global styles, CSS variables, detail page styles
  views/
    machines/
      MachineDetail.vue   # Uses global styles only
    pcs/
      PCDetail.vue        # Global + PC-specific (app-list, etc.)
    printers/
      PrinterDetail.vue   # Global + printer-specific (supplies-grid)
    applications/
      ApplicationDetail.vue  # Global + app-specific (version-list, pc-list)
```
