mapshaper 0.7.60 → 0.7.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/www/page.css CHANGED
@@ -577,6 +577,19 @@ textarea:focus {
577
577
  box-shadow: 0 0 0 2px rgba(212, 168, 0, 0.4);
578
578
  }
579
579
 
580
+ /* The interface is pointer-only. No custom control carries a tabindex, because
581
+ partial keyboard support was worse than none: tabbing reached a few buttons
582
+ and skipped most, and the menus a tab stop could open could not then be
583
+ navigated or dismissed from the keyboard.
584
+
585
+ This is the backstop for what is still focusable of necessity -- a span made
586
+ contenteditable for renaming, an element focused by script -- so that a UA
587
+ focus ring cannot appear over the interface. Text fields are excluded: they
588
+ have to show where the caret is, and the rule above is how they show it. */
589
+ :focus:not(input):not(textarea):not(select) {
590
+ outline: none;
591
+ }
592
+
580
593
  ::placeholder {
581
594
  color: #aaa;
582
595
  opacity: 1;
@@ -1224,8 +1237,9 @@ body.simplify .layer-control-btn {
1224
1237
  }
1225
1238
 
1226
1239
  .layer-control .no-layer-note {
1227
- color: #999;
1240
+ color: #777;
1228
1241
  margin: 0 0 5px 0;
1242
+ font-size: 13px;
1229
1243
  }
1230
1244
 
1231
1245
  .layer-add-links {
@@ -1248,6 +1262,11 @@ body.simplify .layer-control-btn {
1248
1262
  font-weight: bold;
1249
1263
  }
1250
1264
 
1265
+ /* "Draw:", which labels the links that follow it rather than being one */
1266
+ .new-layer-label {
1267
+ color: #777;
1268
+ }
1269
+
1251
1270
  .layer-menu .layer-list {
1252
1271
  margin-top: -5px;
1253
1272
  /* margin: 0 0 7px 0; */
@@ -1972,6 +1991,7 @@ div.basemap-style-btn.active {
1972
1991
 
1973
1992
  .map-layers.add-points,
1974
1993
  .map-layers.draw-tool,
1994
+ .map-layers.label-tool,
1975
1995
  .map-layers.snip-tool,
1976
1996
  .map-layers.ruler-tool {
1977
1997
  cursor: crosshair;
@@ -1981,6 +2001,12 @@ div.basemap-style-btn.active {
1981
2001
  cursor: default;
1982
2002
  }
1983
2003
 
2004
+ /* Over a label's knot or anchor handle, where a drag moves the handle rather
2005
+ than placing a label or panning the map. */
2006
+ .map-layers.label-handle {
2007
+ cursor: move;
2008
+ }
2009
+
1984
2010
  .map-layers.ruler-dragging {
1985
2011
  cursor: grabbing;
1986
2012
  }
@@ -2060,23 +2086,181 @@ body.pan.panning .map-layers:not(.drawing) {
2060
2086
  cursor: pointer;
2061
2087
  }
2062
2088
 
2063
- .map-layers > svg text.active-label {
2064
- stroke: #fac0ff; /* #efacf4; */
2089
+ .map-layers > svg text.label-style-selected {
2090
+ stroke: #fce300;
2091
+ stroke-opacity: 0.25;
2065
2092
  paint-order: stroke fill;
2066
2093
  stroke-width: 4px;
2067
2094
  stroke-linecap: round;
2068
2095
  stroke-linejoin: round;
2069
2096
  }
2070
2097
 
2071
- .map-layers > svg text.label-style-selected {
2072
- stroke: #fce300;
2073
- stroke-opacity: 0.25;
2098
+ /* A path label whose text is longer than its path. Export drops these; the
2099
+ editor keeps them visible and marked, because a hidden label can't be
2100
+ found and so can't be fixed. Same halo convention as the states above. */
2101
+ .map-layers > svg text.label-overflow {
2102
+ stroke: #ff7f2a;
2103
+ stroke-opacity: 0.45;
2074
2104
  paint-order: stroke fill;
2075
2105
  stroke-width: 4px;
2076
2106
  stroke-linecap: round;
2077
2107
  stroke-linejoin: round;
2078
2108
  }
2079
2109
 
2110
+ /* --- IN-PLACE LABEL EDITING --- */
2111
+
2112
+ /* The textarea is the model of record for an editing session: it holds focus so
2113
+ that keyboard handling, selection, clipboard and IME all come from the
2114
+ browser. It is invisible but must stay focusable, so it cannot be hidden with
2115
+ display:none or visibility:hidden. */
2116
+ .label-edit-input {
2117
+ position: absolute;
2118
+ z-index: -1;
2119
+ opacity: 0;
2120
+ width: 1px;
2121
+ height: 1em;
2122
+ padding: 0;
2123
+ border: none;
2124
+ resize: none;
2125
+ overflow: hidden;
2126
+ white-space: pre;
2127
+ pointer-events: none;
2128
+ }
2129
+
2130
+ .label-edit-overlay {
2131
+ pointer-events: none;
2132
+ }
2133
+
2134
+ /* Label tool cues: an outline around the object rather than a halo over the
2135
+ glyphs, so that it reads as something you have hold of. A solid line, to
2136
+ distinguish it from the dashed box of a label being typed into -- the two
2137
+ states look different because they behave differently.
2138
+
2139
+ Hover and selection draw the same shapes, because they mean the same thing
2140
+ about the same object; hover is simply lighter. The alternative, highlighting
2141
+ the anchor point on hover and the label on selection, made the two look like
2142
+ different objects. */
2143
+ .label-cue {
2144
+ pointer-events: none;
2145
+ }
2146
+
2147
+ .label-cue-hovered {
2148
+ opacity: 0.4;
2149
+ }
2150
+
2151
+ /* Hairline, like every other reference shape the tool draws: the box says where
2152
+ the label is, and a heavier outline reads as an object in its own right
2153
+ rather than as a mark on one. */
2154
+ .label-cue-box {
2155
+ fill: none;
2156
+ stroke: #3a7ddc;
2157
+ stroke-width: 1px;
2158
+ vector-effect: non-scaling-stroke;
2159
+ }
2160
+
2161
+ /* The anchor is what the text is positioned against, and label-pos can put it
2162
+ well outside the box, so it is worth marking on its own.
2163
+ Drawn as a ring rather than a dot, and beneath the glyphs: a centered label
2164
+ sits right on top of its anchor, and a filled marker would blot out a letter.
2165
+ Same treatment as the path guide's knots, since both are handles. */
2166
+ .label-cue-anchor {
2167
+ fill: #fff;
2168
+ stroke: #3a7ddc;
2169
+ stroke-width: 1.5px;
2170
+ vector-effect: non-scaling-stroke;
2171
+ }
2172
+
2173
+ /* The line between an anchor and the text being dragged away from it, drawn
2174
+ only while that drag is on. Dashed, and a shade lighter than the box: it is
2175
+ the relationship being edited rather than a part of either end. */
2176
+ .label-cue-tether {
2177
+ fill: none;
2178
+ stroke: #3a7ddc;
2179
+ stroke-width: 1px;
2180
+ stroke-dasharray: 2 2;
2181
+ opacity: 0.7;
2182
+ vector-effect: non-scaling-stroke;
2183
+ }
2184
+
2185
+ /* A copy of the baseline the label's text runs along. Unfilled: stroking an
2186
+ open curve must not paint the area it encloses. */
2187
+ .label-cue-curve {
2188
+ fill: none;
2189
+ stroke: #3a7ddc;
2190
+ stroke-width: 1px;
2191
+ stroke-opacity: 0.8;
2192
+ vector-effect: non-scaling-stroke;
2193
+ }
2194
+
2195
+ /* The grab handles on a selected curve, one per knot. Same ring treatment as
2196
+ the anchor marker and the path guide's knots, because all three are handles
2197
+ and a knot is an anchor on a curve.
2198
+ A corner knot is drawn as a square by the renderer; both shapes share this
2199
+ rule, so a corner differs in outline and not in colour. */
2200
+ .label-cue-knot {
2201
+ fill: #fff;
2202
+ stroke: #3a7ddc;
2203
+ stroke-width: 1.5px;
2204
+ vector-effect: non-scaling-stroke;
2205
+ }
2206
+
2207
+ /* Transparent rather than fill:none / stroke:none, because an unpainted shape
2208
+ is not hit-testable. */
2209
+ .label-edit-hit-area {
2210
+ fill: transparent;
2211
+ stroke: none;
2212
+ pointer-events: all;
2213
+ }
2214
+
2215
+ /* The thickened baseline follows the curve, so only its stroke should catch
2216
+ clicks -- filling an open curve would claim the whole area it encloses. */
2217
+ .label-edit-hit-baseline {
2218
+ fill: none;
2219
+ stroke: transparent;
2220
+ pointer-events: stroke;
2221
+ }
2222
+
2223
+ /* The curve a label's text is set along, ghosted while the label is open for
2224
+ editing. Fainter than the guide drawn while the path is being placed, and in
2225
+ the same violet, because it is the same curve at a later stage. */
2226
+ .label-edit-path {
2227
+ fill: none;
2228
+ stroke: #cc6acc;
2229
+ stroke-width: 1.2px;
2230
+ stroke-opacity: 0.4;
2231
+ vector-effect: non-scaling-stroke;
2232
+ }
2233
+
2234
+ /* Shows where an anchored label is even when it has no text -- the state a
2235
+ label is in between being created and being typed into. A path label shows
2236
+ its ghosted curve instead.
2237
+ Solid, not dashed: at the size of an empty label the dashes were most of the
2238
+ outline, which made the box look tentative rather than like a place to type. */
2239
+ .label-edit-box {
2240
+ fill: none;
2241
+ stroke: #cc6acc;
2242
+ stroke-width: 1px;
2243
+ vector-effect: non-scaling-stroke;
2244
+ }
2245
+
2246
+ .label-edit-selection {
2247
+ fill: #cc6acc;
2248
+ fill-opacity: 0.3;
2249
+ stroke: none;
2250
+ }
2251
+
2252
+ .label-edit-caret {
2253
+ stroke: #000;
2254
+ stroke-width: 1.5px;
2255
+ vector-effect: non-scaling-stroke;
2256
+ animation: label-caret-blink 1.1s steps(1) infinite;
2257
+ }
2258
+
2259
+ @keyframes label-caret-blink {
2260
+ 0%, 50% { stroke-opacity: 1; }
2261
+ 50.01%, 100% { stroke-opacity: 0; }
2262
+ }
2263
+
2080
2264
  /* --- MAP BUTTONS --- */
2081
2265
 
2082
2266
  /*.pointer-btn {
@@ -2174,11 +2358,31 @@ body.pan.panning .map-layers:not(.drawing) {
2174
2358
  }
2175
2359
 
2176
2360
  .label-style-panel {
2361
+ /* The shape of a field in every style panel: the label panel set them,
2362
+ and the line, polygon and circle panels are drawn from the same parts.
2363
+ A row is only legible when the select, the colour field, the size field
2364
+ and the button beside them are all the same height. */
2365
+ --label-field-height: 22px;
2366
+ --label-field-radius: 4px;
2367
+ --label-field-border: #bbb;
2368
+ /* The narrow column of a split row, wide enough for the longest caption
2369
+ over it -- "Letter spacing", which measures 71px -- and the same in
2370
+ every row of every panel, so that the field edges line up down the
2371
+ whole stack. */
2372
+ --label-split-b-width: 74px;
2373
+ /* Same glyph on native <select>s and the saved-style menu. The custom menu
2374
+ used a CSS triangle; the font menus used the OS arrow. A drawn stroke
2375
+ rather than a filled triangle: it is lighter than the text beside it,
2376
+ which is what a menu's arrow should be. */
2377
+ --label-menu-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath fill='none' stroke='%23666' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5l4 4 4-4'/%3E%3C/svg%3E");
2177
2378
  position: absolute;
2178
2379
  z-index: 21;
2179
2380
  top: 6px;
2180
2381
  right: 36px;
2181
- width: 185px;
2382
+ /* Wide enough for a pair of controls side by side, which is what these
2383
+ panels are mostly made of: at 185px the narrow column of a pair was too
2384
+ narrow for its own caption, and "Letter spacing" was clipped. */
2385
+ width: 216px;
2182
2386
  padding: 8px 12px 12px;
2183
2387
  text-align: left;
2184
2388
  background-color: white;
@@ -2190,7 +2394,7 @@ body.pan.panning .map-layers:not(.drawing) {
2190
2394
  .label-style-panel button,
2191
2395
  .label-style-panel select,
2192
2396
  .label-style-panel input {
2193
- font-size: 12.5px;
2397
+ font-size: 12px;
2194
2398
  }
2195
2399
 
2196
2400
  .label-style-panel button.label-style-close {
@@ -2198,14 +2402,26 @@ body.pan.panning .map-layers:not(.drawing) {
2198
2402
  }
2199
2403
 
2200
2404
 
2405
+ .label-style-panel select,
2406
+ .label-saved-style-btn {
2407
+ background-image: var(--label-menu-chevron);
2408
+ background-repeat: no-repeat;
2409
+ background-position: right 6px center;
2410
+ background-size: 10px 7px;
2411
+ }
2412
+
2201
2413
  .label-style-panel select {
2202
- padding: 1px 0;
2414
+ -webkit-appearance: none;
2415
+ appearance: none;
2416
+ padding: 0 20px 0 6px;
2203
2417
  }
2204
2418
 
2419
+ /* The size the app's dialogs title themselves at (.info-box h3), so that a
2420
+ panel pinned over the map is headed like the menus beside it. */
2205
2421
  .label-style-panel-title {
2206
2422
  position: relative;
2207
2423
  font-weight: 600;
2208
- font-size: 16px;
2424
+ font-size: 17px;
2209
2425
  }
2210
2426
 
2211
2427
  .label-style-close {
@@ -2235,9 +2451,13 @@ body.pan.panning .map-layers:not(.drawing) {
2235
2451
  box-sizing: border-box;
2236
2452
  }
2237
2453
 
2238
- .label-style-panel select {
2454
+ .label-style-panel select,
2455
+ .label-style-panel input[type="text"] {
2456
+ box-sizing: border-box;
2457
+ height: var(--label-field-height);
2239
2458
  background-color: white;
2240
- border: 1px solid #999;
2459
+ border: 1px solid var(--label-field-border);
2460
+ border-radius: var(--label-field-radius);
2241
2461
  box-shadow: none;
2242
2462
  }
2243
2463
 
@@ -2250,10 +2470,6 @@ body.pan.panning .map-layers:not(.drawing) {
2250
2470
  .label-style-panel input[type="text"] {
2251
2471
  -webkit-appearance: none;
2252
2472
  appearance: none;
2253
- background: white;
2254
- border: 1px solid #999;
2255
- border-radius: 0;
2256
- box-shadow: none;
2257
2473
  }
2258
2474
 
2259
2475
  .label-style-panel input[type="text"]:focus {
@@ -2283,277 +2499,616 @@ body.pan.panning .map-layers:not(.drawing) {
2283
2499
  color: #555;
2284
2500
  }
2285
2501
 
2286
- .label-saved-style-row select {
2287
- width: 100%;
2288
- margin-bottom: 4px;
2289
- }
2290
-
2291
- .label-saved-style-row {
2292
- margin-top: 10px;
2293
- padding-top: 8px;
2294
- border-top: 1px solid #ccc;
2295
- }
2296
-
2297
- .label-saved-style-row button,
2298
2502
  .label-style-row button {
2299
2503
  margin-right: 5px;
2300
2504
  }
2301
2505
 
2302
- .label-split-row {
2303
- display: grid;
2304
- grid-template-columns: 105px 1fr;
2305
- column-gap: 8px;
2306
- align-items: start;
2307
- }
2506
+ /* Saved styles: a menu that applies one and a button that saves the current
2507
+ one, under a heading. The menu is built from divs rather than being a
2508
+ <select>, because each row carries its own delete button -- see
2509
+ gui-style-preset-control.mjs.
2308
2510
 
2309
- .label-split-cell > span,
2310
- .label-split-cell > .label-style-row-label {
2311
- display: block;
2312
- margin-bottom: 2px;
2313
- color: #555;
2511
+ A section of the panel like Text and Icon, so the spacing above it is the
2512
+ whitespace those sections are separated by, in the panel that has them, and
2513
+ this margin in the layer panel, which does not. */
2514
+ .label-saved-style-row {
2515
+ margin-top: 11px;
2314
2516
  }
2315
2517
 
2316
- .label-split-row .label-size-row,
2317
- .label-split-row .label-icon-size-row {
2318
- justify-self: end;
2319
- white-space: nowrap;
2518
+ /* The two controls sit in the panel's A and B columns, on the same grid as a
2519
+ split row: Apply style lines up with the fields and menus above it, and
2520
+ Save current with the narrow column beside them. Every style panel is the
2521
+ same width, so one grid does for all three. */
2522
+ .label-saved-style-controls {
2523
+ display: grid;
2524
+ grid-template-columns: 1fr var(--label-split-b-width);
2525
+ column-gap: 10px;
2526
+ margin-top: 4px;
2320
2527
  }
2321
2528
 
2322
- .label-color-row {
2529
+ /* min-width:0 lets the menu shrink to its column: a grid track is otherwise
2530
+ at least as wide as its contents, and the names in the list can be long. */
2531
+ .label-saved-style-menu {
2323
2532
  position: relative;
2533
+ min-width: 0;
2324
2534
  }
2325
2535
 
2326
- .label-panel-btn {
2536
+ /* Sized, bordered and lettered like the panel's fields rather than like its
2537
+ 19px icon buttons, because that is what these two are: a menu the width of
2538
+ a field, and a button beside it. The type is the fields' own -- these are
2539
+ divs, so the panel's rule for button, select and input does not reach them
2540
+ and they were left at the app's 14px. */
2541
+ .label-saved-style-button {
2327
2542
  box-sizing: border-box;
2328
- user-select: none;
2543
+ display: flex;
2544
+ align-items: center;
2545
+ height: var(--label-field-height);
2546
+ padding: 0 6px;
2547
+ border: 1px solid var(--label-field-border);
2548
+ border-radius: var(--label-field-radius);
2549
+ background-color: white;
2550
+ font-size: 12px;
2551
+ color: #000;
2329
2552
  cursor: pointer;
2330
- border: 1px solid #999;
2331
- border-radius: 3px;
2332
- background: #eee;
2333
- padding: 0;
2334
- height: 19px;
2335
- width: 19px;
2553
+ user-select: none;
2336
2554
  }
2337
2555
 
2338
- .label-panel-btn.disabled {
2339
- color: #999;
2340
- cursor: default;
2556
+ /* "Save current" is 63px of SourceSans3 and 68px of the Arial it falls back
2557
+ to, in a column of 74: too near its edges to read as inset from them, so
2558
+ the label is centred and the padding is only what keeps the widest of the
2559
+ two fonts off the border. */
2560
+ .label-saved-style-save {
2561
+ justify-content: center;
2562
+ padding: 0 2px;
2563
+ }
2564
+
2565
+ /* The panel's own disabled look: live colours, faded as a whole. */
2566
+ .label-saved-style-button.disabled {
2341
2567
  opacity: 0.45;
2568
+ cursor: default;
2342
2569
  }
2343
2570
 
2344
- .label-color-row .label-color-chit {
2345
- display: inline-block;
2346
- width: 19px;
2347
- height: 19px;
2348
- margin-right: 5px;
2349
- padding: 0;
2350
- vertical-align: top;
2351
- border: 1px solid #888;
2352
- cursor: pointer;
2571
+ .label-saved-style-button:hover:not(.disabled),
2572
+ .label-saved-style-button.open {
2573
+ background-color: #f0f0f0;
2353
2574
  }
2354
2575
 
2355
- .label-color-row input[type="text"] {
2356
- width: 79px;
2357
- height: 19px;
2358
- padding-top: 0;
2359
- padding-bottom: 0;
2360
- box-sizing: border-box;
2361
- vertical-align: top;
2576
+ .label-saved-style-btn {
2577
+ padding-right: 20px;
2362
2578
  }
2363
2579
 
2364
- .label-color-row .label-color-picker {
2365
- left: auto;
2366
- right: 0;
2580
+ /* The label gives way to the chevron rather than running under it. */
2581
+ .label-saved-style-btn span {
2582
+ flex: 1;
2583
+ min-width: 0;
2584
+ overflow: hidden;
2585
+ white-space: nowrap;
2586
+ text-overflow: ellipsis;
2367
2587
  }
2368
2588
 
2369
- .label-color-row .label-color-picker input[type="text"] {
2370
- width: 45px;
2589
+ /* Above the panel's own rows, and free to hang past its bottom edge -- the
2590
+ panel does not clip, and this row is the last thing in it. */
2591
+ .label-saved-style-list {
2592
+ position: absolute;
2593
+ top: 100%;
2594
+ left: 0;
2595
+ /* At least as wide as the menu, and wider if the names need it: the button
2596
+ is only as wide as the space left over beside Save current, and a style
2597
+ name is what the user is trying to read. It can hang over the map --
2598
+ nothing clips it -- but not indefinitely; past that, names ellipsize. */
2599
+ min-width: 100%;
2600
+ width: max-content;
2601
+ max-width: 240px;
2602
+ z-index: 30;
2603
+ margin-top: 2px;
2604
+ padding: 3px 2px;
2605
+ max-height: 40vh;
2606
+ overflow-y: auto;
2607
+ background: white;
2608
+ /* The size of the button it drops from, which is the size of a field. */
2609
+ font-size: 12px;
2610
+ border: 1px solid #bbb;
2611
+ border-radius: var(--label-field-radius);
2612
+ box-shadow: 0 2px 8px rgba(0,0,0,0.18);
2371
2613
  }
2372
2614
 
2373
- .label-css-row input {
2374
- width: 100%;
2375
- box-sizing: border-box;
2615
+ .label-saved-style-list.drop-up {
2616
+ top: auto;
2617
+ bottom: 100%;
2618
+ margin: 0 0 2px 0;
2376
2619
  }
2377
2620
 
2378
- .layer-style-panel .layer-color-row {
2379
- position: relative;
2621
+ .label-saved-style-item {
2622
+ display: flex;
2623
+ align-items: center;
2380
2624
  }
2381
2625
 
2382
- .point-style-section + .point-style-section {
2383
- margin-top: 10px;
2384
- padding-top: 8px;
2385
- border-top: 1px solid #ccc;
2626
+ /* The name is the target for applying, and the highlight stops where it does,
2627
+ so that the row reads as two things to click rather than one with a hazard
2628
+ at the end of it. */
2629
+ .label-saved-style-name {
2630
+ flex: 1;
2631
+ min-width: 0;
2632
+ padding: 3px 4px 3px 8px;
2633
+ border-radius: 3px;
2634
+ color: var(--normal-text);
2635
+ cursor: pointer;
2636
+ overflow: hidden;
2637
+ white-space: nowrap;
2638
+ text-overflow: ellipsis;
2386
2639
  }
2387
2640
 
2388
- .point-style-section.point-style-first-visible {
2389
- margin-top: 7px;
2390
- padding-top: 0;
2391
- border-top: 0;
2641
+ .label-saved-style-name:hover {
2642
+ background: var(--lt-theme-col);
2392
2643
  }
2393
2644
 
2394
- .point-style-note {
2395
- color: #555;
2396
- line-height: 1.25;
2645
+ /* Hidden by visibility rather than left out, so that a name is the same width
2646
+ whether or not the pointer is on it. */
2647
+ .label-saved-style-delete {
2648
+ flex: none;
2649
+ width: 16px;
2650
+ padding: 2px 4px 2px 0;
2651
+ text-align: center;
2652
+ line-height: 1;
2653
+ color: #999;
2654
+ cursor: pointer;
2655
+ visibility: hidden;
2397
2656
  }
2398
2657
 
2399
- .point-create-labels-row {
2400
- display: flex;
2401
- align-items: center;
2402
- gap: 10px;
2658
+ .label-saved-style-item:hover .label-saved-style-delete {
2659
+ visibility: visible;
2403
2660
  }
2404
2661
 
2405
- .point-create-circles-row {
2406
- display: flex;
2407
- align-items: baseline;
2408
- gap: 2px;
2409
- color: #555;
2662
+ .label-saved-style-delete:hover {
2663
+ color: #c00;
2410
2664
  }
2411
2665
 
2412
- .point-create-circles-row span {
2413
- display: inline;
2414
- margin: 0;
2415
- color: #555;
2666
+ .label-saved-style-empty {
2667
+ padding: 3px 8px;
2668
+ color: #999;
2669
+ cursor: default;
2416
2670
  }
2417
2671
 
2418
- .point-create-copy-label {
2419
- display: inline-flex;
2420
- align-items: center;
2421
- gap: 4px;
2422
- margin: 0;
2423
- color: #555;
2672
+ /* Two controls on one line: a font style and its size, a colour and its
2673
+ opacity, the icon shapes and the icon's size. Both columns are the same
2674
+ width in every row, rather than each row's right column being sized to
2675
+ whatever it happens to hold -- the panel is a stack of these pairs, and
2676
+ sizing them one row at a time leaves every field edge in a slightly
2677
+ different place.
2678
+
2679
+ Bottom-aligned, so that a caption above one control cannot push it out of
2680
+ line with the control beside it: the icon size has a "Size" over it and the
2681
+ shape buttons beside it have nothing, and the two still have to be level. */
2682
+ .label-split-row {
2683
+ display: grid;
2684
+ grid-template-columns: 1fr var(--label-split-b-width);
2685
+ column-gap: 10px;
2686
+ align-items: end;
2424
2687
  }
2425
2688
 
2426
- .point-create-copy-label span {
2427
- display: inline;
2428
- margin: 0;
2689
+ .label-split-cell > span,
2690
+ .label-split-cell > .label-style-row-label {
2691
+ display: block;
2692
+ margin-bottom: 2px;
2429
2693
  color: #555;
2430
2694
  }
2431
2695
 
2432
- .point-symbol-row {
2433
- display: flex;
2434
- align-items: flex-start;
2435
- gap: 12px;
2696
+ /* Captions a couple of sizes down from the values they name. They are read
2697
+ once, when the panel is first met, and the narrow column of a split row is
2698
+ only as wide as the longest of them -- "Letter spacing" is what set it --
2699
+ so quieter captions are also a narrower panel. Direct children only: the
2700
+ colour picker's H/S/B captions are spans in a row too. */
2701
+ .label-style-panel .label-style-row > span,
2702
+ .label-style-panel .label-style-selection-row > span,
2703
+ .label-style-panel .label-split-cell > span,
2704
+ .label-style-panel .label-style-row > .label-style-row-label,
2705
+ .label-style-panel .label-split-cell > .label-style-row-label,
2706
+ .label-style-panel .label-icon-size-caption,
2707
+ .label-style-panel .point-style-note {
2708
+ font-size: 12px;
2436
2709
  }
2437
2710
 
2438
- .point-symbol-size-row {
2439
- display: flex;
2440
- align-items: flex-start;
2441
- gap: 12px;
2711
+ .label-split-row .label-split-cell + .label-split-cell {
2712
+ white-space: nowrap;
2442
2713
  }
2443
2714
 
2444
- .point-symbol-stepper-control {
2445
- display: flex;
2446
- flex-direction: column;
2447
- gap: 2px;
2715
+ /* Each column's control fills its column: the widths are the grid's to decide
2716
+ now, and a control keeping a width of its own would sit inside its cell
2717
+ rather than lining up with the one above it. */
2718
+ .label-split-cell > select,
2719
+ .label-split-cell > .size-field,
2720
+ .label-split-cell > input[type="text"] {
2721
+ width: 100%;
2722
+ box-sizing: border-box;
2448
2723
  }
2449
2724
 
2450
- .point-symbol-stepper-control:last-child {
2451
- margin-left: auto;
2452
- align-items: flex-end;
2725
+ .label-split-cell > .size-field {
2726
+ display: flex;
2453
2727
  }
2454
2728
 
2455
- .point-symbol-color-cell {
2456
- position: relative;
2457
- white-space: nowrap;
2729
+ .label-style-panel .size-field input.size-field-input {
2458
2730
  flex: 1;
2731
+ width: auto;
2459
2732
  }
2460
2733
 
2461
- .point-symbol-row .layer-number-control input[type="text"],
2462
- .point-symbol-width-row input[type="text"] {
2463
- width: 45px;
2464
- height: 19px;
2465
- padding-top: 0;
2466
- padding-bottom: 0;
2734
+ /* A grid track is at least as wide as its contents unless told otherwise, and
2735
+ a text input's contents are twenty characters whatever width it is given --
2736
+ without this the colour field pushes the opacity beside it off the panel. */
2737
+ .label-split-row .label-split-cell {
2738
+ min-width: 0;
2739
+ }
2740
+
2741
+ .label-font-style-row select {
2742
+ width: 100%;
2467
2743
  box-sizing: border-box;
2468
2744
  }
2469
2745
 
2470
- .point-symbol-width-row input[type="text"] {
2471
- width: 55px;
2746
+ /* Both take their width from the split row's narrow column; a value reads from
2747
+ the left edge of the field, where the caption above it starts. */
2748
+ .label-style-panel .label-opacity-input,
2749
+ .label-style-panel .label-measure-input {
2750
+ padding: 0 4px;
2751
+ vertical-align: top;
2472
2752
  }
2473
2753
 
2474
- .layer-style-control-line {
2475
- display: flex;
2476
- align-items: flex-start;
2477
- gap: 12px;
2754
+ /* One disabled look for the whole panel. Left to themselves the controls each
2755
+ fade differently -- a select takes the browser's grey, an input with
2756
+ -webkit-appearance: none takes no styling at all, and a widget built from
2757
+ divs takes whatever it was given -- so an inert row ends up in three or four
2758
+ shades. Each control keeps its live colours and the whole thing is faded by
2759
+ the same amount, which also fades its border. */
2760
+ .label-style-panel select:disabled,
2761
+ .label-style-panel input[type="text"]:disabled,
2762
+ .label-style-panel .size-field.disabled,
2763
+ .label-style-panel .label-color-field.disabled,
2764
+ .label-style-panel .label-panel-action-btn.disabled,
2765
+ .label-style-panel .label-btn-group.disabled {
2766
+ opacity: 0.45;
2767
+ color: var(--normal-text);
2768
+ -webkit-text-fill-color: var(--normal-text);
2769
+ background-color: white;
2770
+ border-color: var(--label-field-border);
2771
+ }
2772
+
2773
+ /* Nothing inside an already-faded control fades again: two 0.45s over each
2774
+ other is 0.2, and a widget made of a wrapper and an input -- the size and
2775
+ colour fields -- was coming out half the weight of the plain input beside
2776
+ it. The parts still take their own disabled state, because a disabled input
2777
+ cannot be typed into and a disabled button cannot be pressed; it is only the
2778
+ fading that belongs to the whole. */
2779
+ .label-style-panel .label-btn-group.disabled .label-panel-btn.disabled,
2780
+ .label-style-panel .size-field.disabled .label-panel-btn.disabled,
2781
+ .label-style-panel .size-field.disabled input:disabled,
2782
+ .label-style-panel .label-color-field.disabled input:disabled,
2783
+ .label-style-panel .label-color-field.disabled .label-color-chit.disabled {
2784
+ opacity: 1;
2785
+ color: inherit;
2478
2786
  }
2479
2787
 
2480
- .layer-color-cell {
2481
- position: relative;
2482
- white-space: nowrap;
2483
- }
2484
-
2485
- .layer-style-panel .layer-color-row input[type="text"] {
2486
- width: 79px;
2487
- height: 19px;
2788
+ /* Whitespace and a bold heading separate the sections; a rule as well would be
2789
+ a third answer to a question already settled twice. */
2790
+ .label-style-panel .label-style-section + .label-style-section {
2791
+ margin-top: 12px;
2488
2792
  padding-top: 0;
2489
- padding-bottom: 0;
2490
- box-sizing: border-box;
2491
- vertical-align: top;
2793
+ border-top: 0;
2492
2794
  }
2493
2795
 
2494
- .layer-row-controls {
2796
+ /* Baselines, not boxes: the heading and the caption beside it are set at
2797
+ different sizes, and centring their boxes leaves the smaller one's baseline
2798
+ a pixel or two high. The switch has no baseline to share, so it centres on
2799
+ the line itself. */
2800
+ .label-style-panel .label-style-section-title {
2495
2801
  display: flex;
2496
- gap: 6px;
2497
- align-items: flex-start;
2498
- justify-content: flex-start;
2802
+ align-items: baseline;
2803
+ gap: 8px;
2499
2804
  }
2500
2805
 
2501
- .layer-number-control {
2502
- display: block;
2503
- margin: 0;
2806
+ .label-style-panel .label-style-section-title .label-toggle {
2807
+ align-self: center;
2504
2808
  }
2505
2809
 
2506
- .layer-number-control > span,
2507
- .label-color-row > span,
2508
- .layer-color-cell > span {
2509
- display: block;
2510
- margin-bottom: 3px;
2810
+ /* .hidden is one class and the rule above is two, so a heading told to hide
2811
+ would go on being laid out. The circle panel hides its Circles heading once
2812
+ the points are circles: the panel's title says so, and the section is the
2813
+ only thing left in it. */
2814
+ .label-style-panel .label-style-section-title.hidden {
2815
+ display: none;
2816
+ }
2817
+
2818
+ /* 15.5px is what the dialogs subhead at (.info-box h4); the weight is the
2819
+ sketch's, which wants these read as headings rather than as captions. */
2820
+ .label-style-panel .label-style-section-name {
2821
+ font-size: 15.5px;
2822
+ color: #222;
2823
+ }
2824
+
2825
+ .label-style-panel .label-style-section-minor .label-style-section-name {
2826
+ font-size: 12px;
2827
+ font-weight: normal;
2828
+ line-height: 1;
2511
2829
  color: #555;
2512
2830
  }
2513
2831
 
2514
- .layer-style-panel .layer-number-control input[type="text"] {
2515
- width: 45px;
2516
- height: 19px;
2517
- padding-top: 0;
2518
- padding-bottom: 0;
2519
- box-sizing: border-box;
2832
+ /* Where label text is measured (gui-label-measure.mjs). Off the page rather
2833
+ than display:none, which would give it no layout and its text no box. */
2834
+ .label-measure-svg {
2835
+ position: absolute;
2836
+ left: -10000px;
2837
+ top: -10000px;
2838
+ width: 1px;
2839
+ height: 1px;
2840
+ overflow: hidden;
2841
+ }
2842
+
2843
+ /* Closer to the heading above it than a row usually is, because that heading
2844
+ line carries this row's "Size" caption: the gap between a caption and the
2845
+ field it names is 2px everywhere else in the panel. */
2846
+ .text-style-panel .label-icon-shapes-row {
2847
+ margin-top: 2px;
2848
+ }
2849
+
2850
+ /* Pushed to the right and given the width of a split row's narrow column, so
2851
+ that it sits over the size field a row below it. */
2852
+ .text-style-panel .label-icon-size-caption {
2853
+ width: var(--label-split-b-width);
2854
+ margin-left: auto;
2855
+ font-weight: normal;
2856
+ color: #555;
2857
+ }
2858
+
2859
+ /* Darker than the app's default placeholder. These fields show the value the
2860
+ renderer will use when the property is absent -- 0 and auto are true of the
2861
+ label, they are just not stored on it -- so at #aaa they read as a control
2862
+ that is switched off rather than as an answer. */
2863
+ .label-style-panel ::placeholder {
2864
+ color: #777;
2520
2865
  }
2521
2866
 
2522
- .layer-stepper-control {
2867
+ /* Buttons that answer one question share a border and sit flush, the way the
2868
+ size field and its stepper do: four shapes are four answers to "which
2869
+ symbol", not four separate controls. They divide the width of their cell so
2870
+ that the group lines up with the field above or below it. */
2871
+ .label-style-panel .label-btn-group {
2523
2872
  display: flex;
2524
- align-items: center;
2525
- gap: 2px;
2873
+ box-sizing: border-box;
2874
+ height: var(--label-field-height);
2875
+ border: 1px solid var(--label-field-border);
2876
+ border-radius: var(--label-field-radius);
2877
+ background: white;
2878
+ overflow: hidden;
2526
2879
  }
2527
2880
 
2528
- .layer-stepper-control .label-panel-btn {
2529
- text-align: center;
2530
- line-height: 18px;
2881
+ .label-style-panel .label-btn-group .label-panel-btn {
2882
+ flex: 1;
2883
+ width: auto;
2884
+ height: auto;
2885
+ border: none;
2886
+ border-radius: 0;
2887
+ background: white;
2531
2888
  }
2532
2889
 
2533
- .layer-stroke-width-value {
2534
- display: inline-block;
2535
- width: 30px;
2536
- min-height: 17px;
2537
- line-height: 17px;
2538
- text-align: center;
2539
- border: 1px solid #999;
2890
+ .label-style-panel .label-btn-group .label-panel-btn + .label-panel-btn {
2891
+ border-left: 1px solid #e0e0e0;
2892
+ }
2893
+
2894
+ /* Hover leaves the selected button alone. Every hover fill in the panel is
2895
+ lighter than the selection it would sit on, so pointing at the chosen answer
2896
+ made it look less chosen than its neighbours -- and there is nothing to
2897
+ preview anyway, since clicking it changes nothing. */
2898
+ .label-style-panel .label-btn-group .label-panel-btn:hover:not(.disabled):not(.selected) {
2899
+ background: #f0f0f0;
2900
+ }
2901
+
2902
+ /* A grey fill, not the inverse the panel's other buttons use: the glyph is
2903
+ what identifies the choice, and reversing it out makes the selected shape
2904
+ the hardest one to read. */
2905
+ .label-style-panel .label-btn-group .label-panel-btn.selected {
2906
+ background: #dcdcdc;
2907
+ color: black;
2908
+ }
2909
+
2910
+ /* A switch, because whether the label has a symbol at all is a yes/no that the
2911
+ four shapes below it then qualify. Off is a pale track with the knob left,
2912
+ on a dark one with the knob right. */
2913
+ .label-toggle {
2914
+ position: relative;
2915
+ flex: none;
2916
+ width: 26px;
2917
+ height: 15px;
2918
+ border-radius: 8px;
2919
+ background: #ccc;
2920
+ cursor: pointer;
2921
+ user-select: none;
2922
+ transition: background-color 0.12s;
2923
+ }
2924
+
2925
+ .label-toggle-knob {
2926
+ position: absolute;
2927
+ top: 2px;
2928
+ left: 2px;
2929
+ width: 11px;
2930
+ height: 11px;
2931
+ border-radius: 50%;
2540
2932
  background: white;
2933
+ box-shadow: 0 1px 2px rgba(0,0,0,0.25);
2934
+ transition: left 0.12s;
2935
+ }
2936
+
2937
+ .label-toggle.on {
2938
+ background: #333;
2939
+ }
2940
+
2941
+ .label-toggle.on .label-toggle-knob {
2942
+ left: 13px;
2943
+ }
2944
+
2945
+ .label-toggle.disabled {
2946
+ opacity: 0.45;
2947
+ cursor: default;
2948
+ }
2949
+
2950
+ .label-style-section-title {
2951
+ margin-top: 11px;
2952
+ font-weight: 600;
2953
+ color: #333;
2954
+ }
2955
+
2956
+ .label-style-section + .label-style-section .label-style-section-title {
2957
+ margin-top: 0;
2958
+ }
2959
+
2960
+ /* The cell a colour field sits in, which is also what its picker hangs from:
2961
+ the picker is wider than the panel, so it opens over the map, and anchoring
2962
+ it to the cell keeps it on the same side of the panel as its field. */
2963
+ .label-color-row {
2964
+ position: relative;
2965
+ }
2966
+
2967
+ .label-panel-btn {
2968
+ box-sizing: border-box;
2969
+ user-select: none;
2970
+ cursor: pointer;
2971
+ border: 1px solid #999;
2972
+ border-radius: 3px;
2973
+ background: #eee;
2974
+ padding: 0;
2975
+ height: 19px;
2976
+ width: 19px;
2977
+ }
2978
+
2979
+ .label-panel-btn.disabled {
2980
+ color: #999;
2981
+ cursor: default;
2982
+ opacity: 0.45;
2983
+ }
2984
+
2985
+ /* A button with a word on it: Clear style, Random fills, Create. The same
2986
+ shape as Save current in the saved styles row, which is the same kind of
2987
+ thing -- a named action, as wide as its name -- and the same height as the
2988
+ fields above it. */
2989
+ .label-panel-action-btn {
2990
+ box-sizing: border-box;
2991
+ display: inline-flex;
2992
+ align-items: center;
2993
+ height: var(--label-field-height);
2994
+ padding: 0 8px;
2995
+ border: 1px solid var(--label-field-border);
2996
+ border-radius: var(--label-field-radius);
2997
+ background-color: white;
2998
+ color: var(--normal-text);
2541
2999
  cursor: pointer;
3000
+ user-select: none;
3001
+ }
3002
+
3003
+ .label-panel-action-btn:hover:not(.disabled) {
3004
+ background-color: #f0f0f0;
3005
+ }
3006
+
3007
+ .label-panel-action-btn.disabled {
3008
+ cursor: default;
3009
+ }
3010
+
3011
+ /* Buttons wrap rather than run off the panel: two of them fit on one line in
3012
+ the polygon panel and would not if either name grew. */
3013
+ .label-panel-button-row {
3014
+ display: flex;
3015
+ flex-wrap: wrap;
3016
+ gap: 4px 6px;
3017
+ }
3018
+
3019
+ /* A swatch and its hex value inside one border: the pair is one answer to one
3020
+ question, and a separate button beside a text box reads as two. The swatch
3021
+ keeps its own outline so that white does not disappear into the field. */
3022
+ .label-style-panel .label-color-field {
3023
+ display: flex;
3024
+ align-items: center;
2542
3025
  box-sizing: border-box;
3026
+ height: var(--label-field-height);
3027
+ padding: 0 3px;
3028
+ border: 1px solid var(--label-field-border);
3029
+ border-radius: var(--label-field-radius);
3030
+ background: white;
2543
3031
  }
2544
3032
 
2545
- .layer-stroke-width-value.editing {
2546
- cursor: text;
3033
+ .label-style-panel .label-color-field:focus-within {
3034
+ border-color: #d3aa00;
2547
3035
  }
2548
3036
 
2549
- .layer-stroke-width-row .layer-number-control {
2550
- display: block;
3037
+ .label-style-panel .label-color-field .label-color-chit {
3038
+ flex: none;
3039
+ width: 13px;
3040
+ height: 13px;
3041
+ margin-right: 6px;
3042
+ border: 1px solid #bbb;
3043
+ border-radius: 2px;
3044
+ }
3045
+
3046
+ .label-style-panel .label-color-field input[type="text"] {
3047
+ flex: 1;
3048
+ width: auto;
3049
+ min-width: 0;
3050
+ height: auto;
3051
+ padding: 0;
3052
+ border: none;
3053
+ border-radius: 0;
3054
+ background: transparent;
3055
+ box-shadow: none;
2551
3056
  }
2552
3057
 
2553
- .layer-style-panel .layer-color-row .label-color-picker input[type="text"] {
3058
+ .label-style-panel .label-color-field input[type="text"]:focus {
3059
+ border: none;
3060
+ box-shadow: none;
3061
+ outline: none;
3062
+ }
3063
+
3064
+ .label-style-panel .label-color-field.disabled {
3065
+ opacity: 0.45;
3066
+ }
3067
+
3068
+ .label-color-row .label-color-picker {
3069
+ left: auto;
3070
+ right: 0;
3071
+ }
3072
+
3073
+ .label-color-row .label-color-picker input[type="text"] {
2554
3074
  width: 45px;
2555
3075
  }
2556
3076
 
3077
+ .label-css-row input {
3078
+ width: 100%;
3079
+ box-sizing: border-box;
3080
+ }
3081
+
3082
+ /* What a section of this panel says when there is nothing in it to set: the
3083
+ layer is drawn some way this panel cannot edit, or the thing the section is
3084
+ for has to be made in the console. Set like a caption, not like a field's
3085
+ value. */
3086
+ .point-style-note {
3087
+ color: #555;
3088
+ line-height: 1.3;
3089
+ }
3090
+
3091
+ .point-create-labels-row {
3092
+ display: flex;
3093
+ align-items: center;
3094
+ gap: 10px;
3095
+ }
3096
+
3097
+ .point-create-copy-label {
3098
+ display: inline-flex;
3099
+ align-items: center;
3100
+ gap: 4px;
3101
+ margin: 0;
3102
+ color: #555;
3103
+ font-size: 12px;
3104
+ }
3105
+
3106
+ .point-create-copy-label span {
3107
+ display: inline;
3108
+ margin: 0;
3109
+ color: #555;
3110
+ }
3111
+
2557
3112
  .label-color-picker {
2558
3113
  position: absolute;
2559
3114
  z-index: 25;
@@ -2657,6 +3212,10 @@ body.dragging-color-picker * {
2657
3212
  margin-left: auto;
2658
3213
  }
2659
3214
 
3215
+ .label-style-selection-row {
3216
+ line-height: 1;
3217
+ }
3218
+
2660
3219
  .label-style-selection-row button {
2661
3220
  margin-right: 5px;
2662
3221
  }
@@ -2699,17 +3258,66 @@ body.dragging-color-picker * {
2699
3258
  background: white;
2700
3259
  }
2701
3260
 
3261
+ /* Nine plain squares: a border on each would double the lines in a shape that
3262
+ is nothing but lines, so the cells are filled and the chosen one is dark. */
3263
+ .text-style-panel .label-position-grid {
3264
+ grid-template-columns: repeat(3, 10px);
3265
+ gap: 3px;
3266
+ margin-top: -13px;
3267
+ margin-bottom: 3px;
3268
+ }
3269
+
3270
+ .text-style-panel .label-position-grid .label-panel-btn {
3271
+ width: 10px;
3272
+ height: 10px;
3273
+ border: none;
3274
+ border-radius: 1px;
3275
+ background: #dcdcdc;
3276
+ }
3277
+
3278
+ .text-style-panel .label-position-grid .label-panel-btn:hover:not(.disabled):not(.selected) {
3279
+ background: #c4c4c4;
3280
+ }
3281
+
3282
+ .text-style-panel .label-position-grid .label-panel-btn.selected {
3283
+ background: black;
3284
+ }
3285
+
3286
+ /* The position a dragged label is nearest, which is where it roughly belongs
3287
+ and the cell that puts it back. Between the unlit grey and the lit black,
3288
+ because it is saying something weaker than either: not "here" but "about
3289
+ here, and not exactly". */
3290
+ .text-style-panel .label-position-grid .label-panel-btn.nearest:not(.selected) {
3291
+ background: #a0a0a0;
3292
+ }
3293
+
3294
+ /* The grid and the Fixed/Draggable toggle share the position row, the toggle
3295
+ against the right edge so that it lines up with the fields in the rows
3296
+ above. */
3297
+ .text-style-panel .label-position-row {
3298
+ margin-top: 3px;
3299
+ display: flex;
3300
+ /* align-items: center; */
3301
+ justify-content: space-between;
3302
+ }
3303
+
3304
+ /* Words rather than glyphs, so these segments size to their text instead of
3305
+ splitting the group evenly, and sit in a group narrow enough to read as one
3306
+ control beside the grid. */
3307
+ .text-style-panel .label-drag-mode-buttons .label-panel-btn {
3308
+ display: flex;
3309
+ flex: 0 0 auto;
3310
+ align-items: center;
3311
+ padding: 0 7px;
3312
+ font-size: 12px;
3313
+ }
3314
+
2702
3315
  .label-panel-btn.selected {
2703
3316
  background-color: black;
2704
3317
  border-color: black;
2705
3318
  color: white;
2706
3319
  }
2707
3320
 
2708
- .label-icon-buttons {
2709
- display: flex;
2710
- gap: 2px;
2711
- }
2712
-
2713
3321
  .label-icon-buttons .label-panel-btn {
2714
3322
  display: flex;
2715
3323
  align-items: center;
@@ -2724,7 +3332,6 @@ body.dragging-color-picker * {
2724
3332
  stroke: none;
2725
3333
  }
2726
3334
 
2727
- .label-icon-buttons .label-panel-btn[data-icon="none"] .label-icon-symbol,
2728
3335
  .label-icon-buttons .label-panel-btn[data-icon="ring"] .label-icon-symbol {
2729
3336
  fill: none;
2730
3337
  stroke: currentColor;
@@ -2737,41 +3344,113 @@ body.dragging-color-picker * {
2737
3344
  transform: scale(1.4);
2738
3345
  }
2739
3346
 
2740
- .label-size-row .label-panel-btn,
2741
- .label-icon-size-row .label-panel-btn {
2742
- display: inline-block;
2743
- text-align: center;
2744
- line-height: 18px;
3347
+ .label-align-buttons .label-panel-btn {
3348
+ display: flex;
3349
+ align-items: center;
3350
+ justify-content: center;
2745
3351
  }
2746
3352
 
2747
- .label-size-row .label-panel-btn,
2748
- .label-icon-size-row .label-panel-btn {
2749
- margin-right: 4px;
3353
+ /* Drawn larger than the icon symbols above, which share its viewBox but not
3354
+ its proportions: three rules across the middle are ink over less than half
3355
+ of the box, where a circle or a square fills most of its own, so at the same
3356
+ 13px both the glyph and its weight read smaller than the shapes beside it.
3357
+ 16px also puts the 16-unit viewBox at one unit per pixel, so the half-pixel
3358
+ baselines the rules are drawn on land where they were meant to. */
3359
+ .label-align-symbol {
3360
+ display: block;
3361
+ width: 16px;
3362
+ height: 16px;
3363
+ fill: none;
3364
+ stroke: currentColor;
3365
+ stroke-width: 1.5;
3366
+ stroke-linecap: round;
2750
3367
  }
2751
3368
 
2752
- .label-split-row .label-size-row .label-panel-btn,
2753
- .label-split-row .label-icon-size-row .label-panel-btn {
2754
- margin-right: 2px;
3369
+ /* A size field and its stepper share a border and sit flush, so that the three
3370
+ elements read as one control rather than three. The buttons are always
3371
+ visible: they are the primary affordance of the panel's most-used numbers,
3372
+ and revealing them on hover would either reserve their space anyway or
3373
+ overlap the value being adjusted. */
3374
+ .size-field {
3375
+ display: inline-flex;
3376
+ align-items: stretch;
3377
+ box-sizing: border-box;
3378
+ height: var(--label-field-height, 19px);
3379
+ border: 1px solid var(--label-field-border, #999);
3380
+ border-radius: var(--label-field-radius, 3px);
3381
+ background: #fff;
3382
+ overflow: hidden;
3383
+ vertical-align: top;
2755
3384
  }
2756
3385
 
2757
- .label-split-row .label-size-row .label-panel-btn:last-child,
2758
- .label-split-row .label-icon-size-row .label-panel-btn:last-child {
2759
- margin-right: 0;
3386
+ .size-field.disabled {
3387
+ opacity: 0.45;
2760
3388
  }
2761
3389
 
2762
- .label-size-row .label-size-value,
2763
- .label-icon-size-row .label-icon-size-value {
2764
- display: inline-block;
2765
- width: 28px;
2766
- margin: 0 3px 0 0;
2767
- text-align: center;
3390
+ /* input.size-field-input, not .size-field-input: the panels give their text
3391
+ inputs a border through an attribute selector, and the field's own border is
3392
+ the one that has to show. */
3393
+ .size-field input.size-field-input {
3394
+ width: 40px;
3395
+ min-width: 0;
3396
+ height: auto;
3397
+ margin: 0;
3398
+ padding: 0 3px;
3399
+ border: none;
3400
+ border-radius: 0;
3401
+ box-shadow: none;
3402
+ background: transparent;
2768
3403
  color: black;
2769
3404
  }
2770
3405
 
2771
- .label-split-row .label-size-value,
2772
- .label-split-row .label-icon-size-value {
2773
- width: 24px;
2774
- margin-right: 2px;
3406
+ /* The field shows focus on its outer border, so that the ring cannot be drawn
3407
+ between the input and the stepper it shares an edge with. */
3408
+ .size-field input.size-field-input:focus {
3409
+ outline: none;
3410
+ border: none;
3411
+ box-shadow: none;
3412
+ }
3413
+
3414
+ .size-field:focus-within {
3415
+ border-color: #d3aa00;
3416
+ }
3417
+
3418
+ /* The stepper is inset from the field's border rather than dividing it: the
3419
+ value is the field's content and the arrows are a control within it, which
3420
+ is also how the native spinner reads. */
3421
+ .size-field .size-field-stepper {
3422
+ display: flex;
3423
+ flex-direction: column;
3424
+ margin: 2px 2px 2px 0;
3425
+ border: 1px solid #d6d6d6;
3426
+ border-radius: 2px;
3427
+ background: #f7f7f7;
3428
+ overflow: hidden;
3429
+ }
3430
+
3431
+ /* Half-height buttons, so the pair occupies the width of one: the panel's rows
3432
+ are tight and a size field shares its row with a label. */
3433
+ .size-field .size-field-btn {
3434
+ display: flex;
3435
+ align-items: center;
3436
+ justify-content: center;
3437
+ width: 13px;
3438
+ height: 8px;
3439
+ border: none;
3440
+ border-radius: 0;
3441
+ background: transparent;
3442
+ }
3443
+
3444
+ .size-field .size-field-arrow {
3445
+ display: block;
3446
+ width: 7px;
3447
+ height: 5px;
3448
+ fill: #444;
3449
+ stroke: none;
3450
+ }
3451
+
3452
+ .size-field .size-field-btn:hover:not(.disabled) {
3453
+ background: #e2e2e2;
2775
3454
  }
2776
3455
 
2777
3456
  .nav-menu-item,
@@ -2937,6 +3616,14 @@ body.dragging-color-picker * {
2937
3616
  transform: translateY(0);
2938
3617
  }
2939
3618
 
3619
+ /* The toolbar being hovered comes forward, so that a tooltip drawn above a
3620
+ button is not painted behind the toolbar stacked over it. The tooltip's own
3621
+ z-index cannot do this: each toolbar is transformed, which makes it a
3622
+ stacking context its descendants cannot escape. */
3623
+ .floating-toolbar:hover {
3624
+ z-index: 1;
3625
+ }
3626
+
2940
3627
  .floating-toolbar-content {
2941
3628
  display: flex;
2942
3629
  align-items: center;
@@ -2984,6 +3671,15 @@ body.dragging-color-picker * {
2984
3671
  background-color: #cdebf9;
2985
3672
  }
2986
3673
 
3674
+ /* A toggle button that is currently armed */
3675
+ .floating-toolbar-btn.selected {
3676
+ background-color: #cdebf9;
3677
+ }
3678
+
3679
+ .floating-toolbar-btn.selected svg * {
3680
+ fill: #166689;
3681
+ }
3682
+
2987
3683
  .floating-toolbar-btn.disabled {
2988
3684
  cursor: default;
2989
3685
  }