Files
Doormilexpress_console/src/globalPolish.css

210 lines
7.0 KiB
CSS

/* Global visual polish that used to come from MUI's <CssBaseline>/<GlobalStyles>
in themes/index.js (ThemeCustomization). Kept as plain CSS now that every
page runs on Astryx — see root CLAUDE.md §6. Astryx's own reset
(@astryxdesign/core/reset.css, imported in index.js) covers the base
element resets; this file carries the handful of things a design token
cannot express: font rendering, scrollbar pseudo-elements, keyframes, the
focus treatment, and the global reduced-motion rule.
NOTE ON COLOUR: nothing here introduces a colour. The console keeps its own
palette (themes/dt/tokens.js) — the KROW work adopted KROW's geometry and
type treatment only. Where a rule below needs an accent it reads
`var(--color-accent)` rather than naming one. */
body {
/* Astryx sets --font-family-body on :root (from themes/astryx.js's
typography config) but nothing was applying it to the document, and
MUI's <CssBaseline> — which used to do this — is no longer mounted.
The result was a body computing to "Times New Roman": Astryx components
rendered correctly because they set their own font, but every plain
element and any text outside a <Text> inherited the browser serif
default. Verified in the browser on /login before and after. */
font-family: var(--font-family-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
/* --------------------------------------------------------------------------
Motion vocabulary.
One set of durations and one easing curve for the whole app, so a hover, a
row highlight and a card entrance all feel like the same product. Three
steps only: `fast` for colour-only changes (anything slower feels laggy
under a moving cursor), `base` for the default, `slow` for layout and
entrances.
Prefixed --dt-* deliberately. These live on :root, and Dispatch.css ALSO
writes :root from a lazy-loaded chunk — an unprefixed name there would win
or lose purely on bundle order rather than on intent.
-------------------------------------------------------------------------- */
:root {
--dt-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--dt-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--dt-duration-fast: 120ms;
--dt-duration-base: 200ms;
--dt-duration-slow: 320ms;
}
/* --------------------------------------------------------------------------
Focus.
Focus is never the browser default. One ring, one width, everywhere. The
colour is the app's own accent token, not a value chosen here, so the ring
follows the brand automatically.
:focus-visible, not :focus — a mouse click on a button should not leave a
ring behind it.
-------------------------------------------------------------------------- */
.dt-focus-ring:focus-visible {
outline: none;
box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px var(--color-accent);
}
/* The inset variant, for a control flush inside a row or table cell where an
offset ring would be clipped by the overflow container. */
.dt-focus-ring-inset:focus-visible {
outline: none;
box-shadow: inset 0 0 0 2px var(--color-accent);
}
/* --------------------------------------------------------------------------
Scrollbars.
Chrome, not content — they should recede until used. The thumb was
rgba(0,0,0,0.26) at 10px, which drew a solid dark bar down the right edge of
every scrollable table and read as a heavy border rather than as a
scrollbar. Lightened and narrowed; it still darkens on hover so it stays
findable.
-------------------------------------------------------------------------- */
* {
scrollbar-width: thin; /* Firefox */
scrollbar-color: rgba(0, 0, 0, 0.16) transparent;
}
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background-color: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.16);
border-radius: 999px;
border: 2px solid transparent;
background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
background-color: rgba(0, 0, 0, 0.34);
}
*::-webkit-scrollbar-corner {
background-color: transparent;
}
/* --------------------------------------------------------------------------
Keyframes.
A skeleton SHIMMERS, it does not pulse: a pulsing block reads as a
rendering glitch, while a travelling highlight reads as progress. Astryx's
own <Skeleton> keeps its built-in treatment; this is for the hand-rolled
placeholders.
-------------------------------------------------------------------------- */
@keyframes dt-shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.dt-shimmer {
background-image: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.65) 50%, transparent 100%);
background-size: 200% 100%;
animation: dt-shimmer 1.6s var(--dt-ease-in-out) infinite;
}
@keyframes dt-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes dt-slide-up {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.dt-fade-in {
animation: dt-fade-in var(--dt-duration-base) var(--dt-ease-out);
}
.dt-slide-up {
animation: dt-slide-up var(--dt-duration-slow) var(--dt-ease-out);
}
/* --------------------------------------------------------------------------
KPI / StatCard (Compact Enterprise Metric Strip).
Static tiles stay quiet and static. Clickable/interactive tiles (e.g. on
Tenants page) get a subtle 150ms hover lift and border emphasis.
-------------------------------------------------------------------------- */
.dt-stat-card {
transition: box-shadow 150ms ease, border-color 150ms ease, transform 150ms ease !important;
}
/* Interactive hover state ONLY when clickable or wrapped in button role */
.dt-stat-card.dt-stat-card-clickable:hover,
.dt-stat-card[data-clickable='true']:hover,
[role='button'] .dt-stat-card:hover,
[role='button'] > .dt-stat-card:hover,
button .dt-stat-card:hover {
border-color: var(--color-border-emphasized, #cbd5e1) !important;
box-shadow: 0 2px 6px rgba(15, 23, 42, 0.08) !important;
transform: translateY(-1px);
}
.dt-stat-card.dt-stat-card-clickable:active,
.dt-stat-card[data-clickable='true']:active,
[role='button'] .dt-stat-card:active {
transform: translateY(0);
}
/* Standardized 14px icon size inside the 26px chip across all call sites */
.dt-stat-card .dt-stat-icon svg {
width: 14px !important;
height: 14px !important;
font-size: 14px !important;
}
/* --------------------------------------------------------------------------
Reduced motion.
Handled ONCE, globally, rather than per component — a per-component opt-out
is a rule that only covers the components somebody remembered.
-------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}