mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
The explorer had no way for a person to change how it reads. Colour mode was the only control, and it is one bit. @hanzo/appearance stores the preference and puts it on <html>; what the numbers mean is bound in styles/tokens.css. Type rides the root font size, so every Tailwind rung — all of them in rem — moves from one declaration and nothing is restated. Density rides --spacing, the single base every p-*, m-* and gap-* computes from. Both default to 1, so an install that never opens the panel renders exactly what it rendered before; measured in a browser against the production build: 16px root, p-4 at 16px, unchanged. @hanzo/design's token sheets stay out. They declare --text-*, --radius-* and --breakpoint-* — the names Tailwind v4 publishes — outside any layer, and v4 compiles utilities to var() references rather than baked values, so importing them silently reanchors the app: --text-base 16px to 14px, --radius-lg 8px to 12px. Two sets under one set of names is a fork, not a migration. They become the ramps when Tailwind leaves and there is one ramp to own. The boot script is hashed for CSP from the same call that renders it, the way the adbutler scripts are. A pasted literal goes stale on the next release of the package and fails silently — policy parses, browser drops the script, page paints at the defaults.
346 lines
16 KiB
CSS
346 lines
16 KiB
CSS
@import "tailwindcss" layer(tailwind);
|
|
@import "../styles/tokens.css";
|
|
|
|
@plugin "tailwindcss-animate";
|
|
|
|
/*
|
|
* Scan the @luxfi/ui component library for Tailwind classes.
|
|
*
|
|
* @luxfi/ui ships pre-built components whose className strings use arbitrary
|
|
* Tailwind utilities that resolve design tokens — e.g. the popover / menu /
|
|
* drawer surfaces are styled with bg-[var(--color-popover-bg)],
|
|
* bg-[var(--color-drawer-bg)], shadow-[var(--shadow-popover)] and
|
|
* border-[var(--color-popover-border)]. Tailwind v4 does not scan node_modules
|
|
* by default, so without this @source those utilities are purged and every
|
|
* floating panel (Sign-in popover, Blockchain menu, chain/network selectors,
|
|
* mobile nav drawer) renders with a transparent background — the page bleeds
|
|
* through and the content is unreadable. Sourcing the package makes Tailwind
|
|
* emit the utilities so the panels get their solid token-driven backgrounds.
|
|
*/
|
|
@source "../node_modules/@luxfi/ui";
|
|
|
|
/*
|
|
* Tailwind v4 theme configuration
|
|
*
|
|
* Breakpoints match Chakra: sm=415px, lg=1000px, xl=1440px, 2xl=1920px
|
|
* Dark mode via .dark class (next-themes "class" strategy)
|
|
* Font families: Geist (body/heading), Geist Mono (mono)
|
|
*
|
|
* The block below is the TAILWIND BRIDGE: names registered so a call site can
|
|
* write `text-good` or `bg-gray-100` and get a utility, with the values coming
|
|
* from @luxfi/ui. It exists because Tailwind is here and it goes when Tailwind
|
|
* goes. The other @theme, in styles/tokens.css, is a different thing with a
|
|
* different lifetime — it binds a reader's own settings to the ramps, and it
|
|
* outlives Tailwind. Two blocks, two reasons; Tailwind merges them.
|
|
*/
|
|
@theme {
|
|
/* --- Breakpoints --- */
|
|
--breakpoint-sm: 415px;
|
|
--breakpoint-lg: 1000px;
|
|
--breakpoint-xl: 1440px;
|
|
--breakpoint-2xl: 1920px;
|
|
|
|
/* --- Font Families --- */
|
|
--font-sans: "Geist", sans-serif;
|
|
--font-heading: "Geist", sans-serif;
|
|
--font-mono: "Geist Mono", monospace;
|
|
|
|
/* --- State. The only two hues Lux keeps, plus the achromatic warn.
|
|
Registered here so a call site can say `text-good` instead of
|
|
`text-[var(--color-status-good)]`, which is the same token and
|
|
shorter than the `text-green-500` it replaced. --- */
|
|
--color-good: var(--color-status-good);
|
|
--color-bad: var(--color-status-bad);
|
|
--color-warn: var(--color-status-warn);
|
|
|
|
/* --- Colors (mapped from CSS custom properties) --- */
|
|
--color-bg-primary: var(--color-bg-primary);
|
|
--color-bg-info: var(--color-bg-info);
|
|
--color-bg-body: var(--color-bg-body);
|
|
|
|
--color-fg-info: var(--color-fg-info);
|
|
|
|
--color-text-primary: var(--color-text-primary);
|
|
--color-text-secondary: var(--color-text-secondary);
|
|
--color-text-error: var(--color-text-error);
|
|
--color-text-success: var(--color-text-success);
|
|
|
|
--color-heading: var(--color-heading);
|
|
|
|
--color-icon-primary: var(--color-icon-primary);
|
|
--color-icon-secondary: var(--color-icon-secondary);
|
|
|
|
--color-border-divider: var(--color-border-divider);
|
|
--color-border-error: var(--color-border-error);
|
|
--color-border-info: var(--color-border-info);
|
|
|
|
--color-link-primary: var(--color-link-primary);
|
|
--color-link-primary-hover: var(--color-link-primary-hover);
|
|
--color-link-secondary: var(--color-link-secondary);
|
|
|
|
--color-hover: var(--color-hover);
|
|
--color-selected-control-bg: var(--color-selected-control-bg);
|
|
--color-selected-control-text: var(--color-selected-control-text);
|
|
--color-selected-option-bg: var(--color-selected-option-bg);
|
|
|
|
--color-button-solid-bg: var(--color-button-solid-bg);
|
|
--color-button-solid-text: var(--color-button-solid-text);
|
|
--color-button-outline-fg: var(--color-button-outline-fg);
|
|
--color-button-subtle-fg: var(--color-button-subtle-fg);
|
|
--color-button-subtle-bg: var(--color-button-subtle-bg);
|
|
--color-button-dropdown-fg: var(--color-button-dropdown-fg);
|
|
--color-button-dropdown-border: var(--color-button-dropdown-border);
|
|
--color-button-header-fg: var(--color-button-header-fg);
|
|
--color-button-header-fg-selected: var(--color-button-header-fg-selected);
|
|
--color-button-header-fg-highlighted: var(--color-button-header-fg-highlighted);
|
|
--color-button-header-border: var(--color-button-header-border);
|
|
--color-button-header-bg-selected: var(--color-button-header-bg-selected);
|
|
--color-button-header-bg-highlighted: var(--color-button-header-bg-highlighted);
|
|
--color-button-pagination-fg: var(--color-button-pagination-fg);
|
|
--color-button-pagination-border: var(--color-button-pagination-border);
|
|
--color-button-hero-bg: var(--color-button-hero-bg);
|
|
--color-button-hero-fg: var(--color-button-hero-fg);
|
|
--color-button-hero-bg-hover: var(--color-button-hero-bg-hover);
|
|
--color-button-hero-fg-hover: var(--color-button-hero-fg-hover);
|
|
--color-button-hero-bg-selected: var(--color-button-hero-bg-selected);
|
|
--color-button-hero-fg-selected: var(--color-button-hero-fg-selected);
|
|
--color-button-segmented-fg: var(--color-button-segmented-fg);
|
|
--color-button-icon-bg: var(--color-button-icon-bg);
|
|
--color-button-icon-background-bg: var(--color-button-icon-background-bg);
|
|
|
|
--color-nav-bg-selected: var(--color-nav-bg-selected);
|
|
--color-nav-text-selected: var(--color-nav-text-selected);
|
|
|
|
--color-input-fg: var(--color-input-fg);
|
|
--color-input-bg: var(--color-input-bg);
|
|
--color-input-border: var(--color-input-border);
|
|
--color-input-border-hover: var(--color-input-border-hover);
|
|
--color-input-placeholder: var(--color-input-placeholder);
|
|
|
|
--color-dialog-bg: var(--color-dialog-bg);
|
|
--color-dialog-fg: var(--color-dialog-fg);
|
|
--color-popover-bg: var(--color-popover-bg);
|
|
--color-drawer-bg: var(--color-drawer-bg);
|
|
|
|
--color-tooltip-bg: var(--color-tooltip-bg);
|
|
--color-tooltip-fg: var(--color-tooltip-fg);
|
|
|
|
--color-table-header-bg: var(--color-table-header-bg);
|
|
--color-table-header-fg: var(--color-table-header-fg);
|
|
|
|
--color-alert-fg: var(--color-alert-fg);
|
|
--color-alert-bg-info: var(--color-alert-bg-info);
|
|
--color-alert-bg-warning: var(--color-alert-bg-warning);
|
|
--color-alert-bg-success: var(--color-alert-bg-success);
|
|
--color-alert-bg-error: var(--color-alert-bg-error);
|
|
|
|
--color-skeleton-start: var(--color-skeleton-start);
|
|
--color-skeleton-end: var(--color-skeleton-end);
|
|
|
|
--color-progress-track: var(--color-progress-track);
|
|
--color-stats-bg: var(--color-stats-bg);
|
|
--color-topbar-bg: var(--color-topbar-bg);
|
|
|
|
--color-graph-line: var(--color-graph-line);
|
|
--color-graph-gradient-start: var(--color-graph-gradient-start);
|
|
--color-graph-gradient-stop: var(--color-graph-gradient-stop);
|
|
|
|
--color-scrollbar-thumb: var(--color-scrollbar-thumb);
|
|
--color-selection-bg: var(--color-selection-bg);
|
|
|
|
/* --- Tabs colors --- */
|
|
--color-tabs-solid-fg: var(--color-tabs-solid-fg);
|
|
--color-tabs-secondary-fg: var(--color-tabs-secondary-fg);
|
|
--color-tabs-secondary-border: var(--color-tabs-secondary-border);
|
|
|
|
/* --- Badge colors --- */
|
|
--color-badge-gray-bg: var(--color-badge-gray-bg);
|
|
--color-badge-gray-fg: var(--color-badge-gray-fg);
|
|
--color-badge-green-bg: var(--color-badge-green-bg);
|
|
--color-badge-green-fg: var(--color-badge-green-fg);
|
|
--color-badge-red-bg: var(--color-badge-red-bg);
|
|
--color-badge-red-fg: var(--color-badge-red-fg);
|
|
--color-badge-purple-bg: var(--color-badge-purple-bg);
|
|
--color-badge-purple-fg: var(--color-badge-purple-fg);
|
|
--color-badge-orange-bg: var(--color-badge-orange-bg);
|
|
--color-badge-orange-fg: var(--color-badge-orange-fg);
|
|
--color-badge-blue-bg: var(--color-badge-blue-bg);
|
|
--color-badge-blue-fg: var(--color-badge-blue-fg);
|
|
--color-badge-yellow-bg: var(--color-badge-yellow-bg);
|
|
--color-badge-yellow-fg: var(--color-badge-yellow-fg);
|
|
--color-badge-teal-bg: var(--color-badge-teal-bg);
|
|
--color-badge-teal-fg: var(--color-badge-teal-fg);
|
|
--color-badge-cyan-bg: var(--color-badge-cyan-bg);
|
|
--color-badge-cyan-fg: var(--color-badge-cyan-fg);
|
|
--color-badge-pink-bg: var(--color-badge-pink-bg);
|
|
--color-badge-pink-fg: var(--color-badge-pink-fg);
|
|
--color-badge-purple-alt-bg: var(--color-badge-purple-alt-bg);
|
|
--color-badge-purple-alt-fg: var(--color-badge-purple-alt-fg);
|
|
--color-badge-blue-alt-bg: var(--color-badge-blue-alt-bg);
|
|
--color-badge-blue-alt-fg: var(--color-badge-blue-alt-fg);
|
|
--color-badge-bright-gray-bg: var(--color-badge-bright-gray-bg);
|
|
--color-badge-bright-gray-fg: var(--color-badge-bright-gray-fg);
|
|
--color-badge-bright-green-bg: var(--color-badge-bright-green-bg);
|
|
--color-badge-bright-green-fg: var(--color-badge-bright-green-fg);
|
|
--color-badge-bright-red-bg: var(--color-badge-bright-red-bg);
|
|
--color-badge-bright-red-fg: var(--color-badge-bright-red-fg);
|
|
--color-badge-bright-blue-bg: var(--color-badge-bright-blue-bg);
|
|
--color-badge-bright-blue-fg: var(--color-badge-bright-blue-fg);
|
|
--color-badge-bright-yellow-bg: var(--color-badge-bright-yellow-bg);
|
|
--color-badge-bright-yellow-fg: var(--color-badge-bright-yellow-fg);
|
|
--color-badge-bright-teal-bg: var(--color-badge-bright-teal-bg);
|
|
--color-badge-bright-teal-fg: var(--color-badge-bright-teal-fg);
|
|
--color-badge-bright-cyan-bg: var(--color-badge-bright-cyan-bg);
|
|
--color-badge-bright-cyan-fg: var(--color-badge-bright-cyan-fg);
|
|
--color-badge-bright-orange-bg: var(--color-badge-bright-orange-bg);
|
|
--color-badge-bright-orange-fg: var(--color-badge-bright-orange-fg);
|
|
--color-badge-bright-purple-bg: var(--color-badge-bright-purple-bg);
|
|
--color-badge-bright-purple-fg: var(--color-badge-bright-purple-fg);
|
|
--color-badge-bright-pink-bg: var(--color-badge-bright-pink-bg);
|
|
--color-badge-bright-pink-fg: var(--color-badge-bright-pink-fg);
|
|
|
|
/* --- Palette rungs ---
|
|
Self-references, like the badge block above: they exist so Tailwind emits
|
|
the `bg-gray-100` / `text-red-500` utilities, while the VALUES come from
|
|
@luxfi/ui/tokens.css (imported outside any @layer at the top, so it wins).
|
|
This block used to hold literals, which is how the explorer kept its own
|
|
browns after the token set went monochrome:
|
|
--color-orange-50 #FFFAF0 --color-orange-100 #FEEBCB
|
|
--color-orange-500 #DD6B20 --color-yellow-400 #ECC94B */
|
|
--color-gray-50: var(--color-gray-50);
|
|
--color-gray-100: var(--color-gray-100);
|
|
--color-gray-200: var(--color-gray-200);
|
|
--color-gray-300: var(--color-gray-300);
|
|
--color-gray-400: var(--color-gray-400);
|
|
--color-gray-500: var(--color-gray-500);
|
|
--color-gray-600: var(--color-gray-600);
|
|
--color-gray-700: var(--color-gray-700);
|
|
--color-gray-800: var(--color-gray-800);
|
|
--color-gray-900: var(--color-gray-900);
|
|
|
|
--color-green-50: var(--color-green-50);
|
|
--color-green-100: var(--color-green-100);
|
|
--color-green-200: var(--color-green-200);
|
|
--color-green-500: var(--color-green-500);
|
|
--color-green-800: var(--color-green-800);
|
|
|
|
--color-red-50: var(--color-red-50);
|
|
--color-red-100: var(--color-red-100);
|
|
--color-red-200: var(--color-red-200);
|
|
--color-red-500: var(--color-red-500);
|
|
--color-red-600: var(--color-red-600);
|
|
--color-red-800: var(--color-red-800);
|
|
|
|
--color-orange-50: var(--color-orange-50);
|
|
--color-orange-100: var(--color-orange-100);
|
|
--color-orange-500: var(--color-orange-500);
|
|
|
|
--color-yellow-400: var(--color-yellow-400);
|
|
|
|
--color-white: var(--color-white);
|
|
--color-black: var(--color-black);
|
|
|
|
/* --- Shadows --- */
|
|
--shadow-action-bar: var(--shadow-action-bar);
|
|
--shadow-popover: var(--shadow-popover);
|
|
--shadow-drawer: var(--shadow-drawer);
|
|
}
|
|
|
|
/*
|
|
* Motion lives in @luxfi/ui/tokens.css (imported above via styles/tokens.css):
|
|
* durations, curves, enter/exit, hover/press, the skeleton, and one global
|
|
* prefers-reduced-motion rule. Add a move to the design system, never here.
|
|
*
|
|
* What this replaced: a local `skeleton-shimmer` that animated
|
|
* background-position on a 200%-wide gradient. The explorer mounts ~1460
|
|
* skeletons, so that was ~1460 full-box repaints per frame on the main thread.
|
|
*/
|
|
|
|
/*
|
|
* Dark mode: use .dark class (next-themes)
|
|
* Tailwind v4 default is prefers-color-scheme; override to class-based.
|
|
*/
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
/*
|
|
* Scope Tailwind to a layer so it does not override Chakra styles.
|
|
* Chakra styles have no layer, so they win by default cascade rules.
|
|
* New Tailwind components explicitly opt in via utility classes.
|
|
*/
|
|
|
|
@font-face {
|
|
font-family: 'InterFallback';
|
|
src: url('/static/fonts/Inter-fallback.woff2') format('woff2');
|
|
unicode-range: U+2192; /* Only the right arrow */
|
|
font-display: swap;
|
|
}
|
|
|
|
/*
|
|
* Global font and body styles.
|
|
* Geist is loaded via Google Fonts in _document.tsx.
|
|
* Apply it universally so Chakra components inherit it too.
|
|
*/
|
|
html, body {
|
|
font-family: 'Geist', sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
}
|
|
|
|
|
|
/* ── light mode: Tamagui text was painting white on white ──────────────────
|
|
The gui provider pins `t_dark` on <body> CLIENT-SIDE in BOTH schemes (the
|
|
server sends a bare <body>), and its theme block reads:
|
|
|
|
.t_dark { --color: var(--c-color-background) } -> #ffffff
|
|
.t_light { --color: var(--c-color-text) } -> #000000
|
|
:root { --c-color-background:#ffffff; --c-color-text:#000000 } invariant
|
|
|
|
so every Tamagui text component (is_H1 / is_H3 / is_Text) resolved --color to
|
|
#ffffff unconditionally — right by accident on dark, invisible on light.
|
|
Measured at 1.00:1 across all four widths on /address/*, /validators and the
|
|
landing page: "Address details", the ADDRESS ITSELF, "Validators", and six
|
|
section headings. A user clicking a Safe link from lux.cloud landed on a page
|
|
that never showed which address they were looking at.
|
|
|
|
The page BACKGROUND is not affected — that follows the Tailwind --lux-* layer,
|
|
which already tracks html.light/html.dark correctly. Only the text colour is
|
|
wrong, so only --color is corrected here. Both selectors are covered because
|
|
the class is applied by script: html.light is the explicit signal, and the
|
|
media query catches the pre-hydration frame.
|
|
|
|
Remove this the day the provider stops hard-pinning t_dark. */
|
|
/* NOT `body.t_dark` — measured 2026-08-06: the provider mounts a SECOND
|
|
`<span class="t_dark">` INSIDE <body>, and `:root .t_dark` matches both. A
|
|
body-scoped rule sets --color on body correctly (verified #000000 there) and
|
|
the nested span immediately re-sets it to #ffffff, which every heading then
|
|
inherits. Scoping to `.t_dark` reaches every Theme wrapper; (0,2,1) still
|
|
beats `:root .t_dark` (0,2,0). */
|
|
/* ⚠️ THIS RULE DOES NOT REACH THE BUNDLE. Do not trust it.
|
|
Three forms have now been shipped and none appear in the served CSS:
|
|
v1.1.20 html.light body.t_dark
|
|
v1.1.21 html.light .t_dark
|
|
v1.1.22 html[class~="light"] .t_dark
|
|
In every build `.t_dark` occurs exactly ONCE in the output — the media-query
|
|
rule below — while the file's OTHER top-level rule (html,body{font-family:
|
|
Geist}) survives intact, so top-level rules are not dropped wholesale.
|
|
Running this file through the real postcss + @tailwindcss/postcss pipeline
|
|
locally KEEPS both rules, so Tailwind is not the cause; something later in
|
|
the Next build removes it. Root cause not yet found.
|
|
|
|
What it is FOR, once it works: OS-dark + stored-theme-light. There <html>
|
|
gets class="light" so the background turns white, but prefers-color-scheme
|
|
is false, so the media rule below is inert and --color stays #ffffff — the
|
|
original white-on-white, measured 1.00:1 with 11,232/11,232 pure-white
|
|
pixels in the h1 band. Injecting this rule into that state live fixes it
|
|
(1.00 -> 21.00, and forcing html.dark still gives 17.93), which is the only
|
|
sense in which it is "verified" — by injection, NOT by the shipped bundle.
|
|
|
|
Not currently user-reachable: there is no theme toggle in the signed-out UI,
|
|
so the site follows the OS. But localStorage.theme IS honoured, so a
|
|
returning user carrying a stale value already lands here, and it goes live
|
|
the moment a toggle ships. */
|
|
html[class~="light"] .t_dark { --color: var(--c-color-text); }
|
|
@media (prefers-color-scheme: light) {
|
|
html:not(.dark) .t_dark { --color: var(--c-color-text); }
|
|
}
|