altitude-design 0.2.0 → 0.2.1

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.
@@ -298,16 +298,19 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
298
298
  transition: box-shadow var(--dur-travel-1) var(--ease-rise), background-color var(--dur-travel-1) var(--ease-rise),
299
299
  color var(--dur-travel-1) var(--ease-rise), transform var(--dur-travel-2) var(--ease-settle);
300
300
  }
301
- /* Focus ring: wraps the whole slab, face and wall, so it follows the button's real shape at every height. */
301
+ /* Focus ring: wraps the whole slab, face and wall, so it follows the button's real shape at every height. It's an
302
+ outline 2px off a box that covers the face and the wall, because an outline never adds to the page's scrollable
303
+ area: a button at the edge of a narrow screen must not give the page a sideways scroll. At rest the box is the face
304
+ alone, so only a keyboard-focused button's wall (the box's bottom edge, at most 5px) reaches past its own box. */
302
305
  .alt-btn:focus-visible, .alt-crumbs button:focus-visible { outline: none; }
303
306
  .alt-btn::after, .alt-crumbs button::after {
304
307
  content: ""; position: absolute; pointer-events: none; opacity: 0;
305
- inset: -4px -4px calc(-4px - var(--wall, 0px) - 1px) -4px; border: 2px solid var(--accent-ink); border-radius: calc(var(--radius-md) + 4px);
306
- transition: opacity var(--dur-travel-1) var(--ease-rise), inset var(--dur-press) ease-out;
308
+ inset: 0; outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--radius-md);
309
+ transition: opacity var(--dur-travel-1) var(--ease-rise), bottom var(--dur-press) ease-out;
307
310
  }
308
- .alt-btn[data-size="sm"]::after, .alt-crumbs button::after { border-radius: calc(var(--radius-sm) + 4px); }
309
- .alt-btn:focus-visible::after, .alt-crumbs button:focus-visible::after { opacity: 1; }
310
- .alt-btn:active::after, .alt-btn[data-loading]::after { inset: -4px; }
311
+ .alt-btn[data-size="sm"]::after, .alt-crumbs button::after { border-radius: var(--radius-sm); }
312
+ .alt-btn:focus-visible::after, .alt-crumbs button:focus-visible::after { opacity: 1; bottom: calc(-1 * var(--wall, 0px) - 1px); }
313
+ .alt-btn:active::after, .alt-btn[data-loading]::after { bottom: 0; }
311
314
  .alt-btn-label { display: inline-flex; align-items: center; gap: var(--space-2); transition: opacity var(--dur-travel-1) var(--ease-rise); }
312
315
  .alt-btn-label > svg { flex: none; margin-inline: -2px; }
313
316
  .alt-btn[data-variant="primary"] {
@@ -703,7 +706,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
703
706
  .alt-avatar[data-presence] .alt-avatar-face { outline: 2px solid var(--alt-presence); outline-offset: 1px; }
704
707
  .alt-avatar-lamp {
705
708
  --b-lamp: var(--positive);
706
- position: absolute; z-index: 2; right: -1px; bottom: -1px; width: max(8px, calc(var(--size) * 0.24)); height: max(8px, calc(var(--size) * 0.24)); border-radius: var(--radius-full);
709
+ position: absolute; z-index: 2; right: 0; bottom: 0; width: max(8px, calc(var(--size) * 0.24)); height: max(8px, calc(var(--size) * 0.24)); border-radius: var(--radius-full);
707
710
  background: var(--b-lamp); box-shadow: 0 0 0 2px var(--surface-ground); transform: translateY(calc(-1 * var(--lift)));
708
711
  }
709
712
  .alt-avatar[data-status="idle"] .alt-avatar-lamp { --b-lamp: var(--caution); }
@@ -711,15 +714,18 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
711
714
  .alt-avatar[data-interactive] { cursor: pointer; outline: none; }
712
715
  .alt-avatar[data-interactive]:focus-visible .alt-avatar-face { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
713
716
  .alt-avatar-group { isolation: isolate; display: inline-flex; align-items: flex-end; padding: calc(var(--size, 36px) * 0.32) 0 calc(var(--size, 36px) * 0.1); }
714
- .alt-avatar-group > .alt-avatar { isolation: auto; margin-left: calc(var(--size) * -0.22); }
715
- .alt-avatar-group > .alt-avatar:first-child { margin-left: 0; }
717
+ .alt-avatar-group .alt-avatar { isolation: auto; }
718
+ /* Faces overlap by a fifth. An avatar that waited for its data sits inside its loading wrapper (display: contents at
719
+ rest), so the overlap is counted from the group's own children either way, imprints included. */
720
+ .alt-avatar-group > .alt-avatar:not(:first-child),
721
+ .alt-avatar-group > :is(.alt-await, .alt-await-wait):not(:first-child) :is(.alt-avatar, .alt-avatar-imprint) { margin-left: calc(var(--size, 36px) * -0.22); }
716
722
  /* Overlapping faces are cut out of each other with a ring of ground; higher faces sit in front. */
717
- .alt-avatar-group > .alt-avatar .alt-avatar-face { box-shadow: 0 0 0 2px var(--surface-ground), inset 0 0 0 1px var(--alt-ring); }
718
- .alt-avatar-group > .alt-avatar[data-status="offline"] .alt-avatar-face { box-shadow: 0 0 0 2px var(--surface-ground), inset 0 0 0 1px var(--alt-ring), inset 0 1px 2px var(--alt-cast); }
719
- .alt-avatar-group > .alt-avatar[data-status="active"] .alt-avatar-face { z-index: 3; }
720
- .alt-avatar-group > .alt-avatar[data-status="idle"] .alt-avatar-face { z-index: 2; }
721
- .alt-avatar-group > .alt-avatar:not([data-tint="more"]):hover { --hr: 0.32; }
722
- .alt-avatar-group > .alt-avatar:not([data-tint="more"]):hover .alt-avatar-face { z-index: 5; }
723
+ .alt-avatar-group .alt-avatar .alt-avatar-face { box-shadow: 0 0 0 2px var(--surface-ground), inset 0 0 0 1px var(--alt-ring); }
724
+ .alt-avatar-group .alt-avatar[data-status="offline"] .alt-avatar-face { box-shadow: 0 0 0 2px var(--surface-ground), inset 0 0 0 1px var(--alt-ring), inset 0 1px 2px var(--alt-cast); }
725
+ .alt-avatar-group .alt-avatar[data-status="active"] .alt-avatar-face { z-index: 3; }
726
+ .alt-avatar-group .alt-avatar[data-status="idle"] .alt-avatar-face { z-index: 2; }
727
+ .alt-avatar-group .alt-avatar:not([data-tint="more"]):hover { --hr: 0.32; }
728
+ .alt-avatar-group .alt-avatar:not([data-tint="more"]):hover .alt-avatar-face { z-index: 5; }
723
729
  .alt-avatar-group .alt-avatar-lamp { display: none; }
724
730
 
725
731
  /* ── Skeleton: an imprint of the content to come, pressed into the page. Light passes over it while it waits; when the
@@ -743,6 +749,38 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
743
749
  }
744
750
  @keyframes alt-rise-out { from { transform: translateY(calc(100% * var(--alt-travel))); opacity: 0.25; } }
745
751
 
752
+ /* ── Suspense: imprints that come with the component. A component given a promise (Avatar's name or src, a Badge's or
753
+ a card title's text, a DataTable's rows) holds its own Suspense boundary, and its fallback is its own imprint, drawn
754
+ from the same props and tokens, so it's the size and shape of what's coming. Card and CardContent hold one for
755
+ anything of yours that suspends inside them. An imprint waits dur-grace before it shows, so quick loads show nothing;
756
+ when content lands it rises 8px out of its imprint as the imprint fills in behind it, within the attention budget.
757
+ Containers just let their content rise. In a RevealGroup, content that lands early stays an imprint until the rest
758
+ arrive. At rest the wrapper is display: contents: it adds nothing to layout or to your selectors' parent chain. ── */
759
+ .alt-await-wait { display: block; animation: alt-onset var(--dur-travel-1) ease-out var(--dur-grace) backwards; }
760
+ .alt-await-wait[data-inline] { display: inline-block; vertical-align: middle; line-height: 0; }
761
+ .alt-await, .alt-await > .alt-await-face { display: contents; }
762
+ .alt-await[data-phase] { display: block; position: relative; isolation: isolate; }
763
+ .alt-await[data-phase] > .alt-await-face { display: block; }
764
+ .alt-await[data-phase][data-inline], .alt-await[data-phase][data-inline] > .alt-await-face { display: inline-block; vertical-align: middle; line-height: 0; }
765
+ .alt-await-imprint { position: absolute; inset: 0; z-index: -1; }
766
+ .alt-await[data-phase="held"] > .alt-await-face { position: absolute; inset: 0 0 auto; visibility: hidden; }
767
+ .alt-await-held { display: block; }
768
+ .alt-await-held[data-early] { animation: alt-onset var(--dur-travel-1) ease-out var(--alt-grace-left, 0ms) backwards; }
769
+ .alt-await[data-inline] > .alt-await-held { display: inline-block; vertical-align: middle; line-height: 0; }
770
+ .alt-await[data-phase="reveal"] > .alt-await-face { --alt-from-y: 8px; animation: alt-emerge var(--dur-travel-3) var(--ease-rise) var(--alt-rise-delay, 0ms) backwards; }
771
+ .alt-await[data-phase="reveal"] > .alt-await-imprint { animation: alt-await-fill var(--dur-travel-3) linear var(--alt-rise-delay, 0ms) forwards; }
772
+ @keyframes alt-await-fill { to { opacity: 0; } }
773
+ /* Text imprints are as tall as the text they stand for: one line-height of the surrounding type per line. */
774
+ .alt-await-lines { display: flex; flex-direction: column; justify-content: space-around; height: calc(var(--lines, 1) * 1lh); max-width: 100%; }
775
+ .alt-await-lines > .alt-skeleton { flex: none; height: 0.6em; }
776
+ .alt-await-card { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); }
777
+ .alt-await-card-body { font: 400 15px/23px var(--font-sans); }
778
+ .alt-avatar-imprint { display: inline-block; vertical-align: middle; }
779
+ .alt-badge-imprint { display: inline-block; vertical-align: middle; width: 64px; height: 22px; }
780
+ .alt-badge-imprint[data-variant="count"] { width: 22px; border-radius: var(--radius-full); }
781
+ .alt-check-imprint { display: block; width: 22px; height: 22px; border-radius: var(--radius-xs); }
782
+ .alt-cell-imprint { display: block; height: 0.6em; margin: calc((1lh - 0.6em) / 2) 0; }
783
+
746
784
  /* ── Alert: a sheet laid on the page. Status that informs is a thin tinted sheet resting on the ground — you can see
747
785
  its paper edge and the contact shadow under it — and it never floats. Status that asks you to act is a raised
748
786
  card. Dismissing lifts the sheet off the page and the gap closes behind it. Several alerts make an AlertStack:
@@ -940,6 +978,9 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
940
978
  }
941
979
  .alt-dialog-head { display: flex; flex-direction: column; gap: 6px; padding-right: var(--space-8); }
942
980
  .alt-dialog-title { margin: 0; font: 600 19px/26px var(--font-sans); letter-spacing: -0.01em; }
981
+ /* A title that takes focus (initialFocus="title") isn't a control: no ring after a click, the usual ring from the keyboard. */
982
+ .alt-dialog-title:focus { outline: none; }
983
+ .alt-dialog-title:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--radius-xs); }
943
984
  .alt-dialog-desc { margin: 0; font: 400 14px/21px var(--font-sans); color: var(--ink-muted); }
944
985
  .alt-dialog-body { margin-top: var(--space-5); font: 400 15px/23px var(--font-sans); }
945
986
  .alt-dialog-footer { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-6); }
@@ -1629,6 +1670,7 @@ body { margin: 0; background: var(--surface-ground); color: var(--ink); font-fam
1629
1670
  /* Forced colours (Windows High Contrast) drop every box-shadow, which erases elevation entirely
1630
1671
  (measured: the ladder renders as blank). Height falls back to border weight and style. */
1631
1672
  @media (forced-colors: active) {
1673
+ .alt-skeleton { outline: 1px dashed CanvasText; outline-offset: -1px; }
1632
1674
  .alt-surface[data-level="1"], .alt-surface[data-level="2"], .alt-card, .alt-btn:not([data-variant="ghost"]), .alt-tabs-indicator,
1633
1675
  .alt-switch-thumb, .alt-slider-thumb, .alt-kbd, .alt-avatar-face, .alt-sort-item, .alt-pile-top, .alt-pile-sheet, .alt-scroll { border: 1px solid CanvasText; }
1634
1676
  .alt-surface[data-level="3"], .alt-surface[data-level="4"], .alt-pop, .alt-tip, .alt-toast, .alt-sort-item[data-held], [data-held] { border: 2px solid CanvasText; }