@m3ui-vue/nuxt 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,6 +75,7 @@ The composables are prefixed so they can't clash with other modules (`useDevice`
75
75
  | Library | Auto-imported as |
76
76
  |---|---|
77
77
  | `useTheme` | `useM3Theme` |
78
+ | `useThemeTokens` | `useM3ThemeTokens` |
78
79
  | `useColorPalette` | `useM3ColorPalette` |
79
80
  | `useToast` | `useM3Toast` |
80
81
  | `useNotification` | `useM3Notification` |
@@ -83,7 +84,7 @@ The composables are prefixed so they can't clash with other modules (`useDevice`
83
84
  | `useWindowSizeClass` | `useM3WindowSizeClass` |
84
85
  | `useConfetti` | `useM3Confetti` |
85
86
  | `useContainerTransform` | `useM3ContainerTransform` |
86
- | `useFieldBg`, `useDebounce`, `usePageProgress`, `useSpring` | `useM3FieldBg`, `useM3Debounce`, `useM3PageProgress`, `useM3Spring` |
87
+ | `useFieldBg`, `useDebounce`, `useAutoGrow`, `usePageProgress`, `useSpring`, `useScrollReveal` | `useM3FieldBg`, `useM3Debounce`, `useM3AutoGrow`, `useM3PageProgress`, `useM3Spring`, `useM3ScrollReveal` |
87
88
  | `useMap`, `useMapLayers`, `useMapFilter` (with `maplibre-gl`) | `useM3Map`, `useM3MapLayers`, `useM3MapFilter` |
88
89
 
89
90
  Anything else (types, `palettes`, `motionSprings`…) is imported from `@m3ui-vue/m3ui-vue` as usual.
@@ -100,6 +101,7 @@ Everything `createM3UI()` takes, plus what the module itself does:
100
101
  | `composables` | `{ prefix: 'M3' }` | Auto-import the composables with this prefix; `false` to import them yourself |
101
102
  | `css` | `true` | Add `@m3ui-vue/m3ui-vue/styles` |
102
103
  | `cookies` | `true` | Keep the theme, palette and contrast in cookies too, so the server renders with them |
104
+ | `viewportHints` | `false` | Ask Chromium for the window's exact size with client hints, so the adaptive layouts render at it on a first visit too (that visit's request is repeated once) |
103
105
  | `themeScript` | `true` | Inline `themeInitScript()` in `<head>` |
104
106
 
105
107
  ## With @nuxtjs/i18n
@@ -148,7 +150,11 @@ a theme saved in the user's account, for instance.
148
150
  The adaptive layouts (`MNavigationSuite`, `MListDetail`, `MSupportingPane`) measure their own size, which the server
149
151
  can't. With `cookies`, the window's size is kept in a cookie too (`m3-viewport`), so from the second visit on they
150
152
  render on the server for the user's screen (one in a narrower column corrects itself on the client). On a first visit
151
- they render their compact layout and adapt once the page is interactive.
153
+ there's no cookie yet: with `@m3ui-vue/m3ui-vue` 1.2.0 or later the server renders for a size standing for the kind of
154
+ device (a phone, a tablet or a desktop, from `Sec-CH-UA-Mobile` and the user agent), and the layout settles on the exact
155
+ one once the page is interactive; before 1.2.0, the compact layout. With `viewportHints`, Chromium sends the exact size
156
+ on a first visit too: the server asks for it (`Accept-CH` / `Critical-CH: Sec-CH-Viewport-Width,
157
+ Sec-CH-Viewport-Height`) and Chromium repeats that first request once with it.
152
158
 
153
159
  A `seed` default can't be generated before the app loads: on the first visit the page shows the baseline palette
154
160
  until it does (later visits use the cached scheme).
package/dist/module.d.mts CHANGED
@@ -38,6 +38,13 @@ interface ModuleOptions extends Omit<M3UIOptions, 'locale'> {
38
38
  * `MListDetail`, `MSupportingPane`) render for the user's screen instead of compact. Default `true`.
39
39
  */
40
40
  cookies?: boolean;
41
+ /**
42
+ * Ask Chromium for the window's exact size with client hints (`Accept-CH` / `Critical-CH: Sec-CH-Viewport-Width,
43
+ * Sec-CH-Viewport-Height`), so the adaptive layouts render at it even on a first visit. It costs that first
44
+ * visit one repeated request in Chromium; later visits have the size in a cookie anyway. Without it, a first
45
+ * visit renders at a size guessed from the kind of device (phone, tablet, desktop). Needs `cookies`. Default `false`.
46
+ */
47
+ viewportHints?: boolean;
41
48
  /**
42
49
  * Inline `themeInitScript()` in `<head>`, so dark mode, the palette and the contrast level the user picked are
43
50
  * on the server-rendered page before the first paint. Default `true`.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0"
6
6
  },
7
- "version": "0.2.4",
7
+ "version": "0.3.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -25,12 +25,13 @@ const module$1 = defineNuxtModule({
25
25
  composables: { prefix: "M3" },
26
26
  css: true,
27
27
  cookies: true,
28
+ viewportHints: false,
28
29
  themeScript: true
29
30
  },
30
31
  async setup(options, nuxt) {
31
32
  const logger = useLogger("@m3ui-vue/nuxt");
32
33
  const resolver = createResolver(import.meta.url);
33
- const { components, composables, css, cookies, themeScript, locale, ...plugin } = options;
34
+ const { components, composables, css, cookies, viewportHints, themeScript, locale, ...plugin } = options;
34
35
  const entry = await resolvePath(LIB, { extensions: [".js", ".mjs"] });
35
36
  let api;
36
37
  try {
@@ -79,6 +80,7 @@ const module$1 = defineNuxtModule({
79
80
  `export const autoLocale = ${auto}`,
80
81
  `export const localePresets = ${auto ? `{ ${Object.entries(LOCALE_PRESETS).map(([code, name]) => `${code}: presets.${name}`).join(", ")} }` : "{}"}`,
81
82
  `export const cookies = ${JSON.stringify(cookies)}`,
83
+ `export const viewportHints = ${JSON.stringify(!!viewportHints)}`,
82
84
  `export const themeScript = ${JSON.stringify(themeScript)}`
83
85
  ].join("\n")
84
86
  });
@@ -91,6 +93,7 @@ const module$1 = defineNuxtModule({
91
93
  ` export const autoLocale: boolean`,
92
94
  ` export const localePresets: Record<string, Partial<import('${LIB}').M3Locale>>`,
93
95
  ` export const cookies: boolean`,
96
+ ` export const viewportHints: boolean`,
94
97
  ` export const themeScript: boolean`,
95
98
  `}`
96
99
  ].join("\n")
@@ -1,18 +1,34 @@
1
1
  import { computed } from "vue";
2
+ import * as m3 from "@m3ui-vue/m3ui-vue";
2
3
  import { createM3UI, themeInitScript } from "@m3ui-vue/m3ui-vue";
3
- import { defineNuxtPlugin, useCookie, useHead } from "#app";
4
- import { autoLocale, cookies, locale, localePresets, options, themeScript } from "#build/m3ui.options.mjs";
4
+ import { defineNuxtPlugin, useCookie, useHead, useRequestEvent, useRequestHeaders, useState } from "#app";
5
+ import { autoLocale, cookies, locale, localePresets, options, themeScript, viewportHints } from "#build/m3ui.options.mjs";
6
+ const fromHeaders = m3.viewportFromHeaders;
7
+ const HINTS = "Sec-CH-Viewport-Width, Sec-CH-Viewport-Height";
5
8
  export default defineNuxtPlugin({
6
9
  name: "m3ui",
7
10
  enforce: "pre",
8
11
  setup(nuxtApp) {
9
12
  const read = (name) => useCookie(name, { decode: decodeURIComponent, readonly: true }).value;
10
- const [width, height] = (read("m3-viewport") ?? "").split("x").map(Number);
13
+ const viewport = useState("m3ui-viewport", () => {
14
+ if (import.meta.server && fromHeaders) return fromHeaders(useRequestHeaders()) ?? null;
15
+ const [width, height] = (read("m3-viewport") ?? "").split("x").map(Number);
16
+ return width && height ? { width, height } : null;
17
+ });
18
+ if (import.meta.server && cookies && viewportHints) {
19
+ const res = useRequestEvent()?.node.res;
20
+ if (res && !res.headersSent) {
21
+ res.setHeader("Accept-CH", HINTS);
22
+ res.setHeader("Critical-CH", HINTS);
23
+ const vary = [res.getHeader("Vary")].flat().filter(Boolean).join(", ");
24
+ res.setHeader("Vary", vary ? `${vary}, ${HINTS}` : HINTS);
25
+ }
26
+ }
11
27
  const initialState = cookies ? {
12
28
  theme: read("m3-theme"),
13
29
  palette: read("m3-palette"),
14
30
  contrast: read("m3-contrast"),
15
- viewport: width && height ? { width, height } : void 0
31
+ viewport: viewport.value ?? void 0
16
32
  } : void 0;
17
33
  const appLocale = autoLocale ? computed(() => {
18
34
  const i18n = nuxtApp.$i18n;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m3ui-vue/nuxt",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Nuxt module for m3ui-vue, the Material Design 3 component library for Vue 3 + Tailwind CSS v4",
5
5
  "keywords": [
6
6
  "nuxt",
@@ -53,7 +53,7 @@
53
53
  "@m3ui-vue/m3ui-vue": "^1.1.3"
54
54
  },
55
55
  "devDependencies": {
56
- "@m3ui-vue/m3ui-vue": "^1.1.3",
56
+ "@m3ui-vue/m3ui-vue": "^1.2.0",
57
57
  "@nuxt/module-builder": "^1.0.3",
58
58
  "@nuxt/schema": "^4.6.2",
59
59
  "@nuxt/test-utils": "^4.3.4",