@marwes-ui/vue 1.0.2 → 1.0.3

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.
package/README.md CHANGED
@@ -352,6 +352,28 @@ import ThemeMenu from "./theme-menu.vue"
352
352
 
353
353
  This layer does not provide SSR no-flash behavior. Server output can still render the fallback mode before the client applies a stored or system preference. Use it for client-side preference and persistence, not for a no-flash SSR guarantee.
354
354
 
355
+ ### Root Target Sync
356
+
357
+ By default Marwes keeps theme state scoped to the provider element. If your app shell also needs the resolved mode on `html` or `body`, set `target` and `attribute`:
358
+
359
+ ```vue
360
+ <template>
361
+ <MarwesProvider
362
+ default-preference="system"
363
+ storage-key="marwes-theme"
364
+ target="html"
365
+ attribute="class"
366
+ disable-transition-on-change
367
+ >
368
+ <AppShell />
369
+ </MarwesProvider>
370
+ </template>
371
+ ```
372
+
373
+ `target` can be `"provider"`, `"html"`, or `"body"`. `attribute` can be `"class"`, `"data-theme"`, or `"data-mode"`. Class mode only adds the active `light` / `dark` class and removes the opposite one; unrelated classes are preserved. The provider root still keeps `mw-theme--light` / `mw-theme--dark` and provider-scoped `--mw-*` variables in every target mode.
374
+
375
+ Use one global-target provider per app shell. If multiple providers target `html` or `body`, deciding which provider owns that global element is app-owned behavior.
376
+
355
377
  For a simple brand pass, override shared values once and let Marwes fill the rest. If your product needs different brand colors in light and dark mode, control `mode` and switch between two small `ThemeInput` override objects:
356
378
 
357
379
  ```vue
package/dist/index.d.ts CHANGED
@@ -4,6 +4,9 @@ import * as _marwes_ui_core from '@marwes-ui/core';
4
4
  import { ThemeInput, ThemePreference, ThemeMode, FontLoadingConfig, ResolvedTheme, ButtonOptions, InputOptions, SelectOptions, SelectAppearance as SelectAppearance$1, SelectOption as SelectOption$1, TextareaOptions, RichTextOptions, InputOtpOptions, CurrencyCode, iconRegistry, IconSize, IconStrokeWidth, AvatarOptions, CheckboxProps as CheckboxProps$1, DividerOptions, DividerSize, DividerOrientation, SpinnerOptions, SpacingOptions, SpacingSize, HeadingOptions, HeadingSize, ParagraphOptions, ParagraphSize, ToastOptions, ToastVariant, ToastIntent, ToastPlacement, TooltipOptions, IconNameType, RadioOptions, BadgeOptions, SwitchOptions, SliderOptions, TabOptions, DialogOptions } from '@marwes-ui/core';
5
5
  export { AvatarSize, AvatarType, BadgeVariant, ButtonAction, ButtonSize, ButtonVariant, Density, FontLoadingConfig, FontLoadingMode, FontLoadingOptions, IconName, MwAvailableFont, MwFontFallback, MwGoogleFontFamily, MwStyledTheme, MwThemeVarName, MwThemeVarNames, MwThemeVarReference, MwThemeVars, ResolvedTheme, SpacingSize, Spacings, SwitchSize, Theme, ThemeInput, ThemeMode, ThemePreference, ToneName, createFontStack, mwAvailableFonts, mwFontFallbacks, mwGoogleFontFamilies, mwStyledTheme, mwThemeVarNames, mwThemeVars, mwVar } from '@marwes-ui/core';
6
6
 
7
+ type ThemeTarget = "provider" | "html" | "body";
8
+ type ThemeAttribute = "class" | "data-theme" | "data-mode";
9
+
7
10
  type MarwesProviderProps = {
8
11
  theme?: ThemeInput;
9
12
  defaultPreference?: ThemePreference;
@@ -15,9 +18,14 @@ type MarwesProviderProps = {
15
18
  onModeChange?: (mode: ThemeMode) => void;
16
19
  storageKey?: string | false;
17
20
  enableSystem?: boolean;
21
+ target?: ThemeTarget;
22
+ attribute?: ThemeAttribute;
23
+ disableTransitionOnChange?: boolean;
18
24
  };
19
25
  declare const MarwesProvider: vue.DefineComponent<{
20
26
  mode?: any;
27
+ target?: any;
28
+ attribute?: any;
21
29
  theme?: any;
22
30
  defaultPreference?: any;
23
31
  preference?: any;
@@ -27,10 +35,13 @@ declare const MarwesProvider: vue.DefineComponent<{
27
35
  onModeChange?: any;
28
36
  storageKey?: any;
29
37
  enableSystem?: any;
38
+ disableTransitionOnChange?: any;
30
39
  }, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
31
40
  [key: string]: any;
32
41
  }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<{
33
42
  mode?: any;
43
+ target?: any;
44
+ attribute?: any;
34
45
  theme?: any;
35
46
  defaultPreference?: any;
36
47
  preference?: any;
@@ -40,6 +51,7 @@ declare const MarwesProvider: vue.DefineComponent<{
40
51
  onModeChange?: any;
41
52
  storageKey?: any;
42
53
  enableSystem?: any;
54
+ disableTransitionOnChange?: any;
43
55
  }> & Readonly<{}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
44
56
 
45
57
  declare function useTheme(): ResolvedTheme;
package/dist/index.js CHANGED
@@ -102,6 +102,42 @@ function writeStoredThemePreference(storageKey, preference) {
102
102
  } catch {
103
103
  }
104
104
  }
105
+ function applyModeAttribute({
106
+ target,
107
+ providerElement,
108
+ mode,
109
+ attribute
110
+ }) {
111
+ const element = getTargetElement(target, providerElement);
112
+ if (!element) return;
113
+ if (attribute === "class") {
114
+ const oppositeMode = mode === ThemeMode.dark ? ThemeMode.light : ThemeMode.dark;
115
+ element.classList.remove(oppositeMode);
116
+ element.classList.add(mode);
117
+ return;
118
+ }
119
+ element.setAttribute(attribute, mode);
120
+ }
121
+ function withoutModeTransitions(callback) {
122
+ if (typeof document === "undefined" || typeof window === "undefined") {
123
+ callback();
124
+ return;
125
+ }
126
+ const style = document.createElement("style");
127
+ style.dataset.marwesDisableTransitions = "true";
128
+ style.textContent = "* { transition: none !important; }";
129
+ document.head.appendChild(style);
130
+ callback();
131
+ window.getComputedStyle(document.body);
132
+ window.requestAnimationFrame(() => {
133
+ style.remove();
134
+ });
135
+ }
136
+ function getTargetElement(target, providerElement) {
137
+ if (target === "provider") return providerElement;
138
+ if (typeof document === "undefined") return null;
139
+ return target === "html" ? document.documentElement : document.body;
140
+ }
105
141
 
106
142
  // src/provider/marwes-provider.ts
107
143
  var MarwesProvider = defineComponent({
@@ -116,7 +152,10 @@ var MarwesProvider = defineComponent({
116
152
  "onPreferenceChange",
117
153
  "onModeChange",
118
154
  "storageKey",
119
- "enableSystem"
155
+ "enableSystem",
156
+ "target",
157
+ "attribute",
158
+ "disableTransitionOnChange"
120
159
  ],
121
160
  setup(rawProps, { slots }) {
122
161
  const props = rawProps;
@@ -128,6 +167,9 @@ var MarwesProvider = defineComponent({
128
167
  );
129
168
  const enableSystem = computed(() => props.enableSystem !== false);
130
169
  const storageKey = computed(() => props.storageKey ?? false);
170
+ const target = computed(() => props.target ?? "provider");
171
+ const attribute = computed(() => props.attribute ?? "class");
172
+ const disableTransitionOnChange = computed(() => props.disableTransitionOnChange === true);
131
173
  const isPreferenceControlled = computed(
132
174
  () => props.preference !== void 0 || props.mode !== void 0 || props.theme?.mode !== void 0
133
175
  );
@@ -161,6 +203,22 @@ var MarwesProvider = defineComponent({
161
203
  }
162
204
  loadThemeFonts(resolved.value, props.fontLoading ?? "auto");
163
205
  }
206
+ function syncTargetModeAttribute() {
207
+ if (target.value === "provider") return;
208
+ const apply = () => {
209
+ applyModeAttribute({
210
+ target: target.value,
211
+ providerElement: rootRef.value,
212
+ mode: activeMode.value,
213
+ attribute: attribute.value
214
+ });
215
+ };
216
+ if (disableTransitionOnChange.value) {
217
+ withoutModeTransitions(apply);
218
+ return;
219
+ }
220
+ apply();
221
+ }
164
222
  let unsubscribeSystemThemeMode;
165
223
  function syncSystemThemeSubscription() {
166
224
  unsubscribeSystemThemeMode?.();
@@ -183,11 +241,13 @@ var MarwesProvider = defineComponent({
183
241
  }
184
242
  syncSystemThemeSubscription();
185
243
  syncThemeToRuntime();
244
+ syncTargetModeAttribute();
186
245
  });
187
246
  onUnmounted(() => {
188
247
  unsubscribeSystemThemeMode?.();
189
248
  });
190
249
  watch(resolved, syncThemeToRuntime);
250
+ watch([activeMode, target, attribute, disableTransitionOnChange], syncTargetModeAttribute);
191
251
  watch([activePreference, enableSystem], syncSystemThemeSubscription);
192
252
  provide(marwesContextKey, {
193
253
  theme: resolved,