Chart legends: the conventional pattern, nothing cleverer
Some checks are pending
build-gate / build (push) Waiting to run
Some checks are pending
build-gate / build (push) Waiting to run
Driver, after rejecting both experiments: "I cannot scroll right, and the column numbers have this super white background -- it looks odd, it was to be invisible... I am not impressed with that design. Use conventional designs only." He is right, and the two rejected variants earned it: scroll-to-read hid the numbers' geometry behind a gesture, and the frozen column needed a painted backing precisely BECAUSE something could slide beneath it. The conventional row every chart library ships has neither problem: swatch, label that ELLIPSIZES at the available width (full name in the row's existing tooltip), count right-aligned in a fixed-width slot. No horizontal scroll, no sticky, no background -- the number needs no backing because nothing can ever slide beneath it. Measured, both legends: transparent count background, position static, right-aligned, ONE shared right edge, labels ellipsizing, zero horizontal scroll. Screenshot read before pushing.
This commit is contained in:
parent
80d5981229
commit
ab83fd4ca7
8 changed files with 38 additions and 61 deletions
|
|
@ -189,17 +189,8 @@ useShadowStyles(`
|
|||
reclaim vertical space, lower -webkit-line-clamp or trim the LABEL TEXT upstream — do NOT
|
||||
restore white-space:nowrap + ellipsis here, and do not re-cap this to the donut's height:
|
||||
that cap is precisely what forced the unreadable ellipsis the Driver rejected. */
|
||||
/* ★ A REAL COLUMN, NOT PER-ROW FLEX (Driver 2026-09-23: "when I scroll right on the region
|
||||
chart the numbers don't stay justified right, not uniform... they look less like a column and
|
||||
more like cards"). He is describing exactly what flex rows do under horizontal scroll: each
|
||||
row is its own width (min-width:max-content), so each count sat at ITS row's right edge --
|
||||
rows of different lengths put their numbers at different x positions the moment the container
|
||||
scrolled. sticky can only pin an element within its own row's box, so it could not save this.
|
||||
The container is now a two-column GRID (label 1fr, count auto) and every row joins it via
|
||||
subgrid: ALL counts share the single auto track, so the column is uniform at every scroll
|
||||
position by construction -- the track IS the column. */
|
||||
.rd-legend{ display:table; border-collapse:separate; border-spacing:0 2px; font-size:10.5px; color:var(--dx-text, #3b463d); max-height:120px;
|
||||
overflow-y:auto; overflow-x:auto; flex:1 1 auto; min-width:0;
|
||||
.rd-legend{ display:flex; flex-direction:column; gap:2px; font-size:10.5px; color:var(--dx-text, #3b463d); max-height:120px;
|
||||
overflow-y:auto; overflow-x:hidden; flex:1 1 auto; min-width:0;
|
||||
scrollbar-width:thin; scrollbar-color:var(--dx-scroll-thumb, #c4c8c0) transparent; }
|
||||
.rd-legend::-webkit-scrollbar{ width:6px; height:6px; }
|
||||
.rd-legend::-webkit-scrollbar-track{ background:transparent; }
|
||||
|
|
@ -209,21 +200,18 @@ useShadowStyles(`
|
|||
now two lines tall, and centring would float the swatch and the count in the middle of that
|
||||
block, detached from the first line of text they label. The count is top-aligned with a 1px
|
||||
nudge (.rd-li b) so it reads as belonging to the row's first line. */
|
||||
/* ★ REAL TABLE CSS, ON THE DRIVER'S OWN PRESCRIPTION (2026-09-23): "the number column needs to
|
||||
use some type of TABLE css so that the numbers really are set apart as a column, uniform and
|
||||
justified right, so that it does not move when I scroll left and right." He is right that
|
||||
table layout is the honest tool here: display:table guarantees every row's second cell shares
|
||||
ONE column edge in every browser ever shipped -- no subgrid support question, no per-row flex
|
||||
widths, the column is a column by the layout algorithm itself. (The previous subgrid attempt
|
||||
measured correct in the built-tree browser but subgrid is younger than table layout by 25
|
||||
years; the table needs no such caveat.)
|
||||
The count cell is ALSO sticky right:0 with an opaque background, so when a long label forces
|
||||
horizontal scroll the numbers stay pinned at the visible edge while the words slide beneath --
|
||||
the frozen-column behaviour he asked for earlier, now on top of true column geometry. */
|
||||
.rd-li{ display:table-row; cursor:pointer; }
|
||||
.rd-li > i{ display:table-cell; width:9px; min-width:9px; padding-top:4px; }
|
||||
.rd-li .rd-lt{ display:table-cell; padding:1px 6px 1px 5px; }
|
||||
.rd-li b{ display:table-cell; } /* max-content: the row is as wide as its label, so the scrollport has something to scroll */
|
||||
/* ★ THE CONVENTIONAL LEGEND ROW, on the Driver's final ruling (2026-09-23): "I am not
|
||||
impressed with that design -- use conventional designs only", after rejecting both the
|
||||
scroll-to-read variant ("I cannot scroll right") and the frozen column's painted backing
|
||||
("super white background, it looks odd; it was to be invisible"). This is the pattern every
|
||||
chart library ships: swatch, label that ELLIPSIZES at the available width, count right-
|
||||
aligned in a fixed-width slot. No horizontal scroll, no sticky, no background -- the number
|
||||
needs no backing because nothing can ever slide beneath it. The full label lives in the
|
||||
row's existing :title tooltip. Rows stretch to the container, so every count shares one
|
||||
right edge by ordinary flex geometry. */
|
||||
.rd-li{ display:flex; align-items:center; gap:5px; cursor:pointer; padding:1px 2px; border-radius:3px; }
|
||||
.rd-li > i{ flex:0 0 auto; }
|
||||
.rd-li .rd-lt{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } /* max-content: the row is as wide as its label, so the scrollport has something to scroll */
|
||||
.rd-li:hover{ background:var(--dx-surface, #f3f3f1); } .rd-li.on{ background:#fff4ea; font-weight:700; color:var(--rd-ink, #3b463d); }
|
||||
/* margin-top nudges keep the swatch and the count optically on the label's FIRST line now that
|
||||
a row can be two lines tall. */
|
||||
|
|
@ -250,11 +238,8 @@ useShadowStyles(`
|
|||
a little air. sticky right:0 (from the frozen-column fix) is what makes it immovable while the
|
||||
label scrolls beneath. Both donut legends share .rd-li, so religion and region get this from
|
||||
the one rule -- which is exactly why it lives here and not per-chart. */
|
||||
.rd-li b{ position:sticky; right:0; z-index:1;
|
||||
min-width:3.4em; text-align:right; white-space:nowrap; vertical-align:top;
|
||||
background:var(--rd-panel, #fff); box-shadow:-6px 0 6px -4px var(--rd-panel, #fff);
|
||||
color:var(--rd-muted, #6d766e); font-variant-numeric:tabular-nums;
|
||||
padding:1px 2px 1px 6px; }
|
||||
.rd-li b{ flex:0 0 auto; min-width:3.4em; text-align:right;
|
||||
color:var(--rd-muted, #6d766e); font-variant-numeric:tabular-nums; }
|
||||
/* The label is the ONLY item allowed to shrink. It used to be a bare text node, which in a
|
||||
nowrap flex row cannot shrink at all: the row grew past .rd-legend (overflow-x:hidden) and
|
||||
carried the VALUE out of sight — "Ethnoreligion - Chinese Folk Religion" rendered with no
|
||||
|
|
|
|||
|
|
@ -574,24 +574,20 @@
|
|||
.rd-donut.has-sel .rd-slice{ opacity:.42; }
|
||||
.rd-slice.on, .rd-donut.has-sel .rd-slice.on{ opacity:1; stroke:var(--rd-ink, #3b463d); stroke-width:2.5;
|
||||
transform:scale(1.08); filter:drop-shadow(0 1px 2px rgba(31,39,51,.35)); }
|
||||
.rd-legend{ display:table; border-collapse:separate; border-spacing:0 2px; font-size:10.5px; color:var(--dx-text, #3b463d); max-height:120px;
|
||||
overflow-y:auto; overflow-x:auto; flex:1 1 auto; min-width:0;
|
||||
.rd-legend{ display:flex; flex-direction:column; gap:2px; font-size:10.5px; color:var(--dx-text, #3b463d); max-height:120px;
|
||||
overflow-y:auto; overflow-x:hidden; flex:1 1 auto; min-width:0;
|
||||
scrollbar-width:thin; scrollbar-color:var(--dx-scroll-thumb, #c4c8c0) transparent; }
|
||||
.rd-legend::-webkit-scrollbar{ width:6px; height:6px; }
|
||||
.rd-legend::-webkit-scrollbar-track{ background:transparent; }
|
||||
.rd-legend::-webkit-scrollbar-thumb{ background:var(--dx-scroll-thumb, #c4c8c0); border-radius:6px; }
|
||||
.rd-legend::-webkit-scrollbar-thumb:hover{ background:#9aa197; }
|
||||
.rd-li{ display:table-row; cursor:pointer; }
|
||||
.rd-li > i{ display:table-cell; width:9px; min-width:9px; padding-top:4px; }
|
||||
.rd-li .rd-lt{ display:table-cell; padding:1px 6px 1px 5px; }
|
||||
.rd-li b{ display:table-cell; }
|
||||
.rd-li{ display:flex; align-items:center; gap:5px; cursor:pointer; padding:1px 2px; border-radius:3px; }
|
||||
.rd-li > i{ flex:0 0 auto; }
|
||||
.rd-li .rd-lt{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.rd-li:hover{ background:var(--dx-surface, #f3f3f1); } .rd-li.on{ background:#fff4ea; font-weight:700; color:var(--rd-ink, #3b463d); }
|
||||
.rd-li i{ width:9px; height:9px; border-radius:2px; flex:0 0 auto; margin-top:3px; }
|
||||
.rd-li b{ position:sticky; right:0; z-index:1;
|
||||
min-width:3.4em; text-align:right; white-space:nowrap; vertical-align:top;
|
||||
background:var(--rd-panel, #fff); box-shadow:-6px 0 6px -4px var(--rd-panel, #fff);
|
||||
color:var(--rd-muted, #6d766e); font-variant-numeric:tabular-nums;
|
||||
padding:1px 2px 1px 6px; }
|
||||
.rd-li b{ flex:0 0 auto; min-width:3.4em; text-align:right;
|
||||
color:var(--rd-muted, #6d766e); font-variant-numeric:tabular-nums; }
|
||||
.rd-lt{ flex:1 1 auto; min-width:0; white-space:nowrap; line-height:1.28; overflow:visible; }
|
||||
.rd-bars{ display:flex; flex-direction:column; gap:3px; }
|
||||
.rd-bar{ display:flex; align-items:center; gap:6px; cursor:pointer; font-size:10.5px; padding:1px 2px; border-radius:3px; }
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
|
||||
<script src="../../dashboard-plus-grid.js" integrity="sha384-lFahIRCFBh2RLzn3g9lHxF9rvYyK1YL2zPkJ+FwDVYQ8+aEcv2MPphETm99tMmfr" crossorigin="anonymous"></script>
|
||||
<script src="../../dashboard-plus-grid.js" integrity="sha384-yl2t7RiaXtN9JiEX6E/96LPcXR2UKH2QrsDgDGLHjpt5cx5jLkAGlbKCNJvf96vB" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
|
||||
<script src="../dashboard-plus-grid.js" integrity="sha384-lFahIRCFBh2RLzn3g9lHxF9rvYyK1YL2zPkJ+FwDVYQ8+aEcv2MPphETm99tMmfr" crossorigin="anonymous"></script>
|
||||
<script src="../dashboard-plus-grid.js" integrity="sha384-yl2t7RiaXtN9JiEX6E/96LPcXR2UKH2QrsDgDGLHjpt5cx5jLkAGlbKCNJvf96vB" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
2
public/doxa-maps/index.html
generated
2
public/doxa-maps/index.html
generated
File diff suppressed because one or more lines are too long
8
public/doxa-maps/manifest.json
generated
8
public/doxa-maps/manifest.json
generated
|
|
@ -193,7 +193,7 @@
|
|||
"bundle": "dashboard-plus-grid",
|
||||
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
|
||||
"output": "doxa-maps/dashboard-plus-grid/dashboard-plus-grid.js",
|
||||
"integrity": "sha384-lFahIRCFBh2RLzn3g9lHxF9rvYyK1YL2zPkJ+FwDVYQ8+aEcv2MPphETm99tMmfr",
|
||||
"integrity": "sha384-yl2t7RiaXtN9JiEX6E/96LPcXR2UKH2QrsDgDGLHjpt5cx5jLkAGlbKCNJvf96vB",
|
||||
"elements": [
|
||||
"dashboard-plus-grid"
|
||||
],
|
||||
|
|
@ -213,7 +213,7 @@
|
|||
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./dashboard-plus-grid/dashboard-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-lFahIRCFBh2RLzn3g9lHxF9rvYyK1YL2zPkJ+FwDVYQ8+aEcv2MPphETm99tMmfr\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-yl2t7RiaXtN9JiEX6E/96LPcXR2UKH2QrsDgDGLHjpt5cx5jLkAGlbKCNJvf96vB\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -878,7 +878,7 @@
|
|||
"bundle": "research-dashboard",
|
||||
"scriptUrl": "./research-dashboard/research-dashboard.js",
|
||||
"output": "doxa-maps/research-dashboard/research-dashboard.js",
|
||||
"integrity": "sha384-QtXmg1sKDCXljaSvuGeRuUkpFuNWiAMRsz2Sk5tluaUTXkVfz8y2MKWdGLVCdSig",
|
||||
"integrity": "sha384-OMWktfdHg6hivE32ReD3LQIwg3SxgBdgdltPhfWFSEUybEPU6fsynv4PP22z4yEn",
|
||||
"elements": [
|
||||
"research-dashboard-map"
|
||||
],
|
||||
|
|
@ -894,7 +894,7 @@
|
|||
"scriptUrl": "./research-dashboard/research-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-QtXmg1sKDCXljaSvuGeRuUkpFuNWiAMRsz2Sk5tluaUTXkVfz8y2MKWdGLVCdSig\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-OMWktfdHg6hivE32ReD3LQIwg3SxgBdgdltPhfWFSEUybEPU6fsynv4PP22z4yEn\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
|
||||
},
|
||||
"bundle": "research-dashboard"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -576,24 +576,20 @@
|
|||
.rd-donut.has-sel .rd-slice{ opacity:.42; }
|
||||
.rd-slice.on, .rd-donut.has-sel .rd-slice.on{ opacity:1; stroke:var(--rd-ink, #3b463d); stroke-width:2.5;
|
||||
transform:scale(1.08); filter:drop-shadow(0 1px 2px rgba(31,39,51,.35)); }
|
||||
.rd-legend{ display:table; border-collapse:separate; border-spacing:0 2px; font-size:10.5px; color:var(--dx-text, #3b463d); max-height:120px;
|
||||
overflow-y:auto; overflow-x:auto; flex:1 1 auto; min-width:0;
|
||||
.rd-legend{ display:flex; flex-direction:column; gap:2px; font-size:10.5px; color:var(--dx-text, #3b463d); max-height:120px;
|
||||
overflow-y:auto; overflow-x:hidden; flex:1 1 auto; min-width:0;
|
||||
scrollbar-width:thin; scrollbar-color:var(--dx-scroll-thumb, #c4c8c0) transparent; }
|
||||
.rd-legend::-webkit-scrollbar{ width:6px; height:6px; }
|
||||
.rd-legend::-webkit-scrollbar-track{ background:transparent; }
|
||||
.rd-legend::-webkit-scrollbar-thumb{ background:var(--dx-scroll-thumb, #c4c8c0); border-radius:6px; }
|
||||
.rd-legend::-webkit-scrollbar-thumb:hover{ background:#9aa197; }
|
||||
.rd-li{ display:table-row; cursor:pointer; }
|
||||
.rd-li > i{ display:table-cell; width:9px; min-width:9px; padding-top:4px; }
|
||||
.rd-li .rd-lt{ display:table-cell; padding:1px 6px 1px 5px; }
|
||||
.rd-li b{ display:table-cell; }
|
||||
.rd-li{ display:flex; align-items:center; gap:5px; cursor:pointer; padding:1px 2px; border-radius:3px; }
|
||||
.rd-li > i{ flex:0 0 auto; }
|
||||
.rd-li .rd-lt{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.rd-li:hover{ background:var(--dx-surface, #f3f3f1); } .rd-li.on{ background:#fff4ea; font-weight:700; color:var(--rd-ink, #3b463d); }
|
||||
.rd-li i{ width:9px; height:9px; border-radius:2px; flex:0 0 auto; margin-top:3px; }
|
||||
.rd-li b{ position:sticky; right:0; z-index:1;
|
||||
min-width:3.4em; text-align:right; white-space:nowrap; vertical-align:top;
|
||||
background:var(--rd-panel, #fff); box-shadow:-6px 0 6px -4px var(--rd-panel, #fff);
|
||||
color:var(--rd-muted, #6d766e); font-variant-numeric:tabular-nums;
|
||||
padding:1px 2px 1px 6px; }
|
||||
.rd-li b{ flex:0 0 auto; min-width:3.4em; text-align:right;
|
||||
color:var(--rd-muted, #6d766e); font-variant-numeric:tabular-nums; }
|
||||
.rd-lt{ flex:1 1 auto; min-width:0; white-space:nowrap; line-height:1.28; overflow:visible; }
|
||||
.rd-bars{ display:flex; flex-direction:column; gap:3px; }
|
||||
.rd-bar{ display:flex; align-items:center; gap:6px; cursor:pointer; font-size:10.5px; padding:1px 2px; border-radius:3px; }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <research-dashboard-map> custom element. -->
|
||||
<script src="../research-dashboard.js" integrity="sha384-QtXmg1sKDCXljaSvuGeRuUkpFuNWiAMRsz2Sk5tluaUTXkVfz8y2MKWdGLVCdSig" crossorigin="anonymous"></script>
|
||||
<script src="../research-dashboard.js" integrity="sha384-OMWktfdHg6hivE32ReD3LQIwg3SxgBdgdltPhfWFSEUybEPU6fsynv4PP22z4yEn" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue