@dxos/ui-theme 0.8.4-main.c85a9c8dae → 0.8.4-main.cb12b3f963

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 (148) hide show
  1. package/dist/lib/browser/index.mjs +401 -475
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +401 -475
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/plugin/node-cjs/{theme.css → main.css} +126 -56
  8. package/dist/plugin/node-cjs/main.css.map +7 -0
  9. package/dist/plugin/node-cjs/meta.json +1 -1
  10. package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs +8 -15
  11. package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs.map +3 -3
  12. package/dist/plugin/node-esm/{theme.css → main.css} +126 -56
  13. package/dist/plugin/node-esm/main.css.map +7 -0
  14. package/dist/plugin/node-esm/meta.json +1 -1
  15. package/dist/plugin/node-esm/plugins/ThemePlugin.mjs +8 -15
  16. package/dist/plugin/node-esm/plugins/ThemePlugin.mjs.map +3 -3
  17. package/dist/types/src/Theme.stories.d.ts.map +1 -1
  18. package/dist/types/src/defs.d.ts +2 -2
  19. package/dist/types/src/defs.d.ts.map +1 -1
  20. package/dist/types/src/fragments/density.d.ts +1 -4
  21. package/dist/types/src/fragments/density.d.ts.map +1 -1
  22. package/dist/types/src/fragments/index.d.ts +0 -5
  23. package/dist/types/src/fragments/index.d.ts.map +1 -1
  24. package/dist/types/src/fragments/text.d.ts +0 -5
  25. package/dist/types/src/fragments/text.d.ts.map +1 -1
  26. package/dist/types/src/index.d.ts +1 -1
  27. package/dist/types/src/index.d.ts.map +1 -1
  28. package/dist/types/src/plugins/ThemePlugin.d.ts +1 -1
  29. package/dist/types/src/plugins/ThemePlugin.d.ts.map +1 -1
  30. package/dist/types/src/theme/components/avatar.d.ts.map +1 -1
  31. package/dist/types/src/theme/components/card.d.ts +3 -2
  32. package/dist/types/src/theme/components/card.d.ts.map +1 -1
  33. package/dist/types/src/theme/components/dialog.d.ts.map +1 -1
  34. package/dist/types/src/theme/components/focus.d.ts +6 -0
  35. package/dist/types/src/theme/components/focus.d.ts.map +1 -0
  36. package/dist/types/src/theme/components/icon-button.d.ts +1 -0
  37. package/dist/types/src/theme/components/icon-button.d.ts.map +1 -1
  38. package/dist/types/src/theme/components/icon.d.ts +3 -0
  39. package/dist/types/src/theme/components/icon.d.ts.map +1 -1
  40. package/dist/types/src/theme/components/index.d.ts +1 -0
  41. package/dist/types/src/theme/components/index.d.ts.map +1 -1
  42. package/dist/types/src/theme/components/input.d.ts +8 -8
  43. package/dist/types/src/theme/components/input.d.ts.map +1 -1
  44. package/dist/types/src/theme/components/link.d.ts.map +1 -1
  45. package/dist/types/src/theme/components/list.d.ts.map +1 -1
  46. package/dist/types/src/theme/components/main.d.ts.map +1 -1
  47. package/dist/types/src/theme/components/message.d.ts.map +1 -1
  48. package/dist/types/src/theme/components/popover.d.ts.map +1 -1
  49. package/dist/types/src/theme/components/scroll-area.d.ts +12 -2
  50. package/dist/types/src/theme/components/scroll-area.d.ts.map +1 -1
  51. package/dist/types/src/theme/components/select.d.ts.map +1 -1
  52. package/dist/types/src/theme/components/status.d.ts +1 -1
  53. package/dist/types/src/theme/components/status.d.ts.map +1 -1
  54. package/dist/types/src/theme/components/toast.d.ts.map +1 -1
  55. package/dist/types/src/theme/components/toolbar.d.ts +0 -1
  56. package/dist/types/src/theme/components/toolbar.d.ts.map +1 -1
  57. package/dist/types/src/theme/components/tooltip.d.ts.map +1 -1
  58. package/dist/types/src/theme/components/treegrid.d.ts.map +1 -1
  59. package/dist/types/src/theme/index.d.ts +1 -0
  60. package/dist/types/src/theme/index.d.ts.map +1 -1
  61. package/dist/types/src/theme/primitives/column.d.ts +25 -3
  62. package/dist/types/src/theme/primitives/column.d.ts.map +1 -1
  63. package/dist/types/src/theme/primitives/panel.d.ts +9 -5
  64. package/dist/types/src/theme/primitives/panel.d.ts.map +1 -1
  65. package/dist/types/src/theme/theme.d.ts.map +1 -1
  66. package/dist/types/src/util/elevation.d.ts.map +1 -0
  67. package/dist/types/src/util/hash-styles.d.ts.map +1 -1
  68. package/dist/types/src/util/index.d.ts +3 -0
  69. package/dist/types/src/util/index.d.ts.map +1 -1
  70. package/dist/types/src/util/mx.d.ts +52 -4
  71. package/dist/types/src/util/mx.d.ts.map +1 -1
  72. package/dist/types/src/util/size.d.ts +27 -0
  73. package/dist/types/src/util/size.d.ts.map +1 -0
  74. package/dist/types/src/util/valence.d.ts +4 -0
  75. package/dist/types/src/util/valence.d.ts.map +1 -0
  76. package/dist/types/tsconfig.tsbuildinfo +1 -1
  77. package/package.json +13 -16
  78. package/src/css/components/button.css +2 -1
  79. package/src/css/components/{focus-ring.css → focus.css} +15 -1
  80. package/src/css/components/icon.css +9 -0
  81. package/src/css/components/panel.css +22 -22
  82. package/src/css/components/selected.css +35 -0
  83. package/src/css/components/selected.md +101 -0
  84. package/src/css/components/tag.css +3 -1
  85. package/src/css/integrations/codemirror.css +5 -3
  86. package/src/css/integrations/tldraw.css +1 -0
  87. package/src/css/layout/main.css +0 -7
  88. package/src/css/layout/size.css +19 -27
  89. package/src/css/theme/animation.css +31 -0
  90. package/src/css/theme/palette.css +8 -0
  91. package/src/css/theme/semantic.css +25 -9
  92. package/src/css/theme/spacing.css +36 -19
  93. package/src/css/utilities.css +114 -3
  94. package/src/defs.ts +1 -1
  95. package/src/fragments/AUDIT.md +57 -0
  96. package/src/fragments/density.ts +8 -5
  97. package/src/fragments/index.ts +1 -6
  98. package/src/fragments/text.ts +1 -6
  99. package/src/index.ts +1 -1
  100. package/src/{theme.css → main.css} +46 -8
  101. package/src/plugins/ThemePlugin.ts +12 -24
  102. package/src/plugins/main.css +45 -0
  103. package/src/theme/components/avatar.ts +3 -4
  104. package/src/theme/components/button.ts +1 -1
  105. package/src/theme/components/card.ts +19 -11
  106. package/src/theme/components/dialog.ts +4 -3
  107. package/src/theme/components/focus.ts +33 -0
  108. package/src/theme/components/icon-button.ts +6 -5
  109. package/src/theme/components/icon.ts +13 -4
  110. package/src/theme/components/index.ts +1 -0
  111. package/src/theme/components/input.ts +15 -30
  112. package/src/theme/components/link.ts +1 -2
  113. package/src/theme/components/list.ts +4 -4
  114. package/src/theme/components/menu.ts +4 -4
  115. package/src/theme/components/message.ts +6 -7
  116. package/src/theme/components/popover.ts +4 -5
  117. package/src/theme/components/scroll-area.ts +58 -46
  118. package/src/theme/components/select.ts +2 -3
  119. package/src/theme/components/status.ts +5 -5
  120. package/src/theme/components/toast.ts +2 -3
  121. package/src/theme/components/toolbar.ts +1 -7
  122. package/src/theme/components/tooltip.ts +1 -2
  123. package/src/theme/components/treegrid.ts +1 -1
  124. package/src/theme/index.ts +1 -0
  125. package/src/theme/primitives/column.ts +49 -8
  126. package/src/theme/primitives/panel.ts +19 -23
  127. package/src/theme/theme.ts +2 -0
  128. package/src/typings.d.ts +3 -0
  129. package/src/util/index.ts +3 -0
  130. package/src/util/mx.ts +119 -8
  131. package/src/util/size.ts +103 -0
  132. package/dist/plugin/node-cjs/theme.css.map +0 -7
  133. package/dist/plugin/node-esm/theme.css.map +0 -7
  134. package/dist/types/src/fragments/elevation.d.ts.map +0 -1
  135. package/dist/types/src/fragments/focus.d.ts +0 -4
  136. package/dist/types/src/fragments/focus.d.ts.map +0 -1
  137. package/dist/types/src/fragments/selected.d.ts +0 -4
  138. package/dist/types/src/fragments/selected.d.ts.map +0 -1
  139. package/dist/types/src/fragments/size.d.ts +0 -7
  140. package/dist/types/src/fragments/size.d.ts.map +0 -1
  141. package/dist/types/src/fragments/valence.d.ts +0 -4
  142. package/dist/types/src/fragments/valence.d.ts.map +0 -1
  143. package/src/fragments/focus.ts +0 -11
  144. package/src/fragments/selected.ts +0 -12
  145. package/src/fragments/size.ts +0 -117
  146. /package/dist/types/src/{fragments → util}/elevation.d.ts +0 -0
  147. /package/src/{fragments → util}/elevation.ts +0 -0
  148. /package/src/{fragments → util}/valence.ts +0 -0
@@ -2,14 +2,18 @@
2
2
  * Tokens
3
3
  */
4
4
 
5
+ /**
6
+ * Utility classes
7
+ */
5
8
  @theme {
6
9
  /** Minimum inset from the viewport edge for floating surfaces. */
7
- --spacing-screen-border: 1rem;
10
+ --spacing-screen-border: 2rem;
8
11
 
9
- --spacing-article-max-width: 50rem;
10
- --spacing-prose-max-width: 50rem;
12
+ --spacing-popover-default-width: 20rem;
13
+ --spacing-popover-min-width: 16rem;
11
14
 
12
- --spacing-popover-min-width: 12rem;
15
+ --spacing-document-max-width: 50rem;
16
+ --spacing-document-width: calc(50rem - env(safe-area-inset-left) - env(safe-area-inset-right));
13
17
 
14
18
  --spacing-card-default-width: 20rem;
15
19
  --spacing-card-min-width: 18rem;
@@ -17,16 +21,17 @@
17
21
  --spacing-card-min-height: 18rem;
18
22
  --spacing-card-max-height: 30rem;
19
23
 
20
- --spacing-trim-xs: 0.375rem;
24
+ --spacing-tag-padding-block: 0.125rem;
25
+
26
+ --spacing-trim-xs: 0.25rem;
21
27
  --spacing-trim-sm: 0.5rem;
22
28
  --spacing-trim-md: 0.75rem;
23
29
  --spacing-trim-lg: 1.5rem;
24
30
 
25
- --spacing-tag-padding-block: 0.125rem;
26
- --spacing-text-content: calc(50rem - env(safe-area-inset-left) - env(safe-area-inset-right));
27
-
28
31
  /** Minimal trim to allow for ring/outline. */
29
32
  --spacing-form-chrome: var(--spacing-trim-xs);
33
+ --spacing-form-gap: var(--spacing-trim-sm);
34
+ --spacing-form-section-gap: var(--spacing-trim-lg);
30
35
 
31
36
  /**
32
37
  * Density: fine
@@ -38,15 +43,20 @@
38
43
 
39
44
  @layer dx-tokens {
40
45
  /**
41
- * Density: coarse
46
+ * TODO(burdon): Density: coarse
42
47
  */
48
+ /*
43
49
  .dx-density-coarse {
44
50
  --spacing-form-padding: var(--spacing-trim-md);
45
51
  --spacing-icon-button-padding: var(--spacing-trim-sm);
46
52
  --spacing-scroll-padding: 8px;
47
53
  }
54
+ */
48
55
  }
49
56
 
57
+ /**
58
+ * CSS variables.
59
+ */
50
60
  @layer dx-tokens {
51
61
  :root {
52
62
  --dx-line-0: 0px;
@@ -58,10 +68,10 @@
58
68
  --dx-lacuna-4: 0.5rem;
59
69
  --dx-lacuna-6: 0.75rem;
60
70
 
61
- --dx-gutter-xs: 0.125rem;
62
- --dx-gutter-sm: 0.5rem;
63
- --dx-gutter-md: 1rem;
64
- --dx-gutter-lg: 2rem;
71
+ --dx-gutter-xs: 0.25rem; /* Input rings */
72
+ --dx-gutter-sm: 1rem;
73
+ --dx-gutter-md: 2rem; /* 32px (fine card) */
74
+ --dx-gutter-lg: 2.5rem; /* 40px (dialog, coarse card) */
65
75
  }
66
76
 
67
77
  :root {
@@ -83,6 +93,8 @@
83
93
 
84
94
  --dx-input-fine: var(--dx-lacuna-3);
85
95
  --dx-input-coarse: var(--dx-lacuna-4);
96
+
97
+ --dx-default-icons-size: 1rem; /* size=4 */
86
98
  }
87
99
 
88
100
  [data-grid-focus-indicator-variant='stack'] {
@@ -91,19 +103,24 @@
91
103
 
92
104
  :root {
93
105
  --dx-rail-content: 3rem;
106
+ /** TODO(burdon): Audit. */
107
+ /** TODO(burdon): Density (2 sizes). */
94
108
  --dx-rail-size: calc(var(--dx-rail-content) + 1px);
95
109
  --dx-rail-item: calc(var(--dx-rail-content) - 1rem);
96
- --dx-rail-action: calc(var(--dx-rail-content) - 0.5rem + 1px);
110
+ /* TODO(burdon): Added 1px for border. */
111
+ /* TODO(burdon): Remove direct usage. */
112
+ --dx-rail-action: calc(var(--dx-rail-content) - 0.5rem);
97
113
 
98
- --dx-topbar-size: var(--dx-rail-size);
99
- --dx-toolbar-size: var(--dx-rail-content);
100
- --dx-statusbar-size: var(--dx-rail-content);
114
+ --dx-topbar-size: var(--dx-rail-size); /* 50px */
115
+ --dx-toolbar-size: var(--dx-rail-action); /* 40px */
116
+ --dx-statusbar-size: var(--dx-rail-item); /* 32px */
101
117
 
102
- --dx-nav-sidebar-size: 23rem;
118
+ /** NOTE: Sidebar optimized to show plank + companion with sidebar open on 16" MBP. */
119
+ --dx-nav-sidebar-size: 350px;
103
120
  --dx-complementary-sidebar-size: 25rem;
104
121
  --dx-drawer-size: 50dvh;
105
122
 
106
- --dx-l0-size: 74px; /* MacOS windowing controls. */
123
+ --dx-l0-size: 72px;
107
124
  --dx-l0-avatar-size: 3rem;
108
125
  --dx-l1-size: calc(var(--dx-nav-sidebar-size) - var(--dx-l0-size));
109
126
  --dx-r0-size: var(--dx-rail-size);
@@ -1,7 +1,118 @@
1
1
  /**
2
- * Misc
2
+ * Tailwind utility classes.
3
3
  */
4
4
 
5
- @utility dx-icon-inline {
6
- @apply inline-block align-[-0.125em];
5
+ /**
6
+ * Layout rules for flex-based scroll containment:
7
+ * `flex flex-col`
8
+ * On a container: makes it a flex column so children stack and can use `flex-1`.
9
+ * `flex-1`
10
+ * On a child: grows to fill the flex parent. Requires the parent to be `flex`.
11
+ * `min-h-0` (alongside `flex-1`):
12
+ * Overrides default flex children: `min-height:auto` (sized to content), which prevents shrinking.
13
+ * Allows element to shrink and trigger overflow/scrolling.
14
+ * Always pair with `flex-1` when scroll is needed.
15
+ * `h-full`:
16
+ * Fills 100% of the parent's *computed* height.
17
+ * Use when the parent has a definite height but is not a flex container (e.g. `overflow:hidden` wrapper).
18
+ * Unlike `flex-1`, does not require the parent to be flex.
19
+ *
20
+ * Pattern for a scrollable region inside a flex ancestor:
21
+ * ancestor → `flex flex-col` (or `flex flex-row`)
22
+ * scroll root → `flex-1 min-h-0` (fills ancestor, can shrink)
23
+ * scroll viewport → `h-full overflow-y-scroll` (fills root, scrolls)
24
+ */
25
+
26
+ /**
27
+ * Fills the available space.
28
+ */
29
+ @utility dx-expander {
30
+ @apply flex-1 min-h-0 min-w-0 h-full w-full;
31
+ }
32
+
33
+ /**
34
+ * Container that fills the available space (extends dx-expander with overflow clipping).
35
+ */
36
+ @utility dx-container {
37
+ @apply dx-expander overflow-hidden;
38
+ }
39
+
40
+ /**
41
+ * Column that fills the available space (extends dx-expander with overflow clipping).
42
+ */
43
+ @utility dx-column {
44
+ @apply flex-1 min-w-0 w-full;
45
+ }
46
+
47
+ /**
48
+ * Fullscreen
49
+ */
50
+ @utility dx-fullscreen {
51
+ @apply absolute inset-0;
52
+ }
53
+
54
+ /**
55
+ * Visual warning to indicate incorrect usage of `slottable`.
56
+ */
57
+ @utility dx-slot-warning {
58
+ @apply border border-rose-500 border-dashed;
59
+ }
60
+
61
+ /**
62
+ * Pseudo-element overlay for ring indicators (focus, current, etc.).
63
+ *
64
+ * A standard CSS `box-shadow` ring is painted behind child content, so children with
65
+ * opaque backgrounds (e.g., cards, avatars) obscure it. By painting the ring on an
66
+ * absolutely-positioned `::after` pseudo-element that sits above the element's children
67
+ * in stacking order, the ring is always visible regardless of child backgrounds.
68
+ *
69
+ * The pseudo-element inherits `border-radius` from its parent and is `pointer-events-none`
70
+ * so it doesn't interfere with interactions. The ring starts transparent; consumers
71
+ * activate it conditionally (e.g., `focus:after:ring-*`, `aria-[current=true]:after:ring-*`).
72
+ */
73
+ @utility dx-ring-pseudo {
74
+ @apply relative after:content-[""] after:absolute after:inset-0 after:rounded-[inherit]
75
+ after:pointer-events-none after:ring after:ring-inset after:ring-transparent;
76
+ }
77
+
78
+ /**
79
+ * Shimmer text — animates opacity left → right across the contained text.
80
+ * See @keyframes shimmer-text in theme/animation.css for the keyframe definition.
81
+ * Geometry: mask-size 200% 100% with mask-repeat: repeat-x means each tile is
82
+ * 2× the element width; the keyframe slides mask-position-x by 200% (one full
83
+ * tile period), giving a seamless loop. The 5-stop gradient (0.4 → 1.0 → 0.4)
84
+ * produces a single bright pulse per cycle that traverses left → right during
85
+ * the first half, with a brief calm interval during the second half.
86
+ */
87
+ @utility shimmer-text {
88
+ mask-image: linear-gradient(
89
+ 90deg,
90
+ rgba(0, 0, 0, 0.4) 0%,
91
+ rgba(0, 0, 0, 0.4) 30%,
92
+ rgba(0, 0, 0, 1) 50%,
93
+ rgba(0, 0, 0, 0.4) 70%,
94
+ rgba(0, 0, 0, 0.4) 100%
95
+ );
96
+ -webkit-mask-image: linear-gradient(
97
+ 90deg,
98
+ rgba(0, 0, 0, 0.4) 0%,
99
+ rgba(0, 0, 0, 0.4) 30%,
100
+ rgba(0, 0, 0, 1) 50%,
101
+ rgba(0, 0, 0, 0.4) 70%,
102
+ rgba(0, 0, 0, 0.4) 100%
103
+ );
104
+ mask-size: 200% 100%;
105
+ -webkit-mask-size: 200% 100%;
106
+ mask-repeat: repeat-x;
107
+ -webkit-mask-repeat: repeat-x;
108
+ animation: shimmer-text 2s linear infinite;
109
+ }
110
+
111
+ @media (prefers-reduced-motion: reduce) {
112
+ .shimmer-text {
113
+ animation: none;
114
+ mask-image: none;
115
+ -webkit-mask-image: none;
116
+ opacity: 0.6;
117
+ }
7
118
  }
package/src/defs.ts CHANGED
@@ -7,7 +7,7 @@ import { type ChromaticPalette } from '@dxos/ui-types';
7
7
  /**
8
8
  * Translation namespace for OS-level translations.
9
9
  */
10
- export const osTranslations = 'dxos.org/i18n/os';
10
+ export const osTranslations = 'org.dxos.i18n.os';
11
11
 
12
12
  /**
13
13
  * Available color hues for UI components.
@@ -0,0 +1,57 @@
1
+ # Fragment Definitions Audit
2
+
3
+ External packages importing fragment definitions from `ui-theme/src/fragments`.
4
+
5
+ ## Current Fragment Exports
6
+
7
+ | File | Definition | External | Internal |
8
+ | ------------- | ------------------------------------- | -------- | -------- |
9
+ | `density.ts` | `densityBlockSize` | - | 1 |
10
+ | `density.ts` | `coarseBlockSize` | - | 1 |
11
+ | `density.ts` | `coarseDimensions` | - | 1 |
12
+ | `density.ts` | `fineBlockSize` | - | 1 |
13
+ | `density.ts` | `fineDimensions` | - | 1 |
14
+ | `disabled.ts` | `staticDisabled` | - | 1 |
15
+ | `disabled.ts` | `dataDisabled` | - | 1 |
16
+ | `focus.ts` | `focusRing` | 2 | 5 |
17
+ | `focus.ts` | `subduedFocus` | - | 2 |
18
+ | `focus.ts` | `staticFocusRing` | - | 1 |
19
+ | `hover.ts` | `subtleHover` | 4 | - |
20
+ | `hover.ts` | `ghostHover` | 8 | 2 |
21
+ | `hover.ts` | `ghostFocusWithin` | 1 | - |
22
+ | `hover.ts` | `hoverableControls` | 9 | - |
23
+ | `hover.ts` | `groupHoverControlItemWithTransition` | 2 | - |
24
+ | `hover.ts` | `hoverableFocusedKeyboardControls` | 1 | - |
25
+ | `hover.ts` | `hoverableFocusedWithinControls` | 9 | - |
26
+ | `hover.ts` | `hoverableOpenControlItem` | 3 | - |
27
+ | `hover.ts` | `hoverableControlItem` | 7 | - |
28
+ | `text.ts` | `descriptionTextPrimary` | 1 | - |
29
+ | `text.ts` | `descriptionMessage` | 5 | - |
30
+
31
+ ## Summary
32
+
33
+ **Total fragments:** 21
34
+ **Total imports (external + internal):** 96
35
+
36
+ - **External:** 63
37
+ - **Internal:** 33
38
+
39
+ **Most imported overall:**
40
+
41
+ - `hoverableControls` (9 external)
42
+ - `hoverableFocusedWithinControls` (9 external)
43
+ - `ghostHover` (8 external + 2 internal = 10 total)
44
+ - `hoverableControlItem` (7 external)
45
+ - `focusRing` (2 external + 5 internal = 7 total)
46
+
47
+ **Internal imports by file:**
48
+
49
+ - `input.ts`: 8 definitions (coarseBlockSize, coarseDimensions, fineBlockSize, fineDimensions, focusRing, staticDisabled, staticFocusRing, subduedFocus)
50
+ - `list.ts`: 3 definitions (densityBlockSize, focusRing, ghostHover)
51
+ - `menu.ts`: 2 definitions (dataDisabled, subduedFocus)
52
+ - `button.ts`, `link.ts`, `popover.ts`, `toast.ts`: 1 definition each (ghostHover, focusRing, focusRing, focusRing)
53
+
54
+ **Completely unused:** 7 fragments
55
+
56
+ - `subtleHover` from `hover.ts`
57
+ - `hoverableOpenControlItem`, `groupHoverControlItemWithTransition` from `hover.ts` (only 2-3 uses)
@@ -4,10 +4,13 @@
4
4
 
5
5
  import { type Density } from '@dxos/ui-types';
6
6
 
7
- export const coarseBlockSize = 'min-h-[2.5rem]';
8
- export const coarseDimensions = `${coarseBlockSize} px-3`;
7
+ const fineBlockSize = 'min-h-[2.5rem] pointer-fine:min-h-[2rem]';
8
+ const coarseBlockSize = 'min-h-[2.5rem]';
9
9
 
10
- export const fineBlockSize = 'min-h-[2.5rem] pointer-fine:min-h-[2rem]';
11
- export const fineDimensions = `${fineBlockSize} px-2`;
10
+ const fineDimensions = `${fineBlockSize} px-2`;
11
+ const coarseDimensions = `${coarseBlockSize} px-3`;
12
12
 
13
- export const densityBlockSize = (density: Density = 'coarse') => (density === 'fine' ? fineBlockSize : coarseBlockSize);
13
+ export const densityDimensions = (density: Density = 'fine') =>
14
+ density === 'fine' ? fineDimensions : coarseDimensions;
15
+
16
+ export const densityBlockSize = (density: Density = 'fine') => (density === 'fine' ? fineBlockSize : coarseBlockSize);
@@ -2,14 +2,9 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- // TODO(burdon): Replace fragments with utility and component classes.
5
+ // TODO(burdon): Remove export of fragments.
6
6
 
7
7
  export * from './density';
8
8
  export * from './disabled';
9
- export * from './elevation';
10
- export * from './focus';
11
9
  export * from './hover';
12
- export * from './selected';
13
- export * from './size';
14
10
  export * from './text';
15
- export * from './valence';
@@ -2,10 +2,5 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- /**
6
- * Content areas that contain the text editor.
7
- */
8
- export const textBlockWidth = 'w-full max-w-text-content mx-auto';
9
-
10
- export const descriptionTextPrimary = 'text-sm font-normal text-base-surface-text';
5
+ // TODO(burdon): Replace with Message component.
11
6
  export const descriptionMessage = 'text-description border border-dashed border-separator rounded-sm p-4';
package/src/index.ts CHANGED
@@ -4,5 +4,5 @@
4
4
 
5
5
  export * from './defs';
6
6
  export * from './fragments';
7
- export * from './theme/index';
7
+ export * from './theme';
8
8
  export * from './util';
@@ -11,7 +11,17 @@
11
11
  */
12
12
  @layer properties, theme, dx-tokens, user-tokens, base, tw-base, dx-base, components, tw-components, dx-components, utilities;
13
13
 
14
- @import 'tailwindcss';
14
+ /**
15
+ * `source(none)` disables Tailwind 4's automatic content discovery (which
16
+ * scans the project root, including `publicDir`). With auto-detection on,
17
+ * every write to `public/icons.svg` from `vite-plugin-icons` triggers a
18
+ * Tailwind CSS regeneration, which invalidates `main.css` through its
19
+ * import graph and produces a 3–5-ping HMR storm per icon-sprite write
20
+ * during cold-start (icons land in tight bursts as plugin chunks stream
21
+ * in). Explicit `@source` directives below replace the auto-scan with a
22
+ * stable, source-only set.
23
+ */
24
+ @import 'tailwindcss' source(none);
15
25
 
16
26
  /* @theme extensions */
17
27
  @import './css/theme/animation.css';
@@ -30,10 +40,12 @@
30
40
  @import './css/components/button.css';
31
41
  @import './css/components/checkbox.css';
32
42
  @import './css/components/dialog.css';
33
- @import './css/components/focus-ring.css';
43
+ @import './css/components/focus.css';
44
+ @import './css/components/icon.css';
34
45
  @import './css/components/panel.css';
35
46
  @import './css/components/link.css';
36
47
  @import './css/components/scrollbar.css';
48
+ @import './css/components/selected.css';
37
49
  @import './css/components/surface.css';
38
50
  @import './css/components/tag.css';
39
51
  @import './css/components/text.css';
@@ -52,7 +64,24 @@
52
64
  @import './css/integrations/tldraw.css';
53
65
 
54
66
  /**
55
- * Force-emit CSS variables for the full color palette for all:
67
+ * With `source(none)` on the Tailwind import above, these are the *only*
68
+ * paths Tailwind scans for utility classes. Cover every workspace source
69
+ * directory so lazy-loaded plugin / UI / SDK chunks don't surface new
70
+ * utilities mid-session, and root-level HTML entrypoints (index.html,
71
+ * internal.html, …) are visible too.
72
+ *
73
+ * Paths are relative to this file (`packages/ui/ui-theme/src/main.css`):
74
+ * ../../../ → `packages/`
75
+ * ../../../../tools/ → `tools/`
76
+ *
77
+ * Tailwind ignores `node_modules` and common build/output dirs by default,
78
+ * so a broad workspace glob is safe.
79
+ */
80
+ @source "../../../**/*.{ts,tsx,html}";
81
+ @source "../../../../tools/**/*.{ts,tsx,html}";
82
+
83
+ /**
84
+ * Force-emit CSS variables for the full color palette for all:
56
85
  * --color-{hue}-{shade}
57
86
  * --color-{hue}-{style}
58
87
  */
@@ -62,6 +91,13 @@
62
91
  @source inline("bg-{info,success,warning,error}-{fill,surface,surface-text,text,border}");
63
92
  @source inline("border-{info,success,warning,error}-border");
64
93
 
94
+ /**
95
+ * Force-emit tokens consumed by web components / .pcss files outside the
96
+ * Tailwind source-scan graph (e.g. lit-grid's dx-grid.pcss uses
97
+ * var(--color-grid-line)).
98
+ */
99
+ @source inline("bg-grid-{surface,highlight,comment,comment-active,hover-overlay,selection-overlay,line,focus-indicator}");
100
+
65
101
  /**
66
102
  * Plugins must come after all imports.
67
103
  */
@@ -75,9 +111,11 @@
75
111
  */
76
112
  @variant dark (&:where(.dark, .dark *));
77
113
 
78
- /**
79
- * Custom variants.
80
- */
81
- @custom-variant is-current (&[aria-current]:not([aria-current="false"]));
82
- @custom-variant hover-hover (@media (hover: hover));
114
+ /** Mobile */
115
+ @custom-variant pointer-coarse (@media (pointer: coarse));
116
+ /** Web */
83
117
  @custom-variant pointer-fine (@media (pointer: fine));
118
+ /** Supports mouse/trackpad (Web) */
119
+ @custom-variant hover-hover (@media (hover: hover));
120
+ /** Active navigation */
121
+ @custom-variant is-current (&[aria-current]:not([aria-current="false"]));
@@ -4,11 +4,10 @@
4
4
 
5
5
  /* eslint-disable no-console */
6
6
 
7
- import { existsSync, readFileSync } from 'node:fs';
8
- import { dirname, resolve } from 'node:path';
9
-
10
7
  import tailwindcss from '@tailwindcss/postcss';
11
8
  import autoprefixer from 'autoprefixer';
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import { dirname, resolve } from 'node:path';
12
11
  import postcssImport from 'postcss-import';
13
12
  import postcssNesting from 'postcss-nesting';
14
13
  import { type HtmlTagDescriptor, type Plugin, type UserConfig } from 'vite';
@@ -44,16 +43,15 @@ export type ThemePluginOptions = {
44
43
  */
45
44
  export const ThemePlugin = (options: ThemePluginOptions): Plugin => {
46
45
  // Prefer source CSS if available (monorepo dev), fall back to dist for installed package.
47
- const srcThemePath = resolve(import.meta.dirname, ROOT, 'src/theme.css');
48
- const distThemePath = resolve(import.meta.dirname, '../theme.css');
46
+ const srcThemePath = resolve(import.meta.dirname, ROOT, 'src/main.css');
47
+ const distThemePath = resolve(import.meta.dirname, '../main.css');
49
48
  const isMonorepo = existsSync(srcThemePath);
50
49
 
51
- // dark-mode.ts is always read from src (ships via "files": ["src"] in package.json).
52
- // In monorepo: import.meta.dirname = src/plugins/, so the file is right here.
53
- // In installed package: import.meta.dirname = dist/plugin/{format}/plugins/, so go up 4 levels.
54
- const srcDarkModePath = resolve(import.meta.dirname, ROOT, 'src/plugins/dark-mode.ts');
55
- const distDarkModePath = resolve(import.meta.dirname, ROOT, 'src/plugins/dark-mode.ts');
56
- const darkModeScriptPath = existsSync(srcDarkModePath) ? srcDarkModePath : distDarkModePath;
50
+ // Static assets shipped via "files": ["src"] in package.json.
51
+ // Both monorepo and installed package resolve to the same src/plugins/ directory.
52
+ const pluginsDir = resolve(import.meta.dirname, ROOT, 'src/plugins');
53
+ const darkModeScriptPath = resolve(pluginsDir, 'dark-mode.ts');
54
+ const mainCssPath = resolve(pluginsDir, 'main.css');
57
55
 
58
56
  const config: ThemePluginOptions = {
59
57
  srcCssPath: isMonorepo ? srcThemePath : distThemePath,
@@ -112,23 +110,13 @@ export const ThemePlugin = (options: ThemePluginOptions): Plugin => {
112
110
  injectTo: 'head-prepend',
113
111
  };
114
112
 
115
- // Critical pre-CSS fallbacks so text is readable before (and during) Vite HMR.
116
- // In dev mode Vite injects CSS via JS; when an HMR update fires the old style tag is
117
- // removed before the new one is inserted, briefly leaving all --color-* vars undefined.
118
- // These static approximations of neutral-50/950 survive that gap.
119
- // Values are intentionally kept in sync with @theme tokens in semantic.css.
113
+ // Critical styles: font sizing, overscroll, color fallbacks.
114
+ // Loaded from critical.css to keep styles maintainable and out of index.html.
120
115
  const criticalTag: HtmlTagDescriptor = {
121
116
  tag: 'style',
122
117
  attrs: { 'data-dxos-critical': '' },
123
118
  injectTo: 'head-prepend',
124
- children: [
125
- ':root { color-scheme: light; }',
126
- 'html { color: oklch(0.145 0 0); background-color: oklch(0.985 0 0); }', // ≈ neutral-950 / neutral-50
127
- 'html.dark { color-scheme: dark; color: oklch(0.985 0 0); background-color: oklch(0.145 0 0); }',
128
- '@media (prefers-color-scheme: dark) {',
129
- ' html:not(.dark) { color: oklch(0.985 0 0); background-color: oklch(0.145 0 0); }',
130
- '}',
131
- ].join('\n'),
119
+ children: readFileSync(mainCssPath, 'utf-8'),
132
120
  };
133
121
 
134
122
  return [darkModeTag, layersTag, criticalTag];
@@ -0,0 +1,45 @@
1
+ /*
2
+ * Critical styles injected into <head> by the Vite ThemePlugin before any stylesheets load.
3
+ * Ensures text is readable and layout is stable on the very first paint.
4
+ */
5
+
6
+ /* Base font size: larger default for touch devices, smaller for precision pointers. */
7
+ html {
8
+ font-size: 20px;
9
+ }
10
+ @media (pointer: fine) {
11
+ html {
12
+ font-size: 16px;
13
+ }
14
+ }
15
+
16
+ /* Prevent overscroll bounce and ensure full viewport height. */
17
+ html,
18
+ body {
19
+ overscroll-behavior: none;
20
+ -webkit-overflow-scrolling: touch;
21
+ }
22
+ body {
23
+ height: 100vh;
24
+ }
25
+
26
+ /* Pre-CSS color fallbacks so text is readable before and during Vite HMR. */
27
+ /* Values are intentionally kept in sync with @theme tokens in semantic.css. */
28
+ :root {
29
+ color-scheme: light;
30
+ }
31
+ html {
32
+ color: oklch(0.145 0 0);
33
+ background-color: oklch(0.985 0 0);
34
+ }
35
+ html.dark {
36
+ color-scheme: dark;
37
+ color: oklch(0.985 0 0);
38
+ background-color: oklch(0.145 0 0);
39
+ }
40
+ @media (prefers-color-scheme: dark) {
41
+ html:not(.dark) {
42
+ color: oklch(0.985 0 0);
43
+ background-color: oklch(0.145 0 0);
44
+ }
45
+ }
@@ -4,8 +4,7 @@
4
4
 
5
5
  import { type ComponentFunction, type Size, type Theme } from '@dxos/ui-types';
6
6
 
7
- import { getSize, getSizeHeight } from '../../fragments';
8
- import { mx } from '../../util';
7
+ import { mx, getSize, getHeight } from '../../util';
9
8
 
10
9
  export type AvatarStyleProps = Partial<{
11
10
  size: Size;
@@ -30,7 +29,7 @@ export const avatarDescription: ComponentFunction<AvatarStyleProps> = ({ srOnly
30
29
  mx('text-description', srOnly && 'sr-only', ...etc);
31
30
 
32
31
  export const avatarFrame: ComponentFunction<AvatarStyleProps> = ({ variant }, ...etc) =>
33
- mx('w-full h-full bg-(--surface-bg)', variant === 'circle' ? 'rounded-full' : 'rounded-sm', ...etc);
32
+ mx('h-full w-full bg-(--surface-bg)', variant === 'circle' ? 'rounded-full' : 'rounded-sm', ...etc);
34
33
 
35
34
  export const avatarStatusIcon: ComponentFunction<AvatarStyleProps> = ({ status, size = 3 }, ...etc) =>
36
35
  mx(
@@ -75,7 +74,7 @@ export const avatarGroupLabel: ComponentFunction<AvatarStyleProps> = ({ size, sr
75
74
  srOnly
76
75
  ? 'sr-only'
77
76
  : 'rounded-full truncate text-sm leading-none py-1 px-2 relative z-[1] flex items-center justify-center',
78
- size && getSizeHeight(size),
77
+ size && getHeight(size),
79
78
  ...etc,
80
79
  );
81
80
 
@@ -32,7 +32,7 @@ export type ButtonStyleProps = Partial<{
32
32
  }>;
33
33
 
34
34
  const buttonRoot: ComponentFunction<ButtonStyleProps> = (_props, ...etc) => {
35
- return mx('dx-button dx-focus-ring group max-w-full [&_span]:truncate', ...etc);
35
+ return mx('dx-button dx-focus-ring group [&_span]:truncate', ...etc);
36
36
  };
37
37
 
38
38
  const buttonGroup: ComponentFunction<{ elevation?: Elevation }> = (_props, ...etc) => {