@dxos/ui-theme 0.8.4-staging.ac66bdf99f → 0.9.1-main.c7dcc2e112

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 (169) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +89 -867
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/node-esm/index.mjs +89 -867
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/plugin/node-cjs/main.css +279 -120
  10. package/dist/plugin/node-cjs/main.css.map +3 -3
  11. package/dist/plugin/node-cjs/meta.json +1 -1
  12. package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs +82 -10
  13. package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs.map +3 -3
  14. package/dist/plugin/node-esm/main.css +279 -120
  15. package/dist/plugin/node-esm/main.css.map +3 -3
  16. package/dist/plugin/node-esm/meta.json +1 -1
  17. package/dist/plugin/node-esm/plugins/ThemePlugin.mjs +84 -12
  18. package/dist/plugin/node-esm/plugins/ThemePlugin.mjs.map +3 -3
  19. package/dist/types/src/Theme.stories.d.ts.map +1 -1
  20. package/dist/types/src/defs.d.ts +1 -1
  21. package/dist/types/src/defs.d.ts.map +1 -1
  22. package/dist/types/src/fragments/density.d.ts +2 -2
  23. package/dist/types/src/fragments/density.d.ts.map +1 -1
  24. package/dist/types/src/fragments/hover.d.ts +0 -1
  25. package/dist/types/src/fragments/hover.d.ts.map +1 -1
  26. package/dist/types/src/index.d.ts +0 -1
  27. package/dist/types/src/index.d.ts.map +1 -1
  28. package/dist/types/src/plugins/ThemePlugin.d.ts +3 -2
  29. package/dist/types/src/plugins/ThemePlugin.d.ts.map +1 -1
  30. package/dist/types/src/util/hash-styles.d.ts +12 -2
  31. package/dist/types/src/util/hash-styles.d.ts.map +1 -1
  32. package/dist/types/src/util/mx.d.ts +0 -36
  33. package/dist/types/src/util/mx.d.ts.map +1 -1
  34. package/dist/types/src/util/size.d.ts.map +1 -1
  35. package/dist/types/src/util/valence.d.ts +8 -2
  36. package/dist/types/src/util/valence.d.ts.map +1 -1
  37. package/dist/types/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +14 -21
  39. package/src/Theme.stories.tsx +94 -27
  40. package/src/css/DESIGN_SYSTEM.md +187 -0
  41. package/src/css/base/base.css +2 -2
  42. package/src/css/components/button.css +57 -12
  43. package/src/css/components/card.css +14 -0
  44. package/src/css/components/checkbox.css +3 -3
  45. package/src/css/components/focus.css +12 -12
  46. package/src/css/components/link.css +4 -1
  47. package/src/css/components/panel.css +45 -45
  48. package/src/css/components/state.css +76 -0
  49. package/src/css/components/state.md +101 -0
  50. package/src/css/components/surface.css +86 -11
  51. package/src/css/components/tag.css +25 -24
  52. package/src/css/integrations/codemirror.css +4 -3
  53. package/src/css/integrations/tldraw.css +1 -1
  54. package/src/css/layout/main.css +6 -0
  55. package/src/css/layout/size.css +17 -4
  56. package/src/css/theme/animation.css +31 -0
  57. package/src/css/theme/palette.css +34 -0
  58. package/src/css/theme/semantic.css +190 -68
  59. package/src/css/theme/spacing.css +30 -13
  60. package/src/css/theme/styles.css +172 -119
  61. package/src/css/utilities.css +42 -0
  62. package/src/defs.ts +3 -1
  63. package/src/fragments/AUDIT.md +0 -2
  64. package/src/fragments/density.ts +34 -7
  65. package/src/fragments/hover.ts +0 -2
  66. package/src/index.ts +1 -1
  67. package/src/main.css +68 -9
  68. package/src/plugins/ThemePlugin.ts +102 -14
  69. package/src/plugins/main.css +10 -7
  70. package/src/util/hash-styles.ts +54 -42
  71. package/src/util/mx.ts +1 -126
  72. package/src/util/valence.ts +15 -5
  73. package/dist/types/src/theme/components/avatar.d.ts +0 -21
  74. package/dist/types/src/theme/components/avatar.d.ts.map +0 -1
  75. package/dist/types/src/theme/components/breadcrumb.d.ts +0 -9
  76. package/dist/types/src/theme/components/breadcrumb.d.ts.map +0 -1
  77. package/dist/types/src/theme/components/button.d.ts +0 -15
  78. package/dist/types/src/theme/components/button.d.ts.map +0 -1
  79. package/dist/types/src/theme/components/card.d.ts +0 -12
  80. package/dist/types/src/theme/components/card.d.ts.map +0 -1
  81. package/dist/types/src/theme/components/dialog.d.ts +0 -17
  82. package/dist/types/src/theme/components/dialog.d.ts.map +0 -1
  83. package/dist/types/src/theme/components/focus.d.ts +0 -6
  84. package/dist/types/src/theme/components/focus.d.ts.map +0 -1
  85. package/dist/types/src/theme/components/icon-button.d.ts +0 -8
  86. package/dist/types/src/theme/components/icon-button.d.ts.map +0 -1
  87. package/dist/types/src/theme/components/icon.d.ts +0 -10
  88. package/dist/types/src/theme/components/icon.d.ts.map +0 -1
  89. package/dist/types/src/theme/components/index.d.ts +0 -27
  90. package/dist/types/src/theme/components/index.d.ts.map +0 -1
  91. package/dist/types/src/theme/components/input.d.ts +0 -115
  92. package/dist/types/src/theme/components/input.d.ts.map +0 -1
  93. package/dist/types/src/theme/components/link.d.ts +0 -7
  94. package/dist/types/src/theme/components/link.d.ts.map +0 -1
  95. package/dist/types/src/theme/components/list.d.ts +0 -14
  96. package/dist/types/src/theme/components/list.d.ts.map +0 -1
  97. package/dist/types/src/theme/components/main.d.ts +0 -28
  98. package/dist/types/src/theme/components/main.d.ts.map +0 -1
  99. package/dist/types/src/theme/components/menu.d.ts +0 -13
  100. package/dist/types/src/theme/components/menu.d.ts.map +0 -1
  101. package/dist/types/src/theme/components/message.d.ts +0 -12
  102. package/dist/types/src/theme/components/message.d.ts.map +0 -1
  103. package/dist/types/src/theme/components/popover.d.ts +0 -11
  104. package/dist/types/src/theme/components/popover.d.ts.map +0 -1
  105. package/dist/types/src/theme/components/scroll-area.d.ts +0 -32
  106. package/dist/types/src/theme/components/scroll-area.d.ts.map +0 -1
  107. package/dist/types/src/theme/components/select.d.ts +0 -13
  108. package/dist/types/src/theme/components/select.d.ts.map +0 -1
  109. package/dist/types/src/theme/components/separator.d.ts +0 -8
  110. package/dist/types/src/theme/components/separator.d.ts.map +0 -1
  111. package/dist/types/src/theme/components/skeleton.d.ts +0 -7
  112. package/dist/types/src/theme/components/skeleton.d.ts.map +0 -1
  113. package/dist/types/src/theme/components/splitter.d.ts +0 -4
  114. package/dist/types/src/theme/components/splitter.d.ts.map +0 -1
  115. package/dist/types/src/theme/components/status.d.ts +0 -9
  116. package/dist/types/src/theme/components/status.d.ts.map +0 -1
  117. package/dist/types/src/theme/components/tag.d.ts +0 -7
  118. package/dist/types/src/theme/components/tag.d.ts.map +0 -1
  119. package/dist/types/src/theme/components/toast.d.ts +0 -12
  120. package/dist/types/src/theme/components/toast.d.ts.map +0 -1
  121. package/dist/types/src/theme/components/toolbar.d.ts +0 -11
  122. package/dist/types/src/theme/components/toolbar.d.ts.map +0 -1
  123. package/dist/types/src/theme/components/tooltip.d.ts +0 -8
  124. package/dist/types/src/theme/components/tooltip.d.ts.map +0 -1
  125. package/dist/types/src/theme/components/treegrid.d.ts +0 -10
  126. package/dist/types/src/theme/components/treegrid.d.ts.map +0 -1
  127. package/dist/types/src/theme/index.d.ts +0 -4
  128. package/dist/types/src/theme/index.d.ts.map +0 -1
  129. package/dist/types/src/theme/primitives/column.d.ts +0 -29
  130. package/dist/types/src/theme/primitives/column.d.ts.map +0 -1
  131. package/dist/types/src/theme/primitives/index.d.ts +0 -3
  132. package/dist/types/src/theme/primitives/index.d.ts.map +0 -1
  133. package/dist/types/src/theme/primitives/panel.d.ts +0 -13
  134. package/dist/types/src/theme/primitives/panel.d.ts.map +0 -1
  135. package/dist/types/src/theme/theme.d.ts +0 -5
  136. package/dist/types/src/theme/theme.d.ts.map +0 -1
  137. package/src/css/components/selected.css +0 -30
  138. package/src/theme/components/avatar.ts +0 -95
  139. package/src/theme/components/breadcrumb.ts +0 -29
  140. package/src/theme/components/button.ts +0 -48
  141. package/src/theme/components/card.ts +0 -102
  142. package/src/theme/components/dialog.ts +0 -61
  143. package/src/theme/components/focus.ts +0 -33
  144. package/src/theme/components/icon-button.ts +0 -18
  145. package/src/theme/components/icon.ts +0 -28
  146. package/src/theme/components/index.ts +0 -30
  147. package/src/theme/components/input.ts +0 -171
  148. package/src/theme/components/link.ts +0 -25
  149. package/src/theme/components/list.ts +0 -46
  150. package/src/theme/components/main.ts +0 -34
  151. package/src/theme/components/menu.ts +0 -50
  152. package/src/theme/components/message.ts +0 -40
  153. package/src/theme/components/popover.ts +0 -41
  154. package/src/theme/components/scroll-area.ts +0 -115
  155. package/src/theme/components/select.ts +0 -52
  156. package/src/theme/components/separator.ts +0 -24
  157. package/src/theme/components/skeleton.ts +0 -23
  158. package/src/theme/components/splitter.ts +0 -20
  159. package/src/theme/components/status.ts +0 -32
  160. package/src/theme/components/tag.ts +0 -23
  161. package/src/theme/components/toast.ts +0 -53
  162. package/src/theme/components/toolbar.ts +0 -35
  163. package/src/theme/components/tooltip.ts +0 -27
  164. package/src/theme/components/treegrid.ts +0 -37
  165. package/src/theme/index.ts +0 -7
  166. package/src/theme/primitives/column.ts +0 -71
  167. package/src/theme/primitives/index.ts +0 -6
  168. package/src/theme/primitives/panel.ts +0 -43
  169. package/src/theme/theme.ts +0 -87
@@ -12,10 +12,10 @@
12
12
  }
13
13
 
14
14
  &:focus-visible {
15
- @apply ring-focus-line ring-offset-focus-offset z-[1] ring-[var(--color-neutral-focus-indicator)];
15
+ @apply ring-focus-line ring-offset-focus-offset z-[1] ring-[var(--color-focus-ring-subtle)];
16
16
 
17
17
  &[data-variant='primary'] {
18
- @apply ring-[var(--color-accent-focus-indicator)];
18
+ @apply ring-[var(--color-focus-ring)];
19
19
  }
20
20
 
21
21
  &:hover {
@@ -45,10 +45,10 @@
45
45
  }
46
46
 
47
47
  &:focus {
48
- @apply ring-focus-line ring-offset-focus-offset z-[1] ring-[var(--color-neutral-focus-indicator)];
48
+ @apply ring-focus-line ring-offset-focus-offset z-[1] ring-[var(--color-focus-ring-subtle)];
49
49
 
50
50
  &[data-variant='primary'] {
51
- @apply ring-[var(--color-accent-focus-indicator)];
51
+ @apply ring-[var(--color-focus-ring)];
52
52
  }
53
53
 
54
54
  &:hover {
@@ -79,10 +79,10 @@
79
79
  .dx-focus-ring-group:focus-visible .dx-focus-ring-group-indicator,
80
80
  .dx-focus-ring-group-x:focus-visible .dx-focus-ring-group-x-indicator,
81
81
  .dx-focus-ring-group-y:focus-visible .dx-focus-ring-group-y-indicator {
82
- @apply ring-focus-line ring-offset-focus-offset ring-[var(--color-neutral-focus-indicator)];
82
+ @apply ring-focus-line ring-offset-focus-offset ring-[var(--color-focus-ring-subtle)];
83
83
 
84
84
  &[data-variant='primary'] {
85
- @apply ring-[var(--color-accent-focus-indicator)];
85
+ @apply ring-[var(--color-focus-ring)];
86
86
  }
87
87
 
88
88
  &:hover {
@@ -97,10 +97,10 @@
97
97
  .dx-focus-ring-group-always:focus .dx-focus-ring-group-indicator,
98
98
  .dx-focus-ring-group-x-always:focus .dx-focus-ring-group-x-indicator,
99
99
  .dx-focus-ring-group-y-always:focus .dx-focus-ring-group-y-indicator {
100
- @apply ring-focus-line ring-offset-focus-offset ring-[var(--color-neutral-focus-indicator)];
100
+ @apply ring-focus-line ring-offset-focus-offset ring-[var(--color-focus-ring-subtle)];
101
101
 
102
102
  &[data-variant='primary'] {
103
- @apply ring-[var(--color-accent-focus-indicator)];
103
+ @apply ring-[var(--color-focus-ring)];
104
104
  }
105
105
 
106
106
  &:hover {
@@ -145,11 +145,11 @@
145
145
 
146
146
  &:focus-visible {
147
147
  &::after {
148
- @apply ring-focus-line ring-offset-focus-offset ring-inset z-[1] ring-[var(--color-neutral-focus-indicator)];
148
+ @apply ring-focus-line ring-offset-focus-offset ring-inset z-[1] ring-[var(--color-focus-ring-subtle)];
149
149
  }
150
150
 
151
151
  &[data-variant='primary']::after {
152
- @apply ring-[var(--color-accent-focus-indicator)];
152
+ @apply ring-[var(--color-focus-ring)];
153
153
  }
154
154
 
155
155
  &:hover {
@@ -178,11 +178,11 @@
178
178
 
179
179
  &:focus {
180
180
  &::after {
181
- @apply ring-focus-line ring-offset-focus-offset ring-inset z-[1] ring-[var(--color-neutral-focus-indicator)];
181
+ @apply ring-focus-line ring-offset-focus-offset ring-inset z-[1] ring-[var(--color-focus-ring-subtle)];
182
182
  }
183
183
 
184
184
  &[data-variant='primary']::after {
185
- @apply ring-[var(--color-accent-focus-indicator)];
185
+ @apply ring-[var(--color-focus-ring)];
186
186
  }
187
187
 
188
188
  &:hover {
@@ -4,6 +4,9 @@
4
4
 
5
5
  @layer dx-components {
6
6
  .dx-link {
7
- @apply text-accent-text hover:text-accent-text-hover;
7
+ @apply text-accent-text hover:text-accent-text-hover cursor-pointer;
8
+ }
9
+ .dx-link-hover {
10
+ @apply hover:text-accent-text-hover! cursor-pointer;
8
11
  }
9
12
  }
@@ -1,117 +1,117 @@
1
1
  /**
2
- * Panel — applies bg-{hue}-surface / text-{hue}-surface-text / border-{hue}-border tokens via data-hue attribute or BEM modifier.
2
+ * Panel — applies bg-{hue}-surface / text-{hue}-fg / border-{hue}-border tokens via data-hue attribute or BEM modifier.
3
3
  * Example: <div className='dx-panel' data-hue='blue'>...</div>
4
4
  * Example: <div className='dx-panel dx-panel--blue'>...</div>
5
5
  */
6
6
 
7
7
  @layer dx-components {
8
- .dx-panel[data-hue='neutral'],
8
+ [data-hue='neutral'] .dx-panel,
9
9
  .dx-panel--neutral {
10
- @apply bg-neutral-surface text-neutral-surface-text border-neutral-border;
10
+ @apply bg-neutral-surface text-neutral-fg border-neutral-border;
11
11
  }
12
12
 
13
- .dx-panel[data-hue='red'],
13
+ [data-hue='red'] .dx-panel,
14
14
  .dx-panel--red {
15
- @apply bg-red-surface text-red-surface-text border-red-border;
15
+ @apply bg-red-surface text-red-fg border-red-border;
16
16
  }
17
17
 
18
- .dx-panel[data-hue='orange'],
18
+ [data-hue='orange'] .dx-panel,
19
19
  .dx-panel--orange {
20
- @apply bg-orange-surface text-orange-surface-text border-orange-border;
20
+ @apply bg-orange-surface text-orange-fg border-orange-border;
21
21
  }
22
22
 
23
- .dx-panel[data-hue='amber'],
23
+ [data-hue='amber'] .dx-panel,
24
24
  .dx-panel--amber {
25
- @apply bg-amber-surface text-amber-surface-text border-amber-border;
25
+ @apply bg-amber-surface text-amber-fg border-amber-border;
26
26
  }
27
27
 
28
- .dx-panel[data-hue='yellow'],
28
+ [data-hue='yellow'] .dx-panel,
29
29
  .dx-panel--yellow {
30
- @apply bg-yellow-surface text-yellow-surface-text border-yellow-border;
30
+ @apply bg-yellow-surface text-yellow-fg border-yellow-border;
31
31
  }
32
32
 
33
- .dx-panel[data-hue='lime'],
33
+ [data-hue='lime'] .dx-panel,
34
34
  .dx-panel--lime {
35
- @apply bg-lime-surface text-lime-surface-text border-lime-border;
35
+ @apply bg-lime-surface text-lime-fg border-lime-border;
36
36
  }
37
37
 
38
- .dx-panel[data-hue='green'],
38
+ [data-hue='green'] .dx-panel,
39
39
  .dx-panel--green {
40
- @apply bg-green-surface text-green-surface-text border-green-border;
40
+ @apply bg-green-surface text-green-fg border-green-border;
41
41
  }
42
42
 
43
- .dx-panel[data-hue='emerald'],
43
+ [data-hue='emerald'] .dx-panel,
44
44
  .dx-panel--emerald {
45
- @apply bg-emerald-surface text-emerald-surface-text border-emerald-border;
45
+ @apply bg-emerald-surface text-emerald-fg border-emerald-border;
46
46
  }
47
47
 
48
- .dx-panel[data-hue='teal'],
48
+ [data-hue='teal'] .dx-panel,
49
49
  .dx-panel--teal {
50
- @apply bg-teal-surface text-teal-surface-text border-teal-border;
50
+ @apply bg-teal-surface text-teal-fg border-teal-border;
51
51
  }
52
52
 
53
- .dx-panel[data-hue='cyan'],
53
+ [data-hue='cyan'] .dx-panel,
54
54
  .dx-panel--cyan {
55
- @apply bg-cyan-surface text-cyan-surface-text border-cyan-border;
55
+ @apply bg-cyan-surface text-cyan-fg border-cyan-border;
56
56
  }
57
57
 
58
- .dx-panel[data-hue='sky'],
58
+ [data-hue='sky'] .dx-panel,
59
59
  .dx-panel--sky {
60
- @apply bg-sky-surface text-sky-surface-text border-sky-border;
60
+ @apply bg-sky-surface text-sky-fg border-sky-border;
61
61
  }
62
62
 
63
- .dx-panel[data-hue='blue'],
63
+ [data-hue='blue'] .dx-panel,
64
64
  .dx-panel--blue {
65
- @apply bg-blue-surface text-blue-surface-text border-blue-border;
65
+ @apply bg-blue-surface text-blue-fg border-blue-border;
66
66
  }
67
67
 
68
- .dx-panel[data-hue='indigo'],
68
+ [data-hue='indigo'] .dx-panel,
69
69
  .dx-panel--indigo {
70
- @apply bg-indigo-surface text-indigo-surface-text border-indigo-border;
70
+ @apply bg-indigo-surface text-indigo-fg border-indigo-border;
71
71
  }
72
72
 
73
- .dx-panel[data-hue='violet'],
73
+ [data-hue='violet'] .dx-panel,
74
74
  .dx-panel--violet {
75
- @apply bg-violet-surface text-violet-surface-text border-violet-border;
75
+ @apply bg-violet-surface text-violet-fg border-violet-border;
76
76
  }
77
77
 
78
- .dx-panel[data-hue='purple'],
78
+ [data-hue='purple'] .dx-panel,
79
79
  .dx-panel--purple {
80
- @apply bg-purple-surface text-purple-surface-text border-purple-border;
80
+ @apply bg-purple-surface text-purple-fg border-purple-border;
81
81
  }
82
82
 
83
- .dx-panel[data-hue='fuchsia'],
83
+ [data-hue='fuchsia'] .dx-panel,
84
84
  .dx-panel--fuchsia {
85
- @apply bg-fuchsia-surface text-fuchsia-surface-text border-fuchsia-border;
85
+ @apply bg-fuchsia-surface text-fuchsia-fg border-fuchsia-border;
86
86
  }
87
87
 
88
- .dx-panel[data-hue='pink'],
88
+ [data-hue='pink'] .dx-panel,
89
89
  .dx-panel--pink {
90
- @apply bg-pink-surface text-pink-surface-text border-pink-border;
90
+ @apply bg-pink-surface text-pink-fg border-pink-border;
91
91
  }
92
92
 
93
- .dx-panel[data-hue='rose'],
93
+ [data-hue='rose'] .dx-panel,
94
94
  .dx-panel--rose {
95
- @apply bg-rose-surface text-rose-surface-text border-rose-border;
95
+ @apply bg-rose-surface text-rose-fg border-rose-border;
96
96
  }
97
97
 
98
- .dx-panel[data-hue='info'],
98
+ [data-hue='info'] .dx-panel,
99
99
  .dx-panel--info {
100
- @apply bg-info-surface text-info-surface-text border-info-border;
100
+ @apply bg-info-surface text-info-fg border-info-border;
101
101
  }
102
102
 
103
- .dx-panel[data-hue='success'],
103
+ [data-hue='success'] .dx-panel,
104
104
  .dx-panel--success {
105
- @apply bg-success-surface text-success-surface-text border-success-border;
105
+ @apply bg-success-surface text-success-fg border-success-border;
106
106
  }
107
107
 
108
- .dx-panel[data-hue='warning'],
108
+ [data-hue='warning'] .dx-panel,
109
109
  .dx-panel--warning {
110
- @apply bg-warning-surface text-warning-surface-text border-warning-border;
110
+ @apply bg-warning-surface text-warning-fg border-warning-border;
111
111
  }
112
112
 
113
- .dx-panel[data-hue='error'],
113
+ [data-hue='error'] .dx-panel,
114
114
  .dx-panel--error {
115
- @apply bg-error-surface text-error-surface-text border-error-border;
115
+ @apply bg-error-surface text-error-fg border-error-border;
116
116
  }
117
117
  }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Selected / Current / Highlighted state utilities.
3
+ *
4
+ * See `./selected.md` for the ARIA pairing grammar — every class below
5
+ * fires off a specific attribute and silently does nothing without it.
6
+ */
7
+
8
+ @layer dx-components {
9
+ /*
10
+ * Pure visual affordance, no ARIA pair. Combine with dx-selected or dx-current.
11
+ *
12
+ * Picks the hover variant matching the element's underlying state:
13
+ * - neither aria-selected nor aria-current → hover-surface
14
+ * - aria-selected="true" → selected-surface-hover
15
+ * - aria-current="true" → current-surface-hover (wins if both)
16
+ *
17
+ * Higher-specificity rules (attribute selectors) override the plain :hover rule,
18
+ * and `aria-current` is listed last so it beats `aria-selected` at equal specificity.
19
+ */
20
+ .dx-hover {
21
+ @apply cursor-pointer
22
+ hover:bg-hover-surface-subtle! hover:text-hover-fg!
23
+ hover:aria-selected:bg-selected-surface-hover hover:aria-selected:text-selected-fg
24
+ hover:aria-[current=true]:bg-current-surface-hover hover:aria-[current=true]:text-current-fg;
25
+ }
26
+ .dx-hover-row {
27
+ @apply group-hover/row:bg-hover-surface-subtle! group-hover/row:text-hover-fg!
28
+ group-hover/row:group-aria-selected/row:bg-selected-surface-hover!
29
+ group-hover/row:group-aria-selected/row:text-selected-fg!
30
+ group-hover/row:group-aria-[current=true]/row:bg-current-surface-hover!
31
+ group-hover/row:group-aria-[current=true]/row:text-current-fg!;
32
+ }
33
+
34
+ /*
35
+ * Pairs with `aria-selected`. Use for listbox/option selection (master/detail).
36
+ * `!` is required: these utilities live in the `dx-components` layer, but
37
+ * background utilities like `bg-card-surface` live in Tailwind's
38
+ * `utilities` layer (declared later in the cascade), so without
39
+ * `!important` the card background would override the state colour.
40
+ *
41
+ * NOTE: Declared before `.dx-current` so that when an element has both
42
+ * `aria-selected="true"` and `aria-current="true"`, `dx-current` wins at
43
+ * equal specificity (consistent with how `dx-hover` resolves the same
44
+ * conflict).
45
+ */
46
+ .dx-selected {
47
+ @apply aria-selected:bg-selected-surface! aria-selected:text-selected-fg!
48
+ aria-selected:font-semibold aria-selected:tracking-normal
49
+ transition-[color,font-variation-settings,letter-spacing];
50
+ }
51
+
52
+ /*
53
+ * Pairs with `aria-current="true|page|…"`. Use for "you are here" navigation.
54
+ *
55
+ * The ring uses the subdued focus indicator (neutral) rather than the
56
+ * accent blue — `dx-current` marks position, not focus, so a saturated
57
+ * ring would compete with the actual keyboard focus ring.
58
+ */
59
+ .dx-current {
60
+ @apply dx-ring-pseudo
61
+ aria-[current=true]:bg-current-surface! aria-[current=true]:text-current-fg!
62
+ aria-[current=true]:after:ring-focus-ring-subtle!;
63
+ }
64
+ .dx-current-row {
65
+ @apply group-aria-[current=true]/row:bg-current-surface! group-aria-[current=true]/row:text-current-fg!;
66
+ }
67
+
68
+ /*
69
+ * Pairs with Radix-managed `data-highlighted`. Don't set the attribute manually.
70
+ */
71
+ .dx-highlighted {
72
+ @apply data-[highlighted]:bg-current-surface
73
+ data-[highlighted]:text-current-fg
74
+ hover:data-[highlighted]:bg-current-surface-hover;
75
+ }
76
+ }
@@ -0,0 +1,101 @@
1
+ # `dx-*` selection / navigation grammar
2
+
3
+ Companion to `selected.css`. The four selection-state utilities below are
4
+ each bound to a specific ARIA attribute (or `data-*`) selector. **The
5
+ class only fires when the matching ARIA attribute is set on the same
6
+ element.** Mismatches (e.g. `dx-current` paired with `aria-selected`)
7
+ silently render as a plain row.
8
+
9
+ | Class | Bound selector | Pair with | Use when |
10
+ | ---------------- | ---------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `dx-hover` | `hover:` | (no ARIA — pure visual affordance) | Always, on any clickable row. Adds the cursor + hover bg/text. |
12
+ | `dx-selected` | `aria-selected:` | `aria-selected={boolean}` | A row is "the chosen one" driving a master/detail panel. Listbox / option pattern. Multi-select OK. |
13
+ | `dx-current` | `aria-[current=true]:` | `aria-current='true'` | A row is "where you are" in a navigation set (current page, current step). At most one current per group. The CSS variant fires only on `aria-current='true'`, so use that literal even on `<a>`/nav links. |
14
+ | `dx-highlighted` | `data-[highlighted]:` | Radix `data-highlighted` (managed) | A Radix-managed transient highlight (menu / combobox active option). Don't set the attribute manually. |
15
+
16
+ ## Rules
17
+
18
+ 1. **Never combine `dx-selected` and `dx-current` on the same element**
19
+ unless you also set both `aria-selected` and `aria-current`. They
20
+ express different things (chosen vs you-are-here) and the visual
21
+ weight is similar enough that combining them defeats both.
22
+ 2. **`aria-pressed`, `aria-checked`, `aria-expanded`, `aria-disabled`
23
+ are not selection-row attributes.** None of the `dx-*` selection
24
+ utilities pair with them. Toggle buttons (mute/unmute) want
25
+ `aria-pressed`; a selection row wants `aria-selected`.
26
+ 3. **`aria-selected` is only valid on roles that admit it** — `option`,
27
+ `tab`, `gridcell`, `row`, `treeitem`, `columnheader`, `rowheader`.
28
+ On a plain `<button>` inside a `<ul role='listbox'>`, give the button
29
+ `role='option'` so assistive tech sees the attribute.
30
+ 4. **`dx-active` does not exist.** If you see it in code it's a no-op
31
+ string. Use `dx-selected` (selection) or `dx-current` (navigation)
32
+ instead.
33
+ 5. **Set ARIA before adding the class.** The class is inert without
34
+ the attribute; it's not a fallback styler. Reviewers should reject
35
+ PRs that add `dx-selected` without `aria-selected`.
36
+
37
+ ## Canonical patterns
38
+
39
+ ### Selectable row in a list (master/detail)
40
+
41
+ ```tsx
42
+ <ul role='listbox' aria-label='…'>
43
+ {items.map((item) => (
44
+ <li key={item.id} role='presentation'>
45
+ <button
46
+ type='button'
47
+ role='option'
48
+ aria-selected={item.id === selectedId}
49
+ className='dx-hover dx-selected'
50
+ onClick={() => onSelect(item.id)}
51
+ >
52
+ {item.label}
53
+ </button>
54
+ </li>
55
+ ))}
56
+ </ul>
57
+ ```
58
+
59
+ ### "You are here" in a navigation list
60
+
61
+ ```tsx
62
+ <nav>
63
+ <ul>
64
+ {pages.map((page) => (
65
+ <li key={page.href}>
66
+ <a
67
+ href={page.href}
68
+ aria-current={page.href === currentHref ? 'true' : undefined}
69
+ className='dx-hover dx-current'
70
+ >
71
+ {page.label}
72
+ </a>
73
+ </li>
74
+ ))}
75
+ </ul>
76
+ </nav>
77
+ ```
78
+
79
+ ### Toggle button (NOT a row)
80
+
81
+ Don't reach for `dx-selected` here. Toggle buttons want their own
82
+ visual treatment via `aria-pressed:` variants — out of scope for this
83
+ file.
84
+
85
+ ```tsx
86
+ <button type='button' aria-pressed={muted} onClick={toggleMute}>
87
+ {muted ? 'Unmute' : 'Mute'}
88
+ </button>
89
+ ```
90
+
91
+ ## Enforcing the grammar
92
+
93
+ A future ESLint rule could flag:
94
+
95
+ - `dx-selected` without `aria-selected` on the same element.
96
+ - `dx-current` without `aria-current` on the same element.
97
+ - `dx-active` literal anywhere.
98
+
99
+ Until that exists, reviewers and the `react-ui-list` `RowList` /
100
+ `CardList` containers (which set both attribute and class together)
101
+ are the enforcement.
@@ -1,34 +1,109 @@
1
1
  /**
2
2
  * Surfaces
3
+ *
4
+ * A surface zone paints a background AND publishes its colour as `--surface-bg`, so descendant
5
+ * controls can derive a visible hover/current highlight from the actual surface (see the
6
+ * derivation block below) and surface-matching elements (avatars, gradients) can read it directly.
7
+ *
8
+ * Always enter a surface via one of these `dx-*-surface` classes. A bare `bg-*-surface` utility
9
+ * paints the colour but does NOT publish `--surface-bg`, so control hover collapses onto the
10
+ * base-surface fallback and can become invisible when the surface shares that elevation.
3
11
  */
4
12
 
5
13
  @layer dx-components {
6
14
  .dx-base-surface {
7
- @apply bg-base-surface text-base-surface-text;
15
+ @apply bg-base-surface text-base-fg;
8
16
  --surface-bg: var(--color-base-surface);
9
17
  }
10
18
 
19
+ .dx-deck-surface {
20
+ @apply bg-deck-surface text-base-fg;
21
+ --surface-bg: var(--color-deck-surface);
22
+ }
23
+
11
24
  .dx-sidebar-surface {
12
- @apply bg-sidebar-surface text-base-surface-text;
25
+ @apply bg-sidebar-surface text-base-fg;
13
26
  --surface-bg: var(--color-sidebar-surface);
14
27
  }
15
28
 
29
+ .dx-header-surface {
30
+ @apply bg-header-surface text-base-fg;
31
+ --surface-bg: var(--color-header-surface);
32
+ }
33
+
34
+ .dx-card-surface {
35
+ @apply bg-card-surface text-base-fg;
36
+ --surface-bg: var(--color-card-surface);
37
+ }
38
+
39
+ .dx-group-surface {
40
+ @apply bg-group-surface text-base-fg;
41
+ --surface-bg: var(--color-group-surface);
42
+ }
43
+
44
+ .dx-input-surface {
45
+ @apply bg-input-surface text-base-fg;
46
+ --surface-bg: var(--color-input-surface);
47
+ }
48
+
49
+ .dx-toolbar-surface {
50
+ @apply bg-toolbar-surface text-base-fg;
51
+ --surface-bg: var(--color-toolbar-surface);
52
+ }
53
+
16
54
  .dx-modal-surface {
17
- @apply bg-modal-surface text-base-surface-text backdrop-blur-md;
55
+ @apply bg-modal-surface text-base-fg backdrop-blur-md;
18
56
  --surface-bg: var(--color-modal-surface);
19
57
  }
20
58
 
59
+ .dx-popover-surface {
60
+ @apply bg-popover-surface text-base-fg backdrop-blur-md;
61
+ --surface-bg: var(--color-popover-surface);
62
+ }
63
+
21
64
  .dx-attention-surface {
22
- @apply bg-attention-surface text-base-surface-text;
65
+ @apply bg-attention-surface text-base-fg;
23
66
  --surface-bg: var(--color-attention-surface);
24
67
  }
25
68
  }
26
69
 
27
- /* TODO(burdon): Resolve. */
28
- /* @layer dx-tokens {
29
- .dx-sidebar-surface, .dx-modal-surface {
30
- --color-hover-surface: light-dark(var(--color-neutral-300), var(--color-neutral-700));
31
- --color-input-surface: light-dark(var(--color-neutral-200), var(--color-neutral-800));
32
- --color-separator: light-dark(var(--color-neutral-300), var(--color-neutral-700));
70
+ @layer dx-tokens {
71
+ /*
72
+ * Re-derive the state colours (hover, current, selected) off the host surface for every context
73
+ * that publishes its own --surface-bg. Custom-property substitution runs once per scope, so the
74
+ * formula must be repeated here (it cannot simply inherit from semantic.css, which resolved it
75
+ * against the root surface). Listing all surface zones in one block keeps the offsets identical
76
+ * across surfaces — there is no per-surface divergence to tune.
77
+ *
78
+ * Magnitudes mirror semantic.css; selected-* track current-* via the aliases declared there.
79
+ */
80
+ .dx-base-surface,
81
+ .dx-deck-surface,
82
+ .dx-sidebar-surface,
83
+ .dx-header-surface,
84
+ .dx-card-surface,
85
+ .dx-group-surface,
86
+ .dx-input-surface,
87
+ .dx-toolbar-surface,
88
+ .dx-modal-surface,
89
+ .dx-popover-surface,
90
+ .dx-attention-surface,
91
+ .dx-main-sidebar {
92
+ --color-hover-surface: light-dark(
93
+ oklch(from var(--surface-bg) calc(l - 0.08) c h),
94
+ oklch(from var(--surface-bg) calc(l + 0.08) c h)
95
+ );
96
+ --color-hover-surface-subtle: light-dark(
97
+ oklch(from var(--surface-bg) calc(l - 0.02) c h),
98
+ oklch(from var(--surface-bg) calc(l + 0.02) c h)
99
+ );
100
+ --color-current-surface: light-dark(
101
+ oklch(from var(--surface-bg) calc(l - 0.1) c h),
102
+ oklch(from var(--surface-bg) calc(l + 0.1) c h)
103
+ );
104
+ --color-current-surface-hover: light-dark(
105
+ oklch(from var(--surface-bg) calc(l - 0.12) c h),
106
+ oklch(from var(--surface-bg) calc(l + 0.12) c h)
107
+ );
33
108
  }
34
- } */
109
+ }