|
| 1 | +# UI Standards Guide |
| 2 | + |
| 3 | +This document defines the canonical UI patterns for the XtremeIdiots Portal. All views must follow these standards to ensure visual consistency across the site. |
| 4 | + |
| 5 | +## Page Structure |
| 6 | + |
| 7 | +Every page follows this wrapper structure: |
| 8 | + |
| 9 | +```html |
| 10 | +<div class="wrapper wrapper-content animated fadeInRight"> |
| 11 | + <div class="row"> |
| 12 | + <div class="col-12"> |
| 13 | + <div class="container-fluid"> |
| 14 | + <div class="ibox"> |
| 15 | + <div class="ibox-title"><h5>Section Title</h5></div> |
| 16 | + <div class="ibox-content"><!-- Content --></div> |
| 17 | + <div class="ibox-footer"><!-- Action buttons --></div> |
| 18 | + </div> |
| 19 | + </div> |
| 20 | + </div> |
| 21 | + </div> |
| 22 | +</div> |
| 23 | +``` |
| 24 | + |
| 25 | +### Rules |
| 26 | + |
| 27 | +- Every content section **must** be wrapped in `ibox > ibox-title + ibox-content`. |
| 28 | +- The `ibox-title` **must** contain an `<h5>` element. Never place raw text in `ibox-title`. |
| 29 | +- Action buttons (Save, Cancel, Delete, Back) go in `ibox-footer`, not loose inside `ibox-content`. |
| 30 | +- Use `ibox-footer--sticky` on long forms (e.g. GameServers/Edit) to keep the save bar visible. |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Buttons |
| 35 | + |
| 36 | +### Hierarchy |
| 37 | + |
| 38 | +| Role | Class | Icon | Size | |
| 39 | +|------|-------|------|------| |
| 40 | +| **Primary action** (Save, Create, Submit) | `btn btn-primary` | Optional FA icon + text | Default | |
| 41 | +| **Secondary action** (Cancel, Back) | `btn btn-outline-secondary` | No icon, or `fa-arrow-left` for back | Default | |
| 42 | +| **Destructive action** (Delete link in table) | `btn btn-outline-danger` | `fa-trash` | `btn-sm` | |
| 43 | +| **Destructive confirm** (Delete button on confirmation page) | `btn btn-danger` | `fa-trash` | Default | |
| 44 | +| **Cautionary action** (Lift ban) | `btn btn-warning` | `fa-unlock` | Default | |
| 45 | +| **Inline table actions** (Edit, Details) | `btn btn-outline-secondary` | Icon + text | `btn-sm` | |
| 46 | +| **Filter reset** | `btn btn-outline-secondary btn-sm` | No icon | `btn-sm` | |
| 47 | + |
| 48 | +### Prohibited Button Classes for Actions |
| 49 | + |
| 50 | +Do **not** use these for action buttons (reserve for badges/status only): |
| 51 | + |
| 52 | +- `btn-success` — use `btn-primary` instead |
| 53 | +- `btn-info` — use `btn-outline-secondary` instead |
| 54 | +- `btn-xs` — use `btn-sm` instead (btn-xs is a bridge alias only) |
| 55 | + |
| 56 | +### Button Placement |
| 57 | + |
| 58 | +- **List pages:** "Create New" button in `.ibox-tools` within `.ibox-title` (top-right). |
| 59 | +- **Form pages:** Submit + Cancel in `.ibox-footer`. Primary action on the right, Cancel on the left. |
| 60 | +- **Detail pages:** Action buttons in `.ibox-footer`. |
| 61 | +- **Table rows:** `btn-group btn-group-sm` in the last column. |
| 62 | +- **Confirmation pages:** Cancel (left) + Confirm (right) in `.ibox-footer`. |
| 63 | + |
| 64 | +### Cancel Button Wording |
| 65 | + |
| 66 | +- On **confirmation pages**: use `Cancel` (no icon). |
| 67 | +- On **form/edit pages**: use `Back to List` or `Back to [Parent]` with `fa-arrow-left` icon. |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +## Icons |
| 72 | + |
| 73 | +All icons use Font Awesome 6 solid style with fixed-width class: `fa-solid fa-fw fa-[icon-name]`. |
| 74 | + |
| 75 | +### Standard Action Icons |
| 76 | + |
| 77 | +| Action | Icon | Usage | |
| 78 | +|--------|------|-------| |
| 79 | +| Create / Add | `fa-plus` | Create buttons, add links | |
| 80 | +| Edit | `fa-pen-to-square` | Edit buttons and links | |
| 81 | +| Delete | `fa-trash` | Delete buttons and links | |
| 82 | +| View / Details | `fa-eye` | Details/view buttons | |
| 83 | +| Back | `fa-arrow-left` | Back navigation buttons | |
| 84 | +| Save | `fa-floppy-disk` | Save/submit buttons | |
| 85 | +| Clone | `fa-clone` | Clone/copy actions | |
| 86 | +| Import | `fa-file-import` | Import actions | |
| 87 | +| Refresh / Sync | `fa-rotate` | Sync/refresh actions | |
| 88 | +| Cancel (operation) | `fa-ban` | Cancel running operations | |
| 89 | + |
| 90 | +### Rules |
| 91 | + |
| 92 | +- **Always** include `fa-fw` on icons for fixed-width alignment. |
| 93 | +- **Never** use deprecated icons: `fa-save` → `fa-floppy-disk`, `fa-edit` → `fa-pen-to-square`, `fa-file-text` → use `fa-flag` or contextual icon. |
| 94 | +- Include `aria-hidden="true"` on decorative icons (those accompanied by text). |
| 95 | +- Do not use `me-1` for spacing when `fa-fw` is present — `fa-fw` provides consistent width. |
| 96 | + |
| 97 | +--- |
| 98 | + |
| 99 | +## Forms |
| 100 | + |
| 101 | +### Layout |
| 102 | + |
| 103 | +- Use **vertical labels** (label above input) with `form-label` class. |
| 104 | +- Wrap each field in `<div class="mb-3">`. |
| 105 | +- Never use horizontal form layout (`col-sm-2` + `col-sm-10`). |
| 106 | +- Use `form-select` for `<select>` elements, not `form-control`. |
| 107 | +- Use `form-text` for helper text beneath inputs, not `help-block`. |
| 108 | +- Use `form-check` + `form-check-input` + `form-check-label` for checkboxes. |
| 109 | + |
| 110 | +### Textarea Rows |
| 111 | + |
| 112 | +- Short text fields: `rows="3"` |
| 113 | +- Descriptions / content: `rows="6"` |
| 114 | + |
| 115 | +### Long Forms |
| 116 | + |
| 117 | +- Use **Bootstrap nav-tabs** for forms with 5+ logical sections (e.g. GameServers/Edit). |
| 118 | +- Use `ibox-footer--sticky` to keep Save/Cancel visible during scrolling. |
| 119 | +- Toggle tab visibility with CSS class `d-none`, not inline `style="display:none"`. |
| 120 | + |
| 121 | +### Validation |
| 122 | + |
| 123 | +- Always include `<div asp-validation-summary="ModelOnly" class="text-danger"></div>` at the top of forms. |
| 124 | +- Use `<span asp-validation-for="Field" class="text-danger"></span>` below inputs. |
| 125 | + |
| 126 | +--- |
| 127 | + |
| 128 | +## Detail Pages |
| 129 | + |
| 130 | +Use the `detail-fields` component for label-value displays: |
| 131 | + |
| 132 | +```html |
| 133 | +<dl class="detail-fields row"> |
| 134 | + <div class="detail-field col-sm-6"> |
| 135 | + <dt class="detail-label">Label</dt> |
| 136 | + <dd class="detail-value">Value</dd> |
| 137 | + </div> |
| 138 | +</dl> |
| 139 | +``` |
| 140 | + |
| 141 | +### Rules |
| 142 | + |
| 143 | +- Always use `<dl>` semantics with `detail-fields` class. |
| 144 | +- Use `detail-label` (uppercase, muted, small) and `detail-value` (normal text). |
| 145 | +- Arrange fields in a responsive grid: `col-sm-6` (two-column) or `col-sm-4` (three-column). |
| 146 | +- Never use `dl-horizontal` (legacy Bootstrap 3 class). |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +## Filter Bars |
| 151 | + |
| 152 | +Use the `list-filters` class for filter bars on list/index pages: |
| 153 | + |
| 154 | +```html |
| 155 | +<div class="list-filters mb-2"> |
| 156 | + <div class="filter-group"> |
| 157 | + <label class="form-label" for="filterGameType">Game Type</label> |
| 158 | + <select id="filterGameType" class="form-select">...</select> |
| 159 | + </div> |
| 160 | + <div class="filter-group"> |
| 161 | + <label class="form-label" for="resetFilters">Reset</label> |
| 162 | + <button type="button" id="resetFilters" class="btn btn-outline-secondary btn-sm">Reset Filters</button> |
| 163 | + </div> |
| 164 | +</div> |
| 165 | +``` |
| 166 | + |
| 167 | +### Rules |
| 168 | + |
| 169 | +- Use `list-filters` class, not `admin-actions-filters` (legacy alias retained for backward compatibility). |
| 170 | +- Always label the reset button **"Reset Filters"** — not "Clear Filters" or "Reset". |
| 171 | +- Place the filter bar **inside** `.ibox-content`, above the table. |
| 172 | + |
| 173 | +--- |
| 174 | + |
| 175 | +## Tables & DataTables |
| 176 | + |
| 177 | +### Table Classes |
| 178 | + |
| 179 | +All data tables must use: `table table-striped table-hover`. |
| 180 | + |
| 181 | +### Column Width Utilities |
| 182 | + |
| 183 | +- Date/timestamp columns: add `class="table-date-col"` to `<th>` — applies `white-space: nowrap; width: 1%`. |
| 184 | +- Action button columns: add `class="table-action-col"` to `<th>` — same constrained width. |
| 185 | + |
| 186 | +### Empty States |
| 187 | + |
| 188 | +- DataTables: set `language.emptyTable` to a contextual message (e.g. `"No game servers found"`). A global fallback of `"No records found"` is set in `site.js`. |
| 189 | +- Non-table contexts: use the `.empty-state` component: |
| 190 | + |
| 191 | +```html |
| 192 | +<div class="empty-state"> |
| 193 | + <i class="fa-solid fa-fw fa-folder-open empty-state-icon"></i> |
| 194 | + <p class="empty-state-message">No records found</p> |
| 195 | + <div class="empty-state-action"> |
| 196 | + <a href="..." class="btn btn-primary btn-sm">Create New</a> |
| 197 | + </div> |
| 198 | +</div> |
| 199 | +``` |
| 200 | + |
| 201 | +--- |
| 202 | + |
| 203 | +## Destructive Operations |
| 204 | + |
| 205 | +All destructive actions must have a confirmation gate. Use one of two tiers: |
| 206 | + |
| 207 | +### Tier 1: Dedicated Confirmation Page |
| 208 | + |
| 209 | +For permanent, irreversible deletes of primary entities (game servers, admin actions, ban file monitors, demos, tags, map rotations). |
| 210 | + |
| 211 | +**Standard structure:** |
| 212 | + |
| 213 | +```html |
| 214 | +<div class="ibox"> |
| 215 | + <div class="ibox-title"><h5>Delete [Entity Type]</h5></div> |
| 216 | + <div class="ibox-content"> |
| 217 | + <p>Are you sure you want to delete this [entity type]?</p> |
| 218 | + <div class="alert alert-warning mb-3"> |
| 219 | + <i class="fa-solid fa-fw fa-triangle-exclamation" aria-hidden="true"></i> |
| 220 | + This action cannot be undone. [Specific consequence describing sub-entity impact.] |
| 221 | + </div> |
| 222 | + <dl class="detail-fields row"> |
| 223 | + <!-- Entity details shown here --> |
| 224 | + </dl> |
| 225 | + </div> |
| 226 | + <div class="ibox-footer"> |
| 227 | + <a class="btn btn-outline-secondary" asp-action="Index">Cancel</a> |
| 228 | + <button type="submit" class="btn btn-danger"> |
| 229 | + <i class="fa-solid fa-fw fa-trash"></i> Delete [Entity Type] |
| 230 | + </button> |
| 231 | + </div> |
| 232 | +</div> |
| 233 | +``` |
| 234 | + |
| 235 | +### Tier 2: Inline `data-confirm` Dialog |
| 236 | + |
| 237 | +For quick destructive actions within list/detail views (unassign, remove tag, delete map from host, delete permission). |
| 238 | + |
| 239 | +Add a `data-confirm` attribute to the button or link: |
| 240 | + |
| 241 | +```html |
| 242 | +<button type="submit" class="btn btn-outline-danger btn-sm" |
| 243 | + data-confirm="Are you sure you want to [verb] this [entity]? [Consequence if applicable.]"> |
| 244 | + <i class="fa-solid fa-fw fa-trash"></i> Delete |
| 245 | +</button> |
| 246 | +``` |
| 247 | + |
| 248 | +The `data-confirm` handler in `enhanced-ui.js` intercepts the click and shows a browser confirm dialog. No inline `onclick` or `onsubmit` handlers. |
| 249 | + |
| 250 | +### Confirm Message Format |
| 251 | + |
| 252 | +All confirmation messages follow this pattern: |
| 253 | + |
| 254 | +> Are you sure you want to **[verb]** this **[entity]**? **[Consequence if applicable.]** |
| 255 | +
|
| 256 | +Examples: |
| 257 | +- "Are you sure you want to delete this protected name?" |
| 258 | +- "Are you sure you want to unassign this server? Maps will be removed from the server." |
| 259 | +- "Are you sure you want to remove this permission? It may take up to 15 minutes to take effect." |
| 260 | + |
| 261 | +### What NOT to Do |
| 262 | + |
| 263 | +- ❌ `onclick="return confirm('...')"` — use `data-confirm` instead. |
| 264 | +- ❌ `onsubmit="return confirm('...')"` — use `data-confirm` on the submit button instead. |
| 265 | +- ❌ Direct POST with no confirmation on destructive actions. |
| 266 | +- ❌ `btn-danger` on non-destructive actions (Claim, Lift) — use `btn-primary` or `btn-warning`. |
| 267 | + |
| 268 | +--- |
| 269 | + |
| 270 | +## Badges & Status Indicators |
| 271 | + |
| 272 | +- Use SCSS-defined `badge-*` classes or Bootstrap 5 `bg-*` classes. |
| 273 | +- Colour semantics: green = active/online/success, red = error/expired/danger, amber = warning/pending, teal = primary. |
| 274 | +- Use square badges (border-radius: 3px), not pill badges. |
| 275 | +- All status values must be wrapped in a badge — never raw text. |
| 276 | + |
| 277 | +--- |
| 278 | + |
| 279 | +## Typography |
| 280 | + |
| 281 | +- Font: Open Sans throughout (loaded via Google Fonts). |
| 282 | +- ibox-title `<h5>`: renders at 16px (`$h3-size`) with `font-weight: 600`. |
| 283 | +- Dashboard stat numbers: use `stat-value` class (renders at `$stat-value-size: 2rem`). |
| 284 | +- Never hardcode `px` values in views — use token variables in SCSS. |
| 285 | + |
| 286 | +--- |
| 287 | + |
| 288 | +## Legacy Patterns to Avoid |
| 289 | + |
| 290 | +These patterns are deprecated. The SCSS retains bridge definitions for backward compatibility, but new code must not use them: |
| 291 | + |
| 292 | +| Deprecated | Replacement | |
| 293 | +|-----------|-------------| |
| 294 | +| `control-label` | `form-label` | |
| 295 | +| `help-block` | `form-text` | |
| 296 | +| `float-e-margins` | Remove — no replacement needed | |
| 297 | +| `btn-xs` | `btn-sm` | |
| 298 | +| `dl-horizontal` | `detail-fields` with `detail-field` / `detail-label` / `detail-value` | |
| 299 | +| `admin-actions-filters` | `list-filters` | |
| 300 | +| `form-control` on `<select>` | `form-select` | |
| 301 | +| `fa-save` | `fa-floppy-disk` | |
| 302 | +| `fa-edit` | `fa-pen-to-square` | |
| 303 | +| `type="button"` on `<a>` | Remove — not valid HTML on anchors | |
0 commit comments