diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index facf5d3890..4a01b0fb2d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -279,8 +279,8 @@ catalogs:
specifier: 4.0.1
version: 4.0.1
playwright:
- specifier: 1.62.1
- version: 1.62.1
+ specifier: 1.63.0
+ version: 1.63.0
postcss:
specifier: 8.5.26
version: 8.5.26
@@ -444,7 +444,7 @@ importers:
version: 17.4.1
playwright:
specifier: 'catalog:'
- version: 1.62.1
+ version: 1.63.0
prettier:
specifier: 3.4.2
version: 3.4.2
@@ -658,6 +658,9 @@ importers:
'@vitest/browser':
specifier: 'catalog:'
version: 5.0.0(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
+ '@vitest/browser-playwright':
+ specifier: 'catalog:'
+ version: 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/coverage-istanbul':
specifier: 'catalog:'
version: 5.0.0(vitest@5.0.0)
@@ -1059,7 +1062,7 @@ importers:
version: 5.0.0(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/browser-playwright':
specifier: 'catalog:'
- version: 5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
+ version: 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/coverage-istanbul':
specifier: 'catalog:'
version: 5.0.0(vitest@5.0.0)
@@ -1095,7 +1098,7 @@ importers:
version: 6.0.0
playwright:
specifier: 'catalog:'
- version: 1.62.1
+ version: 1.63.0
pngjs:
specifier: 7.0.0
version: 7.0.0
@@ -1724,7 +1727,7 @@ importers:
version: 7.1.2
playwright:
specifier: 'catalog:'
- version: 1.62.1
+ version: 1.63.0
stylelint:
specifier: 'catalog:'
version: 17.14.1(supports-color@10.2.2)(typescript@6.0.3)
@@ -2080,7 +2083,7 @@ importers:
version: 5.0.0(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/browser-playwright':
specifier: 'catalog:'
- version: 5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
+ version: 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
eslint:
specifier: 10.11.0
version: 10.11.0(jiti@2.7.0)(supports-color@10.2.2)
@@ -9601,11 +9604,6 @@ packages:
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
- fsevents@2.3.2:
- resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -11962,13 +11960,13 @@ packages:
pkg-types@2.3.1:
resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
- playwright-core@1.62.1:
- resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==}
+ playwright-core@1.63.0:
+ resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==}
engines: {node: '>=20'}
hasBin: true
- playwright@1.62.1:
- resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==}
+ playwright@1.63.0:
+ resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==}
engines: {node: '>=20'}
hasBin: true
@@ -19811,11 +19809,11 @@ snapshots:
vite: 8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0)
vue: 3.5.42(typescript@6.0.3)
- '@vitest/browser-playwright@5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
+ '@vitest/browser-playwright@5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
dependencies:
'@vitest/browser': 5.0.0(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))
- playwright: 1.62.1
+ playwright: 1.63.0
tinyrainbow: 3.1.1
vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-istanbul@5.0.0)(@vitest/ui@5.0.0)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
@@ -19825,11 +19823,11 @@ snapshots:
- vite
optional: true
- '@vitest/browser-playwright@5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
+ '@vitest/browser-playwright@5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
dependencies:
'@vitest/browser': 5.0.0(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))
- playwright: 1.62.1
+ playwright: 1.63.0
tinyrainbow: 3.1.1
vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-istanbul@5.0.0)(@vitest/ui@5.0.0)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
@@ -19838,11 +19836,11 @@ snapshots:
- utf-8-validate
- vite
- '@vitest/browser-playwright@5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
+ '@vitest/browser-playwright@5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
dependencies:
'@vitest/browser': 5.0.0(vite@8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))
- playwright: 1.62.1
+ playwright: 1.63.0
tinyrainbow: 3.1.1
vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-istanbul@5.0.0)(@vitest/ui@5.0.0)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
@@ -19852,11 +19850,11 @@ snapshots:
- vite
optional: true
- '@vitest/browser-playwright@5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
+ '@vitest/browser-playwright@5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)':
dependencies:
'@vitest/browser': 5.0.0(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))
- playwright: 1.62.1
+ playwright: 1.63.0
tinyrainbow: 3.1.1
vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/browser-playwright@5.0.0)(@vitest/coverage-istanbul@5.0.0)(@vitest/ui@5.0.0)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
@@ -22476,9 +22474,6 @@ snapshots:
fs.realpath@1.0.0: {}
- fsevents@2.3.2:
- optional: true
-
fsevents@2.3.3:
optional: true
@@ -25353,13 +25348,11 @@ snapshots:
exsolve: 1.1.1
pathe: 2.0.3
- playwright-core@1.62.1: {}
+ playwright-core@1.63.0: {}
- playwright@1.62.1:
+ playwright@1.63.0:
dependencies:
- playwright-core: 1.62.1
- optionalDependencies:
- fsevents: 2.3.2
+ playwright-core: 1.63.0
please-upgrade-node@3.2.0:
dependencies:
@@ -28104,7 +28097,7 @@ snapshots:
optionalDependencies:
'@opentelemetry/api': 1.9.1
'@types/node': 26.4.1
- '@vitest/browser-playwright': 5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
+ '@vitest/browser-playwright': 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/coverage-istanbul': 5.0.0(vitest@5.0.0)
'@vitest/ui': 5.0.0(vitest@5.0.0)
jsdom: 30.0.1
@@ -28130,7 +28123,7 @@ snapshots:
optionalDependencies:
'@opentelemetry/api': 1.9.1
'@types/node': 26.4.1
- '@vitest/browser-playwright': 5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.24))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
+ '@vitest/browser-playwright': 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/coverage-istanbul': 5.0.0(vitest@5.0.0)
'@vitest/ui': 5.0.0(vitest@5.0.0)
jsdom: 30.0.1
@@ -28156,7 +28149,7 @@ snapshots:
optionalDependencies:
'@opentelemetry/api': 1.9.1
'@types/node': 26.5.0
- '@vitest/browser-playwright': 5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
+ '@vitest/browser-playwright': 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.5.0)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/coverage-istanbul': 5.0.0(vitest@5.0.0)
'@vitest/ui': 5.0.0(vitest@5.0.0)
jsdom: 30.0.1
@@ -28182,7 +28175,7 @@ snapshots:
optionalDependencies:
'@opentelemetry/api': 1.9.1
'@types/node': 26.5.0
- '@vitest/browser-playwright': 5.0.0(playwright@1.62.1)(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
+ '@vitest/browser-playwright': 5.0.0(playwright@1.63.0)(vite@8.2.2(@types/node@26.5.0)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.1(postcss@8.5.26))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.0)
'@vitest/coverage-istanbul': 5.0.0(vitest@5.0.0)
'@vitest/ui': 5.0.0(vitest@5.0.0)
jsdom: 30.0.1
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index ba672d15c5..2be543e301 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -119,7 +119,7 @@ catalog:
markdown-it: 15.0.1
markdown-it-link-attributes: 4.0.1
minisearch: 7.2.0
- playwright: 1.62.1
+ playwright: 1.63.0
postcss: 8.5.26
publint: 0.3.24
react: 19.2.8
diff --git a/projects/code/DEVELOPMENT.md b/projects/code/DEVELOPMENT.md
index 599835d4a1..ff4b5b3d0d 100644
--- a/projects/code/DEVELOPMENT.md
+++ b/projects/code/DEVELOPMENT.md
@@ -8,6 +8,7 @@
| `pnpm run test` | Run unit tests |
| `pnpm run test:watch` | Run unit tests in watch mode |
| `pnpm run test:axe` | Run accessibility tests |
+| `pnpm run test:ssr` | Run server-side rendering tests |
| `pnpm run test:coverage` | Run unit tests with coverage |
| `pnpm run test:lighthouse` | Run Lighthouse performance tests |
| `pnpm run ci` | Run the full CI pipeline |
diff --git a/projects/code/package.json b/projects/code/package.json
index 28250aa096..ed053bb55e 100644
--- a/projects/code/package.json
+++ b/projects/code/package.json
@@ -4,7 +4,7 @@
"release": {
"extends": "../../release.config.js"
},
- "description": "Code authoring related components. Providing syntax-highlighted code blocks with support for multiple programming languages.",
+ "description": "NVIDIA Elements code authoring related components such as syntax-highlighted code blocks.",
"keywords": [
"nvidia",
"web-components",
@@ -186,6 +186,7 @@
"lint": "wireit",
"test": "wireit",
"test:axe": "wireit",
+ "test:ssr": "wireit",
"test:coverage": "wireit",
"test:watch": "wireit",
"test:lighthouse": "wireit"
@@ -235,6 +236,7 @@
"lint",
"publint",
"test:axe",
+ "test:ssr",
"test:coverage"
]
},
@@ -380,6 +382,28 @@
"NODE_ENV": "production"
}
},
+ "test:ssr": {
+ "command": "vitest run --config=vitest.ssr.ts",
+ "files": [
+ "dist/**/*.js",
+ "src/**/*.test.ssr.ts",
+ "tsconfig.json",
+ "vitest.ssr.ts"
+ ],
+ "output": [
+ "coverage/ssr/**"
+ ],
+ "dependencies": [
+ "../internals/vite:ci",
+ {
+ "script": "build",
+ "cascade": false
+ }
+ ],
+ "env": {
+ "NODE_ENV": "production"
+ }
+ },
"lint": {
"dependencies": [
"lint:eslint",
diff --git a/projects/code/src/codeblock/codeblock.test.ssr.ts b/projects/code/src/codeblock/codeblock.test.ssr.ts
new file mode 100644
index 0000000000..3adb718c40
--- /dev/null
+++ b/projects/code/src/codeblock/codeblock.test.ssr.ts
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import { html } from 'lit';
+import { describe, expect, it } from 'vitest';
+import { ssrRunner } from '@internals/vite';
+import { CodeBlock } from '@nvidia-elements/code/codeblock';
+import '@nvidia-elements/code/codeblock/define.js';
+
+describe(CodeBlock.metadata.tag, () => {
+ it('should render its shadow root and preserve source and actions', async () => {
+ const result = await ssrRunner.render(html`
+
+
+ echo hello
echo hello');
+ expect(result).toContain('');
+ });
+});
diff --git a/projects/code/src/index.test.lighthouse.ts b/projects/code/src/index.test.lighthouse.ts
index 03a3bda7c5..6717e2eacc 100644
--- a/projects/code/src/index.test.lighthouse.ts
+++ b/projects/code/src/index.test.lighthouse.ts
@@ -24,7 +24,7 @@ describe('lighthouse report', () => {
`);
- expect(report.payload.javascript.kb).toBeLessThan(45);
+ expect(report.payload.javascript.kb).toBeLessThan(46.5);
expect(report.payload.javascript.requests['define.js']!.kb).toBeLessThan(11);
expect(report.payload.javascript.requests['core.js']!.kb).toBeLessThan(9);
expect(report.payload.javascript.requests['bash.js']!.kb).toBeLessThan(3);
@@ -52,7 +52,7 @@ describe('lighthouse report', () => {
expect(report.scores.bestPractices).toBe(100);
const bundleKb = report.payload.javascript.requests[Object.keys(report.payload.javascript.requests)[0]!]!.kb;
expect(bundleKb).toBeGreaterThan(30);
- expect(bundleKb).toBeLessThan(31.5);
+ expect(bundleKb).toBeLessThan(32.1);
});
});
@@ -65,6 +65,6 @@ describe('lighthouse report', () => {
`);
expect(report.payload.javascript.kb).toBeGreaterThan(31);
- expect(report.payload.javascript.kb).toBeLessThan(32.6);
+ expect(report.payload.javascript.kb).toBeLessThan(33.2);
});
});
diff --git a/projects/code/vitest.ssr.ts b/projects/code/vitest.ssr.ts
new file mode 100644
index 0000000000..260dee001d
--- /dev/null
+++ b/projects/code/vitest.ssr.ts
@@ -0,0 +1,15 @@
+import { resolve } from 'path';
+import { mergeConfig } from 'vitest/config';
+import { libraryLitSSRTestConfig } from '@internals/vite/configs/ssr.js';
+
+export default mergeConfig(libraryLitSSRTestConfig, {
+ resolve: {
+ alias: { '@nvidia-elements/code': resolve(import.meta.dirname, './dist') }
+ },
+ test: {
+ include: ['./src/**/*.test.ssr.ts'],
+ outputFile: {
+ junit: './coverage/ssr/junit.xml'
+ }
+ }
+});
diff --git a/projects/core/.visual/iframe.dark.png b/projects/core/.visual/iframe.dark.png
new file mode 100644
index 0000000000..745647248a
--- /dev/null
+++ b/projects/core/.visual/iframe.dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:65ef665b043d2a6bbbc13345fb8d693ec116bebc890fab56dd2bf20db1a6ad2b
+size 1029
diff --git a/projects/core/.visual/iframe.png b/projects/core/.visual/iframe.png
new file mode 100644
index 0000000000..31c79d3d09
--- /dev/null
+++ b/projects/core/.visual/iframe.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:053575e9579be233fdb51e2bad34691381a07fe7f06dbb42f2cfcb25e813036d
+size 1037
diff --git a/projects/core/package.json b/projects/core/package.json
index 85ddda7016..db9a33d556 100644
--- a/projects/core/package.json
+++ b/projects/core/package.json
@@ -507,6 +507,18 @@
"types": "./dist/icon-button/define.d.ts",
"default": "./dist/icon-button/define.js"
},
+ "./iframe": {
+ "types": "./dist/iframe/index.d.ts",
+ "default": "./dist/iframe/index.js"
+ },
+ "./iframe/index.js": {
+ "types": "./dist/iframe/index.d.ts",
+ "default": "./dist/iframe/index.js"
+ },
+ "./iframe/define.js": {
+ "types": "./dist/iframe/define.d.ts",
+ "default": "./dist/iframe/define.js"
+ },
"./input": {
"types": "./dist/input/index.d.ts",
"default": "./dist/input/index.js"
@@ -1016,6 +1028,7 @@
"@nvidia-elements/styles": "workspace:^",
"@typescript/lib-dom": "npm:@types/web@0.0.312",
"@vitest/browser": "catalog:",
+ "@vitest/browser-playwright": "catalog:",
"@vitest/coverage-istanbul": "catalog:",
"axe-core": "catalog:",
"eslint": "catalog:",
diff --git a/projects/core/src/bundle.ts b/projects/core/src/bundle.ts
index ce448142a0..4ceea00a89 100644
--- a/projects/core/src/bundle.ts
+++ b/projects/core/src/bundle.ts
@@ -37,6 +37,7 @@ import '@nvidia-elements/core/gauge/define.js';
import '@nvidia-elements/core/grid/define.js';
import '@nvidia-elements/core/icon/define.js';
import '@nvidia-elements/core/icon-button/define.js';
+import '@nvidia-elements/core/iframe/define.js';
import '@nvidia-elements/core/input/define.js';
import '@nvidia-elements/core/logo/define.js';
import '@nvidia-elements/core/menu/define.js';
@@ -109,6 +110,7 @@ export * from '@nvidia-elements/core/gauge';
export * from '@nvidia-elements/core/grid';
export * from '@nvidia-elements/core/icon';
export * from '@nvidia-elements/core/icon-button';
+export * from '@nvidia-elements/core/iframe';
export * from '@nvidia-elements/core/input';
export * from '@nvidia-elements/core/logo';
export * from '@nvidia-elements/core/menu';
diff --git a/projects/core/src/iframe/define.ts b/projects/core/src/iframe/define.ts
new file mode 100644
index 0000000000..06bd8e433e
--- /dev/null
+++ b/projects/core/src/iframe/define.ts
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import { define } from '@nvidia-elements/core/internal';
+import { Iframe } from './iframe.js';
+
+define(Iframe);
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'nve-iframe': Iframe;
+ }
+}
diff --git a/projects/core/src/iframe/iframe.css b/projects/core/src/iframe/iframe.css
new file mode 100644
index 0000000000..dce2c68689
--- /dev/null
+++ b/projects/core/src/iframe/iframe.css
@@ -0,0 +1,29 @@
+/* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+:host {
+ --width: var(--_width, fit-content);
+ --height: var(--_height, fit-content);
+ --border: none;
+ display: block;
+ width: fit-content;
+ height: fit-content;
+ border: var(--border);
+}
+
+iframe {
+ width: var(--width);
+ height: var(--height);
+ border: 0;
+}
+
+@supports (frame-sizing: content-height) {
+ :host {
+ /* Native sizing locks the initial viewport height as its minimum. */
+ --height: var(--_height, 0px);
+ }
+
+ iframe {
+ frame-sizing: content-height;
+ }
+}
diff --git a/projects/core/src/iframe/iframe.examples.ts b/projects/core/src/iframe/iframe.examples.ts
new file mode 100644
index 0000000000..155efc8c4c
--- /dev/null
+++ b/projects/core/src/iframe/iframe.examples.ts
@@ -0,0 +1,112 @@
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import { html } from 'lit';
+import '@nvidia-elements/core/iframe/define.js';
+
+export default {
+ title: 'Elements/Iframe',
+ component: 'nve-iframe'
+};
+
+/**
+ * @summary Supplies the iframe with Elements themes, utilities, fonts, and component registrations through its head template. Use this structure because iframe documents do not inherit resources from the parent document.
+ */
+export const Default = {
+ render: () => html`
+override iframe size
+Initial iframe content.
+ +