@glimstone/core 4.1.0 → 4.1.2

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/tokens.css CHANGED
@@ -107,6 +107,9 @@
107
107
  --carbon-surface3: #525252;
108
108
  --carbon-hover: #353535;
109
109
  --carbon-hover-raised: #6f6f6f;
110
+ /* A switch that is off. On the light ramp surface3 stands too close to a card
111
+ and to the knob to see, so the track goes down to 3:1 against both. */
112
+ --toggle-off: #525252;
110
113
  --carbon-border: #393939;
111
114
 
112
115
  /* The About card's brand marks at rest, adjusted from the published brand
@@ -213,6 +216,7 @@
213
216
  --carbon-surface3: #d1d1d1;
214
217
  --carbon-hover: #e0e0e0;
215
218
  --carbon-hover-raised: #c6c6c6;
219
+ --toggle-off: #8d8d8d;
216
220
  --carbon-border: #d1d1d1;
217
221
 
218
222
  /* Warm brands deepened, dark ones left dark (see the dark block). */
@@ -278,6 +282,7 @@
278
282
  --carbon-surface3: #d1d1d1;
279
283
  --carbon-hover: #e0e0e0;
280
284
  --carbon-hover-raised: #c6c6c6;
285
+ --toggle-off: #8d8d8d;
281
286
  --carbon-border: #d1d1d1;
282
287
 
283
288
  /* The same brand values as the media block, for an explicit light choice. */
@@ -389,14 +394,20 @@ body {
389
394
  row is a bigger target, and in glyph mode the symbol is the whole row.
390
395
  `flex: none` because a flex line squeezes the glyph beside a long label
391
396
  first. */
392
- .glim-nav-row > svg {
397
+ :is(.glim-nav-row, .glim-nav-row-main) > svg {
393
398
  width: 1.25rem;
394
399
  height: 1.25rem;
395
400
  flex: none;
396
401
  }
402
+ /* A tree's row keeps a deep name its room with a smaller glyph. */
403
+ :is(.glim-nav-row--tree, .glim-nav-row--tree > .glim-nav-row-main) > svg {
404
+ width: 1.05rem;
405
+ height: 1.05rem;
406
+ }
397
407
 
398
408
  /* A closed reactive row must not reserve a gap beside a zero-width label. */
399
- .glim-nav-row.glim-reactive {
409
+ .glim-nav-row.glim-reactive:not(.glim-nav-row--slots),
410
+ .glim-nav-row--slots.glim-reactive > .glim-nav-row-main {
400
411
  gap: 0;
401
412
  }
402
413
 
@@ -1935,6 +1946,24 @@ body {
1935
1946
  .glim-btn-sm { min-width: var(--btn-w-sm); }
1936
1947
  .glim-btn-md { min-width: var(--btn-w-md); }
1937
1948
  .glim-btn-lg { min-width: var(--btn-w-lg); }
1949
+ /* A foot holds several answers in one row, so they hug their words down to the
1950
+ smallest floor. An explicit stage is a width of its own and stays. */
1951
+ .glim-modal-foot > :is(.glim-btn-sm, .glim-btn-md, .glim-btn-lg),
1952
+ .glim-modal-foot > .glim-tip-wrap > :is(.glim-btn-sm, .glim-btn-md, .glim-btn-lg) { min-width: var(--btn-w-xs); }
1953
+ /* A button wider than the whole foot, as on a phone, runs its words onto a
1954
+ second line and grows with them instead of ending in an ellipsis. */
1955
+ :is(.glim-modal-foot, .glim-modal-foot > .glim-tip-wrap) > .glim-btn {
1956
+ height: auto;
1957
+ min-height: var(--btn-h);
1958
+ padding-block: 0.375rem;
1959
+ }
1960
+ :is(.glim-modal-foot, .glim-modal-foot > .glim-tip-wrap) > .glim-btn-key { min-height: var(--btn-h-key); }
1961
+ :is(.glim-modal-foot, .glim-modal-foot > .glim-tip-wrap) > .glim-btn > .glim-btn-label {
1962
+ white-space: normal;
1963
+ text-wrap: balance;
1964
+ text-align: center;
1965
+ }
1966
+ .glim-path > .glim-btn:not(.glim-btn-icon) { min-width: 0; }
1938
1967
 
1939
1968
  /* Rule 40. A surface-filled button's hovered ground takes 22% of the tint and
1940
1969
  its glyph all of it; its words keep the text colour, which the accent on a
@@ -1956,7 +1985,7 @@ body {
1956
1985
  .glim-btn-bare:not(:disabled):is(:hover, :focus-visible) {
1957
1986
  color: var(--btn-tint);
1958
1987
  }
1959
- .glim-btn-bare:is(:hover, :focus-visible) {
1988
+ .glim-btn-bare:not([aria-pressed="true"]):is(:hover, :focus-visible) {
1960
1989
  background-color: transparent;
1961
1990
  }
1962
1991
 
@@ -2291,14 +2320,17 @@ body {
2291
2320
  height, sized by its glyph. In a mode that shows words it is an ordinary
2292
2321
  button with its label (see `ButtonVariant` in packages/react/src/Button.tsx). */
2293
2322
  .glim-btn-icon {
2294
- width: var(--btn-h);
2295
- min-width: 0;
2323
+ min-width: var(--btn-h);
2296
2324
  padding-inline: 0;
2297
2325
  }
2326
+ /* A square that holds more than its glyph, such as a count, grows to hold it. */
2327
+ .glim-btn-icon:has(> :not(.glim-btn-glyph, .glim-sr-only)) {
2328
+ padding-inline: var(--btn-pad-x);
2329
+ }
2298
2330
 
2299
2331
  /* A row action promoted to a key control stays square at the other height. */
2300
2332
  .glim-btn-icon.glim-btn-key {
2301
- width: var(--btn-h-key);
2333
+ min-width: var(--btn-h-key);
2302
2334
  }
2303
2335
 
2304
2336
  /* A glyph alone in a square fills half its box, as on a 32px icon badge with a
@@ -2588,6 +2620,13 @@ body {
2588
2620
  outline: 2px solid var(--focus-ring);
2589
2621
  outline-offset: 1px;
2590
2622
  }
2623
+ /* In a well or a sub-card the trigger stands among fields, so it takes their
2624
+ ground and hovers up the ramp (rule 21). */
2625
+ :is(.glim-well, .glim-subcard) :is(.glim-select-trigger:not(.glim-select-trigger--inset), .glim-combobox-control) {
2626
+ border-color: transparent;
2627
+ background: var(--carbon-surface3);
2628
+ }
2629
+ :is(.glim-well, .glim-subcard) .glim-select-trigger:not(.glim-select-trigger--inset):hover { background: var(--carbon-hover-raised); }
2591
2630
  .glim-select-trigger[data-disabled] { opacity: 0.5; cursor: default; }
2592
2631
  /* Busy: the trigger keeps its focus and takes no input until the answer is back. */
2593
2632
  .glim-select-trigger[data-readonly] { opacity: 0.5; cursor: progress; }
@@ -2756,6 +2795,37 @@ body {
2756
2795
  @media (pointer: coarse) {
2757
2796
  .glim-select-item { min-height: 2.75rem; }
2758
2797
  }
2798
+ /* The box a closed list hangs from stays on the body for the machine, out of
2799
+ the way. */
2800
+ .glim-select-positioner:has(> [hidden]) { display: none; }
2801
+ /* A list that draws a context menu's rows (Select with rows="menu"): the menu's
2802
+ panel, and the states the machine marks with attributes instead of the focus.
2803
+ The wash comes before the highlight, which covers it, as a hover does in the
2804
+ menu. */
2805
+ .glim-select-content--menu {
2806
+ min-width: max(var(--reference-width, 0px), 16rem);
2807
+ padding: 0.35rem;
2808
+ box-shadow: var(--elevation), var(--hairline);
2809
+ }
2810
+ .glim-select-content--menu .glim-menu-item[data-state="checked"] { background-color: var(--accent-soft); }
2811
+ .glim-select-content--menu .glim-menu-item[data-highlighted] { background-color: var(--carbon-hover); }
2812
+ .glim-select-content--menu .glim-menu-item[data-disabled] {
2813
+ opacity: 0.45;
2814
+ background-color: transparent;
2815
+ cursor: default;
2816
+ }
2817
+ .glim-select-content--menu .glim-menu-tick:not([data-state="checked"]) { visibility: hidden; }
2818
+ .glim-select-content--menu .glim-select-group + .glim-select-group {
2819
+ margin-top: 0.3rem;
2820
+ padding-top: 0.3rem;
2821
+ border-top-color: color-mix(in srgb, var(--carbon-text) 9%, transparent);
2822
+ }
2823
+ @media (pointer: coarse) {
2824
+ .glim-select-content--menu .glim-menu-item {
2825
+ height: auto;
2826
+ min-height: 2.75rem;
2827
+ }
2828
+ }
2759
2829
  .glim-select-empty {
2760
2830
  padding: 0.5rem 0.75rem;
2761
2831
  color: var(--carbon-text-muted);
@@ -2795,6 +2865,7 @@ body {
2795
2865
  .glim-slider-thumb[data-dragging] { cursor: grabbing; }
2796
2866
  .glim-slider-thumb[data-disabled] { opacity: 0.5; cursor: default; }
2797
2867
  .glim-slider-value {
2868
+ margin-inline-start: auto;
2798
2869
  color: var(--carbon-text);
2799
2870
  font-size: var(--text-body);
2800
2871
  font-variant-numeric: tabular-nums;
@@ -2813,7 +2884,7 @@ body {
2813
2884
  :where([class*="glim-"]) :where(*, ::before, ::after) {
2814
2885
  box-sizing: border-box;
2815
2886
  }
2816
- :where(.glim-btn, .glim-badge, .glim-readme-btn, .glim-toggle-track, .glim-toast-dismiss, .glim-window-button, .glim-give-network, .glim-give-coin, .glim-give-amount, .glim-sel-seg, .glim-num-field, .glim-menu-item, .glim-menu-head-edit, .glim-menubar-item, .glim-checkbox, .glim-input, .glim-secret-eye) {
2887
+ :where(.glim-btn, .glim-badge, .glim-readme-btn, .glim-toggle-track, .glim-toast-dismiss, .glim-window-button, .glim-give-network, .glim-give-coin, .glim-give-amount, .glim-sel-seg, .glim-num-field, .glim-menu-item, .glim-menu-head-edit, .glim-menubar-item, .glim-checkbox, .glim-input, .glim-secret-eye, .glim-icon-btn, .glim-glyph-field-input, .glim-nav-row-main, button.glim-nav-row) {
2817
2888
  margin: 0;
2818
2889
  padding: 0;
2819
2890
  border: 0 solid;
@@ -2905,11 +2976,20 @@ body {
2905
2976
  }
2906
2977
  .glim-modal-foot {
2907
2978
  display: flex;
2979
+ flex-wrap: wrap;
2908
2980
  align-items: center;
2909
2981
  justify-content: flex-end;
2910
2982
  gap: 0.75rem;
2911
2983
  padding: 1rem 1.25rem;
2912
2984
  }
2985
+ /* A button keeps its words: in a flex row what stands beside it gives way, and
2986
+ in a foot it moves to the next line. In the layer, so an app's own flex
2987
+ rule for a button still wins. */
2988
+ .glim-btn {
2989
+ flex-shrink: 0;
2990
+ max-width: 100%;
2991
+ }
2992
+ .glim-modal-foot > .glim-tip-wrap { flex: none; }
2913
2993
 
2914
2994
  /* The fills a button can take (ButtonTone). `ok`, `danger` and `warn` put the
2915
2995
  solid status tokens under the ground's ink: in both themes the solid values
@@ -2957,7 +3037,7 @@ body {
2957
3037
  width: 2.25rem;
2958
3038
  height: 1.25rem;
2959
3039
  border-radius: var(--radius-pill);
2960
- background-color: var(--carbon-surface3);
3040
+ background-color: var(--toggle-off);
2961
3041
  transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
2962
3042
  }
2963
3043
  .glim-toggle-track[aria-checked="true"] { background-color: var(--accent); }
@@ -3328,7 +3408,7 @@ body {
3328
3408
  display: flex;
3329
3409
  align-items: flex-start;
3330
3410
  gap: 0.625rem;
3331
- width: 20rem;
3411
+ width: 24rem;
3332
3412
  max-width: calc(100vw - 2rem);
3333
3413
  padding: 0.75rem 0.875rem;
3334
3414
  border-radius: var(--radius-card);
@@ -3345,14 +3425,32 @@ body {
3345
3425
  .glim-toast-glyph--success { color: var(--status-ok-solid); }
3346
3426
  .glim-toast-glyph--warn { color: var(--status-warn-solid); }
3347
3427
  .glim-toast-glyph--fail { color: var(--status-fail-solid); }
3348
- .glim-toast-message {
3428
+ .glim-toast-body {
3429
+ display: flex;
3349
3430
  flex: 1 1 0%;
3431
+ flex-direction: column;
3432
+ gap: 0.5rem;
3350
3433
  min-width: 0;
3434
+ }
3435
+ .glim-toast-message {
3351
3436
  margin: 0;
3437
+ text-wrap: pretty;
3352
3438
  font-size: 0.875rem;
3353
3439
  line-height: 1.375;
3354
3440
  overflow-wrap: break-word;
3355
3441
  }
3442
+ /* A phone keeps the window in view: the newest two across its width. */
3443
+ @media (max-width: 600px) {
3444
+ .glim-toast-stack {
3445
+ inset-inline-start: 0;
3446
+ padding: 0.5rem;
3447
+ }
3448
+ .glim-toast-stack > :nth-last-child(n + 3) { display: none; }
3449
+ .glim-toast-card {
3450
+ width: 100%;
3451
+ max-width: none;
3452
+ }
3453
+ }
3356
3454
  .glim-toast-dismiss {
3357
3455
  flex-shrink: 0;
3358
3456
  margin: -0.25rem;
@@ -3527,7 +3625,12 @@ body {
3527
3625
  so two always fit a row. */
3528
3626
  .glim-sel--pairs { align-items: stretch; }
3529
3627
  @media (max-width: 600px) {
3530
- .glim-sel--pairs { --seg-basis: calc(50% - 0.25rem); }
3628
+ .glim-sel--pairs {
3629
+ --seg-basis: calc(50% - 0.25rem);
3630
+ width: 100%;
3631
+ }
3632
+ /* A pill around several rows bulges at its ends. */
3633
+ .glim-sel--pairs.glim-sel--well { border-radius: var(--radius-card); }
3531
3634
  }
3532
3635
 
3533
3636
  .glim-sel-seg {
@@ -3635,6 +3738,7 @@ body {
3635
3738
  .glim-sel-label--wrap {
3636
3739
  white-space: normal;
3637
3740
  text-wrap: balance;
3741
+ overflow-wrap: break-word;
3638
3742
  }
3639
3743
 
3640
3744
  /* Under a finger a 20px pill is too small to hit. A well is a row of
@@ -3650,7 +3754,8 @@ body {
3650
3754
  its glyph reads the fill's ink. */
3651
3755
  .glim-sel-seg.glim-hue-icon:not(.glim-active) > svg,
3652
3756
  .glim-sel-tile.glim-hue-icon:not(.glim-active) > svg,
3653
- .glim-nav-row:not(.glim-active) > svg {
3757
+ .glim-nav-row:not(.glim-active) > svg,
3758
+ .glim-nav-row:not(.glim-active) > .glim-nav-row-main > svg {
3654
3759
  color: var(--accent-glyph);
3655
3760
  }
3656
3761
 
@@ -3780,6 +3885,46 @@ body {
3780
3885
  .glim-nav-row:not(.glim-active):hover:dir(rtl) { translate: -2px 0; }
3781
3886
  }
3782
3887
  .glim-nav-row--centred { justify-content: center; }
3888
+ button.glim-nav-row {
3889
+ width: 100%;
3890
+ text-align: start;
3891
+ cursor: pointer;
3892
+ }
3893
+ /* A row with something beside its link. The link's press covers the row, and
3894
+ what stands in a slot lies above that press. The narrow rail has no room for
3895
+ a slot. */
3896
+ .glim-nav-row--slots { gap: 0.5rem; }
3897
+ /* A tree's row: each level steps in by the chevron's width, and the smaller
3898
+ glyph and gap leave a deep name its room. */
3899
+ .glim-nav-row--tree {
3900
+ gap: 0.5rem;
3901
+ padding-inline-start: calc(0.5rem + var(--nav-depth, 0) * 0.85rem);
3902
+ }
3903
+ .glim-nav-row--tree > .glim-nav-row-main { gap: 0.5rem; }
3904
+ .glim-nav-row-main {
3905
+ display: flex;
3906
+ flex: 1 1 auto;
3907
+ align-items: center;
3908
+ gap: 0.75rem;
3909
+ min-width: 0;
3910
+ text-align: start;
3911
+ text-decoration: none;
3912
+ cursor: pointer;
3913
+ }
3914
+ .glim-nav-row-main::after {
3915
+ content: "";
3916
+ position: absolute;
3917
+ inset: 0;
3918
+ border-radius: inherit;
3919
+ }
3920
+ .glim-nav-row-slot {
3921
+ position: relative;
3922
+ z-index: 1;
3923
+ display: inline-flex;
3924
+ flex: none;
3925
+ align-items: center;
3926
+ gap: 0.25rem;
3927
+ }
3783
3928
  .glim-nav-label {
3784
3929
  min-width: 0;
3785
3930
  overflow: hidden;
@@ -3929,8 +4074,12 @@ body {
3929
4074
  gap: 1rem;
3930
4075
  padding-top: 1.75rem;
3931
4076
  }
4077
+ /* The start text gives way first and wraps, so a button does not drop to the
4078
+ next line for a few pixels. */
3932
4079
  .glim-window-start {
3933
- margin-inline-end: auto;
4080
+ flex: 1 1 0;
4081
+ min-width: min(4rem, 100%);
4082
+ overflow-wrap: anywhere;
3934
4083
  font-size: var(--text-meta);
3935
4084
  color: var(--carbon-text-muted);
3936
4085
  }
@@ -4147,6 +4296,11 @@ body {
4147
4296
  /* The data pill (Pill): one value of a row, quiet at rest. A state takes a
4148
4297
  wash of its solid colour over the second surface, so the text keeps the
4149
4298
  body ink and a column of pills still reads as one column. */
4299
+ .glim-well,
4300
+ .glim-subcard {
4301
+ --pill-ground: var(--carbon-surface3);
4302
+ --pill-state: var(--carbon-hover-raised);
4303
+ }
4150
4304
  .glim-pill {
4151
4305
  display: inline-flex;
4152
4306
  flex: none;
@@ -4154,15 +4308,15 @@ body {
4154
4308
  height: 1.35rem;
4155
4309
  padding-inline: 0.55rem;
4156
4310
  border-radius: var(--radius-pill);
4157
- background-color: var(--carbon-surface2);
4311
+ background-color: var(--pill-ground, var(--carbon-surface2));
4158
4312
  font-size: var(--text-meta);
4159
4313
  color: var(--carbon-text-sub);
4160
4314
  white-space: nowrap;
4161
4315
  }
4162
- .glim-pill--state { background-color: var(--carbon-surface3); color: var(--carbon-text); }
4163
- .glim-pill--ok { background-color: color-mix(in srgb, var(--carbon-surface2), var(--status-ok-solid) 30%); color: var(--carbon-text); }
4164
- .glim-pill--warn { background-color: color-mix(in srgb, var(--carbon-surface2), var(--status-warn-solid) 35%); color: var(--carbon-text); }
4165
- .glim-pill--fail { background-color: color-mix(in srgb, var(--carbon-surface2), var(--status-fail-solid) 35%); color: var(--carbon-text); }
4316
+ .glim-pill--state { background-color: var(--pill-state, var(--carbon-surface3)); color: var(--carbon-text); }
4317
+ .glim-pill--ok { background-color: color-mix(in srgb, var(--pill-ground, var(--carbon-surface2)), var(--status-ok-solid) 30%); color: var(--carbon-text); }
4318
+ .glim-pill--warn { background-color: color-mix(in srgb, var(--pill-ground, var(--carbon-surface2)), var(--status-warn-solid) 35%); color: var(--carbon-text); }
4319
+ .glim-pill--fail { background-color: color-mix(in srgb, var(--pill-ground, var(--carbon-surface2)), var(--status-fail-solid) 35%); color: var(--carbon-text); }
4166
4320
 
4167
4321
  /* A text field (PasswordField, CopyField): borderless and filled (rule 14),
4168
4322
  a step up the ramp on focus and inside a sub-card, like the number field.
@@ -4179,8 +4333,12 @@ body {
4179
4333
  line-height: 1.25rem;
4180
4334
  }
4181
4335
  .glim-input:focus,
4182
- .glim-well .glim-input { background-color: var(--carbon-surface3); }
4183
- .glim-well .glim-input:focus { background-color: var(--carbon-hover-raised); }
4336
+ .glim-well .glim-input,
4337
+ .glim-subcard .glim-input { background-color: var(--carbon-surface3); }
4338
+ .glim-well .glim-num-field { background-color: var(--carbon-surface3); }
4339
+ .glim-well .glim-num-field:focus { background-color: var(--carbon-hover-raised); }
4340
+ .glim-well .glim-input:focus,
4341
+ .glim-subcard .glim-input:focus { background-color: var(--carbon-hover-raised); }
4184
4342
  .glim-input:disabled {
4185
4343
  opacity: 0.5;
4186
4344
  cursor: not-allowed;
@@ -4192,6 +4350,364 @@ body {
4192
4350
  text-overflow: ellipsis;
4193
4351
  }
4194
4352
 
4353
+ /* A few lines of text (TextArea): the field's look, grown by its corner. */
4354
+ .glim-input--area {
4355
+ display: block;
4356
+ height: auto;
4357
+ min-height: calc(var(--btn-h) * 2);
4358
+ padding-block: 0.4rem;
4359
+ resize: vertical;
4360
+ }
4361
+
4362
+ /* One labelled row (FieldRow, rule 35): the name at the start and its controls
4363
+ together at the end. The name's margin takes the free room, so a growing
4364
+ control gets all of it, and a control that drops under the name keeps the
4365
+ row's end through the justify. */
4366
+ .glim-field-row {
4367
+ display: flex;
4368
+ flex-wrap: wrap;
4369
+ align-items: center;
4370
+ justify-content: flex-end;
4371
+ gap: 0.35rem 0.75rem;
4372
+ min-height: var(--btn-h);
4373
+ }
4374
+ .glim-field-row-name {
4375
+ display: flex;
4376
+ flex: 0 1 auto;
4377
+ align-items: center;
4378
+ gap: 0.35rem;
4379
+ min-width: 0;
4380
+ margin-inline-end: auto;
4381
+ }
4382
+ .glim-field-row-label small {
4383
+ font-size: var(--text-meta);
4384
+ color: var(--carbon-text-muted);
4385
+ }
4386
+ .glim-field-row > :not(.glim-field-row-name) { max-width: 100%; }
4387
+
4388
+ .glim-field-row > .glim-slider {
4389
+ flex: 1 1 13rem;
4390
+ min-width: 0;
4391
+ }
4392
+ .glim-field-row > .glim-combobox {
4393
+ flex: 0 1 16rem;
4394
+ min-width: 0;
4395
+ }
4396
+
4397
+ /* Rows that share a column for their names (FieldRowGroup): a grid as wide as
4398
+ the longest name, handed down to each row. Their controls sit in one cell,
4399
+ and on a phone the rows go back to wrapping on their own. */
4400
+ .glim-field-row-group {
4401
+ display: grid;
4402
+ grid-template-columns: repeat(var(--field-columns, 1), max-content minmax(0, 1fr));
4403
+ gap: 0.5rem 1.5rem;
4404
+ }
4405
+ .glim-field-row-group > .glim-field-row {
4406
+ display: grid;
4407
+ grid-column: span 2;
4408
+ grid-template-columns: subgrid;
4409
+ column-gap: 0.75rem;
4410
+ }
4411
+ .glim-field-row-group > .glim-field-row--wide { grid-column: 1 / -1; }
4412
+ .glim-field-row--wide > .glim-field-row-controls { grid-column: 2 / -1; }
4413
+ .glim-field-row-controls {
4414
+ display: flex;
4415
+ align-items: center;
4416
+ justify-content: flex-end;
4417
+ gap: 0.35rem 0.75rem;
4418
+ min-width: 0;
4419
+ }
4420
+ @media (max-width: 600px) {
4421
+ .glim-field-row-group {
4422
+ display: flex;
4423
+ flex-direction: column;
4424
+ }
4425
+ .glim-field-row-group > .glim-field-row { display: flex; }
4426
+ .glim-field-row-controls { flex: 1 1 13rem; }
4427
+ }
4428
+
4429
+ /* A field with a glyph inside (GlyphField). The box is the field, so it takes
4430
+ the input's focus step, and what stands after the input sits in its room. */
4431
+ .glim-glyph-field {
4432
+ display: flex;
4433
+ align-items: center;
4434
+ gap: 0.4rem;
4435
+ padding-inline: 0.65rem 0.15rem;
4436
+ cursor: text;
4437
+ }
4438
+ .glim-glyph-field:focus-within { background-color: var(--carbon-surface3); }
4439
+ :is(.glim-well, .glim-subcard) .glim-glyph-field:focus-within { background-color: var(--carbon-hover-raised); }
4440
+ .glim-glyph-field-glyph {
4441
+ display: inline-grid;
4442
+ flex: none;
4443
+ width: 0.95rem;
4444
+ height: 0.95rem;
4445
+ color: var(--carbon-text-sub);
4446
+ }
4447
+ .glim-glyph-field-glyph > svg {
4448
+ width: 100%;
4449
+ height: 100%;
4450
+ }
4451
+ .glim-glyph-field-input {
4452
+ flex: 1;
4453
+ min-width: 0;
4454
+ height: 100%;
4455
+ text-overflow: ellipsis;
4456
+ }
4457
+ .glim-glyph-field-input::placeholder { color: var(--carbon-text-muted); }
4458
+ .glim-glyph-field > .glim-icon-btn {
4459
+ width: 1.6rem;
4460
+ height: 1.6rem;
4461
+ }
4462
+
4463
+ /* A small holder for a glyph (GlyphTile). It fills with the accent while the
4464
+ row, button or link around it is reached ("Lists"). */
4465
+ .glim-tile {
4466
+ display: inline-grid;
4467
+ flex: none;
4468
+ place-items: center;
4469
+ width: 1.75rem;
4470
+ height: 1.75rem;
4471
+ border-radius: var(--radius-control);
4472
+ color: var(--accent-glyph);
4473
+ }
4474
+ .glim-tile > svg {
4475
+ width: 1rem;
4476
+ height: 1rem;
4477
+ }
4478
+ .glim-tile--quiet { color: var(--carbon-text-sub); }
4479
+ .glim-tile--ground { background-color: var(--carbon-surface3); }
4480
+ .glim-tile--active,
4481
+ :is(button, a, .glim-list-row, .glim-nav-row):is(:hover, :focus-visible, :focus-within) > .glim-tile {
4482
+ background-color: var(--accent);
4483
+ color: var(--accent-contrast);
4484
+ }
4485
+
4486
+ /* A short list of facts in columns (Table). Hairlines at low opacity part the
4487
+ rows, and there is no box around them (rule 5). */
4488
+ .glim-table {
4489
+ width: 100%;
4490
+ border-collapse: collapse;
4491
+ font-size: var(--text-subline);
4492
+ }
4493
+ .glim-table :is(th, td) {
4494
+ padding: 0.4rem 0.5rem;
4495
+ text-align: start;
4496
+ white-space: nowrap;
4497
+ }
4498
+ .glim-table th {
4499
+ font-size: var(--text-meta);
4500
+ font-weight: 500;
4501
+ color: var(--carbon-text-muted);
4502
+ }
4503
+ .glim-table tr + tr td,
4504
+ .glim-table thead + tbody tr:first-child td {
4505
+ box-shadow: inset 0 1px 0 color-mix(in srgb, var(--carbon-text) 7%, transparent);
4506
+ }
4507
+ .glim-table :is(th, td).glim-table-end { text-align: end; }
4508
+ .glim-table td.glim-table-muted { color: var(--carbon-text-muted); }
4509
+ .glim-table td.glim-table-accent { color: var(--accent-ink); }
4510
+
4511
+ /* A path and its picker's button (PathField). */
4512
+ .glim-path {
4513
+ display: flex;
4514
+ flex: 1 1 13rem;
4515
+ align-items: center;
4516
+ gap: 0.4rem;
4517
+ min-width: 0;
4518
+ max-width: 30rem;
4519
+ }
4520
+ .glim-path > .glim-input { flex: 1; }
4521
+
4522
+ /* A hierarchy to walk through (Tree). Each level steps in by the chevron's
4523
+ width, and the chosen row is filled as a selection is (rule 3). */
4524
+ .glim-tree {
4525
+ display: flex;
4526
+ flex-direction: column;
4527
+ gap: 2px;
4528
+ min-width: 0;
4529
+ overflow: auto;
4530
+ }
4531
+ .glim-tree-row {
4532
+ display: flex;
4533
+ flex: none;
4534
+ align-items: center;
4535
+ gap: 0.3rem;
4536
+ height: var(--btn-h);
4537
+ padding-inline: calc(0.3rem + var(--tree-level, 0) * 0.85rem) 0.55rem;
4538
+ border-radius: var(--radius-control);
4539
+ font-size: var(--text-body);
4540
+ cursor: pointer;
4541
+ user-select: none;
4542
+ }
4543
+ .glim-tree-row:not([aria-selected="true"]):hover { background-color: var(--carbon-hover); }
4544
+ .glim-well .glim-tree-row:not([aria-selected="true"]):hover { background-color: var(--carbon-surface3); }
4545
+ .glim-tree-row[aria-selected="true"] {
4546
+ background-color: var(--accent);
4547
+ color: var(--accent-contrast);
4548
+ }
4549
+ .glim-tree-toggle {
4550
+ display: grid;
4551
+ flex: none;
4552
+ place-items: center;
4553
+ width: 1.1rem;
4554
+ height: 1.1rem;
4555
+ border-radius: var(--radius-control);
4556
+ color: var(--carbon-text-muted);
4557
+ }
4558
+ .glim-tree-toggle:hover {
4559
+ background-color: color-mix(in srgb, currentColor 18%, transparent);
4560
+ color: var(--carbon-text);
4561
+ }
4562
+ .glim-tree-toggle > svg {
4563
+ width: 0.7rem;
4564
+ height: 0.7rem;
4565
+ rotate: -90deg;
4566
+ }
4567
+ .glim-tree-toggle:dir(rtl) > svg { rotate: 90deg; }
4568
+ [aria-expanded="true"] > .glim-tree-toggle > svg { rotate: none; }
4569
+ .glim-tree-glyph {
4570
+ display: inline-grid;
4571
+ flex: none;
4572
+ width: 1.05rem;
4573
+ height: 1.05rem;
4574
+ color: var(--accent-glyph);
4575
+ }
4576
+ .glim-tree-glyph > svg {
4577
+ width: 100%;
4578
+ height: 100%;
4579
+ }
4580
+ [aria-selected="true"] > :is(.glim-tree-toggle, .glim-tree-glyph) { color: var(--accent-contrast); }
4581
+ .glim-tree-label {
4582
+ min-width: 0;
4583
+ overflow: hidden;
4584
+ text-overflow: ellipsis;
4585
+ white-space: nowrap;
4586
+ }
4587
+
4588
+ /* The round mark on a row picked for an action on many (RowMark, rule 39).
4589
+ The fill reads --accent, so a row with a rainbow position hands it its hue. */
4590
+ .glim-rowmark {
4591
+ display: grid;
4592
+ flex: none;
4593
+ place-items: center;
4594
+ width: 1.125rem;
4595
+ height: 1.125rem;
4596
+ border-radius: var(--radius-pill);
4597
+ background-color: color-mix(in srgb, var(--carbon-surface3) 60%, transparent);
4598
+ color: transparent;
4599
+ }
4600
+ .glim-rowmark > svg {
4601
+ width: 0.75rem;
4602
+ height: 0.75rem;
4603
+ }
4604
+ .glim-rowmark--on {
4605
+ background-color: var(--accent);
4606
+ color: var(--accent-contrast);
4607
+ }
4608
+
4609
+ /* One row of a list (ListRow). The buttons stand at their place while out of
4610
+ sight, so a hover moves nothing, and a row without hover shows them (rule 6). */
4611
+ .glim-list-row {
4612
+ display: flex;
4613
+ align-items: center;
4614
+ gap: 0.6rem;
4615
+ min-width: 0;
4616
+ min-height: 2.6rem;
4617
+ padding-inline: 0.5rem;
4618
+ border-radius: var(--radius-control);
4619
+ }
4620
+ .glim-list-row:is(:hover, :focus-within) { background-color: var(--carbon-hover); }
4621
+ .glim-list-row--opens { cursor: pointer; }
4622
+ .glim-list-row-tile {
4623
+ display: grid;
4624
+ flex: none;
4625
+ place-items: center;
4626
+ width: 1.75rem;
4627
+ height: 1.75rem;
4628
+ border-radius: var(--radius-control);
4629
+ color: var(--accent-glyph);
4630
+ }
4631
+ .glim-list-row-tile > svg {
4632
+ width: 1.05rem;
4633
+ height: 1.05rem;
4634
+ }
4635
+ .glim-list-row:is(:hover, :focus-within) > .glim-list-row-tile {
4636
+ background-color: var(--accent);
4637
+ color: var(--accent-contrast);
4638
+ }
4639
+ .glim-list-row-name {
4640
+ display: flex;
4641
+ flex: 1 1 0;
4642
+ align-items: center;
4643
+ gap: 0.4rem;
4644
+ min-width: 0;
4645
+ white-space: nowrap;
4646
+ }
4647
+ .glim-list-row-name--mono {
4648
+ font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
4649
+ font-size: var(--text-subline);
4650
+ }
4651
+ .glim-list-row-title {
4652
+ min-width: 0;
4653
+ overflow: hidden;
4654
+ text-overflow: ellipsis;
4655
+ }
4656
+ .glim-list-row-tip { display: contents; }
4657
+ .glim-list-row-badges {
4658
+ display: flex;
4659
+ flex: none;
4660
+ flex-wrap: wrap;
4661
+ align-items: center;
4662
+ justify-content: flex-end;
4663
+ gap: 0.35rem;
4664
+ }
4665
+ .glim-list-row-actions {
4666
+ display: flex;
4667
+ flex: none;
4668
+ gap: 0.15rem;
4669
+ opacity: 0;
4670
+ }
4671
+ .glim-list-row:is(:hover, :focus-within) > .glim-list-row-actions { opacity: 1; }
4672
+ @media (hover: none) {
4673
+ .glim-list-row-actions { opacity: 1; }
4674
+ }
4675
+ @media (max-width: 600px) {
4676
+ .glim-list-row-badges { display: none; }
4677
+ }
4678
+
4679
+ /* A glyph in a square in every label mode (IconButton), half its box as any
4680
+ glyph alone (rule 13). It has no ground, so .glim-btn-bare turns the glyph
4681
+ to the tint when it is reached (rule 40). The gap holds the place of a
4682
+ button a row does not have. */
4683
+ .glim-icon-btn {
4684
+ display: grid;
4685
+ flex: none;
4686
+ place-items: center;
4687
+ width: var(--btn-h);
4688
+ height: var(--btn-h);
4689
+ border-radius: var(--radius-control);
4690
+ color: var(--carbon-text-sub);
4691
+ cursor: pointer;
4692
+ }
4693
+ .glim-icon-btn > svg {
4694
+ width: 1rem;
4695
+ height: 1rem;
4696
+ }
4697
+ .glim-icon-btn:disabled {
4698
+ opacity: 0.45;
4699
+ cursor: default;
4700
+ }
4701
+ .glim-icon-btn[aria-pressed="true"] {
4702
+ background-color: var(--accent-soft);
4703
+ color: var(--accent-glyph);
4704
+ }
4705
+ .glim-icon-btn-gap {
4706
+ flex: none;
4707
+ width: var(--btn-h);
4708
+ height: var(--btn-h);
4709
+ }
4710
+
4195
4711
  /* The reveal eye ("The reveal eye" in the design language): a bare glyph in
4196
4712
  the field's trailing padding, neutral, and the wrapper as wide as a plain
4197
4713
  field. Edge draws an eye of its own into every password field, which
@@ -4234,6 +4750,28 @@ body {
4234
4750
  }
4235
4751
  .glim-copy > .glim-input { flex: 1; }
4236
4752
 
4753
+ /* A field's width (FieldWidth), on the input or on the wrapper that holds it.
4754
+ After the wrappers, whose own width it replaces. */
4755
+ .glim-field--short {
4756
+ flex: none;
4757
+ width: 5.5rem;
4758
+ }
4759
+ .glim-field--medium {
4760
+ flex: none;
4761
+ width: 12rem;
4762
+ max-width: 100%;
4763
+ }
4764
+ .glim-field--wide {
4765
+ flex: none;
4766
+ width: 22rem;
4767
+ max-width: 100%;
4768
+ }
4769
+ .glim-field--grow {
4770
+ flex: 1 1 13rem;
4771
+ width: auto;
4772
+ min-width: 0;
4773
+ }
4774
+
4237
4775
  /* Words for a screen reader alone, such as the "Copied" a copy button
4238
4776
  announces. */
4239
4777
  .glim-sr-only {
@@ -4339,9 +4877,36 @@ body {
4339
4877
  /* Outside the layer, or the house ring's offset would win: inside a menu the
4340
4878
  ring is drawn on the row, where the menu's edge cannot clip it. */
4341
4879
  .glim-menu-item:focus-visible,
4342
- .glim-menu-head-edit:focus-visible { outline-offset: -2px; }
4880
+ .glim-menu-head-edit:focus-visible,
4881
+ .glim-tree-row:focus-visible,
4882
+ .glim-list-row:focus-visible { outline-offset: -2px; }
4343
4883
  .glim-menu[data-pointer] :focus-visible { outline: none; }
4884
+ /* Also outside the layer: a field says it has the focus with its fill and its
4885
+ caret, not with a ring (rule 14). */
4886
+ .glim-input:focus-visible,
4887
+ .glim-num-field:focus-visible,
4888
+ .glim-glyph-field-input:focus-visible { outline: none; }
4889
+ /* The ring of a row with slots goes around the row, not around its link alone. */
4890
+ .glim-nav-row-main:focus-visible { outline: none; }
4891
+ .glim-nav-row--slots:has(> .glim-nav-row-main:focus-visible) {
4892
+ outline: 2px solid var(--focus-ring);
4893
+ outline-offset: 2px;
4894
+ }
4895
+ /* An app's own rule for links outranks the layer, so a link with a button's look
4896
+ takes its colour back from there. */
4897
+ a:is(.glim-btn, .glim-icon-btn) {
4898
+ color: revert-layer;
4899
+ text-decoration: none;
4900
+ }
4901
+ /* Under the message, at its end. The stage's floor is for a row of buttons. */
4902
+ .glim-toast-action {
4903
+ align-self: flex-end;
4904
+ min-width: 0;
4905
+ }
4344
4906
 
4345
4907
  @media (prefers-reduced-motion: no-preference) {
4346
- .glim-checkbox-box { transition: background-color var(--motion-control-dur) ease; }
4908
+ .glim-checkbox-box,
4909
+ .glim-rowmark { transition: background-color var(--motion-control-dur) ease; }
4910
+ .glim-list-row-actions { transition: opacity var(--motion-control-dur) ease; }
4911
+ .glim-tree-toggle > svg { transition: rotate var(--motion-control-dur) ease; }
4347
4912
  }