@moldable-ai/ui 0.2.26 → 0.2.31

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 (186) hide show
  1. package/dist/catalog/component-catalog.d.ts +9 -2
  2. package/dist/catalog/component-catalog.d.ts.map +1 -1
  3. package/dist/catalog/component-catalog.js +11 -7
  4. package/dist/catalog/core-control-stories.d.ts.map +1 -1
  5. package/dist/catalog/core-control-stories.js +1 -1
  6. package/dist/catalog/core-foundation-stories.d.ts.map +1 -1
  7. package/dist/catalog/core-foundation-stories.js +15 -3
  8. package/dist/components/ui/app-frame.d.ts +20 -1
  9. package/dist/components/ui/app-frame.d.ts.map +1 -1
  10. package/dist/components/ui/app-frame.js +37 -7
  11. package/dist/components/ui/badge.d.ts +1 -1
  12. package/dist/components/ui/badge.js +1 -1
  13. package/dist/components/ui/button.d.ts +5 -3
  14. package/dist/components/ui/button.d.ts.map +1 -1
  15. package/dist/components/ui/button.js +39 -10
  16. package/dist/components/ui/card.d.ts +12 -2
  17. package/dist/components/ui/card.d.ts.map +1 -1
  18. package/dist/components/ui/card.js +3 -2
  19. package/dist/components/ui/checkbox.d.ts +4 -1
  20. package/dist/components/ui/checkbox.d.ts.map +1 -1
  21. package/dist/components/ui/checkbox.js +4 -4
  22. package/dist/components/ui/context-menu.js +6 -6
  23. package/dist/components/ui/date-field.d.ts +1 -1
  24. package/dist/components/ui/dropdown-menu.js +6 -6
  25. package/dist/components/ui/icon-button.d.ts +1 -1
  26. package/dist/components/ui/icon-button.d.ts.map +1 -1
  27. package/dist/components/ui/icon-button.js +1 -0
  28. package/dist/components/ui/index.d.ts +1 -0
  29. package/dist/components/ui/index.d.ts.map +1 -1
  30. package/dist/components/ui/index.js +1 -0
  31. package/dist/components/ui/input-group.d.ts +2 -2
  32. package/dist/components/ui/input.d.ts +6 -2
  33. package/dist/components/ui/input.d.ts.map +1 -1
  34. package/dist/components/ui/input.js +2 -2
  35. package/dist/components/ui/material.d.ts +8 -3
  36. package/dist/components/ui/material.d.ts.map +1 -1
  37. package/dist/components/ui/material.js +2 -2
  38. package/dist/components/ui/search-field.d.ts +3 -1
  39. package/dist/components/ui/search-field.d.ts.map +1 -1
  40. package/dist/components/ui/search-field.js +16 -6
  41. package/dist/components/ui/segmented-control.d.ts +3 -1
  42. package/dist/components/ui/segmented-control.d.ts.map +1 -1
  43. package/dist/components/ui/segmented-control.js +23 -17
  44. package/dist/components/ui/select.js +2 -2
  45. package/dist/components/ui/sidebar.d.ts +5 -3
  46. package/dist/components/ui/sidebar.d.ts.map +1 -1
  47. package/dist/components/ui/sidebar.js +53 -15
  48. package/dist/components/ui/status.d.ts +1 -1
  49. package/dist/components/ui/status.js +1 -1
  50. package/dist/components/ui/switch.d.ts +3 -1
  51. package/dist/components/ui/switch.d.ts.map +1 -1
  52. package/dist/components/ui/switch.js +3 -2
  53. package/dist/components/ui/toolbar-controls.d.ts +49 -0
  54. package/dist/components/ui/toolbar-controls.d.ts.map +1 -0
  55. package/dist/components/ui/toolbar-controls.js +47 -0
  56. package/dist/components/ui/toolbar.d.ts +5 -4
  57. package/dist/components/ui/toolbar.d.ts.map +1 -1
  58. package/dist/components/ui/toolbar.js +3 -3
  59. package/dist/index.d.ts +2 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +3 -1
  62. package/dist/lib/frame-lifecycle.d.ts +1 -0
  63. package/dist/lib/frame-lifecycle.d.ts.map +1 -1
  64. package/dist/lib/frame-lifecycle.js +7 -0
  65. package/dist/lib/theme.d.ts +39 -5
  66. package/dist/lib/theme.d.ts.map +1 -1
  67. package/dist/lib/theme.js +105 -17
  68. package/dist/tokens/contracts.d.ts +79 -0
  69. package/dist/tokens/contracts.d.ts.map +1 -0
  70. package/dist/tokens/contracts.js +57 -0
  71. package/dist/tokens/index.d.ts +2 -0
  72. package/dist/tokens/index.d.ts.map +1 -0
  73. package/dist/tokens/index.js +1 -0
  74. package/docs/README.md +2 -0
  75. package/docs/adaptive-materials.md +14 -9
  76. package/docs/design-tokens.md +10 -6
  77. package/docs/foundations.md +3 -3
  78. package/docs/macos-design-audit.md +144 -0
  79. package/docs/native-integration.md +4 -4
  80. package/docs/platform-architecture.md +189 -0
  81. package/docs/standalone-app-windows.md +37 -0
  82. package/docs/visual-regression.md +6 -8
  83. package/package.json +13 -1
  84. package/src/components/ui/accordion.md +46 -0
  85. package/src/components/ui/alert.md +12 -4
  86. package/src/components/ui/app-frame.md +8 -5
  87. package/src/components/ui/aspect-ratio.md +37 -0
  88. package/src/components/ui/avatar.md +48 -0
  89. package/src/components/ui/breadcrumb.md +52 -0
  90. package/src/components/ui/button-group.md +43 -0
  91. package/src/components/ui/button.md +20 -4
  92. package/src/components/ui/calendar.md +42 -0
  93. package/src/components/ui/card.md +43 -0
  94. package/src/components/ui/carousel.md +47 -0
  95. package/src/components/ui/chart.md +52 -0
  96. package/src/components/ui/checkbox.md +36 -0
  97. package/src/components/ui/collapsible.md +2 -3
  98. package/src/components/ui/color-well.md +14 -4
  99. package/src/components/ui/combobox.md +3 -3
  100. package/src/components/ui/confirm-dialog.md +5 -4
  101. package/src/components/ui/context-menu.md +3 -2
  102. package/src/components/ui/date-field.md +6 -1
  103. package/src/components/ui/date-picker.md +6 -3
  104. package/src/components/ui/dialog.md +14 -12
  105. package/src/components/ui/drawer.md +58 -0
  106. package/src/components/ui/error-boundary-view.md +1 -1
  107. package/src/components/ui/field.md +2 -1
  108. package/src/components/ui/form.md +71 -0
  109. package/src/components/ui/grid.md +4 -3
  110. package/src/components/ui/icon-button.md +4 -1
  111. package/src/components/ui/input-group.md +49 -0
  112. package/src/components/ui/input-otp.md +56 -0
  113. package/src/components/ui/input.md +40 -0
  114. package/src/components/ui/inspector.md +47 -7
  115. package/src/components/ui/item.md +5 -3
  116. package/src/components/ui/kbd.md +30 -0
  117. package/src/components/ui/label.md +29 -0
  118. package/src/components/ui/list.md +2 -2
  119. package/src/components/ui/material.md +25 -12
  120. package/src/components/ui/menu-items.md +60 -0
  121. package/src/components/ui/menubar.md +58 -0
  122. package/src/components/ui/message-scroller-primitive.md +70 -0
  123. package/src/components/ui/message-scroller.md +86 -0
  124. package/src/components/ui/navigation-button-group.md +4 -3
  125. package/src/components/ui/navigation-menu.md +52 -0
  126. package/src/components/ui/number-input.md +8 -0
  127. package/src/components/ui/pagination.md +51 -0
  128. package/src/components/ui/panel.md +6 -0
  129. package/src/components/ui/popover.md +10 -7
  130. package/src/components/ui/progress.md +31 -0
  131. package/src/components/ui/radio-group.md +36 -0
  132. package/src/components/ui/resizable.md +2 -1
  133. package/src/components/ui/scroll-area.md +3 -1
  134. package/src/components/ui/search-field.md +6 -1
  135. package/src/components/ui/segmented-control.md +6 -1
  136. package/src/components/ui/separator.md +35 -0
  137. package/src/components/ui/sidebar.md +20 -3
  138. package/src/components/ui/skeleton.md +36 -0
  139. package/src/components/ui/slider.md +58 -0
  140. package/src/components/ui/spinner.md +41 -0
  141. package/src/components/ui/split-view.md +8 -4
  142. package/src/components/ui/switch.md +44 -0
  143. package/src/components/ui/table.md +7 -7
  144. package/src/components/ui/tabs.md +3 -1
  145. package/src/components/ui/textarea.md +41 -0
  146. package/src/components/ui/toggle-group.md +2 -2
  147. package/src/components/ui/toggle.md +9 -9
  148. package/src/components/ui/toolbar-controls.md +83 -0
  149. package/src/components/ui/toolbar.md +12 -6
  150. package/src/styles/index.css +199 -107
  151. package/src/tokens/contracts.ts +131 -0
  152. package/src/tokens/index.css +6 -0
  153. package/src/tokens/index.ts +23 -0
  154. package/src/tokens/macos-26/README.md +37 -0
  155. package/src/tokens/macos-26/aliases.css +53 -0
  156. package/src/tokens/macos-26/compatibility.css +7 -0
  157. package/src/tokens/macos-26/index.css +3 -0
  158. package/src/tokens/macos-26/source/provenance.generated.json +28 -0
  159. package/src/tokens/macos-26/source/sidebar.generated.css +29 -0
  160. package/src/tokens/macos-26/source.css +7 -0
  161. package/src/tokens/macos-26/web-adaptations.json +25 -0
  162. package/src/tokens/macos-26/web-adaptations.md +15 -0
  163. package/src/tokens/macos-27/accessibility.css +11 -0
  164. package/src/tokens/macos-27/aliases.css +304 -0
  165. package/src/tokens/macos-27/compatibility.css +212 -0
  166. package/src/tokens/macos-27/index.css +15 -0
  167. package/src/tokens/macos-27/recipes.css +23 -0
  168. package/src/tokens/macos-27/source/chrome.generated.css +338 -0
  169. package/src/tokens/macos-27/source/colors.generated.css +140 -0
  170. package/src/tokens/macos-27/source/context.generated.css +18 -0
  171. package/src/tokens/macos-27/source/controls.generated.css +764 -0
  172. package/src/tokens/macos-27/source/effects.generated.css +68 -0
  173. package/src/tokens/macos-27/source/kit.generated.css +50 -0
  174. package/src/tokens/macos-27/source/overlays-status.generated.css +931 -0
  175. package/src/tokens/macos-27/source/paint-styles.generated.css +81 -0
  176. package/src/tokens/macos-27/source/provenance.generated.json +62 -0
  177. package/src/tokens/macos-27/source/sizes.generated.css +385 -0
  178. package/src/tokens/macos-27/source/source.generated.json +554 -0
  179. package/src/tokens/macos-27/source/text-styles.generated.css +116 -0
  180. package/src/tokens/macos-27/source.css +17 -0
  181. package/src/tokens/macos-27/web-adaptations.css +98 -0
  182. package/src/tokens/macos-27/web-adaptations.json +163 -0
  183. package/src/tokens/macos-27/web-adaptations.md +117 -0
  184. package/src/tokens/moldable/brand.css +48 -0
  185. package/src/tokens/moldable/index.css +1 -0
  186. package/src/tokens/platform-contract.test.ts +290 -0
package/dist/lib/theme.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useCallback, useContext, useEffect, useState, } from 'react';
4
4
  import { TooltipProvider } from '../components/ui/tooltip.js';
5
+ import { resolvePlatformPack } from '../tokens/contracts.js';
5
6
  const ThemeContext = createContext(null);
6
7
  const STORAGE_KEY = 'moldable-theme';
7
8
  function getSystemTheme() {
@@ -22,14 +23,16 @@ function getUrlTheme() {
22
23
  }
23
24
  return null;
24
25
  }
25
- function getStoredTheme() {
26
+ function getStoredTheme(storageKey = STORAGE_KEY) {
26
27
  if (typeof window === 'undefined')
27
28
  return 'system';
28
29
  // URL parameter takes precedence (for iframe embedding)
29
30
  const urlTheme = getUrlTheme();
30
31
  if (urlTheme)
31
32
  return urlTheme;
32
- const stored = localStorage.getItem(STORAGE_KEY);
33
+ if (!storageKey)
34
+ return 'system';
35
+ const stored = localStorage.getItem(storageKey);
33
36
  if (stored === 'light' || stored === 'dark' || stored === 'system') {
34
37
  return stored;
35
38
  }
@@ -41,36 +44,109 @@ function resolveTheme(theme) {
41
44
  }
42
45
  return theme;
43
46
  }
44
- export function ThemeProvider({ children }) {
45
- const [theme, setThemeState] = useState(() => getStoredTheme());
46
- const [resolvedTheme, setResolvedTheme] = useState(() => resolveTheme(theme));
47
- const setTheme = useCallback((newTheme) => {
48
- setThemeState(newTheme);
49
- localStorage.setItem(STORAGE_KEY, newTheme);
47
+ export function ThemeProvider({ children, platform = 'macos', platformVersion = '27', uiTheme: uiThemeProp = 'moldable', sidebarStyle: sidebarStyleProp, appearance: appearanceProp, controlSize: controlSizeProp = 'medium', materialContext: materialContextProp = 'content', preferences = {}, storageKey = STORAGE_KEY, }) {
48
+ const [platformSelection, setPlatformSelection] = useState(platform);
49
+ const [platformVersionSelection, setPlatformVersionSelection] = useState(platformVersion);
50
+ const platformPack = resolvePlatformPack(platformSelection, platformVersionSelection);
51
+ const [uiTheme, setUIThemeState] = useState(uiThemeProp);
52
+ const [sidebarStyle, setSidebarStyle] = useState(() => sidebarStyleProp ?? 'edge-to-edge');
53
+ const [appearance, setAppearanceState] = useState(() => appearanceProp ?? getStoredTheme(storageKey));
54
+ const [resolvedAppearance, setResolvedAppearance] = useState(() => resolveTheme(appearance));
55
+ const [controlSize, setControlSize] = useState(controlSizeProp);
56
+ const [materialContext, setMaterialContext] = useState(materialContextProp);
57
+ const setAppearance = useCallback((newAppearance) => {
58
+ setAppearanceState(newAppearance);
59
+ if (storageKey && typeof window !== 'undefined') {
60
+ localStorage.setItem(storageKey, newAppearance);
61
+ }
62
+ }, [storageKey]);
63
+ const setPlatformPack = useCallback((nextPlatform, nextVersion) => {
64
+ setPlatformSelection(nextPlatform);
65
+ setPlatformVersionSelection(nextVersion);
50
66
  }, []);
51
- // Apply theme to document
67
+ const setUITheme = useCallback((nextTheme) => {
68
+ setUIThemeState(nextTheme);
69
+ if (sidebarStyleProp === undefined) {
70
+ setSidebarStyle('edge-to-edge');
71
+ }
72
+ }, [sidebarStyleProp]);
73
+ useEffect(() => setPlatformSelection(platform), [platform]);
74
+ useEffect(() => setPlatformVersionSelection(platformVersion), [platformVersion]);
75
+ useEffect(() => {
76
+ setUIThemeState(uiThemeProp);
77
+ setSidebarStyle(sidebarStyleProp ?? 'edge-to-edge');
78
+ }, [sidebarStyleProp, uiThemeProp]);
52
79
  useEffect(() => {
53
- const resolved = resolveTheme(theme);
54
- setResolvedTheme(resolved);
80
+ if (appearanceProp)
81
+ setAppearanceState(appearanceProp);
82
+ }, [appearanceProp]);
83
+ useEffect(() => setControlSize(controlSizeProp), [controlSizeProp]);
84
+ useEffect(() => setMaterialContext(materialContextProp), [materialContextProp]);
85
+ useEffect(() => {
86
+ const resolved = resolveTheme(appearance);
87
+ setResolvedAppearance(resolved);
55
88
  const root = document.documentElement;
56
89
  root.classList.remove('light', 'dark');
57
90
  root.classList.add(resolved);
58
- }, [theme]);
59
- // Listen for system theme changes
91
+ root.dataset.uiTheme = uiTheme;
92
+ root.dataset.uiPlatform = platformPack.platform;
93
+ root.dataset.uiPlatformVersion = platformPack.version;
94
+ root.dataset.uiPlatformPack = platformPack.id;
95
+ root.dataset.uiSidebarStyle = sidebarStyle;
96
+ root.dataset.uiAppearance = resolved;
97
+ root.dataset.controlSize = controlSize;
98
+ root.dataset.materialContext = materialContext;
99
+ root.dataset.contrast = preferences.contrast ?? 'system';
100
+ root.dataset.transparency = preferences.transparency ?? 'system';
101
+ root.dataset.motion = preferences.motion ?? 'system';
102
+ }, [
103
+ appearance,
104
+ controlSize,
105
+ materialContext,
106
+ preferences.contrast,
107
+ preferences.motion,
108
+ preferences.transparency,
109
+ platformPack.id,
110
+ platformPack.platform,
111
+ platformPack.version,
112
+ sidebarStyle,
113
+ uiTheme,
114
+ ]);
60
115
  useEffect(() => {
61
- if (theme !== 'system')
116
+ if (appearance !== 'system')
62
117
  return;
63
118
  const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
64
119
  const handleChange = () => {
65
120
  const resolved = getSystemTheme();
66
- setResolvedTheme(resolved);
121
+ setResolvedAppearance(resolved);
67
122
  document.documentElement.classList.remove('light', 'dark');
68
123
  document.documentElement.classList.add(resolved);
124
+ document.documentElement.dataset.uiAppearance = resolved;
69
125
  };
70
126
  mediaQuery.addEventListener('change', handleChange);
71
127
  return () => mediaQuery.removeEventListener('change', handleChange);
72
- }, [theme]);
73
- return (_jsx(ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme }, children: _jsx(TooltipProvider, { children: children }) }));
128
+ }, [appearance]);
129
+ return (_jsx(ThemeContext.Provider, { value: {
130
+ uiTheme,
131
+ setUITheme,
132
+ platform: platformPack.platform,
133
+ platformVersion: platformPack.version,
134
+ platformPack,
135
+ setPlatformPack,
136
+ sidebarStyle,
137
+ setSidebarStyle,
138
+ appearance,
139
+ resolvedAppearance,
140
+ setAppearance,
141
+ controlSize,
142
+ setControlSize,
143
+ materialContext,
144
+ setMaterialContext,
145
+ preferences,
146
+ theme: appearance,
147
+ resolvedTheme: resolvedAppearance,
148
+ setTheme: setAppearance,
149
+ }, children: _jsx(TooltipProvider, { children: children }) }));
74
150
  }
75
151
  export function useTheme() {
76
152
  const context = useContext(ThemeContext);
@@ -79,6 +155,10 @@ export function useTheme() {
79
155
  }
80
156
  return context;
81
157
  }
158
+ /** Internal-friendly optional access for components that also work standalone. */
159
+ export function useOptionalTheme() {
160
+ return useContext(ThemeContext);
161
+ }
82
162
  /**
83
163
  * Inline script to prevent theme flash on page load.
84
164
  * Add this to your <head> in layout.tsx:
@@ -100,5 +180,13 @@ export const themeScript = `
100
180
  theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
101
181
  }
102
182
  document.documentElement.classList.add(theme);
183
+ document.documentElement.dataset.uiTheme = 'moldable';
184
+ document.documentElement.dataset.uiPlatform = 'macos';
185
+ document.documentElement.dataset.uiPlatformVersion = '27';
186
+ document.documentElement.dataset.uiPlatformPack = 'macos-27';
187
+ document.documentElement.dataset.uiSidebarStyle = 'edge-to-edge';
188
+ document.documentElement.dataset.uiAppearance = theme;
189
+ document.documentElement.dataset.controlSize = 'medium';
190
+ document.documentElement.dataset.materialContext = 'content';
103
191
  })();
104
192
  `;
@@ -0,0 +1,79 @@
1
+ export type UITheme = 'platform' | 'moldable';
2
+ export type UIPlatform = 'macos' | 'windows' | 'web';
3
+ export type UIPlatformVersion = string;
4
+ /**
5
+ * A component recipe can intentionally come from a different versioned
6
+ * platform source than the application's base platform pack.
7
+ *
8
+ * Public names describe the anatomy an app is choosing. The registry below
9
+ * records which immutable platform source defines each recipe.
10
+ */
11
+ export type UISidebarStyle = 'platform' | 'floating' | 'edge-to-edge';
12
+ export type PlatformPackId = `${UIPlatform}-${string}`;
13
+ export interface UIPlatformPack {
14
+ id: PlatformPackId;
15
+ platform: UIPlatform;
16
+ version: UIPlatformVersion;
17
+ capabilities: {
18
+ nativeMaterials: boolean;
19
+ nativeWindowChrome: boolean;
20
+ overlayScrollbars: boolean;
21
+ };
22
+ }
23
+ export type UIAppearance = 'light' | 'dark' | 'system';
24
+ export type ResolvedUIAppearance = Exclude<UIAppearance, 'system'>;
25
+ export type ControlSize = 'mini' | 'small' | 'medium' | 'large' | 'xl';
26
+ export type LayoutDensity = 'compact' | 'regular' | 'comfortable';
27
+ export type MaterialThickness = 'ultra-thick' | 'thick' | 'medium' | 'thin' | 'ultra-thin';
28
+ export type MaterialContext = 'content' | 'over-glass';
29
+ export type ContrastPreference = 'system' | 'standard' | 'increased';
30
+ export type TransparencyPreference = 'system' | 'standard' | 'reduced';
31
+ export type MotionPreference = 'system' | 'standard' | 'reduced';
32
+ export interface UIPreferences {
33
+ contrast?: ContrastPreference;
34
+ transparency?: TransparencyPreference;
35
+ motion?: MotionPreference;
36
+ }
37
+ export declare const CONTROL_SIZES: readonly ["mini", "small", "medium", "large", "xl"];
38
+ export declare const MATERIAL_THICKNESSES: readonly ["ultra-thick", "thick", "medium", "thin", "ultra-thin"];
39
+ export declare const SIDEBAR_STYLES: {
40
+ readonly platform: {
41
+ readonly id: "platform";
42
+ readonly sourcePlatform: null;
43
+ readonly sourceVersion: null;
44
+ };
45
+ readonly floating: {
46
+ readonly id: "floating";
47
+ readonly sourcePlatform: "macos";
48
+ readonly sourceVersion: "26";
49
+ };
50
+ readonly 'edge-to-edge': {
51
+ readonly id: "edge-to-edge";
52
+ readonly sourcePlatform: "macos";
53
+ readonly sourceVersion: "27";
54
+ };
55
+ };
56
+ export declare const PLATFORM_PACKS: {
57
+ readonly 'macos-27': {
58
+ readonly id: "macos-27";
59
+ readonly platform: "macos";
60
+ readonly version: "27";
61
+ readonly capabilities: {
62
+ readonly nativeMaterials: true;
63
+ readonly nativeWindowChrome: true;
64
+ readonly overlayScrollbars: true;
65
+ };
66
+ };
67
+ readonly 'web-default': {
68
+ readonly id: "web-default";
69
+ readonly platform: "web";
70
+ readonly version: "default";
71
+ readonly capabilities: {
72
+ readonly nativeMaterials: false;
73
+ readonly nativeWindowChrome: false;
74
+ readonly overlayScrollbars: false;
75
+ };
76
+ };
77
+ };
78
+ export declare function resolvePlatformPack(platform: UIPlatform, version: UIPlatformVersion): UIPlatformPack;
79
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/tokens/contracts.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,UAAU,CAAA;AAE7C,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,CAAA;AAEpD,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,UAAU,GAAG,cAAc,CAAA;AAErE,MAAM,MAAM,cAAc,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,CAAA;AAEtD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,cAAc,CAAA;IAClB,QAAQ,EAAE,UAAU,CAAA;IACpB,OAAO,EAAE,iBAAiB,CAAA;IAC1B,YAAY,EAAE;QACZ,eAAe,EAAE,OAAO,CAAA;QACxB,kBAAkB,EAAE,OAAO,CAAA;QAC3B,iBAAiB,EAAE,OAAO,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEtD,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;AAElE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAA;AAEtE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAA;AAEjE,MAAM,MAAM,iBAAiB,GACzB,aAAa,GACb,OAAO,GACP,QAAQ,GACR,MAAM,GACN,YAAY,CAAA;AAEhB,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,YAAY,CAAA;AAEtD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAA;AAEpE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;AAEtE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;AAEhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IAC7B,YAAY,CAAC,EAAE,sBAAsB,CAAA;IACrC,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAC1B;AAED,eAAO,MAAM,aAAa,qDAMiB,CAAA;AAE3C,eAAO,MAAM,oBAAoB,mEAMgB,CAAA;AAEjD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;CAuB1B,CAAA;AAED,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;CAqBwB,CAAA;AAEnD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,iBAAiB,GACzB,cAAc,CAMhB"}
@@ -0,0 +1,57 @@
1
+ export const CONTROL_SIZES = [
2
+ 'mini',
3
+ 'small',
4
+ 'medium',
5
+ 'large',
6
+ 'xl',
7
+ ];
8
+ export const MATERIAL_THICKNESSES = [
9
+ 'ultra-thick',
10
+ 'thick',
11
+ 'medium',
12
+ 'thin',
13
+ 'ultra-thin',
14
+ ];
15
+ export const SIDEBAR_STYLES = {
16
+ platform: {
17
+ id: 'platform',
18
+ sourcePlatform: null,
19
+ sourceVersion: null,
20
+ },
21
+ floating: {
22
+ id: 'floating',
23
+ sourcePlatform: 'macos',
24
+ sourceVersion: '26',
25
+ },
26
+ 'edge-to-edge': {
27
+ id: 'edge-to-edge',
28
+ sourcePlatform: 'macos',
29
+ sourceVersion: '27',
30
+ },
31
+ };
32
+ export const PLATFORM_PACKS = {
33
+ 'macos-27': {
34
+ id: 'macos-27',
35
+ platform: 'macos',
36
+ version: '27',
37
+ capabilities: {
38
+ nativeMaterials: true,
39
+ nativeWindowChrome: true,
40
+ overlayScrollbars: true,
41
+ },
42
+ },
43
+ 'web-default': {
44
+ id: 'web-default',
45
+ platform: 'web',
46
+ version: 'default',
47
+ capabilities: {
48
+ nativeMaterials: false,
49
+ nativeWindowChrome: false,
50
+ overlayScrollbars: false,
51
+ },
52
+ },
53
+ };
54
+ export function resolvePlatformPack(platform, version) {
55
+ const exact = Object.values(PLATFORM_PACKS).find((pack) => pack.platform === platform && pack.version === version);
56
+ return exact ?? PLATFORM_PACKS['web-default'];
57
+ }
@@ -0,0 +1,2 @@
1
+ export { CONTROL_SIZES, MATERIAL_THICKNESSES, PLATFORM_PACKS, SIDEBAR_STYLES, resolvePlatformPack, type ContrastPreference, type ControlSize, type LayoutDensity, type MaterialContext, type MaterialThickness, type MotionPreference, type PlatformPackId, type ResolvedUIAppearance, type TransparencyPreference, type UIAppearance, type UIPlatform, type UIPlatformPack, type UIPlatformVersion, type UIPreferences, type UISidebarStyle, type UITheme, } from './contracts.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,OAAO,GACb,MAAM,aAAa,CAAA"}
@@ -0,0 +1 @@
1
+ export { CONTROL_SIZES, MATERIAL_THICKNESSES, PLATFORM_PACKS, SIDEBAR_STYLES, resolvePlatformPack, } from './contracts.js';
package/docs/README.md CHANGED
@@ -8,6 +8,8 @@ feels at home on macOS without imitating private platform implementation.
8
8
 
9
9
  - [Foundations](./foundations.md)
10
10
  - [Design tokens](./design-tokens.md)
11
+ - [Platform architecture](./platform-architecture.md)
12
+ - [macOS design audit](./macos-design-audit.md)
11
13
  - [Adaptive materials](./adaptive-materials.md)
12
14
  - [App anatomy](./app-anatomy.md)
13
15
  - [Standalone app windows](./standalone-app-windows.md)
@@ -6,14 +6,15 @@ not replace content surfaces.
6
6
 
7
7
  ## Roles
8
8
 
9
- Use `regular` material for normal toolbar and window chrome, grouped controls,
10
- menus, and temporary control overlays. It provides enough frosting for text
11
- and icons to remain stable as content moves behind it.
9
+ Choose `ultra-thick`, `thick`, `medium`, `thin`, or `ultra-thin` according to
10
+ the surface hierarchy. Medium material is the normal choice for window chrome,
11
+ grouped controls, menus, and temporary control overlays.
12
12
 
13
- Use `clear` material only for compact controls floating over photos, maps,
13
+ Use ultra-thin material only for compact controls floating over photos, maps,
14
14
  video, or another richly colored canvas. Check the control against the
15
15
  lightest, darkest, and busiest content it can cover. If contrast is not stable,
16
- use `regular`.
16
+ use medium. The `regular` and `clear` names remain compatibility aliases for
17
+ `medium` and `ultra-thin`, respectively.
17
18
 
18
19
  Documents, calendars, tables, forms, cards, sheets, inspectors, and primary
19
20
  working canvases remain opaque. Do not stack translucent content cards to
@@ -24,7 +25,7 @@ create hierarchy.
24
25
  `Material` applies one adaptive surface:
25
26
 
26
27
  ```tsx
27
- <Material asChild variant="clear" shape="circle" interactive>
28
+ <Material asChild variant="ultra-thin" shape="circle" interactive>
28
29
  <IconButton label="Center map" variant="ghost">
29
30
  <Locate />
30
31
  </IconButton>
@@ -34,7 +35,7 @@ create hierarchy.
34
35
  `MaterialGroup` applies one sampled layer to a related cluster:
35
36
 
36
37
  ```tsx
37
- <MaterialGroup variant="regular" shape="capsule" spacing="compact">
38
+ <MaterialGroup variant="medium" shape="capsule" spacing="compact">
38
39
  <IconButton label="Previous" variant="ghost">
39
40
  <ChevronLeft />
40
41
  </IconButton>
@@ -75,5 +76,9 @@ sampled layers and avoid nesting them.
75
76
  CSS material samples content inside the app renderer. It cannot sample the
76
77
  desktop behind a native window. Native behind-window material, window
77
78
  transparency, active-window appearance, and energy policy remain host-owned
78
- capabilities. Apps should use the same semantic variants regardless of which
79
- adapter renders them.
79
+ capabilities. Apps should use the same thickness roles regardless of which
80
+ adapter renders them. Apps opt into native regions per window through
81
+ `moldable.json`; renderer-owned background and sidebar surfaces additionally
82
+ opt in through `AppFrame`. Full-window background material uses a frosted,
83
+ tinted host surface rather than uncovered transparency. Content that needs a
84
+ stable reading surface can remain opaque above it.
@@ -114,8 +114,9 @@ disabled, and invalid states. Disabled wins over pointer states. Focus remains
114
114
  visible when combined with selected or invalid. Selection is durable state;
115
115
  hover is not a substitute for it.
116
116
 
117
- Focus rings use the neutral translucent `--ring` token at one pixel. Never use
118
- plain `focus:` ring utilities or accent colors for keyboard focus.
117
+ Focus rings use the shared `--ring` token and the width defined by each
118
+ component recipe. Never use plain `focus:` ring utilities or invent a
119
+ component-local focus color.
119
120
 
120
121
  Do not use state-layer colors as arbitrary fills. Prefer the shared component
121
122
  when one exists.
@@ -141,11 +142,14 @@ stay theme-aware and do not inherit Moldable's legacy brand orange. Apps may
141
142
  override the complete series palette at a theme boundary, but chart
142
143
  components and stories should never embed raw series colors.
143
144
 
144
- Adaptive material has two semantic variants. Each includes fill, fallback,
145
- border, highlight, blur, and saturation tokens:
145
+ Adaptive material has five standard thickness variants: `ultra-thick`,
146
+ `thick`, `medium`, `thin`, and `ultra-thin`. Each includes fill, fallback,
147
+ border, highlight, blur, and saturation tokens. `regular` remains a
148
+ compatibility alias for `medium`, and `clear` remains a compatibility alias
149
+ for `ultra-thin`.
146
150
 
147
- - `--material-regular-*` supports normal toolbar and control chrome.
148
- - `--material-clear-*` supports compact controls over media or a richly
151
+ - Medium material supports normal toolbar and control chrome.
152
+ - Ultra-thin material supports compact controls over media or a richly
149
153
  colored canvas.
150
154
  - `shadow-panel`, `shadow-overlay`, `shadow-dialog`, and `shadow-window`
151
155
  provide elevation independently from translucency.
@@ -22,9 +22,9 @@ Status color communicates status only; it is not decoration.
22
22
  - Preserve usable pointer targets even when the visible control is small.
23
23
  - Prefer separators, background shifts, and whitespace over nested cards.
24
24
  - Working panes own scrolling. The window shell should not accidentally scroll.
25
- - Set `data-density="compact"`, `"default"`, or `"comfortable"` at a
26
- composition boundary. Components should consume density tokens rather than
27
- inventing per-screen heights.
25
+ - Set `data-density="compact"` or `"comfortable"` at a composition boundary;
26
+ omit it for regular/default density. Components should consume density
27
+ tokens rather than inventing per-screen heights.
28
28
 
29
29
  ## Surfaces
30
30
 
@@ -0,0 +1,144 @@
1
+ # macOS design audit for Moldable UI
2
+
3
+ Status: active
4
+ Target: macOS desktop apps rendered in Moldable-owned Tauri windows
5
+ Scope: `@moldable-ai/ui`, app-owned titlebars, and the Moldable UI catalog
6
+
7
+ This audit treats macOS AppKit and SwiftUI behavior as the semantic reference
8
+ while allowing a React implementation. The goal is not to copy source pixels.
9
+ The goal is to preserve macOS hierarchy, density, control roles, window
10
+ behavior, input behavior, and accessibility expectations.
11
+
12
+ ## Reference baseline
13
+
14
+ - Foundations: Typography — Ensuring legibility, Conveying hierarchy, Using
15
+ system fonts, Platform considerations/macOS
16
+ - Foundations: Layout — Visual hierarchy, Adaptability, Platform
17
+ considerations/macOS
18
+ - Foundations: Materials — Liquid Glass, Standard materials, Platform
19
+ considerations/macOS
20
+ - Foundations: Color — Best practices, Inclusive color, System colors
21
+ - Foundations: Dark Mode — Best practices, Platform considerations/macOS
22
+ - Foundations: Accessibility — Vision, Mobility, Cognitive
23
+ - Foundations: Icons — Best practices, Platform considerations/macOS
24
+ - Foundations: Motion — Best practices, Providing feedback
25
+ - Platforms: Designing for macOS — Best practices
26
+ - Layout: Sidebars, Split views, Windows, Lists and tables
27
+ - Menus: Toolbars — Titles, Navigation, Actions, Item groupings,
28
+ Platform considerations/macOS
29
+ - Menus: Buttons — Style, Content, Role, Platform considerations/macOS
30
+ - Controls: Toggles, Segmented controls, Text fields
31
+ - Search: Search fields — Best practices, Platform considerations/iPadOS,
32
+ macOS
33
+
34
+ ## Severity-ranked findings
35
+
36
+ ### P0 — window and navigation semantics
37
+
38
+ 1. App-owned titlebars must preserve the native traffic-light exclusion area,
39
+ a draggable noninteractive region, double-click window behavior, and usable
40
+ interactive controls. `Toolbar`, the desktop host, and window metadata must
41
+ be tested together; CSS alone cannot guarantee this contract.
42
+ 2. Toolbar titles must identify the current view or document, not repeat the
43
+ application name. App identity belongs to the window and system surfaces.
44
+ 3. Back controls must call Moldable navigation and expose the same action to
45
+ keyboard and menu commands. A decorative chevron is not navigation.
46
+
47
+ ### P1 — hierarchy and control roles
48
+
49
+ 1. Use a sidebar or tab view for top-level destinations. A segmented control is
50
+ for a small set of closely related modes that affect one view, not primary
51
+ app navigation.
52
+ 2. Keep only frequent actions in a toolbar. Put infrequent actions in a More
53
+ menu and mirror every command in the macOS menu bar.
54
+ 3. Group only controls that are logically related. Standalone navigation
55
+ controls should usually be quiet; groups may own one shared capsule.
56
+ 4. Use the system appearance by default. An appearance selector is appropriate
57
+ in a component test harness, but not as persistent application chrome.
58
+ 5. Prefer compact macOS typography: 13 px body, 15–16 px titles, 18 px section
59
+ headings, and 24 px display headings. Avoid web-dashboard hero sizing in
60
+ ordinary desktop panes.
61
+ 6. Prefer flat content hierarchy, separators, and selection fills over nested
62
+ bordered cards and repeated shadows. Materials belong to navigation and
63
+ transient controls, not every content surface.
64
+ 7. Use switches for immediate, emphasized binary settings. Use checkboxes for
65
+ ordinary grouped preferences and radio buttons for mutually exclusive
66
+ choices.
67
+
68
+ ### P2 — desktop completeness
69
+
70
+ 1. Extend the existing resizable `SplitView` with a documented collapsible
71
+ recipe, restoration, and automatic narrow-window collapse.
72
+ 2. Add table column resizing, sorting, optional alternating row backgrounds,
73
+ keyboard range selection, and state restoration.
74
+ 3. Extend command metadata so toolbar items, menus, keyboard shortcuts,
75
+ accessibility labels, and disabled state can share one source of truth.
76
+ 4. Separate semantic action roles from visual button variants and cover
77
+ default, cancel, destructive, help, disclosure, toolbar, and icon-only
78
+ actions.
79
+ 5. Validate at increased contrast, reduced transparency, reduced motion,
80
+ keyboard-only input, 200% zoom, and light/dark appearances.
81
+
82
+ ## Current conformance
83
+
84
+ The shared package already provides:
85
+
86
+ - semantic light and dark colors instead of raw app-specific grays;
87
+ - a system-font stack and compact macOS-oriented type tokens;
88
+ - labeled icon buttons with visible keyboard focus;
89
+ - reduced-motion and reduced-transparency fallbacks;
90
+ - explicit status text in addition to color;
91
+ - titlebar insets and window drag handlers for app-owned top toolbars;
92
+ - compact, regular, and comfortable density tokens;
93
+ - a distinct accent-colored `prominent` button role for the single key action
94
+ in a view, without changing the appearance of existing default buttons.
95
+
96
+ The Moldable UI catalog now demonstrates:
97
+
98
+ - top-level source-list navigation in the sidebar;
99
+ - current-view toolbar titles;
100
+ - quiet standalone toolbar navigation;
101
+ - preview-only appearance and accessibility controls;
102
+ - flatter stages, fewer shadows, and smaller radii;
103
+ - package typography tokens instead of oversized page-specific values.
104
+
105
+ ## Component acceptance criteria
106
+
107
+ ### Toolbar
108
+
109
+ - Uses the current view or document title.
110
+ - Preserves native window-control clearance in app-owned windows.
111
+ - Dragging works from every noninteractive toolbar region.
112
+ - Interactive descendants never initiate a window drag.
113
+ - Frequent actions are visible; rare actions are in More.
114
+ - Related actions share a group; unrelated actions do not.
115
+ - Commands have labels, tooltips when helpful, shortcuts, menu equivalents,
116
+ disabled states, and tests.
117
+
118
+ ### Sidebar and split view
119
+
120
+ - Selection is persistent and visually unambiguous.
121
+ - Search filters the sidebar content it sits above.
122
+ - Sidebar width is resizable within reasonable minimum and maximum values.
123
+ - The sidebar can collapse automatically in a narrow window and manually at
124
+ any size.
125
+ - The divider remains subtle and the sidebar doesn’t compete with content.
126
+
127
+ ### Controls
128
+
129
+ - Labels describe outcomes, not implementation.
130
+ - The default action is visually prominent only when the context needs one.
131
+ - Destructive actions are explicit and separated from routine actions.
132
+ - Icons use familiar system metaphors and never carry meaning without an
133
+ accessible label.
134
+ - Controls remain operable with keyboard, pointer, VoiceOver, increased
135
+ contrast, reduced transparency, and reduced motion.
136
+
137
+ ## Review policy
138
+
139
+ Every new public visual primitive must include catalog stories for light and
140
+ dark appearance, compact and regular density, wide and narrow viewport,
141
+ rest/focus/disabled/invalid states where applicable, and accessible naming.
142
+ Every window-chrome change must be verified in a real Moldable-owned macOS
143
+ window because embedded browser rendering cannot validate traffic-light
144
+ placement or native dragging.
@@ -16,10 +16,10 @@ Do not create UI-only no-op stubs for native menus, share sheets, or date
16
16
  pickers. First define ownership, cancellation, errors, replay/reconnect
17
17
  behavior, and platform fallbacks.
18
18
 
19
- `DateField` is the portable renderer baseline. A future system date picker,
20
- share sheet, or native menu must only be selected after the host advertises the
21
- service through a versioned context. Requests must remain bound to the
22
- originating app window and user activation.
19
+ `DateField`, renderer menus, and browser file/share controls remain the
20
+ portable baselines. The package currently exposes host-advertised date/time
21
+ picker, share, native-menu, and file-dialog services with renderer fallbacks.
22
+ Requests remain bound to the originating app window and user activation.
23
23
 
24
24
  Standalone windows should remain host-owned surfaces around an app frame. Do
25
25
  not load arbitrary app URLs directly into a privileged native webview merely to