@dereekb/dbx-web 13.19.0 → 13.21.0

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.
@@ -147,24 +147,32 @@ $calendar-content-border-consideration: 2px;
147
147
  }
148
148
 
149
149
  // Calendar color tokens (theme-aware via --mat-sys-* / --dbx-* CSS variables)
150
+ //
151
+ // Tone-based colors resolve from a public `--dbx-calendar-<name>-color` (hue)
152
+ // + `--dbx-calendar-<name>-tone` (opacity level) pair so consumers can re-tint or
153
+ // soften a state independently without redefining the full color-mix() expression.
150
154
  #{calendar.$cal-event-icon-color-var}: var(--dbx-primary-color);
151
155
  #{calendar.$cal-event-color-primary-var}: var(--mat-sys-on-primary-container);
152
156
  #{calendar.$cal-event-color-secondary-var}: var(--mat-sys-primary-container);
153
- #{calendar.$cal-border-color-var}: var(--mat-sys-outline);
157
+ // Defaults to the shared divider at full strength so calendar cell borders match
158
+ // dbx-table-view / dbx-two-column. --dbx-divider-color is already semi-transparent, so
159
+ // applying an extra tone here would double-fade it; lower --dbx-calendar-border-tone
160
+ // (or this whole calendar's --dbx-divider-color) to soften the dense grid if desired.
161
+ #{calendar.$cal-border-color-var}: color-mix(in srgb, var(--dbx-calendar-border-color, var(--dbx-divider-color)) var(--dbx-calendar-border-tone, 100%), transparent);
154
162
  #{calendar.$cal-bg-primary-var}: var(--mat-sys-background);
155
- #{calendar.$cal-bg-secondary-var}: color-mix(in srgb, var(--mat-sys-on-surface) 4%, transparent);
156
- #{calendar.$cal-bg-hover-var}: color-mix(in srgb, var(--mat-sys-on-surface) 4%, transparent);
157
- #{calendar.$cal-bg-active-var}: color-mix(in srgb, var(--dbx-accent-color) 30%, transparent);
158
- #{calendar.$cal-bg-selected-var}: color-mix(in srgb, var(--dbx-primary-color) 60%, transparent);
163
+ #{calendar.$cal-bg-secondary-var}: color-mix(in srgb, var(--dbx-calendar-secondary-color, var(--mat-sys-on-surface)) var(--dbx-calendar-secondary-tone, 4%), transparent);
164
+ #{calendar.$cal-bg-hover-var}: color-mix(in srgb, var(--dbx-calendar-hover-color, var(--mat-sys-on-surface)) var(--dbx-calendar-hover-tone, 4%), transparent);
165
+ #{calendar.$cal-bg-active-var}: color-mix(in srgb, var(--dbx-calendar-active-day-color, var(--dbx-primary-color)) var(--dbx-calendar-active-day-tone, 30%), transparent);
166
+ #{calendar.$cal-bg-selected-var}: color-mix(in srgb, var(--dbx-calendar-selected-day-color, var(--dbx-primary-color)) var(--dbx-calendar-selected-day-tone, 60%), transparent);
159
167
  #{calendar.$cal-bg-disabled-var}: var(--mat-sys-surface);
160
168
  #{calendar.$cal-bg-not-applicable-var}: var(--mat-sys-surface);
161
- #{calendar.$cal-bg-highlight-var}: color-mix(in srgb, var(--dbx-accent-color) 30%, transparent);
162
- #{calendar.$cal-today-bg-var}: color-mix(in srgb, var(--mat-sys-on-surface) 4%, transparent);
169
+ #{calendar.$cal-bg-highlight-var}: color-mix(in srgb, var(--dbx-calendar-highlight-color, var(--dbx-accent-color)) var(--dbx-calendar-highlight-tone, 30%), transparent);
170
+ #{calendar.$cal-today-bg-var}: color-mix(in srgb, var(--dbx-calendar-today-color, var(--mat-sys-on-surface)) var(--dbx-calendar-today-tone, 4%), transparent);
163
171
  #{calendar.$cal-weekend-color-var}: var(--mat-sys-on-surface);
164
172
  #{calendar.$cal-badge-color-var}: var(--dbx-accent-color);
165
173
  #{calendar.$cal-selected-var}: var(--mat-sys-on-primary);
166
174
  #{calendar.$cal-disabled-var}: var(--dbx-warn-color);
167
- #{calendar.$cal-not-applicable-var}: color-mix(in srgb, var(--mat-sys-on-surface) 30%, transparent);
175
+ #{calendar.$cal-not-applicable-var}: color-mix(in srgb, var(--dbx-calendar-not-applicable-color, var(--mat-sys-on-surface)) var(--dbx-calendar-not-applicable-tone, 30%), transparent);
168
176
  #{calendar.$cal-current-time-marker-color-var}: var(--dbx-accent-color);
169
177
  #{calendar.$cal-white-var}: var(--mat-sys-surface);
170
178
  #{calendar.$cal-gray-var}: var(--mat-sys-on-surface-variant);
@@ -6,6 +6,11 @@
6
6
  // MARK: Mixin
7
7
  @mixin core() {
8
8
  .dbx-table-view {
9
+ // Route Material's table row outline through the shared divider token so the row
10
+ // separators match dbx-two-column / dbx-calendar dividers. (Material always sets
11
+ // --mat-table-row-item-outline-color, so a fallback alone never takes effect.)
12
+ --mat-table-row-item-outline-color: var(--dbx-divider-color);
13
+
9
14
  .dbx-table-view-table-wrapper {
10
15
  max-height: var(--dbx-table-view-max-height, 600px);
11
16
  overflow: auto;
@@ -45,7 +50,7 @@
45
50
 
46
51
  // MARK: Full summary row
47
52
  .dbx-table-view-full-summary-row-container > .mat-mdc-row {
48
- border-top-color: var(--mat-table-row-item-outline-color, var(--mat-sys-outline-variant));
53
+ border-top-color: var(--mat-table-row-item-outline-color, var(--dbx-divider-color));
49
54
  border-top-width: var(--mat-table-row-item-outline-width, 1px);
50
55
  border-top-style: solid;
51
56
  }
@@ -1,5 +1,3 @@
1
- @use '../../style/theming';
2
-
3
1
  // Sections
4
2
  @mixin core() {
5
3
  .cdk-overlay-pane .mat-menu-panel {
@@ -9,8 +7,7 @@
9
7
  .cdk-overlay-pane.ng-overlay-container {
10
8
  color: var(--mat-sys-on-surface);
11
9
  background-color: var(--mat-sys-surface);
12
- @include theming.elevation(8);
13
- border: 1px solid color-mix(in srgb, var(--mat-sys-on-surface) 10%, transparent);
10
+ border-radius: var(--dbx-overlay-border-radius, var(--mat-sys-corner-medium));
14
11
  }
15
12
  }
16
13
 
@@ -18,6 +18,12 @@ $two-columns-right-padding-size: var(--dbx-padding-2);
18
18
  flex-direction: row;
19
19
  overflow-x: hidden;
20
20
 
21
+ // Top boundary separating the two-column block from the page header above it.
22
+ // Uses the shared divider token; apps can disable or restyle the boundary by
23
+ // overriding --dbx-two-column-border-top (e.g. to `none`).
24
+ box-sizing: border-box;
25
+ border-top: var(--dbx-two-column-border-top, 1px solid var(--dbx-divider-color));
26
+
21
27
  .dbx-content-container {
22
28
  margin: 0;
23
29
  }
@@ -84,7 +90,7 @@ $two-columns-right-padding-size: var(--dbx-padding-2);
84
90
 
85
91
  .dbx-two-column-head {
86
92
  padding: 0 var(--dbx-padding-2); // only padded on the left
87
- border-bottom: 1px solid var(--mat-sys-outline-variant);
93
+ border-bottom: 1px solid var(--dbx-divider-color);
88
94
  height: $two-column-navbar-height;
89
95
 
90
96
  display: flex;
@@ -33,12 +33,18 @@ $dbx-border-opacity-default: 20%;
33
33
  }
34
34
 
35
35
  /// @dbx-utility content-elevate
36
- /// @intent padded content surface lifted with elevation 2combines inner padding with a Material elevation shadow
36
+ /// @intent padded content surface lifted above its surroundingsthe mirror of `.dbx-content-pit`. Per Material 3, elevation is communicated primarily with color (the brightest `surface-bright` tone, always lighter than the pit's `surface-dim` in both light and dark) plus a soft lift shadow for protection against the background, so it reads as raised and stays clearly distinct from the recessed pit. Tonal by default — a low-opacity wash of the active dbxColor over `surface-bright`; rounded by default, opt out with `.dbx-corners-none`. Override `--dbx-content-elevate-tone` (percentage) to tune the wash, `--dbx-content-elevate-bg` to replace the background outright, or `--dbx-content-elevate-shadow` to retune/remove (`none`) the shadow.
37
37
  /// @role layout
38
- /// @see-also dbx-content-border, dbx-content-pit
38
+ /// @see-also dbx-content-border, dbx-content-pit, dbx-corners-none
39
39
  .dbx-content-elevate {
40
40
  padding: $content-border-inner-padding;
41
- @include theming.elevation(2);
41
+ }
42
+
43
+ // Elevate rounds by default. Kept OUT of the `.dbx-content-elevate` rule and
44
+ // wrapped in `:where()` so specificity stays at 0 — any single-class utility
45
+ // (`.dbx-corners-none`, `.dbx-corners-large`, or an app class) overrides it.
46
+ :where(.dbx-content-elevate) {
47
+ border-radius: theming.$dbx-content-pit-rounded-border-radius;
42
48
  }
43
49
 
44
50
  /// @dbx-utility content-box
@@ -57,7 +63,7 @@ $dbx-border-opacity-default: 20%;
57
63
  }
58
64
 
59
65
  /// @dbx-utility content-pit
60
- /// @intent recessed surface pit — `--mat-sys-surface-container` background with a `padding-4` inset, used to visually nest content (e.g. logs, JSON, quoted blocks); rounded by default, opt out with `.dbx-corners-none`
66
+ /// @intent recessed surface pit ("negative elevation") a semi-transparent tonal wash of the active dbxColor over `--mat-sys-surface-dim` (the dimmest/greyest M3 surface, the opposite end of the dim/bright pair from `.dbx-content-elevate`) so the lighter surface behind shows through, with a `padding-4` inset, used to visually nest content (e.g. logs, JSON, quoted blocks); rounded by default, opt out with `.dbx-corners-none`
61
67
  /// @role layout
62
68
  /// @see-also dbx-content-pit-scrollable, dbx-content-border, dbx-content-elevate, dbx-corners-none
63
69
  .dbx-content-pit {
@@ -86,14 +92,39 @@ $dbx-border-opacity-default: 20%;
86
92
  }
87
93
 
88
94
  /// @dbx-utility content-pit-scrollable
89
- /// @intent scrollable modifier for `.dbx-content-pit` — caps height at `theming.$dbx-content-pit-scrollable-max-height` and forces `overflow-y: scroll` for long inner content
95
+ /// @intent scrollable modifier for `.dbx-content-pit` — turns the pit into a fixed frame that clips an inner `.dbx-content-pit-scrollable-content` wrapper; the wrapper (not the frame) scrolls, so the padded/cornered surface never shifts. Drive the height via the `[scrollable]` input (which writes `--dbx-content-pit-scrollable-max-height`) or fall back to `theming.$dbx-content-pit-scrollable-max-height`.
90
96
  /// @role layout
91
97
  /// @parent content-pit
92
- /// @see-also dbx-content-pit
98
+ /// @see-also dbx-content-pit, dbx-content-pit-scrollable-content
93
99
  .dbx-content-pit-scrollable {
100
+ // Fixed frame that clips its scrolling child. Keeping the pit's padding here
101
+ // and the overflow on the inner wrapper (rather than both on this box) avoids
102
+ // the Chrome scroll-anchor / overscroll shift that moves the whole pit when
103
+ // the scroll reaches its top or bottom.
104
+ overflow: hidden;
105
+ max-width: 100%;
106
+ }
107
+
108
+ /// @dbx-utility content-pit-scrollable-content
109
+ /// @intent inner scroll region for a scrollable `.dbx-content-pit` — wrap the pit body in this element when using `[scrollable]`; it owns the height cap + `overflow-y` so the pit frame stays put. The cap reads `--dbx-content-pit-scrollable-max-height` (published per-pit by `dbxContentPit`; `none` when the pit isn't scrollable).
110
+ /// @role layout
111
+ /// @parent content-pit-scrollable
112
+ /// @see-also dbx-content-pit, dbx-content-pit-scrollable
113
+ .dbx-content-pit .dbx-content-pit-scrollable-content {
94
114
  max-height: theming.$dbx-content-pit-scrollable-max-height;
95
115
  max-width: 100%;
96
- overflow-y: scroll;
116
+ overflow-y: auto;
117
+ overscroll-behavior: contain;
118
+ overflow-anchor: none;
119
+
120
+ // Negate the pit's vertical padding so the scroll region spans the full pit
121
+ // height, then re-add it as padding here — the inset now scrolls with the
122
+ // content so it can scroll flush to the pit's top and bottom edges, instead
123
+ // of being clipped by a static padding band.
124
+ margin-top: calc(-1 * #{$pit-padding});
125
+ margin-bottom: calc(-1 * #{$pit-padding});
126
+ padding-top: $pit-padding;
127
+ padding-bottom: $pit-padding;
97
128
  }
98
129
 
99
130
  /// @dbx-utility content-pit-floating-button
@@ -110,7 +141,9 @@ $dbx-border-opacity-default: 20%;
110
141
  margin-bottom: $dbx-content-pit-floating-button-margin;
111
142
  }
112
143
 
113
- .dbx-content-pit-scrollable > .dbx-content-pit-floating-button {
144
+ // In a scrollable pit the floating button lives inside the inner scroll region
145
+ // so `position: sticky` anchors to it; pin it flush to the top of that region.
146
+ .dbx-content-pit-scrollable-content > .dbx-content-pit-floating-button {
114
147
  top: 0;
115
148
  }
116
149
 
@@ -225,19 +258,47 @@ $dbx-border-opacity-default: 20%;
225
258
  padding-bottom: var(--dbx-scroll-content-bottom-padding, #{$scroll-content-bottom-padding});
226
259
  }
227
260
 
261
+ // Recessed surface ("negative elevation"): the pit reads as a tonal step below the surfaces it
262
+ // nests in. It sits on `surface-dim` — the dimmest/greyest M3 surface tone — so it reads as a
263
+ // grey recess in light mode and the darkest surface in dark mode, the opposite end of the
264
+ // dim/bright pair from `.dbx-content-elevate`. (`surface-dim`/`surface-bright` are the one M3
265
+ // pairing whose light/dark luminance ordering is stable, so the pit is reliably dimmer than the
266
+ // elevate in both themes — the container ladder flips and can't guarantee that.) Tonal by
267
+ // default — a low-opacity wash of the active dbxColor (`--dbx-bg-color-current`, primary by
268
+ // default, driven by an ancestor/host `[dbxColor]`) laid over `surface-dim`, so the pit both
269
+ // steps down a tone AND picks up the color context, instead of being a flat grey. That dim wash is
270
+ // then rendered semi-transparent (`--dbx-content-pit-opacity`, default 50%) so the lighter surface
271
+ // behind it shows through — the pit reads as a soft, airy recess rather than a heavy grey block,
272
+ // while keeping its grey/dim character so it stays clearly dimmer than the elevate's
273
+ // `surface-bright`. Override `--dbx-content-pit-tone` (percentage) to tune the wash strength,
274
+ // `--dbx-content-pit-opacity` to make the recess lighter (lower %, more transparent) or heavier
275
+ // (higher %), or set `--dbx-content-pit-bg` to replace the background outright (e.g. on a painted
276
+ // surface where the tonal wash would clash).
228
277
  .dbx-content-pit {
229
- background: var(--mat-sys-surface-container);
278
+ background: var(--dbx-content-pit-bg, color-mix(in srgb, color-mix(in srgb, #{theming.$dbx-bg-color} var(--dbx-content-pit-tone, 8%), var(--mat-sys-surface-dim)) var(--dbx-content-pit-opacity, 50%), transparent));
230
279
  }
231
280
 
232
- // pits on a [dbxColor]-painted surface wash from the current contrast color instead of the
233
- // surface-container token, which would clash with the painted background (e.g. a dark inverse
234
- // card). Override --dbx-content-pit-color-tone (percentage) to tune the wash.
235
- .dbx-color .dbx-content-pit {
236
- background: color-mix(in srgb, #{theming.$dbx-color} var(--dbx-content-pit-color-tone, 11%), transparent);
281
+ // Elevated surface ("positive elevation"): the tonal mirror of the pit. It sits on
282
+ // `surface-bright` the brightest M3 surface tone so it reads lighter than its surroundings
283
+ // (and always lighter than the pit's `surface-dim`, in BOTH themes, since dim/bright are the one
284
+ // M3 pairing with a stable luminance ordering). It also carries a soft M3 shadow: per M3 the lift
285
+ // is communicated primarily with color, and the subtle shadow is the sanctioned "protection
286
+ // against a background" that sells the raise. Tonal by default — a low-opacity wash of the active
287
+ // dbxColor (`--dbx-bg-color-current`, primary by default, driven by an ancestor/host `[dbxColor]`)
288
+ // over `surface-bright`. Override `--dbx-content-elevate-tone` (percentage) to tune the wash,
289
+ // `--dbx-content-elevate-bg` to replace the background outright, or `--dbx-content-elevate-shadow`
290
+ // to retune/remove (`none`) the lift shadow.
291
+ .dbx-content-elevate {
292
+ background: var(--dbx-content-elevate-bg, color-mix(in srgb, #{theming.$dbx-bg-color} var(--dbx-content-elevate-tone, 8%), var(--mat-sys-surface-bright)));
293
+ box-shadow: var(--dbx-content-elevate-shadow, var(--mat-sys-level1));
237
294
  }
238
295
 
239
296
  .dbx-content-border {
240
- border: 3px dashed color-mix(in srgb, var(--dbx-border-color, var(--mat-sys-outline-variant)) var(--dbx-border-opacity, $dbx-border-opacity-default), transparent);
297
+ // Faint dashed frame keeps its own low opacity locally while sharing the global
298
+ // border base color. The computed --dbx-border-color-current re-blends with this
299
+ // overridden opacity at the point of use.
300
+ --dbx-border-opacity: #{$dbx-border-opacity-default};
301
+ border: 3px dashed var(--dbx-border-color-current);
241
302
  }
242
303
  }
243
304
 
@@ -9,17 +9,144 @@ $list-item-padded-min-height: 42px;
9
9
  overflow: hidden;
10
10
  height: 100%;
11
11
 
12
- // dbx-list (selection/standard) rows default to an M3 medium corner via the overridable
13
- // `--dbx-list-item-border-radius` token; `.dbx-list-square-items` zeroes it and the style-demo
14
- // `list-corner-*` levers re-point it. Both shape tokens resolve to the same value, so no
15
- // `border-radius` redeclaration is needed; the more specific `.dbx-list-card-items-list` item rule
16
- // deliberately wins for card lists.
17
- --mat-list-active-indicator-shape: var(--dbx-list-item-border-radius, var(--mat-sys-corner-medium, 12px));
18
- --mat-list-list-item-container-shape: var(--dbx-list-item-border-radius, var(--mat-sys-corner-medium, 12px));
12
+ // `--dbx-list-item-border-radius` (medium default) is the "how round" knob for the corners that
13
+ // actually round the first row's top, the last row's bottom, and any hovered/selected/activated
14
+ // row. Interior corners stay square so the rows read as one connected group (see the per-position
15
+ // and per-state rules below). `.dbx-list-square-items` zeroes the token and the style-demo
16
+ // `list-corner-*` levers re-point it, so both keep working. The more specific
17
+ // `.dbx-list-card-items-list` item rules deliberately win for card lists.
18
+ //
19
+ // IMPORTANT: do NOT declare `--dbx-list-item-border-radius` here — the style-demo levers set it on
20
+ // an ancestor, and a local declaration would shadow that inherited value (killing the levers).
21
+ // Instead resolve it once (with the medium fallback) into a private var the rules below consume;
22
+ // referencing it here still reads the inherited lever value, so the levers keep control.
23
+ --dbx-list-item-radius-resolved: var(--dbx-list-item-border-radius, var(--mat-sys-corner-medium, 12px));
24
+
25
+ // Hairline gap between rows. Default 1px.
26
+ --dbx-list-item-spacing: 1px;
27
+
28
+ // Selected (selection list) / activated (nav list) row tone. Defaults to the M3 primary container
29
+ // tone instead of the grey `--mat-sys-secondary-container` Material ships, with an on-tone for the
30
+ // row's text + icons.
31
+ --dbx-list-item-selected-color: var(--mat-sys-primary-container);
32
+ --dbx-list-item-selected-on-color: var(--mat-sys-on-primary-container);
33
+
34
+ // Active route (nav/value list `dbx-anchor-active`) row tone. A `dbx-anchor` row marks the active
35
+ // route with its own `dbx-anchor-active` class rather than Material's `.mdc-list-item--activated`, so
36
+ // it carries its own tone token here. Defaults to the selected/activated tone above (so an active
37
+ // route reads the same as a selected row out of the box), but can be retargeted independently — e.g.
38
+ // set `--dbx-list-item-active-color` on the list host — to give active routes a distinct color.
39
+ --dbx-list-item-active-color: var(--dbx-list-item-selected-color);
40
+ --dbx-list-item-active-on-color: var(--dbx-list-item-selected-on-color);
41
+
42
+ // Rows are square by default; their corners round per-position / per-state below.
43
+ --mat-list-active-indicator-shape: 0;
44
+ --mat-list-list-item-container-shape: 0;
45
+
46
+ // Selected (selection-list `--selected`) + activated (nav-list `--activated`) rows pick up the
47
+ // primary tone rather than the grey default.
48
+ --mat-list-list-item-selected-container-color: var(--dbx-list-item-selected-color);
49
+ --mat-list-active-indicator-color: var(--dbx-list-item-selected-color);
50
+
51
+ // Soften the hover state-layer so hovering doesn't grey-out as heavily (M3 default is 0.08).
52
+ --mat-list-list-item-hover-state-layer-opacity: var(--dbx-list-item-hover-state-layer-opacity, 0.04);
19
53
 
20
54
  .mat-mdc-list-item {
55
+ // Focus ring follows the row's own (per-position / per-state) shape token.
21
56
  --mat-focus-indicator-border-radius: var(--mat-list-list-item-container-shape);
57
+ // Hairline gap between rows; the trailing row's gap is stripped by the :last-of-type / :only-of-type rules.
58
+ margin-bottom: var(--dbx-list-item-spacing);
22
59
  }
60
+
61
+ // Angular Material's `.mat-mdc-nav-list .mat-mdc-list-item` rule pins `border-radius` to
62
+ // `--mat-list-active-indicator-shape` (which we set to 0 for square-by-default rows), so it would
63
+ // otherwise ignore the per-position / per-state `--mat-list-list-item-container-shape` we set below
64
+ // and leave nav/value-list rows square. Redeclare `border-radius` from the container-shape token at
65
+ // higher specificity so those rows round too. (Selection-list `<mat-list-option>` rows have no
66
+ // nav-list ancestor and already honor the container-shape token directly.)
67
+ .dbx-list-view .mat-mdc-list-item {
68
+ border-radius: var(--mat-list-list-item-container-shape);
69
+ }
70
+
71
+ // Connected-group corner shaping. The list rows are square by default (token = 0 above); here we
72
+ // round only the outer corners of the group and any row in an interactive/selected state.
73
+ //
74
+ // Selection list (`<mat-selection-list>` > `<mat-list-option>` siblings). NOTE: the element's CSS
75
+ // class is `.mat-mdc-selection-list` (there is no `.mat-selection-list` class), so match the host
76
+ // by its element/tag name instead.
77
+ mat-selection-list > .dbx-list-view-item:first-of-type {
78
+ --mat-list-list-item-container-shape: var(--dbx-list-item-radius-resolved) var(--dbx-list-item-radius-resolved) 0 0;
79
+ }
80
+
81
+ mat-selection-list > .dbx-list-view-item:last-of-type {
82
+ --mat-list-list-item-container-shape: 0 0 var(--dbx-list-item-radius-resolved) var(--dbx-list-item-radius-resolved);
83
+ margin-bottom: 0;
84
+ }
85
+
86
+ mat-selection-list > .dbx-list-view-item:only-of-type {
87
+ --mat-list-list-item-container-shape: var(--dbx-list-item-radius-resolved);
88
+ }
89
+
90
+ // Value/nav list (`dbx-list-view-content-group` > `dbx-anchor` rows). First/last is resolved per
91
+ // group, so a grouped list reads as one connected block per group; a single-group list rounds the
92
+ // whole list. Header/footer are `div`s, so `:first/last-of-type` of `dbx-anchor` is correct.
93
+ .dbx-list-view-group-content > dbx-anchor:first-of-type .dbx-list-view-item {
94
+ --mat-list-list-item-container-shape: var(--dbx-list-item-radius-resolved) var(--dbx-list-item-radius-resolved) 0 0;
95
+ }
96
+
97
+ .dbx-list-view-group-content > dbx-anchor:last-of-type .dbx-list-view-item {
98
+ --mat-list-list-item-container-shape: 0 0 var(--dbx-list-item-radius-resolved) var(--dbx-list-item-radius-resolved);
99
+ }
100
+
101
+ .dbx-list-view-group-content > dbx-anchor:last-of-type .dbx-list-view-item.mat-mdc-list-item {
102
+ margin-bottom: 0;
103
+ }
104
+
105
+ .dbx-list-view-group-content > dbx-anchor:only-of-type .dbx-list-view-item {
106
+ --mat-list-list-item-container-shape: var(--dbx-list-item-radius-resolved);
107
+ }
108
+
109
+ // Any hovered / focused / selected / activated / active-route row rounds all four corners. These must
110
+ // out-specify the per-position first/last/only rules above — and the value-list ones
111
+ // (`.dbx-list-view-group-content > dbx-anchor:first-of-type .dbx-list-view-item`) carry an element +
112
+ // pseudo-class, landing at (0,3,1). A bare `.dbx-list-view .mat-mdc-list-item` state selector is only
113
+ // (0,3,0), so it would LOSE and leave the outer corner square — e.g. the active first row keeping a
114
+ // square bottom. Qualifying each row with `.dbx-list-view-item` raises these to (0,4,0) so the
115
+ // all-corners shape wins (every `.dbx-list-view` row — value-list `<a mat-list-item>` and selection
116
+ // `<mat-list-option>` alike — carries that class). `.dbx-anchor-active` covers nav/value-list rows
117
+ // whose active route is signaled by `dbx-anchor` — it never adds Material's `.mdc-list-item--activated`,
118
+ // so without this the active (un-hovered) row would otherwise fall back to the square per-position shape.
119
+ .dbx-list-view .dbx-list-view-item.mat-mdc-list-item:hover,
120
+ .dbx-list-view .dbx-list-view-item.mat-mdc-list-item:focus,
121
+ .dbx-list-view .dbx-anchor-active .dbx-list-view-item.mat-mdc-list-item,
122
+ .dbx-list-view .dbx-list-view-item.mat-mdc-list-item.mdc-list-item--selected,
123
+ .dbx-list-view .dbx-list-view-item.mat-mdc-list-item.mdc-list-item--activated,
124
+ .dbx-list-view .dbx-list-view-item.mat-mdc-list-item[aria-selected='true'] {
125
+ --mat-list-list-item-container-shape: var(--dbx-list-item-radius-resolved);
126
+ }
127
+
128
+ // Paint the selected / activated row with the primary tone, and set an on-tone `color` so the
129
+ // injected row content stays legible (Material only recolors its own `.mdc-list-item__primary-text`
130
+ // / `__start`).
131
+ //
132
+ // A multi-select `mat-list-option` conveys selection via `aria-selected` + its checkbox — it does
133
+ // NOT get the `.mdc-list-item--selected` class (Material only adds that in single-select mode), and
134
+ // it never paints `--mat-list-list-item-selected-container-color`. So match `[aria-selected='true']`
135
+ // too and drive the tint through `--mat-list-list-item-container-color` (the row's own background
136
+ // token), which paints every selected row regardless of the missing class. Nav rows use
137
+ // `.mdc-list-item--activated`.
138
+ .dbx-list-view .mat-mdc-list-item.mdc-list-item--selected,
139
+ .dbx-list-view .mat-mdc-list-item.mdc-list-item--activated,
140
+ .dbx-list-view .mat-mdc-list-item[aria-selected='true'] {
141
+ --mat-list-list-item-container-color: var(--dbx-list-item-selected-color);
142
+ color: var(--dbx-list-item-selected-on-color);
143
+ }
144
+
145
+ // Active-route row tone (a `dbx-anchor` row whose route is active) is painted in `_anchorlist.scss`,
146
+ // which targets both `.dbx-anchor-list` and `.dbx-list-view` nav lists via the
147
+ // `--dbx-anchor-list-item-active-color` token. That token defaults to `--dbx-list-item-active-color`
148
+ // (defined above), so a value list's active route matches its selected tone out of the box; the
149
+ // sidenav overrides the token for its accent treatment. Only the corner shape is handled here (above).
23
150
  }
24
151
 
25
152
  // dbx-anchor-list (nav) rows default to an M3 medium corner via their OWN
@@ -35,13 +162,18 @@ $list-item-padded-min-height: 42px;
35
162
  }
36
163
  }
37
164
 
38
- // The sidenav anchor list rows sit flush against the left screen edge, so only the RIGHT corners round —
39
- // the radius is composed into a `0 r r 0` shorthand (top-left / top-right / bottom-right / bottom-left),
165
+ // Sidenav anchor-list rows sit flush against the left drawer edge, so only the RIGHT corners round — the
166
+ // radius is composed into a `0 r r 0` shorthand (top-left / top-right / bottom-right / bottom-left),
40
167
  // leaving the left corners square. The "how round" radius defaults to the regular anchor-list radius
41
168
  // (`--dbx-anchor-list-item-border-radius`) so the sidenav tracks the `anchor-list-corner-*` lever by default,
42
- // but `--dbx-sidenav-anchor-list-item-border-radius` overrides it for sidenav-specific tuning. Higher
43
- // specificity than `.dbx-anchor-list`, so it wins for the inner nav-list inside a sidenav host.
44
- .dbx-sidenav-anchor-list .dbx-anchor-list {
169
+ // but `--dbx-sidenav-anchor-list-item-border-radius` overrides it for sidenav-specific tuning.
170
+ //
171
+ // Scoped to EVERY anchor list inside a sidenav drawer (`.dbx-sidenav .mat-drawer .dbx-anchor-list`), not
172
+ // just the `[anchors]` list (`.dbx-sidenav-anchor-list`): a separate `<dbx-anchor-list>` dropped into a
173
+ // `[top]` / `[bottom]` content slot must round the same way, otherwise it falls back to the generic
174
+ // all-corners `.dbx-anchor-list` radius and reads as floating full-radius pills beside the flush main
175
+ // rows. Higher specificity than `.dbx-anchor-list`, so it wins for any inner nav-list inside a sidenav.
176
+ .dbx-sidenav .mat-drawer .dbx-anchor-list {
45
177
  --mat-list-active-indicator-shape: 0 var(--dbx-sidenav-anchor-list-item-border-radius, var(--dbx-anchor-list-item-border-radius, var(--mat-sys-corner-medium, 12px))) var(--dbx-sidenav-anchor-list-item-border-radius, var(--dbx-anchor-list-item-border-radius, var(--mat-sys-corner-medium, 12px))) 0;
46
178
  --mat-list-list-item-container-shape: 0 var(--dbx-sidenav-anchor-list-item-border-radius, var(--dbx-anchor-list-item-border-radius, var(--mat-sys-corner-medium, 12px))) var(--dbx-sidenav-anchor-list-item-border-radius, var(--dbx-anchor-list-item-border-radius, var(--mat-sys-corner-medium, 12px))) 0;
47
179
  }
@@ -219,6 +351,18 @@ $list-item-padded-min-height: 42px;
219
351
  margin-bottom: var(--dbx-list-card-items-list-gap);
220
352
  }
221
353
 
354
+ // Keep every card a fully-rounded standalone surface — defeat the connected-list per-position /
355
+ // per-state corner shaping defined on `.dbx-list` (those rules out-specify the card item rule
356
+ // above). Re-scoping under `.dbx-list` adds the card-ancestor class so these always win.
357
+ .dbx-list .dbx-list-view-group-content > dbx-anchor .dbx-list-view-item.mat-mdc-list-item,
358
+ .dbx-list mat-selection-list > .dbx-list-view-item.mat-mdc-list-item,
359
+ .dbx-list .dbx-list-view .mat-mdc-list-item:hover,
360
+ .dbx-list .dbx-list-view .mat-mdc-list-item:focus,
361
+ .dbx-list .dbx-list-view .mat-mdc-list-item.mdc-list-item--selected,
362
+ .dbx-list .dbx-list-view .mat-mdc-list-item.mdc-list-item--activated {
363
+ --mat-list-list-item-container-shape: var(--dbx-list-item-card-border-radius, var(--mat-sys-corner-large, calc(var(--mat-chip-container-shape-radius) * 2)));
364
+ }
365
+
222
366
  // Strip the trailing card's bottom gap. The nav-list path wraps each
223
367
  // item in a `<dbx-anchor>`; the selection-list path renders
224
368
  // `<mat-list-option>` siblings directly under `<mat-selection-list>`.
@@ -346,13 +490,53 @@ $list-item-padded-min-height: 42px;
346
490
  position: unset; // do not touch the divider in dbx-list
347
491
  }
348
492
 
493
+ // Accordion list view renders as a card with a rounded TOP. The rounded frame lives on the scroll
494
+ // VIEWPORT (`.dbx-list-content`) so it clips the scrolling content — including panels that scroll BEHIND a
495
+ // sticky group header — which stops them bleeding through the rounded corner. (A rounded ancestor can't
496
+ // clip a composited `position: sticky` element in Chrome, so each group header additionally rounds its OWN
497
+ // top corners below.) Only the top corners are rounded — the bottom stays square so the last panel's
498
+ // elevation shadow isn't clipped by a rounded bottom edge. Scoped with `:has()` so only accordion lists
499
+ // get framed.
500
+ .dbx-list-content:has(.dbx-list-accordion-view) {
501
+ border-top-left-radius: var(--dbx-list-accordion-view-border-radius, var(--mat-sys-corner-medium, 12px));
502
+ border-top-right-radius: var(--dbx-list-accordion-view-border-radius, var(--mat-sys-corner-medium, 12px));
503
+ }
504
+
349
505
  .dbx-list-accordion-view {
506
+ // Breathing-room margin (`--dbx-list-accordion-view-margin`, default `--dbx-padding-1` / 2px) so the
507
+ // card sits inset from its container and its walls stay visible — flush against the container the card's
508
+ // edges read as cut off. `padding-bottom` leaves room below the last panel so its elevation shadow shows
509
+ // instead of being clipped at the scroll edge.
510
+ display: block;
511
+ margin: var(--dbx-list-accordion-view-margin, var(--dbx-padding-1));
512
+ padding-bottom: var(--dbx-list-accordion-view-bottom-space, var(--dbx-padding-2));
513
+ --dbx-list-accordion-view-radius-resolved: var(--dbx-list-accordion-view-border-radius, var(--mat-sys-corner-medium, 12px));
350
514
  --mat-expansion-container-shape: 0px;
515
+
516
+ // Round the top corners of the first entry (the very top of the card) AND of every group header. Group
517
+ // headers are sticky — when one pins at the rounded top corner, the scroll frame's clip would otherwise
518
+ // shave that header's square corner (and its border) flat, so each header rounds its OWN top corners to
519
+ // follow the curve. `overflow: hidden` clips the header's content/border to those corners.
520
+ mat-accordion > :first-child,
521
+ .dbx-list-view-group-header {
522
+ border-top-left-radius: var(--dbx-list-accordion-view-radius-resolved);
523
+ border-top-right-radius: var(--dbx-list-accordion-view-radius-resolved);
524
+ overflow: hidden;
525
+ }
526
+
527
+ // Box every group header with the same hairline the two-column view uses (`1px solid
528
+ // var(--dbx-divider-color)`) rather than the heavier/darker `--mat-divider-*`; the shorthand overrides
529
+ // the bottom divider on the base `.dbx-list-view-group-header` rule so all four sides match. The `-1px`
530
+ // margin pulls the header out so its border overlaps the adjacent panel edges (no doubled hairline / gap).
531
+ .dbx-list-view-group-header {
532
+ margin: -1px;
533
+ border: 1px solid var(--dbx-divider-color);
534
+ }
351
535
  }
352
536
 
353
537
  .dbx-list-view-group-header {
354
538
  color: var(--mdc-list-list-item-label-text-color);
355
- background: var(--mat-sidenav-content-background-color);
539
+ background: var(--dbx-list-view-group-header-background, var(--mat-sidenav-content-background-color));
356
540
 
357
541
  .item-details {
358
542
  color: var(--mdc-list-list-item-supporting-text-color);
@@ -56,6 +56,16 @@ $default-mdc-list-list-item-leading-icon-size: 24px;
56
56
  .dbx-list-view .mat-mdc-nav-list {
57
57
  --mat-divider-color: color-mix(in srgb, var(--mat-sys-outline-variant) 15%, transparent);
58
58
 
59
+ // Active/selected route rows default to the shared dbx list tone so anchor lists match value lists
60
+ // out of the box, instead of a bare on-surface overlay. A standalone `.dbx-anchor-list` isn't
61
+ // necessarily inside a `.dbx-list`, so fall back to the M3 container tones directly when the
62
+ // `--dbx-list-item-*-color` knobs aren't inherited. Override the `--dbx-anchor-list-item-*` tokens to
63
+ // give a list its own active/selected treatment (the sidenav does, for its accent tint + border).
64
+ --dbx-anchor-list-item-selected-color: var(--dbx-list-item-selected-color, var(--mat-sys-primary-container));
65
+ --dbx-anchor-list-item-selected-on-color: var(--dbx-list-item-selected-on-color, var(--mat-sys-on-primary-container));
66
+ --dbx-anchor-list-item-active-color: var(--dbx-list-item-active-color, var(--dbx-anchor-list-item-selected-color));
67
+ --dbx-anchor-list-item-active-on-color: var(--dbx-list-item-active-on-color, var(--dbx-anchor-list-item-selected-on-color));
68
+
59
69
  // MARK: Muting
60
70
  // items that are not active are muted
61
71
  .mat-mdc-list-item {
@@ -77,11 +87,32 @@ $default-mdc-list-list-item-leading-icon-size: 24px;
77
87
  }
78
88
  }
79
89
 
90
+ // MARK: Anchor Tree
91
+ // Nested child rows get a subtle depth-shading background. This is the BASE tone for a child row, so
92
+ // it is declared BEFORE the selected / active-route rules below: those match the same row at equal
93
+ // specificity (0,4,0), so source order decides — keeping this first lets an active or selected child
94
+ // row layer its own tone on top instead of being masked by the depth shading (which left an active
95
+ // child reading as un-selected: grey background, only the text recolored).
96
+ //
97
+ // The tone is an override point: set `--dbx-anchor-list-item-child-background` (e.g. to `transparent`)
98
+ // to flatten the depth-shade so nested children read like top-level root rows. Because the
99
+ // active/selected rules below win by source order, overriding the token never clears an active child's
100
+ // accent — only the inactive depth-shade. The `.dbx-sidenav-flat` modifier uses this.
101
+
102
+ /// Background tone applied to nested (depth > 0) anchor-list child rows to set them apart from
103
+ /// top-level root rows. Override to `transparent` to flatten the nesting depth-shade.
104
+ /// @role color
105
+ /// @intent nested child row background, anchor list depth shade, flatten nested rows
106
+ .dbx-anchor-list-child .mat-mdc-list-item {
107
+ background: var(--dbx-anchor-list-item-child-background, color-mix(in srgb, var(--mat-sys-on-surface) 14%, transparent));
108
+ }
109
+
80
110
  // selected
81
111
  .dbx-anchor-selected {
82
112
  .mat-mdc-list-item {
83
113
  opacity: #{$anchor-list-item-active-opacity};
84
- background: color-mix(in srgb, var(--mat-sys-on-surface) 8%, transparent);
114
+ background: var(--dbx-anchor-list-item-selected-color);
115
+ color: var(--dbx-anchor-list-item-selected-on-color);
85
116
  }
86
117
  }
87
118
 
@@ -89,14 +120,10 @@ $default-mdc-list-list-item-leading-icon-size: 24px;
89
120
  .dbx-anchor-active-eq {
90
121
  .mat-mdc-list-item {
91
122
  opacity: #{$anchor-list-item-active-opacity};
92
- background: color-mix(in srgb, var(--mat-sys-on-surface) 14%, transparent);
123
+ background: var(--dbx-anchor-list-item-active-color);
124
+ color: var(--dbx-anchor-list-item-active-on-color);
93
125
  }
94
126
  }
95
-
96
- // MARK: Anchor Tree
97
- .dbx-anchor-list-child .mat-mdc-list-item {
98
- background: color-mix(in srgb, var(--mat-sys-on-surface) 14%, transparent);
99
- }
100
127
  }
101
128
  }
102
129
 
@@ -21,6 +21,19 @@ $active-background-transparent-color: 0.93;
21
21
 
22
22
  > mat-sidenav {
23
23
  width: var(--dbx-sidenav-width, #{$width});
24
+ border-top-right-radius: var(--dbx-sidenav-top-corner-radius, var(--mat-sys-corner-extra-large, 24px));
25
+ border-bottom-right-radius: var(--dbx-sidenav-bottom-corner-radius, var(--mat-sys-corner-extra-large, 24px));
26
+
27
+ // Uniform nav items (default). A sidenav is typically assembled from differently-created nav
28
+ // elements — top-level root anchors and nested child anchors in the `[anchors]` tree, plus separate
29
+ // `<dbx-anchor-list>` lists dropped into the `[top]` / `[bottom]` content slots. Left to the generic
30
+ // anchor-list styling these would NOT match up (a separate list rounds all four corners as floating
31
+ // pills, and nested child rows carry a depth-shade). The sidenav normalizes them so every element
32
+ // reads as one consistent nav: nested children drop the depth-shade here (the active/selected rules
33
+ // override this token by source order, so the active route still keeps its accent), the right-only
34
+ // flush-left corner shape is applied to all drawer anchor lists in `_list.scss`, and child content is
35
+ // aligned with root rows below. Apps can restore the depth-shade per list via the token if desired.
36
+ --dbx-anchor-list-item-child-background: transparent;
24
37
 
25
38
  .mat-drawer-inner-container {
26
39
  display: flex;
@@ -38,6 +51,12 @@ $active-background-transparent-color: 0.93;
38
51
  }
39
52
  }
40
53
 
54
+ // Align nested child content with root rows (cancel the small left nudge `_anchorlist.scss` adds
55
+ // to child rows) so a nested child reads like a top-level root item.
56
+ .dbx-anchor-list-child .mat-mdc-list-item .mdc-list-item__content {
57
+ padding-left: 0;
58
+ }
59
+
41
60
  // directly active links have a side border indicator
42
61
  .dbx-anchor-active-eq,
43
62
  .dbx-anchor-list-child .dbx-anchor-active {
@@ -133,6 +152,10 @@ $active-background-transparent-color: 0.93;
133
152
  // --dbx-sidenav-mobile-width Mobile overlay drawer width (default: 80vw)
134
153
  // --dbx-sidenav-icon-width Icon rail drawer width (default: 65px)
135
154
  //
155
+ // Shape:
156
+ // --dbx-sidenav-top-corner-radius Top trailing-edge corner radius (default: --mat-sys-corner-extra-large / 24px)
157
+ // --dbx-sidenav-bottom-corner-radius Bottom trailing-edge corner radius (default: --mat-sys-corner-extra-large / 24px)
158
+ //
136
159
  // Drawer colors:
137
160
  // --dbx-sidenav-background Drawer background color
138
161
  // --dbx-sidenav-text-color Drawer text color
@@ -159,6 +182,17 @@ $active-background-transparent-color: 0.93;
159
182
  .dbx-anchor-list.mat-mdc-nav-list {
160
183
  --dbx-sidenav-active-accent: color-mix(in srgb, var(--dbx-accent-color) 65%, var(--mat-sys-surface));
161
184
 
185
+ // The sidenav opts out of the default solid list tone (which anchor lists now inherit from
186
+ // `--dbx-list-item-active-color`) in favor of its subtle accent overlay + side-border treatment.
187
+ // Define the anchor-list active/selected override tokens here so the base `_anchorlist.scss` rows
188
+ // pick up the accent tint, and pin the on-color to the drawer text color so the active label is
189
+ // not recolored to the list's on-primary-container tone. The explicit root/child background +
190
+ // border rules below still apply on top (they distinguish root vs child and add the indicator).
191
+ --dbx-anchor-list-item-active-color: var(--dbx-sidenav-active-item-background, color-mix(in srgb, var(--dbx-sidenav-active-accent) 12%, transparent));
192
+ --dbx-anchor-list-item-active-on-color: var(--dbx-sidenav-text-color, var(--dbx-color-current, var(--mat-sys-on-surface)));
193
+ --dbx-anchor-list-item-selected-color: var(--dbx-anchor-list-item-active-color);
194
+ --dbx-anchor-list-item-selected-on-color: var(--dbx-anchor-list-item-active-on-color);
195
+
162
196
  .dbx-anchor-active {
163
197
  .mat-mdc-list-item {
164
198
  .mat-mdc-list-item-icon {