Files
brk/website/styles/panes/explorer.css
2026-04-20 18:11:30 +02:00

568 lines
15 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#explorer {
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
.dim {
opacity: 0.5;
}
@container aside (max-width: 767px) {
overflow-y: auto;
padding: var(--main-padding) 0;
flex-direction: column;
}
--cube: 4.5rem;
/* Iso geometry. --iso-scale is the half-width ratio of the hex
silhouette (= cos(30deg) = sqrt(3)/2); the silhouette spans
2·iso-scale wide and 2 tall in cube-face units. The per-face
transforms use the HTML-demo math with ox=oy=0, so the outer
compensation on `.face` is just translateY(50%). */
--iso-scale: calc(sqrt(3) / 2);
--cube-w: calc(var(--cube) * 2 * var(--iso-scale));
--cube-h: calc(var(--cube) * 2);
--face-step: 0.033;
/* Cube face-color derivations, resolved once at the #explorer
level so .cube state changes (hover / selected) don't force
Safari to re-evaluate `oklch(from var …)` on every paint — a
known source of jank there. Each interaction state gets its own
set; the .cube rule below just swaps which set --face-right /
--face-left / --face-top / --face-bottom reference. */
--cube-neutral-right: light-dark(
oklch(from var(--light-gray) calc(l - var(--face-step) * 2) c h),
var(--dark-gray)
);
--cube-neutral-left: light-dark(
oklch(from var(--light-gray) calc(l - var(--face-step)) c h),
oklch(from var(--dark-gray) calc(l + var(--face-step)) c h)
);
--cube-neutral-top: light-dark(
var(--light-gray),
oklch(from var(--dark-gray) calc(l + var(--face-step) * 2) c h)
);
--cube-neutral-bottom: oklch(
from var(--border-color) calc(l - var(--face-step) * 3) c h
);
--cube-hover-right: light-dark(
oklch(from var(--dark-gray) calc(l - var(--face-step) * 2) c h),
var(--light-gray)
);
--cube-hover-left: light-dark(
oklch(from var(--dark-gray) calc(l - var(--face-step)) c h),
oklch(from var(--light-gray) calc(l + var(--face-step)) c h)
);
--cube-hover-top: light-dark(
var(--dark-gray),
oklch(from var(--light-gray) calc(l + var(--face-step) * 2) c h)
);
--cube-hover-bottom: oklch(
from var(--inv-border-color) calc(l - var(--face-step) * 3) c h
);
--cube-selected-right: light-dark(
oklch(from var(--orange) calc(l - var(--face-step) * 2) c h),
var(--orange)
);
--cube-selected-left: light-dark(
oklch(from var(--orange) calc(l - var(--face-step)) c h),
oklch(from var(--orange) calc(l + var(--face-step)) c h)
);
--cube-selected-top: light-dark(
var(--orange),
oklch(from var(--orange) calc(l + var(--face-step) * 2) c h)
);
--cube-selected-bottom: oklch(
from var(--orange) calc(l - var(--face-step) * 3) c h
);
> * {
padding: 0 var(--main-padding);
@container aside (min-width: 768px) {
padding: var(--main-padding);
}
}
#chain {
flex-shrink: 0;
@container aside (max-width: 767px) {
overflow-x: auto;
padding-bottom: 1rem;
}
@container aside (min-width: 768px) {
height: 100%;
overflow-y: auto;
padding-right: calc(var(--main-padding) / 2);
}
.blocks {
display: flex;
flex-direction: column-reverse;
--min-gap: calc(var(--cube) * -1);
--max-gap: calc(var(--cube) * 6);
--min-dt: 0;
--max-dt: 10800;
@container aside (max-width: 767px) {
--min-gap: 0rem;
flex-direction: row-reverse;
width: max-content;
}
@container aside (min-width: 768px) {
padding-bottom: 6rem;
}
}
.cube {
--t: pow(
clamp(
0,
(var(--dt, 600) - var(--min-dt)) / (var(--max-dt) - var(--min-dt)),
1
),
0.7
);
--block-gap: calc(
var(--min-gap) + var(--t) * (var(--max-gap) - var(--min-gap))
);
--empty-alpha: 0.4;
/* Face colors reference the precomputed sets on #explorer
(see top of file). Hover / selected rules just switch which
set each --face-* points at. */
--face-right: var(--cube-neutral-right);
--face-left: var(--cube-neutral-left);
--face-top: var(--cube-neutral-top);
--face-bottom: var(--cube-neutral-bottom);
/* Fill-driven state. --liquid-y is the liquid's vertical scale;
--glass-y the glass-above-liquid's. --is-full / --is-empty
are 1 only at the exact endpoints (fill == 1 / fill == 0)
and drive opacity so those roles hide cleanly there. */
--liquid-y: calc(var(--iso-scale) * var(--fill));
--glass-y: calc(var(--iso-scale) * (1 - var(--fill)));
--is-full: round(down, var(--fill), 1);
--is-empty: round(down, calc(1 - var(--fill)), 1);
flex-shrink: 0;
position: relative;
cursor: pointer;
width: var(--cube-w);
height: var(--cube-h);
/* .cube is an <a>; reset the global anchor styles in
elements.css that would clip the iso silhouette
(overflow:hidden) and underline the empty link. */
overflow: visible;
text-decoration: none;
--state-ease: 50ms cubic-bezier(0.4, 0, 0.2, 1);
color: var(--color);
transition: color var(--state-ease);
user-select: none;
pointer-events: none;
&:hover {
color: var(--background-color);
--face-right: var(--cube-hover-right);
--face-left: var(--cube-hover-left);
--face-top: var(--cube-hover-top);
--face-bottom: var(--cube-hover-bottom);
}
&:active,
&.selected {
color: var(--black);
--face-right: var(--cube-selected-right);
--face-left: var(--cube-selected-left);
--face-top: var(--cube-selected-top);
--face-bottom: var(--cube-selected-bottom);
}
/* Skeleton state (cube painted but data is stale while a new
chunk loads): hide text AND the pool logo. Using visibility
rather than color:transparent so the raw <img> logo hides too. */
&.skeleton .face-text {
visibility: hidden;
}
/* Shared face-transform template. Each face div sets --orient,
--x, --y, --sx, --sy and its role (liquid/glass/face-text)
supplies --y-offset. Faces extend outside .cube's layout box
— the iso silhouette spans ~2·iso·cube × 2·cube, offset into
what would be the next cube's space. Clicks land only on the
transformed face rectangles, not the .cube's empty corners. */
.face {
position: absolute;
transform-origin: 0 0;
box-sizing: border-box;
width: var(--cube);
height: var(--cube);
transform:
translateY(50%)
var(--orient)
translate(calc(var(--cube) * var(--x)), calc(var(--cube) * var(--y)))
scale(var(--sx, 1), var(--sy));
pointer-events: auto;
}
/* Roles:
.liquid opaque liquid (scales with fill)
.glass translucent glass shell
.face-text text overlay spanning the full rhombus
will-change is on the painted roles only (not .face-text,
whose background never changes) so each liquid/glass gets its
own compositor layer for snappy hover/select repaints. */
.liquid,
.glass {
will-change: background-color;
transition: background-color var(--state-ease);
}
.liquid {
background: var(--fc);
opacity: calc(1 - var(--is-empty));
--sy: var(--liquid-y);
--y-offset: var(--glass-y);
}
.glass {
background: oklch(from var(--fc) l c h / var(--empty-alpha));
--sy: var(--glass-y);
--y-offset: 0;
}
.glass.top {
opacity: calc(1 - var(--is-full));
}
.face-text {
--sy: var(--iso-scale);
--y-offset: 0;
pointer-events: none;
padding: 0.1rem;
font-family: var(--font-mono);
font-size: var(--font-size-xs);
font-weight: 450;
}
.face-text.top,
.face-text.right {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.face-text.top {
justify-content: center;
text-transform: uppercase;
}
.face-text.right {
justify-content: space-between;
}
.face-text p {
margin: 0;
}
.face-text .height {
font-size: var(--font-size-sm);
font-weight: normal;
}
.face-text .fees {
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
align-items: center;
}
/* Pool line: raw (un-tinted) logo + miner name, ellipsis-clipped. */
.face-text .pool {
display: flex;
align-items: center;
justify-content: center;
gap: 0.1em;
width: 100%;
}
.face-text .pool img {
width: 1.25em;
height: 1.25em;
flex-shrink: 0;
}
.face-text .pool span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1;
}
/* Per-face geometry. 3 orientations × 4 vertical anchors. Each
face picks one of each (plus its color/horizontal anchor). */
.top,
.bottom {
--orient: rotate(30deg) skewX(-30deg);
--sy: var(--iso-scale);
}
.right,
.rear-left {
--orient: rotate(-30deg) skewX(-30deg);
}
.left,
.rear-right {
--orient: rotate(30deg) skewX(30deg);
}
/* Vertical anchors (cube-face units from the layout origin).
--y-offset is the role-specific fill shift (liquid sides
get glass-y, everything else 0). */
.top,
.rear-right {
--y: calc(var(--y-offset) - var(--iso-scale));
}
.left,
.rear-left {
--y: var(--y-offset);
}
.right {
--y: calc(var(--y-offset) + var(--iso-scale));
}
.bottom {
--y: 0;
}
/* Per-face color + horizontal anchor. */
.top { --fc: var(--face-top); --x: var(--top-x-shift, 0); }
.bottom { --fc: var(--face-bottom); --x: 1; }
.right { --fc: var(--face-right); --x: 1; }
.left { --fc: var(--face-left); --x: 0; }
.rear-right { --fc: var(--face-left); --x: 1; }
.rear-left { --fc: var(--face-top); --x: 1; --sx: -1; }
.liquid.top { --top-x-shift: calc(1 - var(--fill)); }
& + & {
margin-bottom: var(--block-gap);
&::before {
content: "";
position: absolute;
top: 100%;
left: 50%;
width: 1px;
height: var(--block-gap);
background: var(--border-color);
z-index: -1;
}
@container aside (max-width: 767px) {
margin-bottom: 0;
margin-right: var(--block-gap);
&::before {
bottom: auto;
left: auto;
right: calc(-1 * var(--block-gap));
top: 50%;
width: var(--block-gap);
height: 1px;
}
}
}
}
}
#block-details,
#tx-details,
#addr-details {
flex: 1;
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
@container aside (min-width: 768px) {
overflow-y: auto;
padding-left: calc(var(--main-padding) / 2);
}
h1 {
margin-bottom: 1rem;
code {
font-size: 1.5rem;
font-weight: 300;
font-family: Lilex;
color: var(--off-color);
letter-spacing: -0.05rem;
}
}
.row {
display: flex;
justify-content: space-between;
gap: 1rem;
padding: 0.25rem 0;
border-bottom: 1px solid var(--border-color);
}
.label {
color: var(--off-color);
white-space: nowrap;
}
.value {
text-align: right;
word-break: break-all;
}
.transactions {
margin-top: 1rem;
.tx-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
h2 {
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
}
}
.pagination {
display: flex;
align-items: center;
gap: 0.5rem;
button {
color: var(--off-color);
&:disabled {
opacity: 0.25;
pointer-events: none;
}
}
}
.tx {
border: 1px solid var(--border-color);
padding: 0.5rem;
margin-bottom: 0.5rem;
content-visibility: auto;
contain-intrinsic-block-size: auto 8rem;
.tx-head {
display: flex;
justify-content: space-between;
gap: 0.5rem;
margin-bottom: 0.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border-color);
.txid {
font-family: Lilex;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
color: var(--off-color);
overflow: hidden;
text-overflow: ellipsis;
}
.tx-time {
flex-shrink: 0;
color: var(--off-color);
}
}
.tx-body {
display: flex;
gap: 0.5rem;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
}
.tx-inputs,
.tx-outputs {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.125rem;
}
.tx-outputs {
padding-left: 0.5rem;
border-left: 1px solid var(--border-color);
}
.tx-io {
display: flex;
justify-content: space-between;
gap: 0.5rem;
.addr {
display: flex;
min-width: 0;
white-space: nowrap;
color: var(--off-color);
a {
display: flex;
min-width: 0;
}
.addr-head {
overflow: hidden;
text-overflow: ellipsis;
}
.addr-tail {
flex-shrink: 0;
}
&.coinbase {
color: var(--orange);
}
.coinbase-sig {
font-family: Lilex;
font-size: var(--font-size-xs);
color: var(--off-color);
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
&.op-return {
color: var(--off-color);
}
}
.amount {
flex-shrink: 0;
text-align: right;
}
}
.show-more {
color: var(--off-color);
font-size: var(--font-size-xs);
padding: 0.25rem 0;
}
.tx-foot {
display: flex;
justify-content: space-between;
gap: 0.5rem;
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid var(--border-color);
color: var(--off-color);
.total {
color: var(--orange);
}
}
}
}
}
}