spb/coinexplorer Public MIT
Self-hosted, zero-API-key explorer for stablecoins and major crypto.
Python 60.3%
HTML 23.6%
JavaScript 8.1%
CSS 6.8%
SQL 1%
1/* Author: Simon-Pierre Boucher */2/* Mail: contact@spboucher.ai */3/* coinexplorer design system v4 — color roles from the validated dataviz4 reference palette; chrome, depth and rhythm layered on top. */56:root {7 color-scheme: light;8 --page: #f7f7f4; --surface: #fdfdfc; --surface-2: #f2f1ed;9 --ink: #0b0b0b; --ink-2: #52514e; --muted: #898781;10 --grid: #e1e0d9; --baseline: #c3c2b7; --ring: rgba(11,11,11,.08);11 --s1: #2a78d6; --s2: #eb6834; --s3: #1baf7a; --s4: #eda100;12 --seq-150: #b7d3f6;13 --good-text: #006300; --bad: #d03b3b; --div-neg: #e34948;14 --shadow-1: 0 1px 2px rgba(11,11,11,.04), 0 2px 8px rgba(11,11,11,.04);15 --shadow-2: 0 2px 6px rgba(11,11,11,.06), 0 10px 28px rgba(11,11,11,.08);16 --glass: rgba(253,253,252,.82);17 --radius: 14px;18}19@media (prefers-color-scheme: dark) {20 :root {21 color-scheme: dark;22 --page: #0c0c0b; --surface: #171716; --surface-2: #1f1f1d;23 --ink: #ffffff; --ink-2: #c3c2b7; --muted: #898781;24 --grid: #2c2c2a; --baseline: #383835; --ring: rgba(255,255,255,.09);25 --s1: #3987e5; --s2: #d95926; --s3: #199e70; --s4: #c98500;26 --seq-150: #184f95;27 --good-text: #0ca30c; --bad: #e66767; --div-neg: #e66767;28 --shadow-1: none; --shadow-2: 0 12px 32px rgba(0,0,0,.5);29 --glass: rgba(20,20,19,.82);30 }31}3233* { box-sizing: border-box; }34html { -webkit-text-size-adjust: 100%; }35body {36 margin: 0; background: var(--page); color: var(--ink);37 font: 14px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;38 background-image: radial-gradient(1100px 380px at 50% -180px,39 color-mix(in oklab, var(--s1) 7%, transparent), transparent);40 background-repeat: no-repeat;41}42a { color: var(--s1); text-decoration: none; }43a:hover { text-decoration: underline; text-underline-offset: 2px; }44:focus-visible { outline: 2px solid var(--s1); outline-offset: 2px; border-radius: 4px; }4546/* ================= top nav ================= */47.topnav {48 position: sticky; top: 0; z-index: 40;49 background: var(--glass); backdrop-filter: blur(14px) saturate(1.4);50 -webkit-backdrop-filter: blur(14px) saturate(1.4);51 border-bottom: 1px solid var(--ring);52 display: flex; align-items: center; gap: 14px;53 padding: 0 clamp(12px, 3vw, 24px); min-height: 54px; flex-wrap: wrap;54}55.topnav .logo {56 font-weight: 800; font-size: 16px; letter-spacing: -.02em;57 color: var(--ink); white-space: nowrap; padding: 12px 0;58}59.topnav .logo span {60 background: linear-gradient(100deg, var(--s1), color-mix(in oklab, var(--s1) 55%, var(--s3)));61 -webkit-background-clip: text; background-clip: text; color: transparent;62}63.topnav nav {64 display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none;65 -webkit-overflow-scrolling: touch; max-width: 100%;66}67.topnav nav::-webkit-scrollbar { display: none; }68.topnav nav a {69 color: var(--ink-2); padding: 7px 12px; border-radius: 99px;70 font-size: 13px; font-weight: 500; white-space: nowrap;71}72.topnav nav a:hover {73 background: color-mix(in oklab, var(--s1) 9%, transparent);74 text-decoration: none; color: var(--ink);75}76.topnav nav a.active {77 color: #fff; font-weight: 600;78 background: linear-gradient(135deg, var(--s1), color-mix(in oklab, var(--s1) 80%, #000));79 box-shadow: 0 2px 8px color-mix(in oklab, var(--s1) 35%, transparent);80}81.searchbox { margin-left: auto; display: flex; min-width: 150px; flex: 0 1 320px; padding: 8px 0; }82.searchbox input {83 width: 100%; background: var(--surface-2); color: var(--ink);84 border: 1px solid var(--ring); border-radius: 99px; padding: 8px 16px; font: inherit;85 transition: box-shadow .15s, background .15s;86}87.searchbox input:focus {88 outline: none; background: var(--surface);89 box-shadow: 0 0 0 2px color-mix(in oklab, var(--s1) 55%, transparent);90}91@media (max-width: 760px) {92 .searchbox { order: 9; flex-basis: 100%; margin-left: 0; padding-top: 0; }93 .topnav .logo { padding: 10px 0 6px; }94}9596/* ================= layout ================= */97main {98 display: grid; gap: clamp(10px, 1.6vw, 16px);99 padding: clamp(12px, 2.5vw, 20px) clamp(12px, 3vw, 24px) 48px;100 grid-template-columns: repeat(12, 1fr); max-width: 1320px; margin: 0 auto;101}102.card {103 background: var(--surface); border: 1px solid var(--ring);104 border-radius: var(--radius); padding: clamp(12px, 2vw, 18px);105 grid-column: span 12; box-shadow: var(--shadow-1); min-width: 0;106}107@media (min-width: 980px) {108 .tile { grid-column: span 3; }109 .half { grid-column: span 6; }110 .third { grid-column: span 4; }111 .twothird { grid-column: span 8; }112}113@media (min-width: 560px) and (max-width: 979px) {114 .tile { grid-column: span 6; }115 .half, .third, .twothird { grid-column: span 12; }116}117.card h2 {118 font-size: 12px; margin: 0 0 10px; color: var(--muted); font-weight: 700;119 text-transform: uppercase; letter-spacing: .06em;120 display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;121}122.card h2 .sub { color: var(--muted); font-weight: 400; font-size: 12px; text-transform: none; letter-spacing: 0; }123.tile { position: relative; overflow: hidden; }124.tile::before {125 content: ""; position: absolute; inset: 0 auto 0 0; width: 3px;126 background: linear-gradient(180deg, var(--s1), color-mix(in oklab, var(--s1) 40%, transparent));127 border-radius: 3px 0 0 3px;128}129.tile .big {130 font-size: clamp(22px, 2.4vw, 28px); font-weight: 800; letter-spacing: -.02em;131 font-variant-numeric: tabular-nums;132}133.tile .note { color: var(--muted); font-size: 12px; margin-top: 3px; }134.up { color: var(--good-text); } .down { color: var(--bad); }135136.pagehead { grid-column: span 12; display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }137.pagehead h1 { font-size: clamp(19px, 2.6vw, 23px); margin: 6px 0 0; letter-spacing: -.02em; }138.pagehead .sub { color: var(--muted); font-size: 13px; }139.badge {140 font-size: 10.5px; font-weight: 600; padding: 3px 9px; border-radius: 99px;141 border: 1px solid var(--ring); color: var(--ink-2);142 text-transform: uppercase; letter-spacing: .04em;143}144.badge.native { color: var(--good-text); border-color: color-mix(in oklab, var(--good-text) 45%, transparent); background: color-mix(in oklab, var(--good-text) 7%, transparent); }145.badge.bridged { color: var(--s2); border-color: color-mix(in oklab, var(--s2) 45%, transparent); background: color-mix(in oklab, var(--s2) 8%, transparent); }146.badge.discontinued { color: var(--bad); border-color: color-mix(in oklab, var(--bad) 45%, transparent); background: color-mix(in oklab, var(--bad) 7%, transparent); }147148/* ================= filters ================= */149.filters { grid-column: span 12; display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }150.filters select, .filters input, .filters button, .btn {151 background: var(--surface); color: var(--ink); border: 1px solid var(--ring);152 border-radius: 10px; padding: 8px 13px; font: inherit; font-size: 13px;153 cursor: pointer; box-shadow: var(--shadow-1); min-height: 38px;154}155.filters input { cursor: text; }156.filters button:hover, .btn:hover { border-color: color-mix(in oklab, var(--s1) 45%, var(--ring)); }157.presets { display: flex; gap: 0; border: 1px solid var(--ring); border-radius: 10px; overflow: hidden; box-shadow: var(--shadow-1); }158.presets button { border: 0; border-radius: 0; box-shadow: none; background: var(--surface); min-height: 38px; }159.presets button + button { border-left: 1px solid var(--ring); }160.presets button[aria-pressed="true"] { background: var(--s1); color: #fff; font-weight: 700; }161162/* ================= tables ================= */163.scroll { max-height: 420px; overflow: auto; border-radius: 8px; }164table { width: 100%; border-collapse: collapse; font-size: 12.5px; min-width: 520px; }165th {166 text-align: left; color: var(--muted); font-weight: 700; padding: 7px 9px;167 border-bottom: 1px solid var(--grid); position: sticky; top: 0; z-index: 1;168 background: var(--surface); font-size: 11px; text-transform: uppercase; letter-spacing: .05em;169}170td { padding: 7px 9px; border-bottom: 1px solid color-mix(in oklab, var(--grid) 55%, transparent);171 font-variant-numeric: tabular-nums; white-space: nowrap; }172tr:hover td { background: color-mix(in oklab, var(--s1) 5%, transparent); }173td.num, th.num { text-align: right; }174td.mono, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;175 color: var(--ink-2); max-width: 150px; overflow: hidden; text-overflow: ellipsis; }176.dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%;177 background: var(--s1); margin-right: 7px; box-shadow: 0 0 0 3px color-mix(in oklab, var(--s1) 15%, transparent); }178.mint { color: var(--good-text); font-weight: 700; }179.burn { color: var(--bad); font-weight: 700; }180.feed-wrap { max-height: 320px; overflow: auto; }181182/* ================= bars (magnitude) ================= */183.bars { display: flex; flex-direction: column; gap: 2px; }184.bar-row {185 display: grid; grid-template-columns: minmax(72px, 100px) 1fr minmax(72px, 96px);186 align-items: center; gap: 8px; padding: 4px 6px; border-radius: 8px; min-height: 24px;187}188.bar-row:hover { background: color-mix(in oklab, var(--s1) 8%, transparent); }189.bar-label { color: var(--ink-2); font-size: 12.5px; font-weight: 500; white-space: nowrap;190 overflow: hidden; text-overflow: ellipsis; }191.bar-label a { color: inherit; }192.bar-track { height: 13px; border-radius: 0 5px 5px 0;193 border-left: 2px solid var(--baseline); position: relative;194 background: color-mix(in oklab, var(--grid) 30%, transparent); }195.bar-fill { position: absolute; inset: 0 auto 0 0;196 background: linear-gradient(90deg, color-mix(in oklab, var(--s1) 88%, #fff0), var(--s1));197 border-radius: 0 5px 5px 0; min-width: 2px; }198.bar-val { text-align: right; font-variant-numeric: tabular-nums; font-size: 12.5px; font-weight: 600; }199200/* ================= charts ================= */201.chart { width: 100%; }202.chart svg { display: block; width: 100%; height: auto; }203.legend { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 8px; font-size: 12px; color: var(--ink-2); }204.legend .sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px;205 margin-right: 5px; vertical-align: -1px; }206#tooltip {207 position: fixed; pointer-events: none; background: var(--surface);208 border: 1px solid var(--ring); border-radius: 10px; padding: 8px 11px;209 font-size: 12px; box-shadow: var(--shadow-2); display: none; z-index: 60;210 font-variant-numeric: tabular-nums; max-width: 260px;211}212#tooltip b { display: block; margin-bottom: 3px; }213.empty { color: var(--muted); font-size: 12.5px; padding: 14px 4px; }214215/* ================= api docs ================= */216.docwrap { display: grid; grid-template-columns: 220px 1fr; gap: 18px; grid-column: span 12; align-items: start; }217@media (max-width: 900px) { .docwrap { grid-template-columns: 1fr; } }218.toc { position: sticky; top: 66px; display: flex; flex-direction: column; gap: 2px;219 background: var(--surface); border: 1px solid var(--ring); border-radius: var(--radius);220 padding: 10px; box-shadow: var(--shadow-1); }221@media (max-width: 900px) { .toc { position: static; flex-direction: row; flex-wrap: wrap; } }222.toc a { color: var(--ink-2); font-size: 12.5px; padding: 6px 10px; border-radius: 8px; }223.toc a:hover { background: color-mix(in oklab, var(--s1) 8%, transparent); text-decoration: none; }224.toc .group { font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em;225 color: var(--muted); font-weight: 700; padding: 10px 10px 3px; }226.docbody { display: flex; flex-direction: column; gap: 14px; min-width: 0; }227.ep { background: var(--surface); border: 1px solid var(--ring); border-radius: var(--radius);228 padding: 16px 18px; box-shadow: var(--shadow-1); scroll-margin-top: 70px; }229.ep .head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }230.method { font-size: 11px; font-weight: 800; letter-spacing: .05em; padding: 4px 9px;231 border-radius: 7px; color: #fff; background: var(--s1); }232.method.ws { background: var(--s3); }233.ep .path { font-family: ui-monospace, Menlo, monospace; font-size: 13.5px; font-weight: 600;234 word-break: break-all; }235.ep .desc { color: var(--ink-2); font-size: 13px; margin: 8px 0 0; }236.ep .params { margin-top: 10px; }237.ep .params table { min-width: 0; }238pre.code {239 background: var(--surface-2); border: 1px solid var(--ring); border-radius: 10px;240 padding: 12px 14px; font-family: ui-monospace, Menlo, monospace; font-size: 12px;241 overflow-x: auto; margin: 10px 0 0; line-height: 1.55; position: relative;242}243pre.code .cm { color: var(--muted); }244.tryrow { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; align-items: center; }245.try-out { display: none; max-height: 280px; overflow: auto; }246.copybtn { margin-left: auto; font-size: 11px; padding: 4px 10px; min-height: 0; }247.hero { grid-column: span 12; padding: clamp(18px, 4vw, 34px) clamp(16px, 3vw, 28px);248 border-radius: 18px; border: 1px solid var(--ring);249 background: linear-gradient(140deg, color-mix(in oklab, var(--s1) 14%, var(--surface)),250 var(--surface) 55%, color-mix(in oklab, var(--s3) 8%, var(--surface)));251 box-shadow: var(--shadow-1); }252.hero h1 { margin: 0 0 6px; font-size: clamp(21px, 3.4vw, 30px); letter-spacing: -.02em; }253.hero p { margin: 0; color: var(--ink-2); max-width: 62ch; }254.hero .kpis { display: flex; gap: 18px; margin-top: 14px; flex-wrap: wrap; }255.hero .kpis b { font-size: 17px; font-variant-numeric: tabular-nums; }256.hero .kpis span { display: block; color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; }257258/* ================= asset icons ================= */259.asset { display: inline-flex; align-items: center; gap: 6px; color: inherit; }260.asset span { line-height: 1; }261.asset:hover { text-decoration: none; }262.asset:hover span { text-decoration: underline; text-underline-offset: 2px; }263td .asset { color: var(--s1); }264.coin {265 position: relative; display: inline-grid; place-items: center;266 width: var(--sz, 16px); height: var(--sz, 16px); flex: 0 0 auto;267 vertical-align: -3px; border-radius: 50%;268}269.coin i {270 position: absolute; inset: 0; border-radius: 50%; font-style: normal;271 display: grid; place-items: center;272 font-size: calc(var(--sz, 16px) * 0.52); font-weight: 800;273 box-shadow: inset 0 0 0 1px rgba(255,255,255,.18), 0 1px 3px rgba(0,0,0,.18);274}275.coin img {276 position: absolute; inset: 0; width: 100%; height: 100%;277 border-radius: 50%; z-index: 1; background: transparent;278}279.pagehead .coin { vertical-align: -5px; margin-right: 2px; }280.bar-label .asset { max-width: 100%; overflow: hidden; }281282footer { text-align: center; color: var(--muted); font-size: 12px; padding: 0 16px 32px; }283