This document defines the canonical UI patterns for the XtremeIdiots Portal. All views must follow these standards to ensure visual consistency across the site.
Every page follows this wrapper structure:
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-12">
<div class="container-fluid">
<div class="ibox">
<div class="ibox-title"><h5>Section Title</h5></div>
<div class="ibox-content"><!-- Content --></div>
<div class="ibox-footer"><!-- Action buttons --></div>
</div>
</div>
</div>
</div>
</div>- Every content section must be wrapped in
ibox > ibox-title + ibox-content. - The
ibox-titlemust contain an<h5>element. Never place raw text inibox-title. - Action buttons (Save, Cancel, Delete, Back) go in
ibox-footer, not loose insideibox-content. - Use
ibox-footer--stickyon long forms (e.g. GameServers/Edit) to keep the save bar visible.
| Role | Class | Icon | Size |
|---|---|---|---|
| Primary action (Save, Create, Submit) | btn btn-primary |
Optional FA icon + text | Default |
| Secondary action (Cancel, Back) | btn btn-outline-secondary |
No icon, or fa-arrow-left for back |
Default |
| Destructive action (Delete link in table) | btn btn-outline-danger |
fa-trash |
btn-sm |
| Destructive confirm (Delete button on confirmation page) | btn btn-danger |
fa-trash |
Default |
| Cautionary action (Lift ban) | btn btn-warning |
fa-unlock |
Default |
| Inline table actions (Edit, Details) | btn btn-outline-secondary |
Icon + text | btn-sm |
| Filter reset | btn btn-outline-secondary btn-sm |
No icon | btn-sm |
Do not use these for action buttons (reserve for badges/status only):
btn-success— usebtn-primaryinsteadbtn-info— usebtn-outline-secondaryinsteadbtn-xs— usebtn-sminstead (btn-xs is a bridge alias only)
- List pages: "Create New" button in
.ibox-toolswithin.ibox-title(top-right). - Form pages: Submit + Cancel in
.ibox-footer. Primary action on the right, Cancel on the left. - Detail pages: Action buttons in
.ibox-footer. - Table rows:
btn-group btn-group-smin the last column. - Confirmation pages: Cancel (left) + Confirm (right) in
.ibox-footer.
- On confirmation pages: use
Cancel(no icon). - On form/edit pages: use
Back to ListorBack to [Parent]withfa-arrow-lefticon.
All icons use Font Awesome 6 solid style with fixed-width class: fa-solid fa-fw fa-[icon-name].
| Action | Icon | Usage |
|---|---|---|
| Create / Add | fa-plus |
Create buttons, add links |
| Edit | fa-pen-to-square |
Edit buttons and links |
| Delete | fa-trash |
Delete buttons and links |
| View / Details | fa-eye |
Details/view buttons |
| Back | fa-arrow-left |
Back navigation buttons |
| Save | fa-floppy-disk |
Save/submit buttons |
| Clone | fa-clone |
Clone/copy actions |
| Import | fa-file-import |
Import actions |
| Refresh / Sync | fa-rotate |
Sync/refresh actions |
| Cancel (operation) | fa-ban |
Cancel running operations |
- Always include
fa-fwon icons for fixed-width alignment. - Never use deprecated icons:
fa-save→fa-floppy-disk,fa-edit→fa-pen-to-square,fa-file-text→ usefa-flagor contextual icon. - Include
aria-hidden="true"on decorative icons (those accompanied by text). - Do not use
me-1for spacing whenfa-fwis present —fa-fwprovides consistent width.
- Use vertical labels (label above input) with
form-labelclass. - Wrap each field in
<div class="mb-3">. - Never use horizontal form layout (
col-sm-2+col-sm-10). - Use
form-selectfor<select>elements, notform-control. - Use
form-textfor helper text beneath inputs, nothelp-block. - Use
form-check+form-check-input+form-check-labelfor checkboxes.
- Short text fields:
rows="3" - Descriptions / content:
rows="6"
- Use Bootstrap nav-tabs for forms with 5+ logical sections (e.g. GameServers/Edit).
- Use
ibox-footer--stickyto keep Save/Cancel visible during scrolling. - Toggle tab visibility with CSS class
d-none, not inlinestyle="display:none".
- Always include
<div asp-validation-summary="ModelOnly" class="text-danger"></div>at the top of forms. - Use
<span asp-validation-for="Field" class="text-danger"></span>below inputs.
Use the detail-fields component for label-value displays:
<dl class="detail-fields row">
<div class="detail-field col-sm-6">
<dt class="detail-label">Label</dt>
<dd class="detail-value">Value</dd>
</div>
</dl>- Always use
<dl>semantics withdetail-fieldsclass. - Use
detail-label(uppercase, muted, small) anddetail-value(normal text). - Arrange fields in a responsive grid:
col-sm-6(two-column) orcol-sm-4(three-column). - Never use
dl-horizontal(legacy Bootstrap 3 class).
Use the list-filters class for filter bars on list/index pages:
<div class="list-filters mb-2">
<div class="filter-group">
<label class="form-label" for="filterGameType">Game Type</label>
<select id="filterGameType" class="form-select">...</select>
</div>
<div class="filter-group">
<label class="form-label" for="resetFilters">Reset</label>
<button type="button" id="resetFilters" class="btn btn-outline-secondary btn-sm">Reset Filters</button>
</div>
</div>- Use
list-filtersclass, notadmin-actions-filters(legacy alias retained for backward compatibility). - Always label the reset button "Reset Filters" — not "Clear Filters" or "Reset".
- Place the filter bar inside
.ibox-content, above the table.
All data tables must use: table table-striped table-hover.
- Date/timestamp columns: add
class="table-date-col"to<th>— applieswhite-space: nowrap; width: 1%. - Action button columns: add
class="table-action-col"to<th>— same constrained width.
- DataTables: set
language.emptyTableto a contextual message (e.g."No game servers found"). A global fallback of"No records found"is set insite.js. - Non-table contexts: use the
.empty-statecomponent:
<div class="empty-state">
<i class="fa-solid fa-fw fa-folder-open empty-state-icon"></i>
<p class="empty-state-message">No records found</p>
<div class="empty-state-action">
<a href="..." class="btn btn-primary btn-sm">Create New</a>
</div>
</div>All destructive actions must have a confirmation gate. Use one of two tiers:
For permanent, irreversible deletes of primary entities (game servers, admin actions, ban file monitors, demos, tags, map rotations).
Standard structure:
<div class="ibox">
<div class="ibox-title"><h5>Delete [Entity Type]</h5></div>
<div class="ibox-content">
<p>Are you sure you want to delete this [entity type]?</p>
<div class="alert alert-warning mb-3">
<i class="fa-solid fa-fw fa-triangle-exclamation" aria-hidden="true"></i>
This action cannot be undone. [Specific consequence describing sub-entity impact.]
</div>
<dl class="detail-fields row">
<!-- Entity details shown here -->
</dl>
</div>
<div class="ibox-footer">
<a class="btn btn-outline-secondary" asp-action="Index">Cancel</a>
<button type="submit" class="btn btn-danger">
<i class="fa-solid fa-fw fa-trash"></i> Delete [Entity Type]
</button>
</div>
</div>For quick destructive actions within list/detail views (unassign, remove tag, delete map from host, delete permission).
Add a data-confirm attribute to the button or link:
<button type="submit" class="btn btn-outline-danger btn-sm"
data-confirm="Are you sure you want to [verb] this [entity]? [Consequence if applicable.]">
<i class="fa-solid fa-fw fa-trash"></i> Delete
</button>The data-confirm handler in enhanced-ui.js intercepts the click and shows a browser confirm dialog. No inline onclick or onsubmit handlers.
All confirmation messages follow this pattern:
Are you sure you want to [verb] this [entity]? [Consequence if applicable.]
Examples:
- "Are you sure you want to delete this protected name?"
- "Are you sure you want to unassign this server? Maps will be removed from the server."
- "Are you sure you want to remove this permission? It may take up to 15 minutes to take effect."
- ❌
onclick="return confirm('...')"— usedata-confirminstead. - ❌
onsubmit="return confirm('...')"— usedata-confirmon the submit button instead. - ❌ Direct POST with no confirmation on destructive actions.
- ❌
btn-dangeron non-destructive actions (Claim, Lift) — usebtn-primaryorbtn-warning.
Use the data-copy-target attribute pattern for copy-to-clipboard actions. Do not use inline onclick handlers.
<span id="playerId" class="mx-1">some-value</span>
<i class="fa-solid fa-fw fa-copy cursor-pointer" data-copy-target="playerId" title="Copy Player ID" aria-hidden="true"></i>The click handler uses event delegation on [data-copy-target], reads the textContent of the target element by ID, and copies it to the clipboard. Visual feedback is provided by temporarily swapping the icon to a checkmark.
- The
data-copy-targetvalue must match theidof the element whose text content should be copied. - Use
fa-copyicon withcursor-pointerclass. - Never use
onclick="copyToClipboard(...)"— usedata-copy-targetinstead.
- Use SCSS-defined
badge-*classes or Bootstrap 5bg-*classes. - Colour semantics: green = active/online/success, red = error/expired/danger, amber = warning/pending, teal = primary.
- Use square badges (border-radius: 3px), not pill badges.
- All status values must be wrapped in a badge — never raw text.
- Font: Open Sans throughout (loaded via Google Fonts).
- ibox-title
<h5>: renders at 16px ($h3-size) withfont-weight: 600. - Dashboard stat numbers: use
stat-valueclass (renders at$stat-value-size: 2rem). - Never hardcode
pxvalues in views — use token variables in SCSS.
These patterns are deprecated. The SCSS retains bridge definitions for backward compatibility, but new code must not use them:
| Deprecated | Replacement |
|---|---|
control-label |
form-label |
help-block |
form-text |
float-e-margins |
Remove — no replacement needed |
btn-xs |
btn-sm |
dl-horizontal |
detail-fields with detail-field / detail-label / detail-value |
admin-actions-filters |
list-filters |
form-control on <select> |
form-select |
fa-save |
fa-floppy-disk |
fa-edit |
fa-pen-to-square |
type="button" on <a> |
Remove — not valid HTML on anchors |
onclick="copyToClipboard(...)" |
data-copy-target attribute |
style="display:none" on anti-forgery form |
af-hidden CSS class |
Inline style="height: ..." on map divs |
player-location-map CSS class |