rustio-admin 0.21.0

Django Admin, but for Rust. A small, focused admin framework.
Documentation
/* ============================================================
 * rustio-admin / base / typography
 *
 * Three things in cascade order:
 *
 *   1. @font-face declarations for the four self-hosted families.
 *      Self-hosted (SIL OFL-1.1, see /static/fonts/LICENSE.txt) and
 *      baked into the rustio-admin binary; no CDN round-trip, no
 *      FOUT, no GDPR/tracking surface. Browsers select which Arabic
 *      face to load via `unicode-range` + the per-element
 *      `font-family` resolution rules below.
 *
 *   2. Latin-only display polish + Arabic-aware UI/body typography.
 *
 *   3. Headings, links, paragraphs, code, pre — plus the mobile
 *      readability bump below 600px.
 *
 * Tokens (--rio-font-*, --rio-fs-*, --rio-lh-*, --rio-tracking-*)
 * live in tokens/typography.css.
 * ============================================================ */

/* ---- @font-face — self-hosted, OFL-1.1 ----------------------- */

/* Geist (variable Latin) */
@font-face {
  font-family: "Geist";
  src: url("/static/fonts/Geist-Variable.woff2") format("woff2-variations"),
       url("/static/fonts/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/static/fonts/GeistMono-Variable.woff2") format("woff2-variations"),
       url("/static/fonts/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Inter (variable Latin, secondary fallback under Geist).
 * The unicode-range filter covers Inter's published character set —
 * Latin + Latin Extended + Cyrillic (basic + extended) + Greek (basic
 * + extended) + Vietnamese + currency / punctuation extras. A single
 * variable woff2 covers all weights 100..900. Pages with no characters
 * in these ranges (e.g. pure Arabic) never trigger the download. */
@font-face {
  font-family: "Inter";
  src: url("/static/fonts/InterVariable.woff2") format("woff2-variations"),
       url("/static/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range:
    U+0000-024F, U+0259, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0300-0309, U+0323, U+0329, U+0370-03FF,
    U+0400-052F, U+1C80-1C88, U+1D00-1DBF, U+1E00-1EFF,
    U+1F00-1FFF, U+2000-206F, U+20A0-20C0, U+2113, U+2116,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+2C60-2C7F,
    U+A640-A69F, U+A720-A7FF, U+FE2E-FE2F, U+FEFF, U+FFFD;
}

/* Tajawal (Arabic UI: compact surfaces) — three static weights;
 * Tajawal has no public variable axis. The unicode-range filters
 * fetches to Arabic codepoints only. */
@font-face {
  font-family: "Tajawal";
  src: url("/static/fonts/Tajawal-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200E, U+2010-2011, U+204F, U+2E41;
}
@font-face {
  font-family: "Tajawal";
  src: url("/static/fonts/Tajawal-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200E, U+2010-2011, U+204F, U+2E41;
}
@font-face {
  font-family: "Tajawal";
  src: url("/static/fonts/Tajawal-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200E, U+2010-2011, U+204F, U+2E41;
}

/* Noto Naskh Arabic (Arabic paragraph body) */
@font-face {
  font-family: "Noto Naskh Arabic";
  src: url("/static/fonts/NotoNaskhArabic-Variable.woff2") format("woff2-variations"),
       url("/static/fonts/NotoNaskhArabic-Variable.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200E, U+2010-2011, U+204F, U+2E41;
}

/* ---- Latin-only display polish ------------------------------- *
 * Auto-applies to Latin headings. Geist's "ss01" + "ss03" + "cv11"
 * tighten figure widths and swap single-storey alternates so
 * headings feel like a designed face. */
.rio-latin,
h1, h2, h3, h4,
.rio-list-title, .rio-form-title, .rio-dash-greeting, .rio-auth-title {
  font-feature-settings: "kern", "calt", "ss01", "ss03", "cv11";
}

/* ---- Arabic-aware typography --------------------------------- *
 * Two contexts:
 *   - UI surfaces  → Tajawal      (compact, geometric, dense UI)
 *   - Body / prose → Noto Naskh   (humanist, paragraph-y)
 * Both unicode-range trigger only on Arabic codepoints, so no
 * Latin-only page pays the download cost. */

/* Arabic UI context — anything tagged lang="ar" or dir="rtl" defaults
 * to Tajawal. Strip Latin-tuned tracking + stylistic alternates so
 * Arabic shaping isn't disrupted. */
:lang(ar),
[dir="rtl"],
[dir="rtl"] *,
.rio-arabic,
.rio-arabic * {
  font-family: var(--rio-font-arabic);
  letter-spacing: 0 !important;
  font-feature-settings: "kern" !important;
  line-height: var(--rio-lh-ui);
}

/* Arabic body context — paragraphs, descriptions, help text.
 * Switch to Noto Naskh + extra leading. Opt-in via lang="ar" on a
 * <p> / blockquote / .rio-prose container, or via .rio-arabic-body. */
p:lang(ar),
li:lang(ar),
blockquote:lang(ar),
.rio-prose:lang(ar),
.rio-prose:lang(ar) p,
.rio-prose:lang(ar) li,
.rio-arabic-body,
.rio-arabic-body * {
  font-family: var(--rio-font-arabic-body);
  line-height: var(--rio-lh-arabic);
  font-size: var(--rio-fs-lg);
  font-weight: var(--rio-fw-regular);
}

/* ---- Headings, links, paragraphs, code ----------------------- */

h1, h2, h3, h4 {
  margin: 0 0 var(--rio-s3);
  line-height: var(--rio-lh-tight);
  letter-spacing: var(--rio-tracking-heading);
  color: var(--rio-text);
}
h1 {
  font-size: var(--rio-fs-h1);            /* 34px */
  font-weight: var(--rio-fw-bold);
  letter-spacing: var(--rio-tracking-display);
}
h2 {
  font-size: var(--rio-fs-h2);            /* 26px */
  font-weight: var(--rio-fw-semibold);
}
h3 {
  font-size: var(--rio-fs-h3);            /* 22px */
  font-weight: var(--rio-fw-semibold);
}
h4 {
  font-size: var(--rio-fs-md);            /* 15px — section subtitle */
  font-weight: var(--rio-fw-semibold);
  letter-spacing: 0;
}

/* Arabic-tagged headings — keep the size scale, drop the negative
 * tracking. Tajawal carries enough weight for display sizes. */
:lang(ar) h1, :lang(ar) h2, :lang(ar) h3, :lang(ar) h4,
[dir="rtl"] h1, [dir="rtl"] h2, [dir="rtl"] h3, [dir="rtl"] h4,
h1:lang(ar), h2:lang(ar), h3:lang(ar), h4:lang(ar) {
  letter-spacing: 0;
  font-family: var(--rio-font-arabic);
  font-weight: var(--rio-fw-bold);
}

p { margin: 0 0 var(--rio-s4); }

a { color: var(--rio-accent); text-decoration: none; font-weight: var(--rio-fw-medium); }
a:hover { text-decoration: underline; }

code, kbd, samp {
  font-family: var(--rio-font-mono);
  font-size: 0.875em;                     /* relative to context */
  font-variant-ligatures: none;           /* "==" stays as 2 glyphs */
  font-feature-settings: "kern";
  background: rgba(var(--rio-accent-rgb) / 0.07);
  padding: 0.05em 0.35em;
  border-radius: var(--rio-radius-sm);
  letter-spacing: 0;
}
pre {
  font-family: var(--rio-font-mono);
  font-size: var(--rio-fs-sm);            /* 14px */
  line-height: 1.6;
  font-variant-ligatures: none;
  font-feature-settings: "kern";
  background: var(--rio-surface);
  border: 1px solid var(--rio-border);
  border-radius: var(--rio-radius);
  padding: var(--rio-s3) var(--rio-s4);
  overflow-x: auto;
  margin: 0 0 var(--rio-s4);
}
pre code {
  background: none;
  padding: 0;
  font-size: inherit;
  border-radius: 0;
}

/* `<mark>` — emitted by the list-view search-highlight pass to wrap
 * matched substrings inside scanned columns. Browser default is a
 * harsh yellow that fights the design system; replace with a tinted
 * accent background that reads as "this is what you searched for"
 * without screaming. Inline-flex keeps the highlight tight to the
 * glyphs even when the cell ellipsises.
 *
 * Inherits text colour from the cell so the accent-tinted
 * background sits behind whichever foreground the row-cell
 * already chose. */
mark {
  background: rgb(var(--rio-accent-rgb) / 0.18);
  color: inherit;
  padding: 0 0.1em;
  border-radius: 2px;
}

/* Mobile readability bump — push body up to 17px below 600px so
 * thumb-typing forms and reading copy stays comfortable. */
@media (max-width: 600px) {
  html { font-size: 16.5px; }
  .rio-prose { font-size: var(--rio-fs-lg); }
}