rustio-admin 0.31.0

Django Admin, but for Rust. A small, focused admin framework.
Documentation
/* ============================================================
   RustIO — Base layer
   Element defaults + a few primitive utilities built on tokens.
   Logical properties throughout for RTL parity.
   ============================================================ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  /* Contract v2.0: the content area is a flat, barely-warm near-white (#fafcfb).
     The reference screenshots carry no texture, so the dot-grid is omitted. */
  background-color: var(--rio-bg);
  color: var(--rio-text);
  font-family: var(--rio-font-body);
  font-size: var(--rio-text-14);
  line-height: var(--rio-leading-body);
  font-weight: var(--rio-weight-regular);
  letter-spacing: 0; /* contract: flat tracking (the negative track was a Hanken flourish) */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Numerals are ALWAYS Western, lining + tabular. */
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

/* App-canvas utility — full-height app shells paint their own background; it
   matches the flat content-area surface (no dot-grid, per contract v2.0). */
.rio-canvas {
  background-color: var(--rio-bg);
}

/* Headings default to the display family (now Inter — the contract has no serif). */
h1, h2, h3, h4 {
  font-family: var(--rio-font-display);
  color: var(--rio-text-hi);
  letter-spacing: var(--rio-tracking-display);
  line-height: var(--rio-leading-display);
  font-weight: var(--rio-weight-display);
  margin: 0;
  text-wrap: balance; /* tidy, even headline line-breaks */
}

/* Page title — contract §2.1: weight 800, line-height 1.15. Inherited from the
   base layer so pages don't each re-declare title weight. */
h1 {
  font-weight: 800;
  line-height: 1.15;
}

/* Long-form paragraphs break more gracefully. */
p { text-wrap: pretty; }

/* ============================================================
   RTL parity (layout only)
   Layout mirrors via logical properties everywhere. Keep numbers, code,
   and Latin tokens Western/LTR even inside an RTL context.
   ============================================================ */
/* When a developer writes Arabic (or any joined/RTL script), reset Latin
   tracking — letter-spacing breaks Arabic letter-joining — and give headings a
   touch more line-height so the taller Naskh/Tajawal glyphs breathe. The brand
   Latin faces are untouched; this only applies to Arabic-tagged content. */
:lang(ar), [lang="ar"], [dir="rtl"] {
  letter-spacing: 0;
}
:is(:lang(ar), [lang="ar"], [dir="rtl"]) :is(h1, h2, h3, h4) {
  letter-spacing: 0;
  line-height: var(--rio-leading-tight);
}
/* Uppercase eyebrows / legends / table headers carry caps tracking — meaningless
   for Arabic (and letter-spacing breaks the connected script); neutralise both so
   Arabic labels read naturally (contract §12). */
:is(:lang(ar), [lang="ar"], [dir="rtl"]) :is(.rio-eyebrow, .rio-nav-section, .rio-table th, .rio-statstrip-label, .rio-fieldset-legend, .rio-fieldset > legend, .rio-section-head__eyebrow) {
  letter-spacing: 0;
  text-transform: none;
}
[dir="rtl"] :is(code, pre, kbd, samp, .rio-cell-mono, .rio-stat-value) {
  direction: ltr;
  unicode-bidi: isolate;
}

code, pre, kbd, samp {
  font-family: var(--rio-font-mono);
}

a {
  color: var(--rio-rust);
  text-decoration: none;
}
a:hover { color: var(--rio-rust-hover); }

/* --- Focus: always-visible copper ring, offset --- */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--rio-rust);
  outline-offset: 2px;
  border-radius: var(--rio-radius-sm);
}

::selection {
  background: var(--rio-rust-tint-2);
  color: var(--rio-text-hi);
}

/* --- The ◉ mark: calm processing sweep (never a spinner) --- */
@keyframes rio-ring-sweep {
  0%   { stroke-dashoffset: var(--rio-ring-circ, 100); opacity: 0.35; }
  50%  { opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 0.35; }
}

.rio-mark--processing .rio-mark-ring {
  stroke-dasharray: var(--rio-ring-circ, 100);
  animation: rio-ring-sweep var(--rio-dur-sweep) var(--rio-ease-sweep) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .rio-mark--processing .rio-mark-ring { animation: none; }
}

/* --- Orchestrated page-load reveal (staggered) --- */
@keyframes rio-reveal {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: no-preference) {
  .rio-reveal {
    animation: rio-reveal var(--rio-dur-slow) var(--rio-ease) both;
  }
}

/* --- Mono eyebrow / label utility --- */
.rio-eyebrow {
  font-family: var(--rio-font-mono);
  font-size: var(--rio-text-12);
  letter-spacing: var(--rio-tracking-caps);
  text-transform: uppercase;
  color: var(--rio-text-mute);
}