@d4gger/design-system 4.0.0 → 4.0.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.
Files changed (147) hide show
  1. package/dist/brand-effects.css +5 -3
  2. package/dist/components/alert/Alert.cjs +17 -3
  3. package/dist/components/alert/Alert.cjs.map +2 -2
  4. package/dist/components/alert/Alert.d.cts +6 -0
  5. package/dist/components/alert/Alert.d.cts.map +1 -1
  6. package/dist/components/alert/Alert.d.mts +6 -0
  7. package/dist/components/alert/Alert.d.mts.map +1 -1
  8. package/dist/components/alert/Alert.d.ts +6 -0
  9. package/dist/components/alert/Alert.d.ts.map +1 -1
  10. package/dist/components/alert/Alert.js +17 -3
  11. package/dist/components/alert/Alert.js.map +2 -2
  12. package/dist/components/alert.css +62 -5
  13. package/dist/components/bottom-dock.css +8 -0
  14. package/dist/components/bottomDock/BottomDock.cjs +1 -1
  15. package/dist/components/bottomDock/BottomDock.cjs.map +2 -2
  16. package/dist/components/bottomDock/BottomDock.js +1 -1
  17. package/dist/components/bottomDock/BottomDock.js.map +2 -2
  18. package/dist/components/bottomDock/bottomDock.props.cjs +1 -1
  19. package/dist/components/bottomDock/bottomDock.props.cjs.map +2 -2
  20. package/dist/components/bottomDock/bottomDock.props.d.cts +2 -2
  21. package/dist/components/bottomDock/bottomDock.props.d.cts.map +1 -1
  22. package/dist/components/bottomDock/bottomDock.props.d.mts +2 -2
  23. package/dist/components/bottomDock/bottomDock.props.d.mts.map +1 -1
  24. package/dist/components/bottomDock/bottomDock.props.d.ts +2 -2
  25. package/dist/components/bottomDock/bottomDock.props.d.ts.map +1 -1
  26. package/dist/components/bottomDock/bottomDock.props.js +1 -1
  27. package/dist/components/bottomDock/bottomDock.props.js.map +2 -2
  28. package/dist/components/card.css +1 -1
  29. package/dist/components/collapsible/Collapsible.cjs +10 -0
  30. package/dist/components/collapsible/Collapsible.cjs.map +2 -2
  31. package/dist/components/collapsible/Collapsible.d.cts.map +1 -1
  32. package/dist/components/collapsible/Collapsible.d.mts.map +1 -1
  33. package/dist/components/collapsible/Collapsible.d.ts.map +1 -1
  34. package/dist/components/collapsible/Collapsible.js +10 -0
  35. package/dist/components/collapsible/Collapsible.js.map +2 -2
  36. package/dist/components/dialog.css +1 -1
  37. package/dist/components/environment-ribbon.css +73 -0
  38. package/dist/components/environmentRibbon/EnvironmentRibbon.cjs +69 -0
  39. package/dist/components/environmentRibbon/EnvironmentRibbon.cjs.map +7 -0
  40. package/dist/components/environmentRibbon/EnvironmentRibbon.d.cts +15 -0
  41. package/dist/components/environmentRibbon/EnvironmentRibbon.d.cts.map +1 -0
  42. package/dist/components/environmentRibbon/EnvironmentRibbon.d.mts +15 -0
  43. package/dist/components/environmentRibbon/EnvironmentRibbon.d.mts.map +1 -0
  44. package/dist/components/environmentRibbon/EnvironmentRibbon.d.ts +15 -0
  45. package/dist/components/environmentRibbon/EnvironmentRibbon.d.ts.map +1 -0
  46. package/dist/components/environmentRibbon/EnvironmentRibbon.js +39 -0
  47. package/dist/components/environmentRibbon/EnvironmentRibbon.js.map +7 -0
  48. package/dist/components/environmentRibbon/environmentRibbon.props.cjs +39 -0
  49. package/dist/components/environmentRibbon/environmentRibbon.props.cjs.map +7 -0
  50. package/dist/components/environmentRibbon/environmentRibbon.props.d.cts +18 -0
  51. package/dist/components/environmentRibbon/environmentRibbon.props.d.cts.map +1 -0
  52. package/dist/components/environmentRibbon/environmentRibbon.props.d.mts +18 -0
  53. package/dist/components/environmentRibbon/environmentRibbon.props.d.mts.map +1 -0
  54. package/dist/components/environmentRibbon/environmentRibbon.props.d.ts +18 -0
  55. package/dist/components/environmentRibbon/environmentRibbon.props.d.ts.map +1 -0
  56. package/dist/components/environmentRibbon/environmentRibbon.props.js +19 -0
  57. package/dist/components/environmentRibbon/environmentRibbon.props.js.map +7 -0
  58. package/dist/components/field.css +5 -0
  59. package/dist/components/icon/Icon.cjs +16 -0
  60. package/dist/components/icon/Icon.cjs.map +2 -2
  61. package/dist/components/icon/Icon.d.cts +25 -1
  62. package/dist/components/icon/Icon.d.cts.map +1 -1
  63. package/dist/components/icon/Icon.d.mts +25 -1
  64. package/dist/components/icon/Icon.d.mts.map +1 -1
  65. package/dist/components/icon/Icon.d.ts +25 -1
  66. package/dist/components/icon/Icon.d.ts.map +1 -1
  67. package/dist/components/icon/Icon.js +21 -0
  68. package/dist/components/icon/Icon.js.map +2 -2
  69. package/dist/components/icon/icons.cjs +12 -0
  70. package/dist/components/icon/icons.cjs.map +2 -2
  71. package/dist/components/icon/icons.d.cts +30 -0
  72. package/dist/components/icon/icons.d.cts.map +1 -1
  73. package/dist/components/icon/icons.d.mts +30 -0
  74. package/dist/components/icon/icons.d.mts.map +1 -1
  75. package/dist/components/icon/icons.d.ts +30 -0
  76. package/dist/components/icon/icons.d.ts.map +1 -1
  77. package/dist/components/icon/icons.js +12 -0
  78. package/dist/components/icon/icons.js.map +2 -2
  79. package/dist/components/index.cjs +1 -0
  80. package/dist/components/index.cjs.map +2 -2
  81. package/dist/components/index.d.cts +1 -0
  82. package/dist/components/index.d.cts.map +1 -1
  83. package/dist/components/index.d.mts +1 -0
  84. package/dist/components/index.d.mts.map +1 -1
  85. package/dist/components/index.d.ts +1 -0
  86. package/dist/components/index.d.ts.map +1 -1
  87. package/dist/components/index.js +1 -0
  88. package/dist/components/index.js.map +2 -2
  89. package/dist/components/notification-center.css +17 -9
  90. package/dist/components/notificationCenter/NotificationCenter.cjs +7 -2
  91. package/dist/components/notificationCenter/NotificationCenter.cjs.map +2 -2
  92. package/dist/components/notificationCenter/NotificationCenter.d.cts +2 -0
  93. package/dist/components/notificationCenter/NotificationCenter.d.cts.map +1 -1
  94. package/dist/components/notificationCenter/NotificationCenter.d.mts +2 -0
  95. package/dist/components/notificationCenter/NotificationCenter.d.mts.map +1 -1
  96. package/dist/components/notificationCenter/NotificationCenter.d.ts +2 -0
  97. package/dist/components/notificationCenter/NotificationCenter.d.ts.map +1 -1
  98. package/dist/components/notificationCenter/NotificationCenter.js +7 -2
  99. package/dist/components/notificationCenter/NotificationCenter.js.map +2 -2
  100. package/dist/components/page-header.css +21 -0
  101. package/dist/components/pageHeader/PageHeader.cjs +10 -2
  102. package/dist/components/pageHeader/PageHeader.cjs.map +2 -2
  103. package/dist/components/pageHeader/PageHeader.d.cts.map +1 -1
  104. package/dist/components/pageHeader/PageHeader.d.mts.map +1 -1
  105. package/dist/components/pageHeader/PageHeader.d.ts.map +1 -1
  106. package/dist/components/pageHeader/PageHeader.js +10 -2
  107. package/dist/components/pageHeader/PageHeader.js.map +2 -2
  108. package/dist/components/pageHeader/pageHeader.props.cjs +7 -1
  109. package/dist/components/pageHeader/pageHeader.props.cjs.map +2 -2
  110. package/dist/components/pageHeader/pageHeader.props.d.cts +5 -0
  111. package/dist/components/pageHeader/pageHeader.props.d.cts.map +1 -1
  112. package/dist/components/pageHeader/pageHeader.props.d.mts +5 -0
  113. package/dist/components/pageHeader/pageHeader.props.d.mts.map +1 -1
  114. package/dist/components/pageHeader/pageHeader.props.d.ts +5 -0
  115. package/dist/components/pageHeader/pageHeader.props.d.ts.map +1 -1
  116. package/dist/components/pageHeader/pageHeader.props.js +7 -1
  117. package/dist/components/pageHeader/pageHeader.props.js.map +2 -2
  118. package/dist/components/sidebar/Sidebar.cjs +6 -2
  119. package/dist/components/sidebar/Sidebar.cjs.map +2 -2
  120. package/dist/components/sidebar/Sidebar.d.cts +8 -0
  121. package/dist/components/sidebar/Sidebar.d.cts.map +1 -1
  122. package/dist/components/sidebar/Sidebar.d.mts +8 -0
  123. package/dist/components/sidebar/Sidebar.d.mts.map +1 -1
  124. package/dist/components/sidebar/Sidebar.d.ts +8 -0
  125. package/dist/components/sidebar/Sidebar.d.ts.map +1 -1
  126. package/dist/components/sidebar/Sidebar.js +6 -2
  127. package/dist/components/sidebar/Sidebar.js.map +2 -2
  128. package/dist/components/sidebar.css +58 -13
  129. package/dist/components.css +1 -0
  130. package/dist/docs/README.md +1 -0
  131. package/dist/docs/component-contract.md +5 -3
  132. package/dist/docs/component-index.json +9 -1
  133. package/dist/docs/components/alert.md +28 -8
  134. package/dist/docs/components/bottom-dock.md +4 -4
  135. package/dist/docs/components/card.md +1 -1
  136. package/dist/docs/components/collapsible.md +5 -0
  137. package/dist/docs/components/dialog.md +1 -1
  138. package/dist/docs/components/environment-ribbon.md +126 -0
  139. package/dist/docs/components/field.md +2 -0
  140. package/dist/docs/components/icon.md +1 -0
  141. package/dist/docs/components/notification-center.md +14 -9
  142. package/dist/docs/components/page-header.md +18 -6
  143. package/dist/docs/components/sidebar.md +17 -6
  144. package/dist/docs/consumer-guide.md +11 -4
  145. package/dist/styles.css +270 -30
  146. package/dist/tokens.css +2 -0
  147. package/package.json +1 -1
package/dist/styles.css CHANGED
@@ -302,6 +302,7 @@ svg {
302
302
  --dsx-effect-pressed-primary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 16%, transparent);
303
303
  --dsx-effect-hover-secondary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 3%, transparent);
304
304
  --dsx-effect-hover-ghost: color-mix(in srgb, var(--dsx-color-accent-rift-9) 6%, transparent);
305
+ --dsx-effect-ambient-strength: 10%;
305
306
  --dsx-sheet-min-height: 80vh;
306
307
  --dsx-sheet-width: 380px;
307
308
  --dsx-sheet-width-sm: 320px;
@@ -804,6 +805,7 @@ svg {
804
805
  --dsx-effect-pressed-primary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 12%, transparent);
805
806
  --dsx-effect-hover-secondary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 3%, transparent);
806
807
  --dsx-effect-hover-ghost: color-mix(in srgb, var(--dsx-color-accent-rift-9) 6%, transparent);
808
+ --dsx-effect-ambient-strength: 16%;
807
809
  --dsx-_accent-rift-primary: var(--dsx-color-accent-rift-9);
808
810
  --dsx-_accent-rift-primary-container: var(--dsx-color-accent-rift-4);
809
811
  --dsx-_accent-rift-on-primary: #ffffff;
@@ -2705,6 +2707,15 @@ textarea {
2705
2707
  padding: var(--dsx-space-xs) var(--dsx-space-sm);
2706
2708
  }
2707
2709
 
2710
+ /* labels="none": icon-only items share the width. The label stays in the accessible name; the active
2711
+ item is carried by its container fill and aria-current. For widths where five labels do not fit. */
2712
+
2713
+ .dsx-bottom-dock:where([data-labels='none']) .dsx-bottom-dock__item {
2714
+ flex: 1 1 0;
2715
+ gap: 0;
2716
+ padding: 0;
2717
+ }
2718
+
2708
2719
  .dsx-bottom-dock__icon {
2709
2720
  position: relative;
2710
2721
  display: inline-flex;
@@ -3475,8 +3486,10 @@ textarea {
3475
3486
  inset 0 0 0 1px var(--dsx-effect-panel-glass-rim),
3476
3487
  var(--dsx-shadow-panel-shadow);
3477
3488
  border-color: var(--dsx-effect-panel-glass-border);
3478
- backdrop-filter: var(--dsx-effect-panel-glass-backdrop-filter);
3489
+ /* Prefixed first, standard last: Next/Turbopack drops the standard line when it comes first and
3490
+ the prefixed one second (Chromium does not alias the prefix), which left glass with no blur. */
3479
3491
  -webkit-backdrop-filter: var(--dsx-effect-panel-glass-backdrop-filter);
3492
+ backdrop-filter: var(--dsx-effect-panel-glass-backdrop-filter);
3480
3493
  }
3481
3494
 
3482
3495
  [data-dsx-theme='light'] .dsx-card[data-variant='glass']:where(:not([data-active='true'], [data-selected='true'], [aria-selected='true'], [data-state='active'], [data-state='selected'], [data-high-contrast='true'])) {
@@ -3569,8 +3582,8 @@ textarea {
3569
3582
  .dsx-card.dsx-card--outline {
3570
3583
  background: transparent;
3571
3584
  box-shadow: none;
3572
- backdrop-filter: var(--dsx-effect-panel-solid-backdrop-filter);
3573
3585
  -webkit-backdrop-filter: var(--dsx-effect-panel-solid-backdrop-filter);
3586
+ backdrop-filter: var(--dsx-effect-panel-solid-backdrop-filter);
3574
3587
  }
3575
3588
 
3576
3589
  .dsx-card__header {
@@ -5112,6 +5125,82 @@ textarea {
5112
5125
  color: var(--dsx-color-tertiary);
5113
5126
  }
5114
5127
 
5128
+ /* EnvironmentRibbon — a floating warning pill for a non-production environment, with a warm haze behind the top edge.
5129
+ Tokens only. No breakpoints, no motion: the app renders it only off production, and the frame reserves the row it takes on a phone. */
5130
+
5131
+ .dsx-environment-ribbon {
5132
+ position: fixed;
5133
+ inset-block-start: var(--dsx-space-sm);
5134
+ inset-inline: var(--dsx-space-3);
5135
+ z-index: var(--dsx-z-index-fixed);
5136
+ box-sizing: border-box;
5137
+ /* Centred between the two insets with logical properties, so it holds in right-to-left. `fit-content` alone does not cap the width:
5138
+ with `white-space: nowrap` the min-content width equals the max-content width, so the pill would refuse to shrink and spill past
5139
+ the gutter (browser proof, 320px frame). The cap is the containing block less the two gutters, so a long label truncates. */
5140
+ inline-size: fit-content;
5141
+ max-inline-size: calc(100% - 2 * var(--dsx-space-3));
5142
+ margin-inline: auto;
5143
+ min-block-size: var(--dsx-size-md);
5144
+ padding-block: var(--dsx-space-xs);
5145
+ padding-inline: var(--dsx-space-sm);
5146
+ /* The Badge warning recipe (container tint + on-container ink), the look passed in the vision, with a hairline warning edge: it keeps
5147
+ the pill's outline over the haze and under forced colors, where fills are dropped. */
5148
+ border: var(--dsx-border-width-sm) solid var(--dsx-color-warning);
5149
+ border-radius: var(--dsx-rounded-full);
5150
+ background: var(--dsx-color-warning-container);
5151
+ color: var(--dsx-color-on-warning-container);
5152
+ box-shadow: var(--dsx-panel-shadow);
5153
+ font-family: var(--dsx-typography-label-small-font-family), sans-serif;
5154
+ font-size: var(--dsx-typography-label-small-font-size);
5155
+ font-weight: var(--dsx-typography-label-small-font-weight);
5156
+ line-height: var(--dsx-typography-label-small-line-height);
5157
+ letter-spacing: var(--dsx-typography-label-small-letter-spacing);
5158
+ text-transform: var(--dsx-typography-label-small-text-transform);
5159
+ text-decoration: var(--dsx-typography-label-small-text-decoration);
5160
+ text-align: center;
5161
+ /* One short label, one line: a label wider than the room truncates instead of wrapping the pill into a block. */
5162
+ white-space: nowrap;
5163
+ overflow: hidden;
5164
+ text-overflow: ellipsis;
5165
+ /* A status label never blocks what is under it. */
5166
+ pointer-events: none;
5167
+ }
5168
+
5169
+ .dsx-environment-ribbon:where([data-placement='contained']) {
5170
+ position: absolute;
5171
+ }
5172
+
5173
+ /* The haze: a warm radial wash anchored at the top centre, painted behind the chrome. z-index -1 puts it over the background of an
5174
+ isolated ancestor (`isolation: isolate`, as `.dsx-brand-ambient` is) and under in-flow content and glass panels, so a panel's blur
5175
+ softens it and its text never sits under a warm veil. The strength is a multiple of the theme-aware accent-haze strength, so light,
5176
+ whose strength is raised, keeps the same proportions as dark (the multiple is written inline, as Sidebar's ambient wash does: a local
5177
+ `--dsx-*` custom property in a component stylesheet is flagged by qa:tokens as a token that does not exist). */
5178
+
5179
+ .dsx-environment-ribbon__haze {
5180
+ position: fixed;
5181
+ inset: 0;
5182
+ z-index: -1;
5183
+ pointer-events: none;
5184
+ background-image: radial-gradient(
5185
+ ellipse 40rem 15rem at 50% -3.75rem,
5186
+ color-mix(in srgb, var(--dsx-color-warning) calc(var(--dsx-effect-ambient-strength) * 3.2), transparent),
5187
+ transparent 72%
5188
+ );
5189
+ background-repeat: no-repeat;
5190
+ }
5191
+
5192
+ .dsx-environment-ribbon__haze:where([data-placement='contained']) {
5193
+ position: absolute;
5194
+ }
5195
+
5196
+ /* Forced colors replaces fills and keeps borders; the haze is decoration and has nothing to say there. */
5197
+
5198
+ @media (forced-colors: active) {
5199
+ .dsx-environment-ribbon__haze {
5200
+ display: none;
5201
+ }
5202
+ }
5203
+
5115
5204
  .dsx-editable {
5116
5205
  position: relative;
5117
5206
  display: inline-flex;
@@ -8430,8 +8519,8 @@ textarea {
8430
8519
  }
8431
8520
 
8432
8521
  .dsx-dialog__backdrop[data-blur='true'] {
8433
- backdrop-filter: blur(var(--dsx-blur-md, 8px));
8434
8522
  -webkit-backdrop-filter: blur(var(--dsx-blur-md, 8px));
8523
+ backdrop-filter: blur(var(--dsx-blur-md, 8px));
8435
8524
  }
8436
8525
 
8437
8526
  /* Layout-hint element only — position state is driven by data-position */
@@ -8686,21 +8775,49 @@ textarea {
8686
8775
 
8687
8776
  .dsx-alert__icon { flex-shrink: 0; }
8688
8777
 
8689
- .dsx-alert__action {
8690
- align-self: center;
8691
- justify-content: flex-start;
8778
+ /* The icon is one column; the text and the actions are the other. Only this column wraps, so actions that drop below the text
8779
+ start under the text and not under the icon (G-15, 2026-10-05). The gap is the alert's own, per size. */
8780
+
8781
+ .dsx-alert__main {
8782
+ display: flex;
8783
+ flex: 1 1 0;
8692
8784
  flex-wrap: wrap;
8693
- gap: var(--dsx-space-sm);
8785
+ align-items: flex-start;
8786
+ gap: inherit;
8787
+ min-width: 0;
8694
8788
  }
8695
8789
 
8790
+ /* The basis is the width the text needs to read before the actions wrap below it; min() keeps it from overflowing a column
8791
+ narrower than that. */
8792
+
8696
8793
  .dsx-alert__content {
8697
8794
  display: grid;
8698
8795
  gap: var(--dsx-space-xs);
8699
- flex: 1;
8796
+ flex: 1 1 min(calc(var(--dsx-size-xl) * 5), 100%);
8700
8797
  min-width: 0;
8701
8798
  overflow-wrap: anywhere;
8702
8799
  }
8703
8800
 
8801
+ /* The direct-child Alert.Actions are one group. The content grows to push the group to the end of the row, and when the text
8802
+ would drop under its readable width the group wraps below it as a whole, at the start of the column. It has no auto margin:
8803
+ that would push a wrapped group to the far edge. Its own buttons wrap inside it when even the group is too wide. */
8804
+
8805
+ .dsx-alert__actions {
8806
+ display: flex;
8807
+ flex: 0 0 auto;
8808
+ flex-wrap: wrap;
8809
+ align-items: flex-start;
8810
+ gap: var(--dsx-space-sm);
8811
+ max-width: 100%;
8812
+ }
8813
+
8814
+ .dsx-alert__action {
8815
+ align-self: flex-start;
8816
+ justify-content: flex-start;
8817
+ flex-wrap: wrap;
8818
+ gap: var(--dsx-space-sm);
8819
+ }
8820
+
8704
8821
  .dsx-alert[data-variant='warning'] { border-color: var(--dsx-color-warning); }
8705
8822
 
8706
8823
  .dsx-alert[data-variant='success'] { border-color: var(--dsx-color-success); }
@@ -8750,6 +8867,42 @@ textarea {
8750
8867
  min-height: var(--dsx-size-lg);
8751
8868
  }
8752
8869
 
8870
+ /* With a trailing action, the icon, the first line of text and the action share one centre at any width: all
8871
+ three get a control-height row, and the first line's box is that tall (`::first-line`). Alerts without an
8872
+ action are untouched. Kept last so it wins over the size rules above. */
8873
+
8874
+ :where(.dsx-alert:has(> .dsx-alert__main > .dsx-alert__actions)) :is(.dsx-alert__icon, .dsx-alert__action) {
8875
+ min-height: var(--dsx-size-lg);
8876
+ }
8877
+
8878
+ :where(.dsx-alert:has(> .dsx-alert__main > .dsx-alert__actions)) .dsx-alert__content > :where(:first-child)::first-line {
8879
+ line-height: var(--dsx-size-lg);
8880
+ }
8881
+
8882
+ /* `lineClamp` on Alert.Description: the text stops after that many lines with an ellipsis. */
8883
+
8884
+ .dsx-alert__description[data-line-clamp] {
8885
+ display: -webkit-box;
8886
+ -webkit-box-orient: vertical;
8887
+ -webkit-line-clamp: var(--dsx-alert-line-clamp);
8888
+ overflow: hidden;
8889
+ }
8890
+
8891
+ /* Whether `::first-line` reaches into a clamped description depends on the engine (Chromium draws it as a block
8892
+ container, WebKit and Gecko as a legacy flexbox), so the control-height first line above is switched off for it and
8893
+ the same centring is done with padding in every engine: the first line sits on the middle of the control row (`lh`
8894
+ is the element's own line height). */
8895
+
8896
+ :where(.dsx-alert:has(> .dsx-alert__main > .dsx-alert__actions)) .dsx-alert__description[data-line-clamp] {
8897
+ padding-block-start: calc((var(--dsx-size-lg) - 1lh) / 2);
8898
+ }
8899
+
8900
+ /* Same specificity as the first-line rule it switches off (0,1,1), so source order decides: this one comes last. */
8901
+
8902
+ :where(.dsx-alert:has(> .dsx-alert__main > .dsx-alert__actions)) .dsx-alert__description:where([data-line-clamp])::first-line {
8903
+ line-height: inherit;
8904
+ }
8905
+
8753
8906
  .dsx-combobox__trigger {
8754
8907
  display: flex;
8755
8908
  align-items: center;
@@ -9765,6 +9918,10 @@ textarea {
9765
9918
  .dsx-field,
9766
9919
  .dsx-fieldset {
9767
9920
  display: grid;
9921
+ /* One column that fills the container and never asks for more: an implicit `auto` track is sized from its items' min-content,
9922
+ and a control's min-content is its intrinsic width, so a password input widened the column past a narrow card (G-22). The
9923
+ horizontal and responsive orientations below set their own templates. */
9924
+ grid-template-columns: minmax(0, 1fr);
9768
9925
  gap: var(--dsx-space-xs);
9769
9926
  width: 100%;
9770
9927
  min-width: 0;
@@ -9831,6 +9988,7 @@ textarea {
9831
9988
  .dsx-field__content,
9832
9989
  .dsx-field-group {
9833
9990
  display: grid;
9991
+ grid-template-columns: minmax(0, 1fr);
9834
9992
  gap: var(--dsx-space-xs);
9835
9993
  }
9836
9994
 
@@ -10934,6 +11092,28 @@ textarea {
10934
11092
  flex-shrink: 0;
10935
11093
  }
10936
11094
 
11095
+ /* variant="floating": a glass bar that sits inside the page with its own edge, the header of the
11096
+ floating shells (direction C). Same glass recipe as `Card variant="glass"`, except the outer shadow.
11097
+ The page decides whether it sticks. Prefixed backdrop-filter first, standard last (see baseCard.css).
11098
+
11099
+ The shadow falls below the bar only (offset = -spread, so it starts at the bar's bottom edge). The bar sits
11100
+ flush at the edge of a scroll container (`Sidebar.Inset`, overflow: auto), which clips anything painted past
11101
+ its left, right and top edges: a full-ring shadow ended in a vertical cut at the bar's side, visible in the
11102
+ light theme (G-21). Offset, blur and the strong card shadow colour are the existing tokens; at the bar's edge
11103
+ this reads as the old shadow does (measured in the batch's verification.md). */
11104
+
11105
+ .dsx-page-header:where([data-variant='floating']) {
11106
+ border: var(--dsx-border-width-sm) solid var(--dsx-effect-panel-glass-border);
11107
+ border-radius: var(--dsx-rounded-lg);
11108
+ background: var(--dsx-color-panel-translucent);
11109
+ box-shadow:
11110
+ inset 0 1px 0 0 var(--dsx-effect-panel-glass-rim-highlight),
11111
+ inset 0 0 0 1px var(--dsx-effect-panel-glass-rim),
11112
+ 0 var(--dsx-shadow-offset-lg) var(--dsx-shadow-blur-md) calc(-1 * var(--dsx-shadow-offset-lg)) var(--dsx-effect-shadow-card-strong);
11113
+ -webkit-backdrop-filter: var(--dsx-effect-panel-glass-backdrop-filter);
11114
+ backdrop-filter: var(--dsx-effect-panel-glass-backdrop-filter);
11115
+ }
11116
+
10937
11117
  .dsx-page-header__title {
10938
11118
  margin: 0;
10939
11119
  font-family: var(--dsx-typography-headline-medium-font-family);
@@ -11772,12 +11952,23 @@ textarea {
11772
11952
  .dsx-sidebar[data-effect='ambient'] {
11773
11953
  background-color: var(--dsx-color-surface);
11774
11954
  /* Softened 2026-09-19: accent-9 at 18% painted a visible color blob
11775
- (worst on bright accents like spark) instead of an ambient wash. */
11776
- background-image: radial-gradient(
11777
- circle at var(--dsx-space-xl) var(--dsx-space-lg),
11778
- color-mix(in srgb, var(--dsx-color-accent-9) 10%, transparent),
11779
- transparent 58%
11780
- );
11955
+ (worst on bright accents like spark) instead of an ambient wash. The strength is a theme-aware token (G-16, 2026-10-05):
11956
+ 10% in dark, as before; raised in light, where the same wash read about half as strong on the pale surface.
11957
+ A second source at the foot (G-17, 2026-10-05): the corner radial alone reached about 35% of the panel and left the lower
11958
+ half flat. The foot glow is the same colour at 0.9x the same strength, an ellipse anchored to the bottom edge, so the panel
11959
+ is lit at both ends and the nav keeps a calm middle (the account row gets the light the logo gets). House-style, picked by
11960
+ the maintainer in the look lab over a full-height column and a single tall radial. */
11961
+ background-image:
11962
+ radial-gradient(
11963
+ circle at var(--dsx-space-xl) var(--dsx-space-lg),
11964
+ color-mix(in srgb, var(--dsx-color-accent-9) var(--dsx-effect-ambient-strength), transparent),
11965
+ transparent 58%
11966
+ ),
11967
+ radial-gradient(
11968
+ ellipse 130% 38% at 85% 100%,
11969
+ color-mix(in srgb, var(--dsx-color-accent-9) calc(var(--dsx-effect-ambient-strength) * 0.9), transparent),
11970
+ transparent 100%
11971
+ );
11781
11972
  background-repeat: no-repeat;
11782
11973
  }
11783
11974
 
@@ -11921,8 +12112,15 @@ textarea {
11921
12112
  overflow: auto;
11922
12113
  }
11923
12114
 
12115
+ /* G-12: the drawer is flush with the viewport on three sides, so only its free (inline-end) edge is a boundary. The
12116
+ base dialog surface draws the structural border all round, which painted a light outline along the screen edge and
12117
+ made the 100dvh nav 2px taller than the sheet's content box, so the sheet scrolled. The nav owns its own scrolling. */
12118
+
11924
12119
  .dsx-sidebar__mobile-sheet {
11925
12120
  padding: 0;
12121
+ border-block-width: 0;
12122
+ border-inline-start-width: 0;
12123
+ overflow: hidden;
11926
12124
  }
11927
12125
 
11928
12126
  /* The mobile Drawer carries drawer's own content padding; the X sits over
@@ -11958,7 +12156,7 @@ textarea {
11958
12156
  .dsx-sidebar__mobile-sheet > .dsx-sidebar {
11959
12157
  position: relative;
11960
12158
  width: 100%;
11961
- height: 100dvh;
12159
+ height: 100%;
11962
12160
  box-shadow: none;
11963
12161
  }
11964
12162
 
@@ -11983,6 +12181,7 @@ textarea {
11983
12181
 
11984
12182
  .dsx-sidebar__rail:focus-visible,
11985
12183
  .dsx-sidebar__menu-button:focus-visible,
12184
+ .dsx-sidebar__menu-sub-trigger:focus-visible,
11986
12185
  .dsx-sidebar__menu-sub-button:focus-visible {
11987
12186
  outline: none;
11988
12187
  box-shadow: var(--dsx-shadow-focus-ring);
@@ -12180,7 +12379,12 @@ textarea {
12180
12379
  opacity: 1;
12181
12380
  }
12182
12381
 
12183
- .dsx-sidebar__menu-button {
12382
+ /* One row geometry for a plain row and for a parent row that opens a sub-list (G-13, 2026-10-05). The parent is a `.dsx-button` too, and
12383
+ without this base it kept the button's 16px padding (its icon sat 8px right of every other row) and the browser's grey button
12384
+ fill whenever it was neither open nor active. */
12385
+
12386
+ .dsx-sidebar__menu-button,
12387
+ .dsx-sidebar__menu-sub-trigger {
12184
12388
  display: flex;
12185
12389
  align-items: center;
12186
12390
  gap: var(--dsx-space-sm);
@@ -12357,6 +12561,18 @@ textarea {
12357
12561
  box-shadow: var(--dsx-shadow-active-inset-outline);
12358
12562
  }
12359
12563
 
12564
+ /* G-19 (2026-10-05): the active row's own wash in the light theme. Accent-3 at 50% over a tinted light panel is nearly the
12565
+ panel's colour, so in light the row was marked only by its underline and text. Step 4 is the scale's "soft component" step
12566
+ (selected / hover); it reads on Rift, Warp and Ray without a primary fill. Dark keeps the rules above, unchanged. The
12567
+ `:where()` keeps the selector at (0,2,0) (the qa:selectors policy); the tie with the three rules above is decided by
12568
+ source order, so this block stays after them. */
12569
+
12570
+ :where([data-dsx-theme='light']) .dsx-sidebar__menu-button[data-active='true'],
12571
+ :where([data-dsx-theme='light']) .dsx-sidebar__menu-sub-button[data-active='true'],
12572
+ :where([data-dsx-theme='light']) .dsx-sidebar__menu-sub-trigger[data-active='true'] {
12573
+ background: var(--dsx-color-accent-4);
12574
+ }
12575
+
12360
12576
  .dsx-sidebar__menu-sub-trigger-chevron {
12361
12577
  margin-inline-start: auto;
12362
12578
  flex-shrink: 0;
@@ -12404,6 +12620,14 @@ textarea {
12404
12620
  min-height: var(--dsx-size-sm);
12405
12621
  }
12406
12622
 
12623
+ /* Inside a menu item (a flex row) the skeleton is an auto-width flex item, so its 100%-wide label collapses and the
12624
+ row draws as two small pills. Let it take the item's width. */
12625
+
12626
+ .dsx-sidebar__menu-item > .dsx-sidebar__menu-skeleton {
12627
+ flex: 1;
12628
+ min-width: 0;
12629
+ }
12630
+
12407
12631
  .dsx-sidebar__menu-button-text {
12408
12632
  min-width: 0;
12409
12633
  overflow: hidden;
@@ -12411,6 +12635,12 @@ textarea {
12411
12635
  white-space: nowrap;
12412
12636
  }
12413
12637
 
12638
+ /* G-18 (2026-10-05): a quiet outline in the accent, not a raised disc. The disc was a `surface-raised` fill with a drop shadow:
12639
+ shadows do not read on dark, and on the tinted panel the fill is nearly the panel's own colour, so the control vanished in
12640
+ both themes. The ring is the scale's "normal border" step (7), the glyph its text step (11), the hover one step up (fill 3,
12641
+ ring 8): the Radix 12-step jobs, one purposeful step per state. The ring is an inset shadow so the 32px box does not move,
12642
+ and the focus ring below still replaces it. The pin / unpin glyphs of hover-expand mode live inside the same button. */
12643
+
12414
12644
  .dsx-sidebar__trigger {
12415
12645
  display: inline-flex;
12416
12646
  align-items: center;
@@ -12422,14 +12652,14 @@ textarea {
12422
12652
  padding: 0;
12423
12653
  border: 0;
12424
12654
  border-radius: var(--dsx-rounded-full);
12425
- background: var(--dsx-color-surface-raised);
12426
- color: var(--dsx-color-on-surface-variant);
12427
- box-shadow: var(--dsx-effect-shadow-1);
12655
+ background: transparent;
12656
+ color: var(--dsx-color-accent-11);
12657
+ box-shadow: inset 0 0 0 var(--dsx-border-width-sm) var(--dsx-color-accent-7);
12428
12658
  }
12429
12659
 
12430
12660
  .dsx-sidebar__trigger:hover {
12431
- background: var(--dsx-effect-hover-ghost);
12432
- color: var(--dsx-color-on-surface);
12661
+ background: var(--dsx-color-accent-3);
12662
+ box-shadow: inset 0 0 0 var(--dsx-border-width-sm) var(--dsx-color-accent-8);
12433
12663
  }
12434
12664
 
12435
12665
  .dsx-sidebar__trigger:focus-visible {
@@ -17821,15 +18051,25 @@ span.dsx-filter-bar__condition-label {
17821
18051
  border-top: var(--dsx-border-width-sm) solid var(--dsx-color-outline-variant);
17822
18052
  }
17823
18053
 
18054
+ /* G-20 (2026-10-05): the trigger is an ordinary icon button (size sm, 32px, like every other header icon) and the count hangs
18055
+ on its corner, so the hover highlight covers the bell only. It used to be laid out inside the button, which made the
18056
+ button a 59x44 pill that wrapped the count and was the one header control with a different shape (the maintainer: "I feel
18057
+ like no one does it"). The count hangs about 17px past the trailing edge; the margin reserves that room whatever gap the
18058
+ host uses, and it is always reserved so the controls after the bell do not move when a count appears. */
18059
+
17824
18060
  .dsx-notification-center__trigger.dsx-icon-button {
17825
18061
  position: relative;
17826
- display: inline-flex;
17827
- align-items: center;
17828
- gap: 0;
17829
- width: auto;
17830
- min-width: calc(var(--dsx-size-xl) + var(--dsx-space-xs));
17831
- height: calc(var(--dsx-size-xl) + var(--dsx-space-xs));
17832
- padding-inline: var(--dsx-space-sm);
18062
+ margin-inline-end: var(--dsx-space-md);
18063
+ }
18064
+
18065
+ /* The highlight is 32px but the hit area stays 44px (size-xl + space-xs, the floor the earlier bell contract set): this
18066
+ pseudo-element is part of the button for pointer events, and its inset shrinks to nothing on a larger size. The selector
18067
+ stays at (0,1,1): the policy allows (0,2,0) in this file and `.dsx-icon-button` has no pseudo-element rules to outrank. */
18068
+
18069
+ .dsx-notification-center__trigger::after {
18070
+ content: '';
18071
+ position: absolute;
18072
+ inset: calc((100% - (var(--dsx-size-xl) + var(--dsx-space-xs))) / 2);
17833
18073
  }
17834
18074
 
17835
18075
  .dsx-notification-center__trigger .dsx-icon-button__content {
@@ -17837,9 +18077,9 @@ span.dsx-filter-bar__condition-label {
17837
18077
  }
17838
18078
 
17839
18079
  .dsx-notification-center__badge {
17840
- position: relative;
18080
+ position: absolute;
17841
18081
  top: calc(-1 * var(--dsx-space-sm));
17842
- margin-inline-start: calc(-1 * var(--dsx-space-xs));
18082
+ inset-inline-start: calc(50% + var(--dsx-space-xs));
17843
18083
  flex-shrink: 0;
17844
18084
  min-width: var(--dsx-size-sm);
17845
18085
  height: var(--dsx-size-sm);
package/dist/tokens.css CHANGED
@@ -212,6 +212,7 @@
212
212
  --dsx-effect-pressed-primary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 16%, transparent);
213
213
  --dsx-effect-hover-secondary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 3%, transparent);
214
214
  --dsx-effect-hover-ghost: color-mix(in srgb, var(--dsx-color-accent-rift-9) 6%, transparent);
215
+ --dsx-effect-ambient-strength: 10%;
215
216
  --dsx-sheet-min-height: 80vh;
216
217
  --dsx-sheet-width: 380px;
217
218
  --dsx-sheet-width-sm: 320px;
@@ -714,6 +715,7 @@
714
715
  --dsx-effect-pressed-primary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 12%, transparent);
715
716
  --dsx-effect-hover-secondary: color-mix(in srgb, var(--dsx-color-accent-rift-9) 3%, transparent);
716
717
  --dsx-effect-hover-ghost: color-mix(in srgb, var(--dsx-color-accent-rift-9) 6%, transparent);
718
+ --dsx-effect-ambient-strength: 16%;
717
719
  --dsx-_accent-rift-primary: var(--dsx-color-accent-rift-9);
718
720
  --dsx-_accent-rift-primary-container: var(--dsx-color-accent-rift-4);
719
721
  --dsx-_accent-rift-on-primary: #ffffff;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d4gger/design-system",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "Publishable design-system package for D4gger foundations, icons, primitives, and components.",
5
5
  "keywords": [
6
6
  "d4gger",