rustio-admin 0.24.0

Django Admin, but for Rust. A small, focused admin framework.
Documentation
/* ============================================================
 * rustio-admin / layout / shell
 *
 * Page-level flex shell: banner → topbar → (sidebar + main) →
 * footer. Layout uses flex (not grid) so an absent sidebar (login,
 * forbidden, error, public chrome) doesn't leave a 240px ghost
 * column squeezing the main content.
 *
 * Mobile-first off-canvas overrides live in layout/responsive.css.
 * ============================================================ */

/* Unified stacking order — one place that decides who sits above
 * whom. Before v0.18.6 the topbar (z-index:10), sidebar (z-index:20),
 * dropdown panels (z-index:40) and FK results (z-index:50) each
 * picked their own number, so the sticky sidebar scrolled up over
 * the sticky topbar. The ladder below is the single source of
 * truth; every chrome/overlay rule references these tokens. */
:root {
  --rio-z-sidebar:  100;
  --rio-z-topbar:   200;
  --rio-z-dropdown: 300;
  --rio-z-modal:    400;
}

.rio-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.rio-banner {
  padding: var(--rio-s2) var(--rio-s4);
  text-align: center;
  font-weight: 600;
  font-size: var(--rio-fs-sm);
}
.rio-banner--demo {
  background: var(--rio-warning-bg);
  color: var(--rio-warning);
  border-bottom: 1px solid var(--rio-border);
}
/* Read-only mode banner — same shape as the demo banner but
 * tinted to the info/accent family so operators can tell
 * "read-only freeze" from "demo session" at a glance. */
.rio-banner--read-only {
  background: var(--rio-info-bg);
  color: var(--rio-accent);
  border-bottom: 1px solid var(--rio-border);
}

/* Layout uses flex, not grid, so an absent sidebar (login page,
 * forbidden, error, public chrome) doesn't leave a 240px ghost
 * column squeezing the main content. With sidebar: row of two
 * children, sidebar is fixed-width. Without: main takes 100%. */
.rio-layout {
  flex: 1;
  display: flex;
  align-items: stretch;
  min-height: 0;
}

/* v0.16 — generous content padding (32px horizontal desktop, 24px top).
 * `max-width` caps reading width on ultra-wide monitors so tables stay
 * scannable. The inner block is centred so 4K monitors don't leave the
 * content glued to the sidebar edge. */
.rio-main {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--rio-s5) var(--rio-s6) var(--rio-s6);
}
.rio-main > * {
  max-width: var(--rio-content-max);
  margin-inline: auto;
}

/* ============================================================
 * Chrome cascade override (sidebar + footer).
 *
 * `--rio-surface-chrome` is the deep-slate surface used by the
 * sidebar and footer. Components inside those two regions need
 * their text and sub-surface tokens flipped to a light-on-dark
 * palette. Rather than touching every component file, we redefine
 * the relevant tokens locally within the two parent selectors.
 * CSS custom properties cascade through descendants, so every
 * `var(--rio-text)`, `var(--rio-surface-2)`, etc. inside the
 * sidebar / footer picks up the dark-surface values automatically.
 *
 * The topbar is intentionally OUT of this cascade — it uses the
 * light `--rio-surface` token (see `layout/topbar.css`) and reads
 * with the same dark-on-light palette as the rest of the page.
 * GitHub / Vercel / Linear pattern: light topbar over light
 * canvas, with the deep-slate sidebar carrying the navigation
 * weight on the side.
 * ============================================================ */

.rio-sidebar,
.rio-footer {
  /* v0.16 — chrome cascade retuned for slate-900 chrome (`#0F172A`).
   * Every override below stays in the slate family so chrome reads
   * as one designed surface, not a tinted variant. Each text + sub-
   * surface tier passes WCAG AA against the new chrome background. */

  /* Light text on slate-900 chrome — slate-50 → slate-500 ladder. */
  --rio-text-strong: #F8FAFC;   /* slate-50  — sidebar headings */
  --rio-text:        #E2E8F0;   /* slate-200 — sidebar item label */
  --rio-text-muted:  #94A3B8;   /* slate-400 — secondary chrome text */
  --rio-text-subtle: #64748B;   /* slate-500 — captions in chrome */

  /* Sub-surface tones used by hover, pressed, and active states
   * inside chrome. One slate step lighter per tier so interaction
   * is visible without flash. */
  --rio-surface-2: #1E293B;   /* slate-800 — hover */
  --rio-surface-3: #334155;   /* slate-700 — pressed / active */

  /* Dividers — same slate ladder, visible enough to separate
   * sidebar sections without dominating. */
  --rio-border-soft:   #1E293B;   /* slate-800 */
  --rio-border:        #334155;   /* slate-700 */
  --rio-border-strong: #475569;   /* slate-600 */

  /* Lifted accent within chrome. Base accent `#0D9488` (teal-600)
   * on slate-900 measures ~4.3:1 — passes AA but reads as "dark
   * teal blob." Lifting the chrome-scope accent to `#2DD4BF`
   * (teal-400) restores ~9.6:1 — the active sidebar item and any
   * in-chrome accent reads as a clean teal. Same hue family so
   * brand identity stays consistent across light and chrome
   * surfaces. */
  --rio-accent:        #2DD4BF;   /* teal-400 */
  --rio-accent-hover:  #5EEAD4;   /* teal-300 */
  --rio-accent-rgb:    45 212 191;
  --rio-accent-soft:   #134E4A;   /* teal-900 — for selected-item BG within chrome */
  --rio-accent-border: #0F766E;   /* teal-700 — visible focus ring on chrome */
}