One family. Font stacks name Zen and fall back to the native faces, tokens
name the role rather than a face, and the packages that used to supply a
face now point at @hanzo/font.
Self-hosted @font-face blocks are removed rather than renamed: rewriting the
family in one would leave a src pointing at a file this repo no longer ships.
@hanzo/font carries the binaries and declares the faces, so the local copies
go with the declaration that used them.
Comments naming a face we no longer use are deleted, not reworded.
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.
The comment added in v1.1.22 claimed the attribute-selector form was
verified. It was verified by INJECTION into a live page, not in the
shipped bundle -- and the shipped bundle does not contain it. Three
forms have now gone out (html.light body.t_dark, html.light .t_dark,
html[class~="light"] .t_dark) and `.t_dark` occurs exactly once in the
served CSS every time: the media-query rule.
Narrowed but not solved. The file's other top-level rule
(html,body{font-family:Geist}) survives, 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.
The user-visible bug is fixed regardless: the media-query rule is live
and measured at 21.00:1 with 7,111 dark pixels in the heading band
where there were ~0. What remains uncovered is OS-dark +
stored-theme-light, which no UI can currently produce.
A comment that overstates what shipped is worse than no comment.
Co-Authored-By: Hanzo Dev <[email protected]>
`html.light .t_dark` never reached the served CSS in v1.1.20 or v1.1.21
-- `.light` occurs ZERO times in the whole bundle, because no content
file references that token (the app's own theme CSS only ever tests
`:not(.dark)`), so the scanner has nothing to keep it alive.
Only the media-query variant survived, which leaves OS-dark +
stored-theme-light rendering the original white-on-white: html gets
class="light" so the background turns white, but prefers-color-scheme
is false so --color stays #ffffff. Measured in that state: 1.00:1,
11,232/11,232 pure-white pixels in the h1 band. localStorage.theme is
honoured today even though no toggle is exposed, so a returning user
with a stale value already lands here.
The attribute form has identical (0,2,1) specificity and no class token
to miss. Verified by injection: 1.00 -> 21.00, and forcing html.dark
still gives 17.93.
Keep the media-query variant too -- it covers the pre-hydration window
where <html> is classless.
Co-Authored-By: Hanzo Dev <[email protected]>
v1.1.20 set --color on <body> and that part worked (verified #000000 on
body, html.light present, media query matching). The headings stayed
white because the provider mounts a SECOND `<span class="t_dark">`
INSIDE <body>: `:root .t_dark` matches body AND that span, so the span
re-set --color to #ffffff and every heading inherited from there. The
inheritance chain measured html -> body(#000000) -> div -> span.t_dark
(#ffffff) -> ... -> h1.
Dropping `body` from the selector reaches every Theme wrapper; (0,2,1)
still beats `:root .t_dark` (0,2,0).
Co-Authored-By: Hanzo Dev <[email protected]>
The gui provider pins `t_dark` on <body> client-side in BOTH schemes (the
server sends a bare <body>), and its theme block maps
.t_dark { --color: var(--c-color-background) } /* #ffffff */
against scheme-INVARIANT sources (--c-color-background #ffffff,
--c-color-text #000000). So every Tamagui text component resolved --color
to #ffffff unconditionally — correct by accident on dark, invisible on
light. Measured 1.00:1 at all four widths on /address/*, /validators and
the landing page: "Address details", the ADDRESS ITSELF, "Validators",
and six section headings. Pixel ground truth on the 1440 light heading
band: 135,034 of 136,000 pixels pure white, no glyphs at all.
A user clicking a Safe from lux.cloud landed on a page that never showed
which address they were looking at.
Only --color is corrected: the background already tracks html.light via
the Tailwind --lux-* layer. Not the earlier suspect — the circular
`--color-x: var(--color-x)` tokens are inert, because unlayered :root
beats @layer theme regardless of order.
Tamagui goes in cspell.jsonc words[], which is the real dictionary —
.cspell-words.txt is listed in ignorePaths, so it is a file cspell skips,
not a word source.
Co-Authored-By: Hanzo Dev <[email protected]>
styles/tokens.css was a 494-line FORK of @luxfi/ui/tokens.css, which is how it
drifted its own browns: --color-status-warn #B45309 / #FBBF7A and the whole
Chakra orange + yellow badge ramp (#FEEBCB #FFFAF0 #DD6B20 #FFFFF0 #D69E2E
#FEFCBF #B7791F #C05621 #7B341E #744210 #FAF089). It imports the umbrella now
and adds nothing of its own.
next.config.js hand-listed SEVEN of the engine's 55 packages in
transpilePackages and deduped none of them, which is how this app's store holds
seven physical copies of @hanzogui/popper (3.0.0, 3.0.1, 3.0.2 x3, 7.3.0 x2). A
context published at module scope is per-copy, so PopperAnchor read the empty
default and called refs.setReference on undefined once per tooltip trigger. It
uses withLuxUi now — the one place that wiring lives.
47 files reached for Tailwind's chromatic ramps directly (108 occurrences).
They resolve through tokens now, under short semantic names registered in the
@theme block: text-good / text-bad / text-warn, where warn is ACHROMATIC —
attention in Lux is the loudest neutral, not a hue. The Quasar certificate
tooltip stops naming colours it no longer paints ("BLS (green)", "Corona
(blue)", "ML-DSA (purple)") — copy that describes a palette has to change with
it.
Measured on the rendered pages: warm 0%, tinted-neutral 0.0003%, mean chroma
0.004 (/) and 0.003 (/txs) at 1440x900, 0.000 at 390x844, both modes; zero
uncaught setReference errors.
Two pre-existing defects surfaced because lint-staged now lints these files and
are fixed here rather than skipped: `Box` was used but never imported in
AddressEntity.pw.tsx, and eight lines already exceeded the 160-column limit.
Co-authored-by: Hanzo Dev <[email protected]>
Two panels slide in the mobile nav. The main one animates `transform`. Its
sibling animated `left` — which relayouts the nav subtree every frame AND
registers as layout shift, because moving an element's layout box is exactly
what CLS measures. Measured under a 4x CPU throttle at 390x844: CLS 0.726 with
`left`, 0 with `transform`. Google calls anything above 0.25 "poor", so the
menu was scoring nearly 3x poor on a tap.
The rewrite is pixel-identical, not an approximation. The element is absolute
+ w-full inside a containing block with no padding, so `left: 0` is its static
position and translateX(100% + 24px) reproduces the old offset exactly. It now
mirrors its sibling: same distance, same duration, same property.
Motion also moves out of this repo. The local `skeleton-shimmer` keyframe is
retired in favour of the design system's loading state — per-element
background-position shimmer measures 33.6fps at 1460 concurrent skeletons and
13.8fps at 3000 under the same throttle, while skeletons that hold still
measure 120fps at every count. Reasoning and full numbers are in section 6c of
@luxfi/ui/tokens.css.
Also wraps one 174-char className. It was already over the 160 limit; no
staged change had made the linter look at this file until now.
Co-authored-by: Hanzo Dev <[email protected]>
- wallet: brandFamilyChains derives sibling networks (Lux mainnet/testnet/devnet)
from the multi-tenant chain registry so connect/add presents EVERY registered
network for the brand, not just the env chain; dedupe by id.
- login/menu: useProvider queryFn returns null (react-query v5 forbids undefined)
+ callers switched from '= {}' destructure default to '?? {}' so a null result
no longer crashes the wallet hooks (the busted-menu root cause).
- manifest: fix Pars chainId 7070 -> 494949 (regression) and drop the forbidden
'Subnet' term from sovereign L1 network names (-> '<Brand> Network').
- Validators page + /api/pchain proxy present; rebuild makes the live 5-validator
view visible on explore.lux.network.
Co-authored-by: Hanzo Dev <[email protected]>
* operation lifecycle accordion
* operation entity
* adapt layout to mobile device
* basic operations view
* pass response data to operation details
* update types package
* tac operation status and links to tx on ton
* feature config, docs and operation badge
* add info to tx page
* add operations to search
* search input on operations page
* tests
* update tooltips and temporary fix of ts
* add InterFallback typeface with → glyph
* design and api updates
* support new API version
* add time format toggle
* add shield to entity icon
* minor fixes
* update screenshots
* clean up
* fix screenshot