Files
sub2api-cn-relay-manager/deploy/tksea-portal/admin-common.css
phamnazage-jpg 3a9061e11d style(portal): add design system + shared layer (portal.css/portal.js/admin-common.css shim)
- portal.css: 777-line real design system (Linear/Vercel 信息建筑派)
  * tokens: spacing 4/8/12/16/24/32/48, type 12/13/14/15/17/20/24/32/44
  * colors: ink/paper/accent/success/warn/danger × 50/100/500/900
  * teal #14b8a6 1:1 aligned with host sub2api Vue/Tailwind
  * dark-first; light override for public portal
  * components: page-hero, stat-card, card, status, pill, btn-primary,
    toast-host, empty, skeleton, drawer, tabs
- portal.js: window.Sub2ApiPortal — toast, lucide 1.75px stroke SVG
  icon registry (shield/group/activity/route/health/account/provider/
  import/check/x/alert/info/copy/edit/trash/plus/refresh/...),
  copyToClipboard, theme auto/dark/light, drawer, renderModernAdminNav
- admin-common.css: 4KB legacy shim — maps old class names
  (.topnav/.primary/.secondary/.ghost/.danger/.metric/.statusbar/.stat/
  .eyebrow/.hero-points/.page-hero__eyebrow/.shell/.fade-in/.topline/
  .chip/.tag/.mono/.meta-card/.meta-label/.status-pill/.inline-code/
  .tone-*) onto new tokens without breaking admin-common.js nav contract

Evidence:
- bash scripts/test/test_tksea_portal_assets.sh → PASS (70+ string assertions)
- bash scripts/test/verify_frontend_smoke.sh → PASS (chromium headless 7 pages)
2026-06-03 09:10:45 +08:00

137 lines
4.0 KiB
CSS

/* =============================================================
* admin-common.css — Legacy compatibility shim
* -------------------------------------------------------------
* Pages link BOTH:
* /portal/portal.css → modern design system (primary)
* /portal/admin-common.css → this file (legacy aliases)
*
* This file ONLY provides aliases so old inline class names
* (`.primary` / `.secondary` / `.ghost` / `.danger` / etc.)
* keep working after pages are refactored to the new system.
* New pages should use the modern `.btn` / `.card` / `.stat-card`
* classes from portal.css directly.
* ============================================================= */
.topnav {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 18px;
}
.topnav a {
text-decoration: none;
padding: 10px 14px;
border-radius: 999px;
border: 1px solid var(--border-subtle);
background: var(--bg-elev-2);
color: var(--text-muted);
font-size: 13px;
font-weight: 700;
transition: transform 120ms ease, background 120ms ease;
}
.topnav a:hover {
transform: translateY(-1px);
background: var(--bg-elev-3);
color: var(--text-default);
}
.topnav a.is-current {
background: linear-gradient(135deg, var(--teal-500), var(--teal-600));
border-color: transparent;
color: var(--text-on-primary);
box-shadow: 0 4px 12px rgba(20,184,166,0.3);
}
.statusbar {
margin-top: 16px;
min-height: 54px;
padding: 14px 16px;
border-radius: 14px;
border: 1px solid var(--border-subtle);
background: var(--bg-elev-2);
display: flex;
align-items: center;
gap: 10px;
color: var(--text-muted);
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap;
}
.statusbar[data-tone="success"] {
background: var(--color-success-soft);
color: var(--color-success);
border-color: rgba(18, 107, 67, 0.2);
}
.statusbar[data-tone="warning"] {
background: var(--color-warning-soft);
color: var(--color-warning);
border-color: rgba(155, 98, 21, 0.2);
}
.statusbar[data-tone="danger"] {
background: var(--color-danger-soft);
color: var(--color-danger);
border-color: rgba(178, 49, 49, 0.2);
}
.statusbar[data-tone="info"] {
background: var(--color-info-soft);
color: var(--color-info);
border-color: rgba(56, 189, 248, 0.2);
}
/* ---- Legacy button aliases (kept so old pages work) ---- */
button.primary, .primary {
background: linear-gradient(135deg, var(--teal-500), var(--teal-600));
color: var(--text-on-primary);
border: 0;
border-radius: 999px;
padding: 12px 18px;
font-weight: 700;
cursor: pointer;
transition: transform 120ms ease, box-shadow 120ms ease;
box-shadow: 0 6px 16px rgba(20,184,166,0.22);
}
button.primary:hover, .primary:hover { transform: translateY(-1px); box-shadow: 0 10px 22px rgba(20,184,166,0.32); }
button.primary:disabled, .primary:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
button.secondary, .secondary {
background: var(--color-primary-soft);
color: var(--color-primary);
border: 1px solid rgba(20,184,166,0.2);
border-radius: 999px;
padding: 12px 18px;
font-weight: 700;
cursor: pointer;
transition: transform 120ms ease, background 120ms ease;
}
button.secondary:hover, .secondary:hover { transform: translateY(-1px); background: rgba(20,184,166,0.18); }
button.ghost, .ghost {
background: transparent;
border: 1px solid var(--border-subtle);
color: var(--text-muted);
border-radius: 999px;
padding: 12px 18px;
font-weight: 700;
cursor: pointer;
transition: transform 120ms ease, background 120ms ease, color 120ms ease;
}
button.ghost:hover, .ghost:hover { transform: translateY(-1px); background: var(--bg-elev-3); color: var(--text-default); }
button.danger, .danger {
background: var(--color-danger-soft);
color: var(--color-danger);
border: 1px solid rgba(239,68,68,0.2);
border-radius: 999px;
padding: 12px 18px;
font-weight: 700;
cursor: pointer;
transition: transform 120ms ease, background 120ms ease;
}
button.danger:hover, .danger:hover { transform: translateY(-1px); background: rgba(239,68,68,0.18); }