diff --git a/.circleci/config.yml b/.circleci/config.yml
index 477ba225817262..13fd829bf20d28 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,6 +1,6 @@
version: 2.1
orbs:
- code-infra: https://raw.githubusercontent.com/mui/mui-public/fdfa28912aa56f2473702b4a49a5a7a3164f5e4c/.circleci/orbs/code-infra.yml
+ code-infra: https://raw.githubusercontent.com/mui/mui-public/ecaf36c894f9e8bc68959b23bf33e45aefeb319c/.circleci/orbs/code-infra.yml
parameters:
workflow:
@@ -49,32 +49,12 @@ default-context: &default-context
# restore_cache:
# key: v1-repo-{{ .Branch }}-{{ .Revision }}
-commands:
- install-deps:
- parameters:
- ignore-workspace:
- type: boolean
- default: false
- react-version:
- type: string
- default: stable
- typescript-version:
- type: string
- default: stable
- playwright-version:
- type: string
- default: stable
- steps:
- - code-infra/install-deps:
- package-overrides: react@<< parameters.react-version >> typescript@<< parameters.typescript-version >> @playwright/test@<< parameters.playwright-version >> playwright@<< parameters.playwright-version >>
- ignore-workspace: << parameters.ignore-workspace >>
-
jobs:
test_unit:
<<: *default-job
steps:
- checkout
- - install-deps:
+ - code-infra/install-deps:
react-version: << parameters.react-version >>
- run:
name: Tests fake browser
@@ -101,50 +81,30 @@ jobs:
<<: *default-job
steps:
- checkout
- - install-deps
+ - code-infra/install-deps
- code-infra/eslint
- code-infra/stylelint
- code-infra/valelint
test_static:
<<: *default-job
resource_class: medium
- environment:
- <<: *default-environment
- # pnpm 11's `pnpm dedupe --check` exceeds Node's default heap on this runner.
- NODE_OPTIONS: --max-old-space-size=3584
steps:
- checkout
- - install-deps
+ - code-infra/install-deps
- code-infra/check-static-changes
- - run:
- name: Generate PropTypes
+ - code-infra/check-generated:
command: pnpm proptypes
- - run:
- name: '`pnpm proptypes` changes committed?'
- command: git add -A && git diff --exit-code --staged
- - run:
- name: Generate the documentation
+ - code-infra/check-generated:
command: pnpm docs:api
- - run:
- name: '`pnpm docs:api` changes committed?'
- command: git add -A && git diff --exit-code --staged
- - run:
- name: '`pnpm extract-error-codes` changes committed?'
- command: |
- pnpm extract-error-codes
- git add -A && git diff --exit-code --staged
+ - code-infra/extract-error-codes
test_types:
<<: *default-job
resource_class: xlarge.gen2
steps:
- checkout
- - install-deps
- - run:
- name: Transpile TypeScript demos
+ - code-infra/install-deps
+ - code-infra/check-generated:
command: pnpm docs:typescript:formatted
- - run:
- name: '`pnpm docs:typescript:formatted` changes committed?'
- command: git add -A && git diff --exit-code --staged
- run:
name: Tests TypeScript definitions
command: pnpm typescript:ci
@@ -156,7 +116,7 @@ jobs:
resource_class: xlarge.gen2
steps:
- checkout
- - install-deps:
+ - code-infra/install-deps:
typescript-version: << parameters.typescript-version >>
- run:
name: Tests TypeScript definitions
@@ -189,7 +149,7 @@ jobs:
- run:
name: Configure pnpm engine-strict
command: pnpm config set --location project engine-strict false
- - install-deps:
+ - code-infra/install-deps:
react-version: << parameters.react-version >>
playwright-version: '1.49.1'
- run:
@@ -215,7 +175,7 @@ jobs:
playwright-img-version: v1.62.1-noble
steps:
- checkout
- - install-deps:
+ - code-infra/install-deps:
react-version: << parameters.react-version >>
- run:
name: Tests Chromium
@@ -240,7 +200,7 @@ jobs:
playwright-img-version: v1.62.1-noble
steps:
- checkout
- - install-deps:
+ - code-infra/install-deps:
react-version: << parameters.react-version >>
- run:
name: pnpm test:e2e
@@ -254,7 +214,7 @@ jobs:
playwright-img-version: v1.62.1-noble
steps:
- checkout
- - install-deps
+ - code-infra/install-deps
- run:
name: pnpm test:e2e-website
command: pnpm test:e2e-website
@@ -268,7 +228,7 @@ jobs:
playwright-img-version: v1.62.1-noble
steps:
- checkout
- - install-deps:
+ - code-infra/install-deps:
react-version: << parameters.react-version >>
- run:
name: Run visual regression tests
@@ -279,14 +239,12 @@ jobs:
# pnpm-workspace.yaml (overrides) and may touch pnpm-lock.yaml. Exclude those so
# this check still verifies the a11y results without tripping on the pinned deps.
command: git add -A -- . ':(exclude)pnpm-*.yaml' && git diff --exit-code --staged
- - run:
- name: Upload screenshots to Argos CI
- command: pnpm test:argos
+ - code-infra/argos-push
test_bundling_prepare:
<<: *default-job
steps:
- checkout
- - install-deps
+ - code-infra/install-deps
- run:
name: Build packages for fixtures
command: pnpm lerna run --scope "@mui/*" build
@@ -305,7 +263,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -318,7 +276,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -335,7 +293,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -351,7 +309,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -367,7 +325,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -383,7 +341,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -399,7 +357,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -414,7 +372,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -432,7 +390,7 @@ jobs:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- - install-deps:
+ - code-infra/install-deps:
ignore-workspace: true
- run:
name: Test fixture
@@ -442,14 +400,11 @@ jobs:
resource_class: medium
steps:
- checkout
- - install-deps
+ - code-infra/install-deps
- run:
name: Build @mui packages
command: pnpm release:build
- # @TODO: Not using code-infra/upload-size-snapshot since it doesn't support setting concurrency yet
- - run:
- name: Create and upload a size snapshot
- command: pnpm size:snapshot --concurrency 6
+ - code-infra/upload-size-snapshot
- run:
name: Validate type declarations
command: pnpm validate-declarations
diff --git a/.github/workflows/ci-check.yml b/.github/workflows/ci-check.yml
index 852d82b5cf3d96..67ed7f73d37ca3 100644
--- a/.github/workflows/ci-check.yml
+++ b/.github/workflows/ci-check.yml
@@ -18,10 +18,6 @@ on:
permissions: {}
jobs:
- continuous-releases:
- name: Continuous releases
- uses: mui/mui-public/.github/workflows/ci-base.yml@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
-
test-dev:
if: ${{ github.actor != 'l10nbot' }}
runs-on: ${{ matrix.os }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5ef9c00f5f18b3..5423788ab117cd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,6 +15,29 @@ on:
permissions: {}
jobs:
+ continuous-releases:
+ name: Continuous releases / pkg.pr.new releases
+ if: ${{ github.repository_owner == 'mui' }}
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo "${{ github.actor }}"
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ with:
+ # fetch all tags which are required for `pnpm release:changelog`
+ fetch-depth: 0
+ - name: Set up pnpm
+ uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
+ - name: Use Node.js
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
+ with:
+ node-version: '22.23.2'
+ # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies
+ cache: 'pnpm'
+ - run: pnpm install
+ - run: pnpm release:build
+ - name: Publish packages to pkg.pr.new
+ uses: mui/mui-public/.github/actions/ci-publish@ecaf36c894f9e8bc68959b23bf33e45aefeb319c # master
+
# Tests dev-only scripts across all supported dev environments
test-dev:
# l10nbot does not affect dev scripts.
@@ -49,9 +72,6 @@ jobs:
${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml') }}-
${{ runner.os }}-nextjs-
- run: pnpm release:build
- - name: Publish packages to pkg.pr.new
- uses: mui/mui-public/.github/actions/ci-publish@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
- if: ${{ matrix.os == 'ubuntu-latest' }}
- name: Build docs
run: pnpm docs:build
env:
@@ -96,7 +116,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare for publishing
- uses: mui/mui-public/.github/actions/publish-prepare@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
+ uses: mui/mui-public/.github/actions/publish-prepare@ecaf36c894f9e8bc68959b23bf33e45aefeb319c # master
with:
node-version: '22.23.2'
- name: Dry run npm publishing
diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml
index aa63951c17b377..0c32fdd8427430 100644
--- a/.github/workflows/claude-review.yml
+++ b/.github/workflows/claude-review.yml
@@ -13,7 +13,7 @@ permissions: {}
jobs:
claude-review:
- uses: mui/mui-public/.github/workflows/claude-review.yml@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
+ uses: mui/mui-public/.github/workflows/claude-review.yml@ecaf36c894f9e8bc68959b23bf33e45aefeb319c # master
permissions:
contents: read # read the repo (checkout + git diff)
id-token: write # mint the GitHub OIDC token exchanged for a Claude token via WIF
diff --git a/.github/workflows/closed-issue-message.yml b/.github/workflows/closed-issue-message.yml
index f3341e1d90a422..1d194024f1886e 100644
--- a/.github/workflows/closed-issue-message.yml
+++ b/.github/workflows/closed-issue-message.yml
@@ -11,7 +11,7 @@ jobs:
add-comment:
name: Add closing message
if: github.event.issue.state_reason == 'completed'
- uses: mui/mui-public/.github/workflows/issues_add-closing-message.yml@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
+ uses: mui/mui-public/.github/workflows/issues_add-closing-message.yml@ecaf36c894f9e8bc68959b23bf33e45aefeb319c # master
permissions:
contents: read
issues: write
diff --git a/.github/workflows/create-cherry-pick-pr.yml b/.github/workflows/create-cherry-pick-pr.yml
index 1b53f4b33615a4..b50a52f56b3640 100644
--- a/.github/workflows/create-cherry-pick-pr.yml
+++ b/.github/workflows/create-cherry-pick-pr.yml
@@ -12,7 +12,7 @@ permissions: {}
jobs:
create_pr:
name: Create cherry-pick PR
- uses: mui/mui-public/.github/workflows/prs_create-cherry-pick-pr.yml@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
+ uses: mui/mui-public/.github/workflows/prs_create-cherry-pick-pr.yml@ecaf36c894f9e8bc68959b23bf33e45aefeb319c # master
permissions:
contents: write
pull-requests: write
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cb5fdb5f75a151..61e5f246d9895f 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -47,7 +47,7 @@ jobs:
ref: ${{ github.event_name == 'push' && github.sha || inputs.sha }}
fetch-depth: 0 # Fetch full history for proper git operations
- name: Prepare for publishing
- uses: mui/mui-public/.github/actions/publish-prepare@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
+ uses: mui/mui-public/.github/actions/publish-prepare@ecaf36c894f9e8bc68959b23bf33e45aefeb319c # master
with:
node-version: '22.23.2'
- name: Publish packages
@@ -70,7 +70,7 @@ jobs:
ref: ${{ inputs.sha }}
fetch-depth: 0 # Fetch full history for proper git operations
- name: Prepare for publishing
- uses: mui/mui-public/.github/actions/publish-prepare@93fb2edda2af4fb4ccf786cd1ffe8bb4f8d520f4 # master
+ uses: mui/mui-public/.github/actions/publish-prepare@ecaf36c894f9e8bc68959b23bf33e45aefeb319c # master
with:
node-version: '22.23.2'
- name: Publish packages
diff --git a/docs/data/material/integrations/nextjs/nextjs.md b/docs/data/material/integrations/nextjs/nextjs.md
index fd837a5ce16e5d..c10e92c5ab1d8b 100644
--- a/docs/data/material/integrations/nextjs/nextjs.md
+++ b/docs/data/material/integrations/nextjs/nextjs.md
@@ -73,41 +73,78 @@ Use the `options` prop to override the default [cache options](https://emotion.s
### Font optimization
-To integrate [Next.js font optimization](https://nextjs.org/docs/app/getting-started/fonts) with Material UI, create a new file with the `'use client';` directive.
-Then create a theme using `var(--font-roboto)` as a value for the `typography.fontFamily` field.
+To integrate [Next.js font optimization](https://nextjs.org/docs/app/getting-started/fonts) with Material UI, add the font's `className` to the `` element in `src/app/layout.tsx`:
-```js title="src/theme.ts"
+```diff title="app/layout.tsx"
+ import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
++import { Roboto } from 'next/font/google';
+
++const roboto = Roboto({
++ weight: ['300', '400', '500', '700'],
++ subsets: ['latin'],
++ display: 'swap',
++});
+
+ export default function RootLayout(props) {
+ const { children } = props;
+ return (
++
+
+
+ {children}
+
+
+
+ );
+ }
+```
+
+This approach keeps the theme's default `fontFamily` unchanged, so Material UI's Roboto-tuned typography letter-spacing values are preserved.
+Next.js preloads and self-hosts the font at build time, so the browser uses it for any element that references `"Roboto"` — including Material UI components.
+
+:::warning
+Avoid setting `typography.fontFamily` to `var(--font-roboto)` in the theme.
+Material UI applies Roboto-specific letter-spacing only when `fontFamily` exactly matches the default font stack.
+A CSS variable bypasses this check and removes letter-spacing from all typography variants.
+If you are using a custom font instead of Roboto, see [Custom font](#custom-font) below.
+:::
+
+### Custom font
+
+To use a custom font other than Roboto, load it as a CSS variable and set `typography.fontFamily` in the theme.
+Create a theme file with the `'use client'` directive:
+
+```ts title="src/theme.ts"
'use client';
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
typography: {
- fontFamily: 'var(--font-roboto)',
+ fontFamily: 'var(--font-inter)',
},
});
export default theme;
```
-Finally, in `src/app/layout.tsx`, pass the theme to the `ThemeProvider`:
+Then in `src/app/layout.tsx`, load the font with a `variable` name and apply it to the `` element:
```diff title="app/layout.tsx"
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
-+import { Roboto } from 'next/font/google';
++import { Inter } from 'next/font/google';
+import { ThemeProvider } from '@mui/material/styles';
+import theme from '../theme';
-+const roboto = Roboto({
-+ weight: ['300', '400', '500', '700'],
++const inter = Inter({
+ subsets: ['latin'],
+ display: 'swap',
-+ variable: '--font-roboto',
++ variable: '--font-inter',
+});
export default function RootLayout(props) {
const { children } = props;
return (
-+
++
+
@@ -435,26 +472,66 @@ If you are using TypeScript, add `DocumentHeadTagsProps` to the Document's props
### Font optimization
-To integrate [Next.js font optimization](https://nextjs.org/docs/pages/getting-started/fonts) with Material UI, open `pages/_app.tsx` and create a theme using `var(--font-roboto)` as a value for the `typography.fontFamily` field.
+To integrate [Next.js font optimization](https://nextjs.org/docs/pages/getting-started/fonts) with Material UI, open `pages/_app.tsx` and add the font's `className` to the root element:
```diff title="pages/_app.tsx"
import * as React from 'react';
import Head from 'next/head';
import { AppProps } from 'next/app';
import { AppCacheProvider } from '@mui/material-nextjs/v15-pagesRouter';
-+import { ThemeProvider, createTheme } from '@mui/material/styles';
+import { Roboto } from 'next/font/google';
+const roboto = Roboto({
+ weight: ['300', '400', '500', '700'],
+ subsets: ['latin'],
+ display: 'swap',
-+ variable: '--font-roboto',
++});
+
+ export default function MyApp(props: AppProps) {
+ const { Component, pageProps } = props;
+ return (
+
+ ...
++
+
++
+
+ );
+ }
+```
+
+This approach keeps the theme's default `fontFamily` unchanged, so Material UI's Roboto-tuned typography letter-spacing values are preserved.
+Next.js preloads and self-hosts the font at build time, so the browser uses it for any element that references `"Roboto"` — including Material UI components.
+
+:::warning
+Avoid setting `typography.fontFamily` to `var(--font-roboto)` in the theme.
+Material UI applies Roboto-specific letter-spacing only when `fontFamily` exactly matches the default font stack.
+A CSS variable bypasses this check and removes letter-spacing from all typography variants.
+If you are using a custom font instead of Roboto, see [Custom font](#custom-font-2) below.
+:::
+
+### Custom font
+
+To use a custom font other than Roboto, load it as a CSS variable and set `typography.fontFamily` in the theme.
+In `pages/_app.tsx`:
+
+```diff title="pages/_app.tsx"
+ import * as React from 'react';
+ import Head from 'next/head';
+ import { AppProps } from 'next/app';
+ import { AppCacheProvider } from '@mui/material-nextjs/v15-pagesRouter';
++import { ThemeProvider, createTheme } from '@mui/material/styles';
++import { Inter } from 'next/font/google';
+
++const inter = Inter({
++ subsets: ['latin'],
++ display: 'swap',
++ variable: '--font-inter',
+});
+const theme = createTheme({
+ typography: {
-+ fontFamily: 'var(--font-roboto)',
++ fontFamily: 'var(--font-inter)',
+ },
+});
@@ -464,7 +541,7 @@ To integrate [Next.js font optimization](https://nextjs.org/docs/pages/getting-s
...
+
-+
++
+
+
diff --git a/package.json b/package.json
index eeb823a7344fcb..8707877022f11e 100644
--- a/package.json
+++ b/package.json
@@ -86,13 +86,13 @@
"@babel/node": "7.29.7",
"@babel/plugin-transform-react-constant-elements": "7.29.7",
"@eslint/compat": "2.1.0",
- "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.30",
- "@mui/internal-bundle-size-checker": "1.0.9-canary.89",
- "@mui/internal-code-infra": "0.0.4-canary.111",
+ "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.31",
+ "@mui/internal-bundle-size-checker": "1.0.9-canary.90",
+ "@mui/internal-code-infra": "0.0.4-canary.112",
"@mui/internal-docs-utils": "workspace:^",
- "@mui/internal-netlify-cache": "0.0.3-canary.6",
+ "@mui/internal-netlify-cache": "0.0.3-canary.7",
"@mui/internal-scripts": "workspace:^",
- "@mui/internal-test-utils": "2.0.18-canary.33",
+ "@mui/internal-test-utils": "2.0.18-canary.34",
"@mui/material": "workspace:^",
"@mui/utils": "workspace:^",
"@next/eslint-plugin-next": "16.3.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4f0cbeb1009924..e07f195f048f37 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -69,26 +69,26 @@ importers:
specifier: 2.1.0
version: 2.1.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))
'@mui/internal-babel-plugin-minify-errors':
- specifier: 2.0.8-canary.30
- version: 2.0.8-canary.30(@babel/core@7.29.7(supports-color@11.0.0))(supports-color@11.0.0)
+ specifier: 2.0.8-canary.31
+ version: 2.0.8-canary.31(@babel/core@7.29.7(supports-color@11.0.0))(supports-color@11.0.0)
'@mui/internal-bundle-size-checker':
- specifier: 1.0.9-canary.89
- version: 1.0.9-canary.89(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(rolldown@1.2.4)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)
+ specifier: 1.0.9-canary.90
+ version: 1.0.9-canary.90(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(rolldown@1.2.4)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)
'@mui/internal-code-infra':
- specifier: 0.0.4-canary.111
- version: 0.0.4-canary.111(@next/eslint-plugin-next@16.3.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)))(@types/node@20.19.43)(@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(@vitest/expect@4.1.10)(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(postcss@8.5.26)(prettier@3.9.6)(stylelint@17.14.1(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(supports-color@11.0.0)(vitest@4.1.10)
+ specifier: 0.0.4-canary.112
+ version: 0.0.4-canary.112(@next/eslint-plugin-next@16.3.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)))(@types/node@20.19.43)(@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(@vitest/expect@4.1.10)(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(postcss@8.5.26)(prettier@3.9.6)(stylelint@17.14.1(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(supports-color@11.0.0)(vitest@4.1.10)
'@mui/internal-docs-utils':
specifier: workspace:^
version: link:packages-internal/docs-utils/build
'@mui/internal-netlify-cache':
- specifier: 0.0.3-canary.6
- version: 0.0.3-canary.6
+ specifier: 0.0.3-canary.7
+ version: 0.0.3-canary.7
'@mui/internal-scripts':
specifier: workspace:^
version: link:packages-internal/scripts
'@mui/internal-test-utils':
- specifier: 2.0.18-canary.33
- version: 2.0.18-canary.33(@emotion/cache@11.14.0)(@emotion/react@11.14.0(@types/react@19.2.18)(react@19.2.8)(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@vitest/utils@4.1.10)(chai@6.2.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vite@8.2.2(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.10)
+ specifier: 2.0.18-canary.34
+ version: 2.0.18-canary.34(@emotion/cache@11.14.0)(@emotion/react@11.14.0(@types/react@19.2.18)(react@19.2.8)(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@vitest/utils@4.1.10)(chai@6.2.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vite@8.2.2(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.10)
'@mui/material':
specifier: workspace:^
version: link:packages/mui-material/build
@@ -3262,12 +3262,6 @@ packages:
'@babel/core': ^7.0.0 || ^8.0.0
'@babel/preset-react': ^7.0.0 || ^8.0.0
- '@mui/internal-babel-plugin-minify-errors@2.0.8-canary.30':
- resolution: {integrity: sha512-Czh0O1P037IJlwGnAc2fzESO7Zwwan0XM28KdBzAV6r7A1XmlFrRS2Nu+lD3y9t0NJ2iITw10RPczCafPcBIcw==}
- engines: {node: '>=14.0.0'}
- peerDependencies:
- '@babel/core': ^7.0.0 || ^8.0.0
-
'@mui/internal-babel-plugin-minify-errors@2.0.8-canary.31':
resolution: {integrity: sha512-0NAUH/BCKKZk8CNagfk7Dg8knNNZw7wDff6eoYk/Ek9ohDP7pu2VHYn30T1yI+PGfumPa/t6AlQcfkJ7bQbGDg==}
engines: {node: '>=14.0.0'}
@@ -3279,12 +3273,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0 || ^8.0.0
- '@mui/internal-bundle-size-checker@1.0.9-canary.89':
- resolution: {integrity: sha512-PNB4dyE3GTQaOESDcFQwlIYrHr9QcpuS5U5eN0xMIokXGzmoAwOG0QMnFrmq1wlpoUJ1yFhgAEfs3N8tS511+g==}
+ '@mui/internal-bundle-size-checker@1.0.9-canary.90':
+ resolution: {integrity: sha512-jaqQl8ji1DZPZfqqxVJHirgj9HIn+M/CBTyxeYdSawwUgF2eyHVM31z57Eds3zZGhMOaH0ULHfqra6I1ItbKEw==}
hasBin: true
- '@mui/internal-code-infra@0.0.4-canary.111':
- resolution: {integrity: sha512-AzSandBZ/8uh7w3It1AaMdWg0ALKwh0zihyNMeri9lX4A+Y8hYxID0AmXkC3nOX8MHkro7NOJgyyLIEzF3xBoA==}
+ '@mui/internal-code-infra@0.0.4-canary.112':
+ resolution: {integrity: sha512-uupyQ+BiTfzgEyx3xf59cjPIntBBIGEJnxaMq6F/P3eXo3uHRb15jMk3Owu83Lf7vMrx8p2SDGv4kBGfVrTb8w==}
hasBin: true
peerDependencies:
'@next/eslint-plugin-next': '*'
@@ -3295,11 +3289,11 @@ packages:
typescript:
optional: true
- '@mui/internal-netlify-cache@0.0.3-canary.6':
- resolution: {integrity: sha512-pKwVUSqTV1dn+c3nMdvVSB1tHaKuFBAwiLh3qPL5TfxVy/Qx3fgoW6NjR8u0N3NheM6jKHpnF1dqEChRXrL8gA==}
+ '@mui/internal-netlify-cache@0.0.3-canary.7':
+ resolution: {integrity: sha512-D9MuV+C95ONVznxvIL/EegilxIK2nGztWGmoHoYcFcGLeVDnwmSSIOSbZKUG5KAiQawsHXUjbnJ5hWs7YnlIUQ==}
- '@mui/internal-test-utils@2.0.18-canary.33':
- resolution: {integrity: sha512-e2CQt8R8cbma04qtrLqU/XqvD2DzK0qrrFAF3EBj+kobJIAoFGxxicEdEL5oH13zIqPdwOMI8/AGIjiSIneGcQ==}
+ '@mui/internal-test-utils@2.0.18-canary.34':
+ resolution: {integrity: sha512-l0mxplIbgxMUtrFL8QImtPtfZZLK2HR4pW2ia4F1H7JtyVI/UK8JNLyTzYRUwB7ayqWajBA1co2i8WD4h9iHXg==}
peerDependencies:
'@emotion/cache': '11'
'@emotion/react': '11'
@@ -13146,14 +13140,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@mui/internal-babel-plugin-minify-errors@2.0.8-canary.30(@babel/core@7.29.7(supports-color@11.0.0))(supports-color@11.0.0)':
- dependencies:
- '@babel/core': 7.29.7(supports-color@11.0.0)
- '@babel/helper-module-imports': 7.29.7(supports-color@11.0.0)
- find-package-json: 1.2.0
- transitivePeerDependencies:
- - supports-color
-
'@mui/internal-babel-plugin-minify-errors@2.0.8-canary.31(@babel/core@7.29.7(supports-color@11.0.0))(supports-color@11.0.0)':
dependencies:
'@babel/core': 7.29.7(supports-color@11.0.0)
@@ -13167,7 +13153,7 @@ snapshots:
'@babel/core': 7.29.7(supports-color@11.0.0)
resolve: 1.22.12
- '@mui/internal-bundle-size-checker@1.0.9-canary.89(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(rolldown@1.2.4)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)':
+ '@mui/internal-bundle-size-checker@1.0.9-canary.90(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(rolldown@1.2.4)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)':
dependencies:
'@octokit/rest': 22.0.1
chalk: 6.0.0
@@ -13196,7 +13182,7 @@ snapshots:
- tsx
- yaml
- '@mui/internal-code-infra@0.0.4-canary.111(@next/eslint-plugin-next@16.3.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)))(@types/node@20.19.43)(@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(@vitest/expect@4.1.10)(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(postcss@8.5.26)(prettier@3.9.6)(stylelint@17.14.1(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(supports-color@11.0.0)(vitest@4.1.10)':
+ '@mui/internal-code-infra@0.0.4-canary.112(@next/eslint-plugin-next@16.3.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)))(@types/node@20.19.43)(@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript-eslint/parser@8.68.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(@vitest/expect@4.1.10)(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(postcss@8.5.26)(prettier@3.9.6)(stylelint@17.14.1(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(supports-color@11.0.0)(vitest@4.1.10)':
dependencies:
'@argos-ci/core': 6.8.1(@types/node@20.19.43)(supports-color@11.0.0)
'@babel/cli': 7.29.7(@babel/core@7.29.7(supports-color@11.0.0))
@@ -13307,9 +13293,9 @@ snapshots:
- supports-color
- vitest
- '@mui/internal-netlify-cache@0.0.3-canary.6': {}
+ '@mui/internal-netlify-cache@0.0.3-canary.7': {}
- '@mui/internal-test-utils@2.0.18-canary.33(@emotion/cache@11.14.0)(@emotion/react@11.14.0(@types/react@19.2.18)(react@19.2.8)(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@vitest/utils@4.1.10)(chai@6.2.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vite@8.2.2(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.10)':
+ '@mui/internal-test-utils@2.0.18-canary.34(@emotion/cache@11.14.0)(@emotion/react@11.14.0(@types/react@19.2.18)(react@19.2.8)(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@vitest/utils@4.1.10)(chai@6.2.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vite@8.2.2(@types/node@20.19.43)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.10)':
dependencies:
'@babel/runtime': 7.29.7
'@playwright/test': 1.62.1
diff --git a/skills/material-ui-nextjs/AGENTS.md b/skills/material-ui-nextjs/AGENTS.md
index 32494040dcd26d..36c44e9d7fad08 100644
--- a/skills/material-ui-nextjs/AGENTS.md
+++ b/skills/material-ui-nextjs/AGENTS.md
@@ -18,7 +18,7 @@ Material UI uses Emotion for styles. On Next.js you must wire an Emotion cache
1. [App Router (recommended)](#app-router-recommended)
2. [Pages Router](#pages-router)
-3. [Fonts (`next/font`)](#fonts-nextfont)
+3. [Fonts (`next/font`)](#fonts-nextfont) — Roboto vs custom fonts
4. [CSS theme variables and SSR](#css-theme-variables-and-ssr)
5. [Other styling stacks (CSS layers)](#other-styling-stacks-css-layers)
6. [Next.js Link and `component` prop](#nextjs-link-and-component-prop)
@@ -94,11 +94,17 @@ Extend `Document` props with `DocumentHeadTagsProps` from the same import path.
## Fonts (`next/font`)
-App Router: theme modules that call `createTheme` need `'use client'` when they are consumed from server components. Use `next/font/google` (or local fonts), set `variable: '--font-…'`, put `className={font.variable}` on `` (or as in docs), and set `typography.fontFamily` to `'var(--font-…)'`. Wrap with `ThemeProvider` inside `AppRouterCacheProvider` as needed.
+### Roboto (default MUI font)
-Pages Router: similar pattern in `pages/_app.tsx` with `AppCacheProvider` and `ThemeProvider`.
+Apply the font via `className`, not a CSS variable. Load Roboto from `next/font/google` and set `className={roboto.className}` on the `` element (App Router) or a wrapper `` (Pages Router). Do **not** set `typography.fontFamily` in the theme.
-Details: [Next.js integration—Font optimization](https://mui.com/material-ui/integrations/nextjs.md#font-optimization) (App) and [Next.js integration—Font optimization](https://mui.com/material-ui/integrations/nextjs.md#font-optimization-1) (Pages).
+Why: `createTypography` applies Roboto-specific letter-spacing only when `fontFamily` exactly equals the default font stack string `'"Roboto", "Helvetica", "Arial", sans-serif'`. A CSS variable never matches that string, silently removing letter-spacing from all typography variants.
+
+### Custom fonts (non-Roboto)
+
+Use `next/font/google` (or local fonts), set `variable: '--font-…'`, put `className={font.variable}` on `` (App Router) or a wrapper element (Pages Router), and set `typography.fontFamily: 'var(--font-…)'` in a `'use client'` theme file. Wrap with `ThemeProvider` inside `AppRouterCacheProvider` as needed.
+
+Details, App Router: [Font optimization](https://mui.com/material-ui/integrations/nextjs.md#font-optimization) and [Custom font](https://mui.com/material-ui/integrations/nextjs.md#custom-font). Pages Router: [Font optimization](https://mui.com/material-ui/integrations/nextjs.md#font-optimization-2) and [Custom font](https://mui.com/material-ui/integrations/nextjs.md#custom-font-2).
---