@devslab/dds-css 0.9.0 → 0.11.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.
@@ -29,3 +29,18 @@
29
29
  outline-color: Highlight;
30
30
  }
31
31
  }
32
+
33
+ /* Visually hidden — content that stays in the accessibility tree (screen
34
+ * readers, AT) but takes up no visual space. Unlike display: none, which
35
+ * removes the element from the tree too, this only clips it out of view.
36
+ * Consumers: DataTable captions, icon-only header labels, skip links.
37
+ * The ancestor needs position: relative for this to clip against the right
38
+ * box (see table.css's .dds-table-wrap note) rather than the page. */
39
+ .dds-visually-hidden {
40
+ position: absolute;
41
+ inline-size: 1px;
42
+ block-size: 1px;
43
+ overflow: hidden;
44
+ clip-path: inset(50%);
45
+ white-space: nowrap;
46
+ }
@@ -0,0 +1,360 @@
1
+ /* Console shell — the 232px rail + below-900px drawer that a console route
2
+ * renders around its content. The fold breakpoint below is written in the
3
+ * same rem form as table.css's column fold (56.25rem = 900px at a 16px root
4
+ * font size), so a visitor with any browser default font size sees the rail
5
+ * change shape and the table's columns fold at the same viewport width
6
+ * inside the same console.
7
+ *
8
+ * Tokens only. Every class that declares `display` re-declares
9
+ * `[hidden] { display: none }` beside it, at a class+attribute specificity
10
+ * that beats every later `display` declaration for that class — including
11
+ * the ones inside the media query below — so a hidden rail, toggle, or
12
+ * scrim can never be drawn on screen regardless of viewport width. */
13
+
14
+ .dds-console-shell {
15
+ min-block-size: 100vh;
16
+ display: grid;
17
+ grid-template-columns: 14.5rem minmax(0, 1fr);
18
+ background: var(--dds-color-bg-default);
19
+ color: var(--dds-color-text-primary);
20
+ font-size: 1rem;
21
+ line-height: 1.5;
22
+ }
23
+ .dds-console-shell[hidden] {
24
+ display: none;
25
+ }
26
+
27
+ /* The skip link is hidden by .dds-visually-hidden (applied by the
28
+ * component) and only needs to say what it looks like once focused —
29
+ * a visually-hidden element is never in the focus-visible/`:focus` chain
30
+ * while `hidden`, so no `[hidden]` guard applies here. */
31
+ .dds-console-skip:focus {
32
+ position: fixed;
33
+ inset-block-start: var(--dds-space-8);
34
+ inset-inline-start: var(--dds-space-8);
35
+ z-index: 100;
36
+ clip-path: none;
37
+ inline-size: auto;
38
+ block-size: auto;
39
+ overflow: visible;
40
+ white-space: normal;
41
+ padding: var(--dds-space-12) var(--dds-space-16);
42
+ background: var(--dds-color-bg-elevated);
43
+ color: var(--dds-color-text-primary);
44
+ border: 2px solid var(--dds-color-border-focus);
45
+ }
46
+
47
+ .dds-console-rail {
48
+ position: sticky;
49
+ inset-block-start: 0;
50
+ block-size: 100vh;
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: var(--dds-space-16);
54
+ padding: var(--dds-space-20) var(--dds-space-12);
55
+ overflow-y: auto;
56
+ border-inline-end: 1px solid var(--dds-color-border-default);
57
+ background: var(--dds-color-bg-elevated);
58
+ }
59
+ .dds-console-rail[hidden] {
60
+ display: none;
61
+ }
62
+
63
+ .dds-console-rail__brand {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ gap: var(--dds-space-8);
67
+ padding-inline: var(--dds-space-8);
68
+ color: inherit;
69
+ font-size: .9375rem;
70
+ font-weight: 700;
71
+ letter-spacing: -.02em;
72
+ text-decoration: none;
73
+ }
74
+ .dds-console-rail__brand[hidden] {
75
+ display: none;
76
+ }
77
+ .dds-console-rail__brand img {
78
+ inline-size: 20px;
79
+ block-size: 20px;
80
+ }
81
+
82
+ .dds-console-rail__context {
83
+ display: grid;
84
+ gap: var(--dds-space-4);
85
+ margin: 0;
86
+ padding-inline: var(--dds-space-8);
87
+ font-size: var(--dds-typo-caption-font-size);
88
+ line-height: var(--dds-typo-caption-line-height);
89
+ }
90
+ .dds-console-rail__context[hidden] {
91
+ display: none;
92
+ }
93
+ .dds-console-rail__context dt {
94
+ color: var(--dds-color-text-muted);
95
+ }
96
+ .dds-console-rail__context dd {
97
+ margin: 0;
98
+ font-family: var(--dds-font-family-mono);
99
+ overflow-wrap: anywhere;
100
+ }
101
+
102
+ .dds-console-rail__nav {
103
+ display: grid;
104
+ gap: var(--dds-space-16);
105
+ }
106
+ .dds-console-rail__nav[hidden] {
107
+ display: none;
108
+ }
109
+
110
+ .dds-console-rail__group {
111
+ display: grid;
112
+ gap: var(--dds-space-2, 2px);
113
+ }
114
+ .dds-console-rail__group[hidden] {
115
+ display: none;
116
+ }
117
+ .dds-console-rail__group ul {
118
+ margin: 0;
119
+ padding: 0;
120
+ list-style: none;
121
+ display: grid;
122
+ gap: 2px;
123
+ }
124
+ .dds-console-rail__group ul[hidden] {
125
+ display: none;
126
+ }
127
+ .dds-console-rail__group-label {
128
+ margin: 0;
129
+ padding: 0 var(--dds-space-8) var(--dds-space-4);
130
+ color: var(--dds-color-text-muted);
131
+ font-size: .6875rem;
132
+ font-weight: 600;
133
+ letter-spacing: .05em;
134
+ text-transform: uppercase;
135
+ }
136
+
137
+ .dds-console-rail__item {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ gap: var(--dds-space-8);
142
+ min-block-size: var(--dds-space-40);
143
+ padding-inline: var(--dds-space-8);
144
+ border-radius: var(--dds-radius-md);
145
+ color: var(--dds-color-text-secondary);
146
+ font-size: .875rem;
147
+ font-weight: 500;
148
+ text-decoration: none;
149
+ }
150
+ .dds-console-rail__item[hidden] {
151
+ display: none;
152
+ }
153
+ .dds-console-rail__item:hover {
154
+ background: var(--dds-color-bg-subtle);
155
+ color: var(--dds-color-text-primary);
156
+ }
157
+ .dds-console-rail__item[aria-current="page"] {
158
+ background: var(--dds-color-bg-brand-subtle);
159
+ color: var(--dds-color-text-brand);
160
+ font-weight: 650;
161
+ }
162
+ .dds-console-rail__item:focus-visible {
163
+ outline: 2px solid var(--dds-color-border-focus);
164
+ outline-offset: 2px;
165
+ }
166
+
167
+ .dds-console-rail__badge {
168
+ display: inline-grid;
169
+ place-items: center;
170
+ min-inline-size: 1.125rem;
171
+ block-size: 1.125rem;
172
+ padding-inline: .3125rem;
173
+ border-radius: var(--dds-radius-full);
174
+ background: var(--dds-color-bg-brand);
175
+ color: var(--dds-color-text-on-brand);
176
+ font-size: .6875rem;
177
+ font-weight: 700;
178
+ font-variant-numeric: tabular-nums;
179
+ }
180
+ .dds-console-rail__badge[hidden] {
181
+ display: none;
182
+ }
183
+
184
+ .dds-console-rail__foot {
185
+ margin-block-start: auto;
186
+ display: grid;
187
+ gap: var(--dds-space-8);
188
+ justify-items: start;
189
+ padding: var(--dds-space-12) var(--dds-space-8) 0;
190
+ border-block-start: 1px solid var(--dds-color-border-default);
191
+ }
192
+ .dds-console-rail__foot[hidden] {
193
+ display: none;
194
+ }
195
+
196
+ .dds-console-rail__toggle {
197
+ display: none;
198
+ }
199
+ .dds-console-rail__toggle[hidden] {
200
+ display: none;
201
+ }
202
+
203
+ .dds-console-rail__scrim {
204
+ display: none;
205
+ }
206
+ .dds-console-rail__scrim[hidden] {
207
+ display: none;
208
+ }
209
+
210
+ .dds-console-main {
211
+ min-inline-size: 0;
212
+ padding: clamp(1rem, 3vw, 2rem);
213
+ }
214
+
215
+ .dds-console-page-header {
216
+ display: flex;
217
+ align-items: flex-start;
218
+ justify-content: space-between;
219
+ gap: var(--dds-space-32);
220
+ margin-block-end: var(--dds-space-32);
221
+ }
222
+ .dds-console-page-header[hidden] {
223
+ display: none;
224
+ }
225
+ .dds-console-page-header h1 {
226
+ max-inline-size: 24ch;
227
+ margin: .25rem 0 0;
228
+ font-size: clamp(1.75rem, 2.2vw, 2.25rem);
229
+ line-height: 1.1;
230
+ letter-spacing: -.03em;
231
+ text-wrap: balance;
232
+ }
233
+ .dds-console-page-header__actions {
234
+ display: flex;
235
+ gap: var(--dds-space-8);
236
+ }
237
+ .dds-console-page-header__actions[hidden] {
238
+ display: none;
239
+ }
240
+ .dds-console-page-eyebrow {
241
+ margin: 0;
242
+ color: var(--dds-color-text-muted);
243
+ font-size: var(--dds-typo-caption-font-size);
244
+ line-height: var(--dds-typo-caption-line-height);
245
+ }
246
+
247
+ /* Console density: every dds-css control inside the shell reads at the body-2
248
+ * size (14px), not dds-btn's own default (13px, --dds-typo-label-font-size).
249
+ * Scoped to .dds-console-shell rather than left as a global override on
250
+ * .dds-btn/.dds-input/.dds-select/.dds-textarea themselves — a shell that
251
+ * establishes a density owns the override for controls inside it, the same
252
+ * way a shared class must never carry a layout rule that only makes sense
253
+ * for one consumer's container.
254
+ *
255
+ * .dds-btn's :not() exclusions matter: this selector's specificity (0,2,0)
256
+ * beats every size modifier in button.css (.dds-btn--lg, .dds-btn--sm, both
257
+ * 0,1,0), so an unqualified `.dds-console-shell .dds-btn` would override
258
+ * --lg's own font-size back down to body-2 and would give --sm the same
259
+ * font-size as a default button (--sm declares none of its own, so it was
260
+ * inheriting whichever rule won for plain .dds-btn) — the one outcome a
261
+ * size modifier exists to prevent. .dds-input/.dds-select/.dds-textarea
262
+ * carry no such modifiers today, so no matching exclusion is needed there. */
263
+ .dds-console-shell .dds-btn:not(.dds-btn--lg):not(.dds-btn--sm),
264
+ .dds-console-shell .dds-input,
265
+ .dds-console-shell .dds-select,
266
+ .dds-console-shell .dds-textarea {
267
+ font-size: var(--dds-typo-body-2-font-size);
268
+ }
269
+
270
+ @media (max-width: 56.25rem) {
271
+ .dds-console-shell {
272
+ grid-template-columns: minmax(0, 1fr);
273
+ }
274
+ .dds-console-rail__toggle {
275
+ position: fixed;
276
+ inset-block-start: .75rem;
277
+ inset-inline-start: .75rem;
278
+ z-index: 70;
279
+ display: inline-grid;
280
+ place-items: center;
281
+ inline-size: 2.5rem;
282
+ block-size: 2.5rem;
283
+ border: 1px solid var(--dds-color-border-default);
284
+ border-radius: var(--dds-radius-md);
285
+ background: var(--dds-color-bg-elevated);
286
+ cursor: pointer;
287
+ }
288
+ .dds-console-rail__toggle > span[aria-hidden] {
289
+ position: relative;
290
+ display: block;
291
+ inline-size: 16px;
292
+ block-size: 2px;
293
+ border-radius: 2px;
294
+ background: currentColor;
295
+ }
296
+ .dds-console-rail__toggle > span[aria-hidden]::before,
297
+ .dds-console-rail__toggle > span[aria-hidden]::after {
298
+ content: "";
299
+ position: absolute;
300
+ inset-inline-start: 0;
301
+ inline-size: 16px;
302
+ block-size: 2px;
303
+ border-radius: 2px;
304
+ background: currentColor;
305
+ }
306
+ .dds-console-rail__toggle > span[aria-hidden]::before {
307
+ inset-block-start: -5px;
308
+ }
309
+ .dds-console-rail__toggle > span[aria-hidden]::after {
310
+ inset-block-start: 5px;
311
+ }
312
+ /* Translated off-screen is not gone: a closed drawer still takes Tab. Hide
313
+ it from the focus order with visibility, delayed by the slide's
314
+ duration so closing still animates, and lifted with no delay when it
315
+ opens. */
316
+ .dds-console-rail {
317
+ position: fixed;
318
+ inset: 0 auto 0 0;
319
+ z-index: 60;
320
+ inline-size: min(84vw, 18rem);
321
+ block-size: 100dvh;
322
+ visibility: hidden;
323
+ translate: -100% 0;
324
+ transition: translate .22s cubic-bezier(.16, 1, .3, 1), visibility 0s linear .22s;
325
+ box-shadow: var(--dds-elevation-2);
326
+ }
327
+ .dds-console-rail[data-open="true"] {
328
+ visibility: visible;
329
+ translate: 0 0;
330
+ transition-delay: 0s, 0s;
331
+ }
332
+ .dds-console-rail__scrim {
333
+ display: block;
334
+ position: fixed;
335
+ inset: 0;
336
+ z-index: 55;
337
+ background: var(--dds-color-overlay-dim);
338
+ opacity: 0;
339
+ pointer-events: none;
340
+ transition: opacity .22s ease;
341
+ }
342
+ .dds-console-rail__scrim[data-open="true"] {
343
+ opacity: 1;
344
+ pointer-events: auto;
345
+ }
346
+ .dds-console-main {
347
+ padding-block-start: 4rem;
348
+ }
349
+ .dds-console-page-header {
350
+ display: grid;
351
+ gap: var(--dds-space-16);
352
+ }
353
+ }
354
+
355
+ @media (prefers-reduced-motion: reduce) {
356
+ .dds-console-rail,
357
+ .dds-console-rail__scrim {
358
+ transition: none;
359
+ }
360
+ }
@@ -0,0 +1,151 @@
1
+ /* Table — the console list surface. Geometry comes from VisionLinq's shipped
2
+ * console (40px rows, 36px dense, sticky header in the tall variant); the
3
+ * colours are tokens only.
4
+ *
5
+ * .dds-table-wrap keeps position: relative even though it looks like dead
6
+ * positioning: it is what lets an absolutely-positioned .dds-visually-hidden
7
+ * caption or header label (see base.css) resolve against the table's own
8
+ * box instead of the page once the wrap is scrolling horizontally. Don't
9
+ * "clean up" this rule. */
10
+
11
+ .dds-table-wrap {
12
+ position: relative;
13
+ overflow-x: auto;
14
+ border: 1px solid var(--dds-color-border-default);
15
+ }
16
+
17
+ .dds-table-wrap--tall {
18
+ max-block-size: 70vh;
19
+ overflow: auto;
20
+ }
21
+
22
+ .dds-table {
23
+ inline-size: 100%;
24
+ border-collapse: collapse;
25
+ background: var(--dds-color-bg-elevated);
26
+ }
27
+
28
+ .dds-table caption {
29
+ padding: var(--dds-space-16);
30
+ text-align: start;
31
+ font-weight: 650;
32
+ }
33
+
34
+ .dds-table th,
35
+ .dds-table td {
36
+ padding: .625rem var(--dds-space-16);
37
+ border-block-start: 1px solid var(--dds-color-border-default);
38
+ font-size: .875rem;
39
+ line-height: 1.5;
40
+ text-align: start;
41
+ vertical-align: middle;
42
+ white-space: nowrap;
43
+ }
44
+
45
+ .dds-table tbody tr {
46
+ block-size: var(--dds-space-40);
47
+ }
48
+
49
+ .dds-table--fixed {
50
+ table-layout: fixed;
51
+ }
52
+
53
+ .dds-table [data-numeric] {
54
+ text-align: end;
55
+ font-variant-numeric: tabular-nums;
56
+ }
57
+
58
+ .dds-table th small {
59
+ display: block;
60
+ color: var(--dds-color-text-muted);
61
+ font-weight: 400;
62
+ }
63
+ .dds-table th small[hidden] {
64
+ display: none;
65
+ }
66
+
67
+ .dds-table--dense th,
68
+ .dds-table--dense td {
69
+ padding-block: .375rem;
70
+ }
71
+
72
+ .dds-table--dense tbody tr {
73
+ block-size: 2.25rem;
74
+ }
75
+
76
+ .dds-table-wrap--tall thead th {
77
+ position: sticky;
78
+ inset-block-start: 0;
79
+ z-index: 1;
80
+ background: var(--dds-color-bg-elevated);
81
+ }
82
+
83
+ .dds-table__sort {
84
+ appearance: none;
85
+ display: inline-flex;
86
+ align-items: center;
87
+ gap: .375rem;
88
+ padding: 0;
89
+ border: 0;
90
+ background: none;
91
+ font: inherit;
92
+ font-weight: 650;
93
+ color: inherit;
94
+ cursor: pointer;
95
+ }
96
+ .dds-table__sort[hidden] {
97
+ display: none;
98
+ }
99
+ .dds-table__sort:focus-visible {
100
+ outline: 2px solid var(--dds-color-border-focus);
101
+ outline-offset: 2px;
102
+ }
103
+
104
+ .dds-table__sort-mark {
105
+ inline-size: .5rem;
106
+ block-size: .5rem;
107
+ border-inline-end: 2px solid currentColor;
108
+ border-block-end: 2px solid currentColor;
109
+ rotate: 45deg;
110
+ opacity: .35;
111
+ translate: 0 -.125rem;
112
+ }
113
+ .dds-table__sort-mark[data-sort-state="ascending"] {
114
+ rotate: -135deg;
115
+ opacity: 1;
116
+ translate: 0 .125rem;
117
+ }
118
+ .dds-table__sort-mark[data-sort-state="descending"] {
119
+ opacity: 1;
120
+ }
121
+
122
+ .dds-table__actions {
123
+ display: flex;
124
+ justify-content: flex-end;
125
+ gap: var(--dds-space-8);
126
+ }
127
+ .dds-table__actions[hidden] {
128
+ display: none;
129
+ }
130
+
131
+ .dds-table__order {
132
+ margin: 0 0 var(--dds-space-8);
133
+ font-size: .8125rem;
134
+ color: var(--dds-color-text-muted);
135
+ font-family: var(--dds-font-family-mono);
136
+ }
137
+
138
+ .dds-table__pagination {
139
+ display: flex;
140
+ justify-content: flex-end;
141
+ margin-block-start: var(--dds-space-16);
142
+ }
143
+ .dds-table__pagination[hidden] {
144
+ display: none;
145
+ }
146
+
147
+ @media (max-width: 56.25rem) {
148
+ .dds-table [data-fold] {
149
+ display: none;
150
+ }
151
+ }
package/dist/dds.css CHANGED
@@ -33,6 +33,21 @@
33
33
  }
34
34
  }
35
35
 
36
+ /* Visually hidden — content that stays in the accessibility tree (screen
37
+ * readers, AT) but takes up no visual space. Unlike display: none, which
38
+ * removes the element from the tree too, this only clips it out of view.
39
+ * Consumers: DataTable captions, icon-only header labels, skip links.
40
+ * The ancestor needs position: relative for this to clip against the right
41
+ * box (see table.css's .dds-table-wrap note) rather than the page. */
42
+ .dds-visually-hidden {
43
+ position: absolute;
44
+ inline-size: 1px;
45
+ block-size: 1px;
46
+ overflow: hidden;
47
+ clip-path: inset(50%);
48
+ white-space: nowrap;
49
+ }
50
+
36
51
  /* Button — spec §4.2: default → hover → pressed → focus-visible → disabled → loading.
37
52
  * Variants: primary / secondary / ghost / danger. Sizes: sm / md(default) / lg.
38
53
  * Control heights come from the spacing scale (32/40/48); type from the typo
@@ -1207,3 +1222,516 @@ dialog.dds-dialog::backdrop {
1207
1222
  gap: var(--dds-space-8);
1208
1223
  margin-top: var(--dds-space-8);
1209
1224
  }
1225
+
1226
+ /* Table — the console list surface. Geometry comes from VisionLinq's shipped
1227
+ * console (40px rows, 36px dense, sticky header in the tall variant); the
1228
+ * colours are tokens only.
1229
+ *
1230
+ * .dds-table-wrap keeps position: relative even though it looks like dead
1231
+ * positioning: it is what lets an absolutely-positioned .dds-visually-hidden
1232
+ * caption or header label (see base.css) resolve against the table's own
1233
+ * box instead of the page once the wrap is scrolling horizontally. Don't
1234
+ * "clean up" this rule. */
1235
+
1236
+ .dds-table-wrap {
1237
+ position: relative;
1238
+ overflow-x: auto;
1239
+ border: 1px solid var(--dds-color-border-default);
1240
+ }
1241
+
1242
+ .dds-table-wrap--tall {
1243
+ max-block-size: 70vh;
1244
+ overflow: auto;
1245
+ }
1246
+
1247
+ .dds-table {
1248
+ inline-size: 100%;
1249
+ border-collapse: collapse;
1250
+ background: var(--dds-color-bg-elevated);
1251
+ }
1252
+
1253
+ .dds-table caption {
1254
+ padding: var(--dds-space-16);
1255
+ text-align: start;
1256
+ font-weight: 650;
1257
+ }
1258
+
1259
+ .dds-table th,
1260
+ .dds-table td {
1261
+ padding: .625rem var(--dds-space-16);
1262
+ border-block-start: 1px solid var(--dds-color-border-default);
1263
+ font-size: .875rem;
1264
+ line-height: 1.5;
1265
+ text-align: start;
1266
+ vertical-align: middle;
1267
+ white-space: nowrap;
1268
+ }
1269
+
1270
+ .dds-table tbody tr {
1271
+ block-size: var(--dds-space-40);
1272
+ }
1273
+
1274
+ .dds-table--fixed {
1275
+ table-layout: fixed;
1276
+ }
1277
+
1278
+ .dds-table [data-numeric] {
1279
+ text-align: end;
1280
+ font-variant-numeric: tabular-nums;
1281
+ }
1282
+
1283
+ .dds-table th small {
1284
+ display: block;
1285
+ color: var(--dds-color-text-muted);
1286
+ font-weight: 400;
1287
+ }
1288
+ .dds-table th small[hidden] {
1289
+ display: none;
1290
+ }
1291
+
1292
+ .dds-table--dense th,
1293
+ .dds-table--dense td {
1294
+ padding-block: .375rem;
1295
+ }
1296
+
1297
+ .dds-table--dense tbody tr {
1298
+ block-size: 2.25rem;
1299
+ }
1300
+
1301
+ .dds-table-wrap--tall thead th {
1302
+ position: sticky;
1303
+ inset-block-start: 0;
1304
+ z-index: 1;
1305
+ background: var(--dds-color-bg-elevated);
1306
+ }
1307
+
1308
+ .dds-table__sort {
1309
+ appearance: none;
1310
+ display: inline-flex;
1311
+ align-items: center;
1312
+ gap: .375rem;
1313
+ padding: 0;
1314
+ border: 0;
1315
+ background: none;
1316
+ font: inherit;
1317
+ font-weight: 650;
1318
+ color: inherit;
1319
+ cursor: pointer;
1320
+ }
1321
+ .dds-table__sort[hidden] {
1322
+ display: none;
1323
+ }
1324
+ .dds-table__sort:focus-visible {
1325
+ outline: 2px solid var(--dds-color-border-focus);
1326
+ outline-offset: 2px;
1327
+ }
1328
+
1329
+ .dds-table__sort-mark {
1330
+ inline-size: .5rem;
1331
+ block-size: .5rem;
1332
+ border-inline-end: 2px solid currentColor;
1333
+ border-block-end: 2px solid currentColor;
1334
+ rotate: 45deg;
1335
+ opacity: .35;
1336
+ translate: 0 -.125rem;
1337
+ }
1338
+ .dds-table__sort-mark[data-sort-state="ascending"] {
1339
+ rotate: -135deg;
1340
+ opacity: 1;
1341
+ translate: 0 .125rem;
1342
+ }
1343
+ .dds-table__sort-mark[data-sort-state="descending"] {
1344
+ opacity: 1;
1345
+ }
1346
+
1347
+ .dds-table__actions {
1348
+ display: flex;
1349
+ justify-content: flex-end;
1350
+ gap: var(--dds-space-8);
1351
+ }
1352
+ .dds-table__actions[hidden] {
1353
+ display: none;
1354
+ }
1355
+
1356
+ .dds-table__order {
1357
+ margin: 0 0 var(--dds-space-8);
1358
+ font-size: .8125rem;
1359
+ color: var(--dds-color-text-muted);
1360
+ font-family: var(--dds-font-family-mono);
1361
+ }
1362
+
1363
+ .dds-table__pagination {
1364
+ display: flex;
1365
+ justify-content: flex-end;
1366
+ margin-block-start: var(--dds-space-16);
1367
+ }
1368
+ .dds-table__pagination[hidden] {
1369
+ display: none;
1370
+ }
1371
+
1372
+ @media (max-width: 56.25rem) {
1373
+ .dds-table [data-fold] {
1374
+ display: none;
1375
+ }
1376
+ }
1377
+
1378
+ /* Console shell — the 232px rail + below-900px drawer that a console route
1379
+ * renders around its content. The fold breakpoint below is written in the
1380
+ * same rem form as table.css's column fold (56.25rem = 900px at a 16px root
1381
+ * font size), so a visitor with any browser default font size sees the rail
1382
+ * change shape and the table's columns fold at the same viewport width
1383
+ * inside the same console.
1384
+ *
1385
+ * Tokens only. Every class that declares `display` re-declares
1386
+ * `[hidden] { display: none }` beside it, at a class+attribute specificity
1387
+ * that beats every later `display` declaration for that class — including
1388
+ * the ones inside the media query below — so a hidden rail, toggle, or
1389
+ * scrim can never be drawn on screen regardless of viewport width. */
1390
+
1391
+ .dds-console-shell {
1392
+ min-block-size: 100vh;
1393
+ display: grid;
1394
+ grid-template-columns: 14.5rem minmax(0, 1fr);
1395
+ background: var(--dds-color-bg-default);
1396
+ color: var(--dds-color-text-primary);
1397
+ font-size: 1rem;
1398
+ line-height: 1.5;
1399
+ }
1400
+ .dds-console-shell[hidden] {
1401
+ display: none;
1402
+ }
1403
+
1404
+ /* The skip link is hidden by .dds-visually-hidden (applied by the
1405
+ * component) and only needs to say what it looks like once focused —
1406
+ * a visually-hidden element is never in the focus-visible/`:focus` chain
1407
+ * while `hidden`, so no `[hidden]` guard applies here. */
1408
+ .dds-console-skip:focus {
1409
+ position: fixed;
1410
+ inset-block-start: var(--dds-space-8);
1411
+ inset-inline-start: var(--dds-space-8);
1412
+ z-index: 100;
1413
+ clip-path: none;
1414
+ inline-size: auto;
1415
+ block-size: auto;
1416
+ overflow: visible;
1417
+ white-space: normal;
1418
+ padding: var(--dds-space-12) var(--dds-space-16);
1419
+ background: var(--dds-color-bg-elevated);
1420
+ color: var(--dds-color-text-primary);
1421
+ border: 2px solid var(--dds-color-border-focus);
1422
+ }
1423
+
1424
+ .dds-console-rail {
1425
+ position: sticky;
1426
+ inset-block-start: 0;
1427
+ block-size: 100vh;
1428
+ display: flex;
1429
+ flex-direction: column;
1430
+ gap: var(--dds-space-16);
1431
+ padding: var(--dds-space-20) var(--dds-space-12);
1432
+ overflow-y: auto;
1433
+ border-inline-end: 1px solid var(--dds-color-border-default);
1434
+ background: var(--dds-color-bg-elevated);
1435
+ }
1436
+ .dds-console-rail[hidden] {
1437
+ display: none;
1438
+ }
1439
+
1440
+ .dds-console-rail__brand {
1441
+ display: inline-flex;
1442
+ align-items: center;
1443
+ gap: var(--dds-space-8);
1444
+ padding-inline: var(--dds-space-8);
1445
+ color: inherit;
1446
+ font-size: .9375rem;
1447
+ font-weight: 700;
1448
+ letter-spacing: -.02em;
1449
+ text-decoration: none;
1450
+ }
1451
+ .dds-console-rail__brand[hidden] {
1452
+ display: none;
1453
+ }
1454
+ .dds-console-rail__brand img {
1455
+ inline-size: 20px;
1456
+ block-size: 20px;
1457
+ }
1458
+
1459
+ .dds-console-rail__context {
1460
+ display: grid;
1461
+ gap: var(--dds-space-4);
1462
+ margin: 0;
1463
+ padding-inline: var(--dds-space-8);
1464
+ font-size: var(--dds-typo-caption-font-size);
1465
+ line-height: var(--dds-typo-caption-line-height);
1466
+ }
1467
+ .dds-console-rail__context[hidden] {
1468
+ display: none;
1469
+ }
1470
+ .dds-console-rail__context dt {
1471
+ color: var(--dds-color-text-muted);
1472
+ }
1473
+ .dds-console-rail__context dd {
1474
+ margin: 0;
1475
+ font-family: var(--dds-font-family-mono);
1476
+ overflow-wrap: anywhere;
1477
+ }
1478
+
1479
+ .dds-console-rail__nav {
1480
+ display: grid;
1481
+ gap: var(--dds-space-16);
1482
+ }
1483
+ .dds-console-rail__nav[hidden] {
1484
+ display: none;
1485
+ }
1486
+
1487
+ .dds-console-rail__group {
1488
+ display: grid;
1489
+ gap: var(--dds-space-2, 2px);
1490
+ }
1491
+ .dds-console-rail__group[hidden] {
1492
+ display: none;
1493
+ }
1494
+ .dds-console-rail__group ul {
1495
+ margin: 0;
1496
+ padding: 0;
1497
+ list-style: none;
1498
+ display: grid;
1499
+ gap: 2px;
1500
+ }
1501
+ .dds-console-rail__group ul[hidden] {
1502
+ display: none;
1503
+ }
1504
+ .dds-console-rail__group-label {
1505
+ margin: 0;
1506
+ padding: 0 var(--dds-space-8) var(--dds-space-4);
1507
+ color: var(--dds-color-text-muted);
1508
+ font-size: .6875rem;
1509
+ font-weight: 600;
1510
+ letter-spacing: .05em;
1511
+ text-transform: uppercase;
1512
+ }
1513
+
1514
+ .dds-console-rail__item {
1515
+ display: flex;
1516
+ align-items: center;
1517
+ justify-content: space-between;
1518
+ gap: var(--dds-space-8);
1519
+ min-block-size: var(--dds-space-40);
1520
+ padding-inline: var(--dds-space-8);
1521
+ border-radius: var(--dds-radius-md);
1522
+ color: var(--dds-color-text-secondary);
1523
+ font-size: .875rem;
1524
+ font-weight: 500;
1525
+ text-decoration: none;
1526
+ }
1527
+ .dds-console-rail__item[hidden] {
1528
+ display: none;
1529
+ }
1530
+ .dds-console-rail__item:hover {
1531
+ background: var(--dds-color-bg-subtle);
1532
+ color: var(--dds-color-text-primary);
1533
+ }
1534
+ .dds-console-rail__item[aria-current="page"] {
1535
+ background: var(--dds-color-bg-brand-subtle);
1536
+ color: var(--dds-color-text-brand);
1537
+ font-weight: 650;
1538
+ }
1539
+ .dds-console-rail__item:focus-visible {
1540
+ outline: 2px solid var(--dds-color-border-focus);
1541
+ outline-offset: 2px;
1542
+ }
1543
+
1544
+ .dds-console-rail__badge {
1545
+ display: inline-grid;
1546
+ place-items: center;
1547
+ min-inline-size: 1.125rem;
1548
+ block-size: 1.125rem;
1549
+ padding-inline: .3125rem;
1550
+ border-radius: var(--dds-radius-full);
1551
+ background: var(--dds-color-bg-brand);
1552
+ color: var(--dds-color-text-on-brand);
1553
+ font-size: .6875rem;
1554
+ font-weight: 700;
1555
+ font-variant-numeric: tabular-nums;
1556
+ }
1557
+ .dds-console-rail__badge[hidden] {
1558
+ display: none;
1559
+ }
1560
+
1561
+ .dds-console-rail__foot {
1562
+ margin-block-start: auto;
1563
+ display: grid;
1564
+ gap: var(--dds-space-8);
1565
+ justify-items: start;
1566
+ padding: var(--dds-space-12) var(--dds-space-8) 0;
1567
+ border-block-start: 1px solid var(--dds-color-border-default);
1568
+ }
1569
+ .dds-console-rail__foot[hidden] {
1570
+ display: none;
1571
+ }
1572
+
1573
+ .dds-console-rail__toggle {
1574
+ display: none;
1575
+ }
1576
+ .dds-console-rail__toggle[hidden] {
1577
+ display: none;
1578
+ }
1579
+
1580
+ .dds-console-rail__scrim {
1581
+ display: none;
1582
+ }
1583
+ .dds-console-rail__scrim[hidden] {
1584
+ display: none;
1585
+ }
1586
+
1587
+ .dds-console-main {
1588
+ min-inline-size: 0;
1589
+ padding: clamp(1rem, 3vw, 2rem);
1590
+ }
1591
+
1592
+ .dds-console-page-header {
1593
+ display: flex;
1594
+ align-items: flex-start;
1595
+ justify-content: space-between;
1596
+ gap: var(--dds-space-32);
1597
+ margin-block-end: var(--dds-space-32);
1598
+ }
1599
+ .dds-console-page-header[hidden] {
1600
+ display: none;
1601
+ }
1602
+ .dds-console-page-header h1 {
1603
+ max-inline-size: 24ch;
1604
+ margin: .25rem 0 0;
1605
+ font-size: clamp(1.75rem, 2.2vw, 2.25rem);
1606
+ line-height: 1.1;
1607
+ letter-spacing: -.03em;
1608
+ text-wrap: balance;
1609
+ }
1610
+ .dds-console-page-header__actions {
1611
+ display: flex;
1612
+ gap: var(--dds-space-8);
1613
+ }
1614
+ .dds-console-page-header__actions[hidden] {
1615
+ display: none;
1616
+ }
1617
+ .dds-console-page-eyebrow {
1618
+ margin: 0;
1619
+ color: var(--dds-color-text-muted);
1620
+ font-size: var(--dds-typo-caption-font-size);
1621
+ line-height: var(--dds-typo-caption-line-height);
1622
+ }
1623
+
1624
+ /* Console density: every dds-css control inside the shell reads at the body-2
1625
+ * size (14px), not dds-btn's own default (13px, --dds-typo-label-font-size).
1626
+ * Scoped to .dds-console-shell rather than left as a global override on
1627
+ * .dds-btn/.dds-input/.dds-select/.dds-textarea themselves — a shell that
1628
+ * establishes a density owns the override for controls inside it, the same
1629
+ * way a shared class must never carry a layout rule that only makes sense
1630
+ * for one consumer's container.
1631
+ *
1632
+ * .dds-btn's :not() exclusions matter: this selector's specificity (0,2,0)
1633
+ * beats every size modifier in button.css (.dds-btn--lg, .dds-btn--sm, both
1634
+ * 0,1,0), so an unqualified `.dds-console-shell .dds-btn` would override
1635
+ * --lg's own font-size back down to body-2 and would give --sm the same
1636
+ * font-size as a default button (--sm declares none of its own, so it was
1637
+ * inheriting whichever rule won for plain .dds-btn) — the one outcome a
1638
+ * size modifier exists to prevent. .dds-input/.dds-select/.dds-textarea
1639
+ * carry no such modifiers today, so no matching exclusion is needed there. */
1640
+ .dds-console-shell .dds-btn:not(.dds-btn--lg):not(.dds-btn--sm),
1641
+ .dds-console-shell .dds-input,
1642
+ .dds-console-shell .dds-select,
1643
+ .dds-console-shell .dds-textarea {
1644
+ font-size: var(--dds-typo-body-2-font-size);
1645
+ }
1646
+
1647
+ @media (max-width: 56.25rem) {
1648
+ .dds-console-shell {
1649
+ grid-template-columns: minmax(0, 1fr);
1650
+ }
1651
+ .dds-console-rail__toggle {
1652
+ position: fixed;
1653
+ inset-block-start: .75rem;
1654
+ inset-inline-start: .75rem;
1655
+ z-index: 70;
1656
+ display: inline-grid;
1657
+ place-items: center;
1658
+ inline-size: 2.5rem;
1659
+ block-size: 2.5rem;
1660
+ border: 1px solid var(--dds-color-border-default);
1661
+ border-radius: var(--dds-radius-md);
1662
+ background: var(--dds-color-bg-elevated);
1663
+ cursor: pointer;
1664
+ }
1665
+ .dds-console-rail__toggle > span[aria-hidden] {
1666
+ position: relative;
1667
+ display: block;
1668
+ inline-size: 16px;
1669
+ block-size: 2px;
1670
+ border-radius: 2px;
1671
+ background: currentColor;
1672
+ }
1673
+ .dds-console-rail__toggle > span[aria-hidden]::before,
1674
+ .dds-console-rail__toggle > span[aria-hidden]::after {
1675
+ content: "";
1676
+ position: absolute;
1677
+ inset-inline-start: 0;
1678
+ inline-size: 16px;
1679
+ block-size: 2px;
1680
+ border-radius: 2px;
1681
+ background: currentColor;
1682
+ }
1683
+ .dds-console-rail__toggle > span[aria-hidden]::before {
1684
+ inset-block-start: -5px;
1685
+ }
1686
+ .dds-console-rail__toggle > span[aria-hidden]::after {
1687
+ inset-block-start: 5px;
1688
+ }
1689
+ /* Translated off-screen is not gone: a closed drawer still takes Tab. Hide
1690
+ it from the focus order with visibility, delayed by the slide's
1691
+ duration so closing still animates, and lifted with no delay when it
1692
+ opens. */
1693
+ .dds-console-rail {
1694
+ position: fixed;
1695
+ inset: 0 auto 0 0;
1696
+ z-index: 60;
1697
+ inline-size: min(84vw, 18rem);
1698
+ block-size: 100dvh;
1699
+ visibility: hidden;
1700
+ translate: -100% 0;
1701
+ transition: translate .22s cubic-bezier(.16, 1, .3, 1), visibility 0s linear .22s;
1702
+ box-shadow: var(--dds-elevation-2);
1703
+ }
1704
+ .dds-console-rail[data-open="true"] {
1705
+ visibility: visible;
1706
+ translate: 0 0;
1707
+ transition-delay: 0s, 0s;
1708
+ }
1709
+ .dds-console-rail__scrim {
1710
+ display: block;
1711
+ position: fixed;
1712
+ inset: 0;
1713
+ z-index: 55;
1714
+ background: var(--dds-color-overlay-dim);
1715
+ opacity: 0;
1716
+ pointer-events: none;
1717
+ transition: opacity .22s ease;
1718
+ }
1719
+ .dds-console-rail__scrim[data-open="true"] {
1720
+ opacity: 1;
1721
+ pointer-events: auto;
1722
+ }
1723
+ .dds-console-main {
1724
+ padding-block-start: 4rem;
1725
+ }
1726
+ .dds-console-page-header {
1727
+ display: grid;
1728
+ gap: var(--dds-space-16);
1729
+ }
1730
+ }
1731
+
1732
+ @media (prefers-reduced-motion: reduce) {
1733
+ .dds-console-rail,
1734
+ .dds-console-rail__scrim {
1735
+ transition: none;
1736
+ }
1737
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devslab/dds-css",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "DDS CSS component layer — class-based, framework-neutral, semantic-token-only (spec §4.3 v1 inventory, 20 components)",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",