From 462dd67982c43210f1c5c46488a795662a4cf35b Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Mon, 28 Sep 2026 08:21:15 +0000 Subject: [PATCH] legends: physical left/right -> logical, where the rule should mirror Seven conversions, one deliberate keep, and a stale comment. CONVERTED -- each is text that should follow the reading direction: LegendDesktop.vue:138,139 the count and population chips, text-align:right -> end. They are numeric badges at the trailing edge of a row; in RTL the trailing edge is the left. LegendDesktop.vue:154,157 the stats and footer VALUE columns, right -> end. Same argument, and they already used padding-inline-end, so the rule was half-logical and read physical. SemanticTreeLegend.vue:711,1105 two button labels, text-align:left -> start. Label text in a flex row; it follows the reader. SemanticTreeLegend.vue:764 .stl-tab-info border-left -> border-inline-start. This is a DIVIDER between two adjacent chips. In RTL the info button sits on the other side of its sibling, so the divider has to move with it or it draws on the outside of the pair. KEPT PHYSICAL, deliberately: SemanticTreeLegend.vue:805 .stl-hdr-num::after { left:50%; top:50% } with transform:translate(-50%,-50%). That is the CENTRING IDIOM for a 24x24 WCAG 2.5.8 hit target, not a direction: it computes identically in both, and converting it would add review noise that hides the real conversions. PROVEN, not asserted: measured the GLYPH POSITION in a browser rather than the computed keyword, because getComputedStyle returns text-align as the keyword ("end"), so a naive comparison reports DIFFER in LTR for rules that lay out identically. Measuring the span's left edge in a 200px box: LTR right=201px end=201px IDENTICAL left=8px start=8px IDENTICAL RTL right=201px end=8px MIRRORS left=8px start=201px MIRRORS And for the border, where computed style does resolve: LTR 1px/0px both ways (identical), RTL 1px/0px vs 0px/1px (mirrors). Also fixes a comment that said "margin-left:auto pins it to the right edge" above a rule that already read margin-inline-start:auto. Prose naming the physical side is how a reader talks themselves back into a physical property. library/ is the build owner's under rule 5B -- source sha to studio. --- library/components/legends/LegendDesktop.vue | 8 ++++---- library/components/legends/SemanticTreeLegend.vue | 15 +++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/library/components/legends/LegendDesktop.vue b/library/components/legends/LegendDesktop.vue index e9c9b4eaf..1c8ee13f1 100644 --- a/library/components/legends/LegendDesktop.vue +++ b/library/components/legends/LegendDesktop.vue @@ -135,8 +135,8 @@ useShadowStyles(` .legend-item-clickable:hover{transform:translateX(4px);box-shadow:0 2px 4px rgba(0,0,0,0.1);} .legend-item-selected{outline:2px solid #333;outline-offset:2px;} .legend-item-text{flex:1;font-weight:500;color:#333;} -.legend-item-count{font-size:var(--map-font-sm,11px);color:#555;background:rgba(0,0,0,0.08);padding:2px 7px;border-radius:10px;min-width:40px;text-align:right;margin-inline-end:4px;} -.legend-item-population{font-size:var(--map-font-sm,11px);color:#555;background:rgba(0,0,0,0.08);padding:2px 7px;border-radius:10px;min-width:52px;text-align:right;margin-inline-end:4px;} +.legend-item-count{font-size:var(--map-font-sm,11px);color:#555;background:rgba(0,0,0,0.08);padding:2px 7px;border-radius:10px;min-width:40px;text-align:end;margin-inline-end:4px;} +.legend-item-population{font-size:var(--map-font-sm,11px);color:#555;background:rgba(0,0,0,0.08);padding:2px 7px;border-radius:10px;min-width:52px;text-align:end;margin-inline-end:4px;} .legend-export-btn{background:none;border:none;cursor:pointer;font-size:15px;padding:0 4px;} .legend-tree-item{display:flex;flex-direction:column;gap:4px;} .legend-tree-parent{display:flex;align-items:center;gap:6px;} @@ -151,10 +151,10 @@ useShadowStyles(` .legend-stats-label{flex:1;min-width:0;padding:0 10px;} /* Stats column values — 10px right padding aligns with header's last-of-type gap and gives a clean breathing space against the card's right edge. */ -.legend-stats-value{width:58px;text-align:right;flex-shrink:0;font-family:'Courier New',monospace;padding-inline-end:10px;white-space:pre;} +.legend-stats-value{width:58px;text-align:end;flex-shrink:0;font-family:'Courier New',monospace;padding-inline-end:10px;white-space:pre;} .legend-footer{display:flex;align-items:center;padding:5px 0;border-top:1px solid rgba(0,0,0,0.07);font-size:11px;font-weight:600;color:#555;background:#fff;} .legend-footer-label{flex:1;padding-inline-start:10px;color:#777;} -.legend-footer-value{width:56px;text-align:right;padding-inline-end:10px;font-family:'Courier New',monospace;} +.legend-footer-value{width:56px;text-align:end;padding-inline-end:10px;font-family:'Courier New',monospace;} /* ── Scrollbar ── */ .legend-content::-webkit-scrollbar{width:4px;} diff --git a/library/components/legends/SemanticTreeLegend.vue b/library/components/legends/SemanticTreeLegend.vue index 44e1cce57..fc0439034 100644 --- a/library/components/legends/SemanticTreeLegend.vue +++ b/library/components/legends/SemanticTreeLegend.vue @@ -708,7 +708,7 @@ useShadowStyles(` display: inline-flex; align-items: center; gap: 6px; background: transparent; border: 1px solid transparent; border-radius: 6px; margin: -2px -4px; padding: 2px 6px 2px 4px; - cursor: pointer; text-align: left; + cursor: pointer; text-align: start; transition: background 0.12s, border-color 0.12s, color 0.12s; } .stl-tb-up:hover { background: rgba(115,161,127,0.10); border-color: rgba(115,161,127,0.35); } @@ -761,7 +761,7 @@ useShadowStyles(` .stl-tab:hover { color: #c9d1d9; } .stl-tab-chip.active .stl-tab { color: #fff; } .stl-tab-info { - background: rgba(110,118,129,0.15); border: none; border-left: 1px solid #30363d; + background: rgba(110,118,129,0.15); border: none; border-inline-start: 1px solid #30363d; color: #6e7681; cursor: pointer; width: 18px; padding: 0; font: 700 9px ui-monospace, monospace; } @@ -1073,9 +1073,12 @@ useShadowStyles(` .stl-panel[data-theme="light"] .stl-x-chip { color: #57606a; border-color: rgba(208,215,222,0.6); } .stl-panel[data-theme="light"] .stl-x-chip:hover { color: #1f2328; background: rgba(208,215,222,0.3); border-color: #afb8c1; } -/* ── Export affordance (top-right of titlebar) ────────────────────────────── - margin-left:auto pins it to the right edge whether the titlebar shows the - plain title (flex-shrink:0) or the flex:1 breadcrumb button. */ +/* ── Export affordance (trailing end of the titlebar) ─────────────────────── + margin-inline-start:auto pins it to the TRAILING edge — right in LTR, left in + RTL — whether the titlebar shows the plain title (flex-shrink:0) or the flex:1 + breadcrumb button. (The comment said margin-left and "the right edge" after + the rule itself had already been made logical; prose that names the physical + side is how a reader talks themselves back into a physical property.) */ .stl-export { margin-inline-start: auto; position: relative; flex-shrink: 0; } .stl-export-btn { display: inline-flex; align-items: center; gap: 5px; @@ -1102,7 +1105,7 @@ useShadowStyles(` .stl-export-item { display: flex; align-items: center; gap: 8px; background: transparent; border: none; border-radius: 5px; - padding: 7px 8px; width: 100%; text-align: left; + padding: 7px 8px; width: 100%; text-align: start; color: #6e7681; cursor: not-allowed; font: 600 11px system-ui, sans-serif; }