Admin CSS Architecture

The Reldens admin panel CSS follows a strict 3-tier model: Elements, Components, and Containers. The goal is to push styles as far up the cascade as possible, eliminating redundant overrides at lower levels.

The 3-Tier Model

  • Elements: admin-elements.css - global HTML element defaults
  • Components: component-[name].css - reusable UI patterns
  • Containers: container-[name].css - page sections and layouts

Rules

  • Elements never reference other elements.
  • Components can override elements; never reference other components.
  • Containers can override components; never reference other containers.
  • Override cascade: element default -> .component element -> .container .component element.
  • The goal is to push styles as far UP the cascade as possible, eliminating redundant overrides at lower levels.
  • Every container CSS file MUST have exactly ONE root selector that wraps all child styles using nested & .child syntax (the only exception is container-tileset-analyzer.css, see Tier 3).

NOT elements (always part of a component or container): div, span, p, section, fieldset, ul, legend, img.

Tier 1 - Elements (admin-elements.css)

Single global file. Not scoped under any class, except the .section-title rule (margin-top: 0, margin-bottom: 2rem), the only class selector in the file.

The goal is a strong enough baseline that most component and container files have NO element overrides - only layout and structural differences.

Heading styles

  • h1 - white, --font-size-2xl, margin: 0
  • h2 - white, --font-size-xl, margin-top: 0
  • h3 - white, --font-size-lg, margin-top: 0
  • h4 - --color-grey, --font-size-md (labels/toggles, not page headings)
  • h5 - --color-grey, --font-size-base

Link styles

color: var(--color-blue); text-decoration: none / :hover { color: var(--color-white) }

Input / textarea / select styles

background-color: var(--color-grey-70), color: var(--color-grey), border: 1px solid var(--color-grey-40), border-radius: var(--radius-sm), padding: 0.5rem.

Input types: input[type="text"], input[type="number"], input[type="password"], input[type="file"], input[type="color"].

Label styles

color: var(--color-grey); font-weight: 600

Tier 2 - Components

Each component file has ONE main class that wraps all child styles.

  • component-button.css -> .button
  • component-input-box.css -> .input-box
  • component-modal.css -> .modal
  • component-tooltip.css -> .tooltip
  • component-notification.css -> .notification
  • component-entries.css -> .entity-view, .entity-edit
  • component-entry-collapsible.css -> .entry-collapsible
  • component-canvas-panel.css -> .canvas-panel
  • component-config-grid.css -> .config-grid
  • component-side-bar.css -> .side-bar

component-modal.css standard structure

  • .modal, .modal-backdrop, .modal-dialog, .modal-header, .modal-body, .modal-footer
  • .modal-close-btn (standardized name)
  • .modal-message (generic message line)
  • Width modifiers on .modal-dialog: .modal-width-full, .modal-width-auto

Tier 3 - Containers

Each container file has ONE root selector (except container-tileset-analyzer.css, see below). All children are nested inside it using & .child.

  • container-reldens-admin-panel.css -> .reldens-admin-panel
  • container-forms.css -> .forms-container
  • container-entity-list.css -> .entity-list
  • container-maps-wizard.css -> .maps-wizard
  • container-tileset-uploader.css -> .upload-section
  • container-tileset-editor.css -> .review-section
  • container-tileset-legend-panel.css -> .legend-panel
  • container-tileset-animations-panel.css -> .tileset-animations-panel
  • container-generated-files.css -> .generated-files
  • container-tileset-results.css -> .results-section
  • container-tileset-tile-options.css -> .tileset-analyzer
  • container-maps-elements-editor.css -> .maps-elements-editor
  • container-element-tiles-layer-modal.css -> .element-tiles-layer-modal
  • container-admin-dashboard.css -> .admin-dashboard
  • container-room-active-players.css -> .room-active-players-banner

Exception - container-tileset-analyzer.css

This file does not follow the one root selector rule: it has a top-level @keyframes tileset-analyzer-spin and two root selectors, .tileset-analyzer-wrapper and .tileset-analyzer. The .tileset-analyzer root is shared with container-tileset-tile-options.css, which uses it as its own root.

Container nesting rules

  • Context overrides (e.g. "when .tileset-tile-options is inside .global-tile-options") are expressed as & .global-tile-options .tileset-tile-options { } - NOT using parent-context & at the end.
  • min-width: 0 and min-height: 0 are FORBIDDEN - they are a code smell; fix the layout instead.

Import Order (reldens-admin-client.css)

@import './variables.css';
@import './admin-elements.css';
@import './component-button.css';
@import './component-modal.css';
@import './component-notification.css';
@import './component-input-box.css';
@import './component-config-grid.css';
@import './component-tooltip.css';
@import './component-entries.css';
@import './component-entry-collapsible.css';
@import './component-canvas-panel.css';
@import './component-side-bar.css';
@import './container-reldens-admin-panel.css';
@import './container-forms.css';
@import './container-entity-list.css';
@import './container-maps-wizard.css';
@import './container-maps-elements-editor.css';
@import './container-element-tiles-layer-modal.css';
@import './container-tileset-analyzer.css';
@import './container-tileset-uploader.css';
@import './container-tileset-editor.css';
@import './container-tileset-legend-panel.css';
@import './container-tileset-animations-panel.css';
@import './container-generated-files.css';
@import './container-tileset-results.css';
@import './container-tileset-tile-options.css';
@import './container-admin-dashboard.css';
@import './container-room-active-players.css';

Every CSS file under theme/admin/css/ must be @import-ed here - there is no glob, so a file that is not listed is silently never loaded.

Key Rules Summary

  1. One root selector per file - containers scope everything under a single class (container-tileset-analyzer.css is the documented exception).
  2. No min-width: 0 / min-height: 0 - fix the flex layout properly instead.
  3. No cross-tier references - components don't reference other components; containers don't reference other containers.
  4. Push styles up the cascade - if a style applies everywhere, it belongs in elements or a component, not repeated in every container.
  5. Parent-context selectors (some-parent &) inside nested blocks should be rewritten as & .some-parent .child at the container root level for clarity.

Related Documentation

Go Up