diff --git a/NOTICE.md b/NOTICE.md index 67dc23584..21c39086a 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -70,7 +70,7 @@ This product includes the following bundled third-party software: - ignore v7.0.6 [MIT] (used by: @nvidia-elements/pi) Copyright: kael -- lit v3.3.3 [BSD-3-Clause] (used by: @nvidia-elements/code, @nvidia-elements/core, @nvidia-elements/markdown, @nvidia-elements/media, @nvidia-elements/monaco, @nvidia-elements/plot, @nvidia-elements/scene, @nvidia-elements/viewport) +- lit v3.3.3 [BSD-3-Clause] (used by: @nvidia-elements/code, @nvidia-elements/core, @nvidia-elements/foundations, @nvidia-elements/markdown, @nvidia-elements/media, @nvidia-elements/monaco, @nvidia-elements/plot, @nvidia-elements/scene) Copyright: Google LLC - lit-html v3.3.3 [BSD-3-Clause] (used by: @nvidia-elements/code, @nvidia-elements/core, @nvidia-elements/monaco) diff --git a/commitlint.config.js b/commitlint.config.js index 371c4a5b4..8c4098411 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -20,6 +20,7 @@ export default { 'ci', 'deps', 'docs', + 'foundations', 'internals', 'create', 'starters', diff --git a/knip.config.js b/knip.config.js index ec6d50687..66be04636 100644 --- a/knip.config.js +++ b/knip.config.js @@ -236,6 +236,19 @@ export default { ], project: [...PROJECT_FILES, ...SOURCE_FILES] }, + 'projects/foundations': { + entry: [ + ...PACKAGE_FILES, + ...SOURCE_INDEX, + ...DEFINE_ENTRIES, + ...EXAMPLE_ENTRIES, + ...TEST_ENTRIES, + ...TEST_VARIANT_ENTRIES, + ...VITE_CONFIGS, + ...VITEST_CONFIGS + ], + project: [...PROJECT_FILES, ...SOURCE_FILES] + }, 'projects/viewport': { entry: [ ...PACKAGE_FILES, @@ -252,7 +265,12 @@ export default { 'projects/site': { entry: [...PACKAGE_FILES, 'src/**/*.{js,ts}', ...VITEST_CONFIGS], project: [...PACKAGE_FILES, 'src/**/*.{js,ts}', ...VITEST_CONFIGS], - ignoreDependencies: ['@nvidia-elements/plot', '@nvidia-elements/scene', '@nvidia-elements/viewport'] + ignoreDependencies: [ + '@nvidia-elements/foundations', + '@nvidia-elements/plot', + '@nvidia-elements/scene', + '@nvidia-elements/viewport' + ] }, 'projects/styles': { entry: [ diff --git a/package.json b/package.json index 98e5e6fcb..acdc2e160 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "./projects/plot:ci", "./projects/scene:ci", "./projects/viewport:ci", + "./projects/foundations:ci", "./projects/site:ci", "./projects/starters:ci", "./projects/starters/angular:ci", @@ -147,6 +148,7 @@ "./projects/plot:test:lighthouse", "./projects/scene:test:lighthouse", "./projects/viewport:test:lighthouse", + "./projects/foundations:test:lighthouse", "./projects/site:test:lighthouse" ], "output": [ @@ -316,6 +318,7 @@ "./projects/monaco:lint:fix", "./projects/scene:lint:fix", "./projects/viewport:lint:fix", + "./projects/foundations:lint:fix", "./projects/site:lint:fix" ] }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5c09660f..a37dc438a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -801,6 +801,67 @@ importers: specifier: 'catalog:' version: 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)) + projects/foundations: + dependencies: + '@nvidia-elements/core': + specifier: workspace:^ + version: link:../core + '@nvidia-elements/themes': + specifier: workspace:^ + version: link:../themes + lit: + specifier: catalog:publish + version: 3.3.3 + devDependencies: + '@eslint/js': + specifier: 'catalog:' + version: 10.0.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + '@internals/eslint': + specifier: workspace:* + version: link:../internals/eslint + '@internals/testing': + specifier: workspace:* + version: link:../internals/testing + '@internals/vite': + specifier: workspace:* + version: link:../internals/vite + '@nvidia-elements/lint': + specifier: workspace:* + version: link:../lint + '@nvidia-elements/styles': + specifier: workspace:* + version: link:../styles + '@vitest/browser': + specifier: 'catalog:' + version: 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/coverage-istanbul': + specifier: 'catalog:' + version: 5.0.0(vitest@5.0.0) + axe-core: + specifier: 'catalog:' + version: 4.11.0 + eslint: + specifier: 10.11.0 + version: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + publint: + specifier: 'catalog:' + version: 0.3.24 + stylelint: + specifier: 'catalog:' + version: 17.14.1(supports-color@10.2.2)(typescript@6.0.3) + stylelint-config-standard: + specifier: 'catalog:' + version: 40.0.0(stylelint@17.14.1(supports-color@10.2.2)(typescript@6.0.3)) + typescript: + specifier: 'catalog:' + version: 6.0.3 + vite: + specifier: 'catalog:' + version: 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: + specifier: 'catalog:' + version: 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)) + projects/internals/design: devDependencies: '@google/design.md': @@ -1710,6 +1771,9 @@ importers: '@nvidia-elements/forms': specifier: workspace:* version: link:../forms + '@nvidia-elements/foundations': + specifier: workspace:* + version: link:../foundations '@nvidia-elements/lint': specifier: workspace:* version: link:../lint diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5eebe93e8..ed1528414 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -36,6 +36,7 @@ packages: - projects/plot - projects/scene - projects/viewport + - projects/foundations - projects/styles - projects/themes - projects/internals/design diff --git a/projects/foundations/.gitignore b/projects/foundations/.gitignore new file mode 100644 index 000000000..5f030ac5b --- /dev/null +++ b/projects/foundations/.gitignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +coverage/ +.DS_Store diff --git a/projects/foundations/.visual/foundations.dark.png b/projects/foundations/.visual/foundations.dark.png new file mode 100644 index 000000000..a278f4de7 --- /dev/null +++ b/projects/foundations/.visual/foundations.dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c131d1b32d2f5f8cad54d5b4988bd4b0cc430c63273cfd40d771d8a679e79d6 +size 2209 diff --git a/projects/foundations/.visual/foundations.png b/projects/foundations/.visual/foundations.png new file mode 100644 index 000000000..c91a303f6 --- /dev/null +++ b/projects/foundations/.visual/foundations.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e33171ce14db36de5a2c459a4196565234bdba1fc7ab8781aecdbe02024c10b +size 2190 diff --git a/projects/foundations/CHANGELOG.md b/projects/foundations/CHANGELOG.md new file mode 100644 index 000000000..98eff1b99 --- /dev/null +++ b/projects/foundations/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.0 + +Initial release. diff --git a/projects/foundations/DEVELOPMENT.md b/projects/foundations/DEVELOPMENT.md new file mode 100644 index 000000000..273c5f27d --- /dev/null +++ b/projects/foundations/DEVELOPMENT.md @@ -0,0 +1,17 @@ +# Development + +| Command | Description | +| -------------------------- | ----------------------------------------- | +| `pnpm run build` | Build the library | +| `pnpm run build:watch` | Build the library with file watching | +| `pnpm run dev` | Start development mode with file watching | +| `pnpm run lint` | Lint source files | +| `pnpm run lint:fix` | Fix source lint errors | +| `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:coverage` | Run unit tests with coverage | +| `pnpm run test:visual` | Run visual regression tests | +| `pnpm run test:ssr` | Run server-side rendering tests | +| `pnpm run test:lighthouse` | Run Lighthouse performance tests | +| `pnpm run ci` | Run the project CI pipeline | diff --git a/projects/foundations/NOTICE.md b/projects/foundations/NOTICE.md new file mode 100644 index 000000000..2dca28733 --- /dev/null +++ b/projects/foundations/NOTICE.md @@ -0,0 +1,44 @@ +NOTICE + +Elements - NVIDIA Elements Design System +Copyright 2024-2026 NVIDIA Corporation + +This project includes the following bundled third-party software: + +- lit v3.3.3 [BSD-3-Clause] + Copyright: Google LLC + +============================================================================== +BSD-3-Clause +============================================================================== + +The following bundled components are provided under the BSD-3-Clause license: + +lit v3.3.3 - Copyright Google LLC + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +For license details, see: BSD-3-Clause: https://opensource.org/licenses/BSD-3-Clause diff --git a/projects/foundations/README.md b/projects/foundations/README.md new file mode 100644 index 000000000..28183f0fb --- /dev/null +++ b/projects/foundations/README.md @@ -0,0 +1,3 @@ +# @nvidia-elements/foundations + +A slotted container component for NVIDIA Elements. diff --git a/projects/foundations/eslint.config.js b/projects/foundations/eslint.config.js new file mode 100644 index 000000000..ee96dbce9 --- /dev/null +++ b/projects/foundations/eslint.config.js @@ -0,0 +1,5 @@ +import { elementsRecommended } from '@nvidia-elements/lint/eslint'; +import { browserTypescriptConfig, libraryConfig, litConfig, jsonConfig } from '@internals/eslint'; + +/** @type {import('eslint').Linter.Config[]} */ +export default [...elementsRecommended, ...browserTypescriptConfig, ...libraryConfig, ...litConfig, ...jsonConfig]; diff --git a/projects/foundations/package.json b/projects/foundations/package.json new file mode 100644 index 000000000..9e78dd47a --- /dev/null +++ b/projects/foundations/package.json @@ -0,0 +1,348 @@ +{ + "name": "@nvidia-elements/foundations", + "version": "0.0.0", + "private": true, + "release": { + "extends": "../../release.config.js" + }, + "description": "A slotted container component for NVIDIA Elements.", + "keywords": [ + "nvidia", + "web-components", + "custom-elements", + "ui-components", + "design-system" + ], + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/NVIDIA/elements.git" + }, + "customElements": "dist/custom-elements.json", + "contributes": { + "html": { + "customData": [ + "./dist/data.html.json" + ] + }, + "snippets": [ + { + "language": "html", + "path": "./dist/data.snippets.json" + } + ] + }, + "files": [ + "README.md", + "CHANGELOG.md", + "NOTICE.md", + "package.json", + "dist/**/*" + ], + "exports": { + "./package.json": "./package.json", + "./custom-elements.json": "./dist/custom-elements.json", + "./data.html.json": "./dist/data.html.json", + "./*.examples.json": "./dist/*.examples.json", + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./index.js": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./foundations": { + "types": "./dist/foundations/index.d.ts", + "default": "./dist/foundations/index.js" + }, + "./foundations/define.js": { + "types": "./dist/foundations/define.d.ts", + "default": "./dist/foundations/define.js" + } + }, + "sideEffects": [ + "./**/define.js" + ], + "scripts": { + "ci": "wireit", + "dev": "wireit", + "build": "wireit", + "build:watch": "wireit", + "lint": "wireit", + "lint:fix": "wireit", + "test": "wireit", + "test:axe": "wireit", + "test:coverage": "wireit", + "test:ssr": "wireit", + "test:visual": "wireit", + "test:watch": "wireit", + "test:lighthouse": "wireit" + }, + "dependencies": { + "lit": "catalog:publish" + }, + "peerDependencies": { + "@nvidia-elements/core": "workspace:^", + "@nvidia-elements/themes": "workspace:^" + }, + "devDependencies": { + "@eslint/js": "catalog:", + "@internals/eslint": "workspace:*", + "@internals/testing": "workspace:*", + "@internals/vite": "workspace:*", + "@nvidia-elements/lint": "workspace:*", + "@nvidia-elements/styles": "workspace:*", + "@vitest/browser": "catalog:", + "@vitest/coverage-istanbul": "catalog:", + "axe-core": "catalog:", + "eslint": "catalog:", + "lit": "catalog:", + "publint": "catalog:", + "stylelint": "catalog:", + "stylelint-config-standard": "catalog:", + "typescript": "catalog:", + "vite": "catalog:", + "vitest": "catalog:" + }, + "wireit": { + "dev": { + "command": "NODE_ENV=development vite build --watch", + "service": true, + "dependencies": [ + "../core:build", + "../internals/vite:ci" + ] + }, + "ci": { + "dependencies": [ + "build", + "lint", + "publint", + "test:axe", + "test:ssr", + "test:coverage", + "test:visual" + ] + }, + "publint": { + "command": "publint", + "dependencies": [ + "build" + ], + "files": [ + "package.json" + ], + "output": [] + }, + "build": { + "command": "NODE_ENV=production vite build", + "files": [ + "src/**", + "!src/**/*.test.ts", + "!src/**/*.test.lighthouse.ts", + "!src/**/*.test.axe.ts", + "!src/**/*.test.ssr.ts", + "!src/**/__screenshots__/**", + "package.json", + "tsconfig.json", + "tsconfig.lib.json", + "vite.config.ts" + ], + "output": [ + "dist/**" + ], + "dependencies": [ + "../internals/vite:ci", + { + "script": "../themes:build", + "cascade": false + }, + { + "script": "../core:build", + "cascade": false + } + ] + }, + "build:watch": { + "command": "vite build --watch", + "service": true, + "dependencies": [ + "build" + ] + }, + "test": { + "command": "vitest run", + "files": [ + "src/**", + "!src/**/*.test.axe.ts", + "!src/**/*.test.lighthouse.ts", + "!src/**/*.test.ssr.ts", + "!src/**/*.test.visual.ts", + "!src/**/__screenshots__/**", + "tsconfig.json", + "vite.config.ts", + "vitest.config.ts" + ], + "output": [], + "dependencies": [ + "../internals/testing:build", + "../internals/vite:ci", + "../core:build" + ] + }, + "test:coverage": { + "command": "vitest run --coverage", + "files": [ + "src/**", + "!src/**/*.test.axe.ts", + "!src/**/*.test.lighthouse.ts", + "!src/**/*.test.ssr.ts", + "!src/**/*.test.visual.ts", + "!src/**/__screenshots__/**", + "tsconfig.json", + "vite.config.ts", + "vitest.config.ts" + ], + "output": [ + "coverage/unit/**" + ], + "dependencies": [ + "../internals/testing:build", + "../internals/vite:ci", + "../core:build" + ] + }, + "test:watch": { + "command": "vitest --watch", + "service": true + }, + "test:lighthouse": { + "command": "playwright-lock 'vitest run --config=vitest.lighthouse.ts'", + "files": [ + "src/index.test.lighthouse.ts", + "src/**/*.test.lighthouse.ts", + "vitest.lighthouse.html", + "vitest.lighthouse.ts" + ], + "output": [ + ".lighthouse" + ], + "dependencies": [ + "build", + "../internals/vite:ci" + ], + "env": { + "NODE_ENV": "production" + } + }, + "test:visual": { + "command": "vitest run --config=vitest.visual.ts", + "clean": false, + "files": [ + "dist/**/*.js", + "src/**/*.test.visual.ts", + "vitest.visual.html", + "vitest.visual.ts" + ], + "output": [ + ".visual/*.png", + "coverage/visual/**" + ], + "dependencies": [ + "../internals/vite:ci", + { + "script": "build", + "cascade": false + } + ] + }, + "test:axe": { + "command": "vitest run --config=vitest.axe.ts", + "files": [ + "dist/**/*.js", + "src/**/*.test.axe.ts", + "tsconfig.json", + "vitest.axe.ts" + ], + "output": [ + "coverage/axe/**" + ], + "dependencies": [ + "../internals/testing:build", + "../internals/vite:ci", + { + "script": "build", + "cascade": false + } + ], + "env": { + "NODE_ENV": "production" + } + }, + "test:ssr": { + "command": "vitest run --config=vitest.ssr.ts", + "files": [ + "dist/**/*.js", + "src/**/*.test.ssr.ts", + "tsconfig.json", + "vitest.ssr.ts", + "../themes/dist/**/*.css" + ], + "output": [ + "coverage/ssr/**" + ], + "dependencies": [ + "../internals/vite:ci", + { + "script": "build", + "cascade": false + }, + { + "script": "../themes:build", + "cascade": false + } + ], + "env": { + "NODE_ENV": "production" + } + }, + "lint": { + "dependencies": [ + "lint:eslint", + "lint:style" + ] + }, + "lint:eslint": { + "command": "eslint -c ./eslint.config.js --color --cache --cache-location .eslintcache/", + "files": [ + "src/**/*.ts", + "eslint.config.js" + ], + "output": [], + "dependencies": [ + "../internals/eslint:ci", + "../lint:build" + ] + }, + "lint:style": { + "command": "stylelint 'src/**/*.css' --config=../../stylelint.config.mjs", + "files": [ + "src/**/*.css", + "../../stylelint.config.mjs" + ], + "output": [] + }, + "lint:fix": { + "command": "eslint -c ./eslint.config.js --fix", + "dependencies": [ + "../internals/eslint:ci", + "../lint:build" + ] + } + }, + "publishConfig": { + "provenance": true + }, + "license": "Apache-2.0" +} diff --git a/projects/foundations/src/declarations.d.ts b/projects/foundations/src/declarations.d.ts new file mode 100644 index 000000000..5db409c19 --- /dev/null +++ b/projects/foundations/src/declarations.d.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +declare module '*.css' { + const content: string; + export default content; +} + +declare module '*.css?inline' { + const content: string; + export = content; +} diff --git a/projects/foundations/src/foundations/define.ts b/projects/foundations/src/foundations/define.ts new file mode 100644 index 000000000..3206c6c0f --- /dev/null +++ b/projects/foundations/src/foundations/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 { Foundations } from '@nvidia-elements/foundations/foundations'; + +define(Foundations); + +declare global { + interface HTMLElementTagNameMap { + 'nve-foundations': Foundations; + } +} diff --git a/projects/foundations/src/foundations/foundations.css b/projects/foundations/src/foundations/foundations.css new file mode 100644 index 000000000..391ce6345 --- /dev/null +++ b/projects/foundations/src/foundations/foundations.css @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +:host { + display: block; +} diff --git a/projects/foundations/src/foundations/foundations.examples.ts b/projects/foundations/src/foundations/foundations.examples.ts new file mode 100644 index 000000000..c430c4c27 --- /dev/null +++ b/projects/foundations/src/foundations/foundations.examples.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { html } from 'lit'; +import '@nvidia-elements/foundations/foundations/define.js'; + +export default { + title: 'Elements/Foundations', + component: 'nve-foundations', +}; + +/** + * @summary Display content inside a slotted container. + */ +export const Default = { + render: () => html` +Container content + ` +}; diff --git a/projects/foundations/src/foundations/foundations.test.axe.ts b/projects/foundations/src/foundations/foundations.test.axe.ts new file mode 100644 index 000000000..0ec941b03 --- /dev/null +++ b/projects/foundations/src/foundations/foundations.test.axe.ts @@ -0,0 +1,31 @@ +// 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, beforeEach, afterEach } from 'vitest'; +import { createFixture, elementIsStable, removeFixture } from '@internals/testing'; +import { runAxe } from '@internals/testing/axe'; +import { Foundations } from '@nvidia-elements/foundations/foundations'; +import '@nvidia-elements/foundations/foundations/define.js'; + +describe(Foundations.metadata.tag, () => { + let fixture: HTMLElement; + let element: Foundations; + + beforeEach(async () => { + fixture = await createFixture(html` +

Container content

+ `); + element = fixture.querySelector(Foundations.metadata.tag); + await elementIsStable(element); + }); + + afterEach(() => { + removeFixture(fixture); + }); + + it('should pass axe check', async () => { + const results = await runAxe([Foundations.metadata.tag]); + expect(results.violations.length).toBe(0); + }); +}); diff --git a/projects/foundations/src/foundations/foundations.test.lighthouse.ts b/projects/foundations/src/foundations/foundations.test.lighthouse.ts new file mode 100644 index 000000000..b2829c55c --- /dev/null +++ b/projects/foundations/src/foundations/foundations.test.lighthouse.ts @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { expect, test, describe } from 'vitest'; +import { lighthouseRunner } from '@internals/vite'; + +describe('foundations lighthouse report', () => { + test('foundations should meet lighthouse benchmarks', async () => { + const report = await lighthouseRunner.getReport('nve-foundations', /* html */` +

Container content

+ + `); + + expect(report.scores.performance).toBe(100); + expect(report.scores.accessibility).toBe(100); + expect(report.scores.bestPractices).toBe(100); + expect(report.payload.javascript.kb).toBeLessThan(10); + }); +}); diff --git a/projects/foundations/src/foundations/foundations.test.ssr.ts b/projects/foundations/src/foundations/foundations.test.ssr.ts new file mode 100644 index 000000000..fa91a6edb --- /dev/null +++ b/projects/foundations/src/foundations/foundations.test.ssr.ts @@ -0,0 +1,20 @@ +// 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 { Foundations } from '@nvidia-elements/foundations/foundations'; +import '@nvidia-elements/foundations/foundations/define.js'; + +describe(Foundations.metadata.tag, () => { + it('should pass baseline ssr check', async () => { + const result = await ssrRunner.render( + html`

Container content

` + ); + expect(result.includes('shadowroot="open"')).toBe(true); + expect(result.includes('nve-foundations')).toBe(true); + expect(result.includes('')).toBe(true); + expect(result.includes('Container content')).toBe(true); + }); +}); diff --git a/projects/foundations/src/foundations/foundations.test.ts b/projects/foundations/src/foundations/foundations.test.ts new file mode 100644 index 000000000..d3f6b3670 --- /dev/null +++ b/projects/foundations/src/foundations/foundations.test.ts @@ -0,0 +1,44 @@ +// 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, beforeEach, afterEach } from 'vitest'; +import { createFixture, elementIsStable, removeFixture } from '@internals/testing'; +import { Foundations } from '@nvidia-elements/foundations/foundations'; +import '@nvidia-elements/foundations/foundations/define.js'; + +describe(Foundations.metadata.tag, () => { + let fixture: HTMLElement; + let element: Foundations; + + beforeEach(async () => { + fixture = await createFixture(html` +

Container content

+ `); + element = fixture.querySelector(Foundations.metadata.tag); + await elementIsStable(element); + }); + + afterEach(() => { + removeFixture(fixture); + }); + + it('should define element', () => { + expect(customElements.get(Foundations.metadata.tag)).toBeDefined(); + }); + + it('should display content through the default slot', () => { + const slot = element.shadowRoot.querySelector('slot'); + expect(slot.assignedElements()).toEqual([element.querySelector('p')]); + }); + + it('should display content added after rendering', async () => { + const content = document.createElement('p'); + content.textContent = 'Additional content'; + element.append(content); + await elementIsStable(element); + + const slot = element.shadowRoot.querySelector('slot'); + expect(slot.assignedElements()).toContain(content); + }); +}); diff --git a/projects/foundations/src/foundations/foundations.test.visual.ts b/projects/foundations/src/foundations/foundations.test.visual.ts new file mode 100644 index 000000000..170f7e4c7 --- /dev/null +++ b/projects/foundations/src/foundations/foundations.test.visual.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { expect, test, describe } from 'vitest'; +import { visualRunner } from '@internals/vite'; + +describe('foundations visual', () => { + test('foundations should match visual baseline', async () => { + const report = await visualRunner.render('foundations', template()); + expect(report.maxDiffPercentage).toBeLessThan(1); + }); + + test('foundations should match visual baseline dark theme', async () => { + const report = await visualRunner.render('foundations.dark', template('dark')); + expect(report.maxDiffPercentage).toBeLessThan(1); + }); +}); + +function template(theme: '' | 'dark' = '') { + return /* html */ ` + +

Container content

+ `; +} diff --git a/projects/foundations/src/foundations/foundations.ts b/projects/foundations/src/foundations/foundations.ts new file mode 100644 index 000000000..18d5c5214 --- /dev/null +++ b/projects/foundations/src/foundations/foundations.ts @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { html, LitElement } from 'lit'; +import { useStyles } from '@nvidia-elements/core/internal'; +import styles from './foundations.css?inline'; + +/** + * @element nve-foundations + * @description A slotted container component for NVIDIA Elements. + * @documentation https://nvidia.github.io/elements/docs/foundations/component/ + * @since 0.0.0 + * @entrypoint \@nvidia-elements/foundations/foundations + * @slot - Content to display inside the container. + */ +export class Foundations extends LitElement { + static styles = useStyles([styles]); + + static readonly metadata = { + tag: 'nve-foundations', + version: '0.0.0' + }; + + render() { + return html` +
+ +
+ `; + } +} diff --git a/projects/foundations/src/foundations/index.ts b/projects/foundations/src/foundations/index.ts new file mode 100644 index 000000000..56c8572e4 --- /dev/null +++ b/projects/foundations/src/foundations/index.ts @@ -0,0 +1,4 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +export * from './foundations.js'; diff --git a/projects/foundations/src/index.test.lighthouse.ts b/projects/foundations/src/index.test.lighthouse.ts new file mode 100644 index 000000000..8a6c91480 --- /dev/null +++ b/projects/foundations/src/index.test.lighthouse.ts @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { expect, test, describe } from 'vitest'; +import { lighthouseRunner } from '@internals/vite'; + +describe('lighthouse report', () => { + test('@nvidia-elements/foundations JS Bundles should remain within compressed bundle limits', async () => { + const report = await lighthouseRunner.getReport('bundles', /* html */` + + `); + + expect(report.payload.javascript.kb).toBeLessThan(2); + }); +}); diff --git a/projects/foundations/src/index.test.ts b/projects/foundations/src/index.test.ts new file mode 100644 index 000000000..ab6cc2377 --- /dev/null +++ b/projects/foundations/src/index.test.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { describe, it, expect } from 'vitest'; +import { VERSION } from './index.js'; + +describe('@nvidia-elements/foundations', () => { + it('should export VERSION', () => { + expect(VERSION).toBe('0.0.0'); + }); +}); diff --git a/projects/foundations/src/index.ts b/projects/foundations/src/index.ts new file mode 100644 index 000000000..f8f476a94 --- /dev/null +++ b/projects/foundations/src/index.ts @@ -0,0 +1,4 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +export const VERSION = '0.0.0'; diff --git a/projects/foundations/tsconfig.json b/projects/foundations/tsconfig.json new file mode 100644 index 000000000..95a3b08f6 --- /dev/null +++ b/projects/foundations/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "noUncheckedSideEffectImports": true, + "skipLibCheck": true, + "module": "es2022", + "target": "es2022", + "lib": ["es2022", "dom", "dom.iterable"], + "typeRoots": ["./node_modules/@types"], + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist/", + "rootDir": "./", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "useDefineForClassFields": false, + "isolatedModules": true, + "strict": true, + "strictPropertyInitialization": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "paths": { + "@nvidia-elements/foundations": ["./src/index.js"], + "@nvidia-elements/foundations/*": ["./src/*"], + "@nvidia-elements/foundations/*.js": ["./src/*.js"] + } + }, + "include": ["src/**/*.ts"] +} diff --git a/projects/foundations/tsconfig.lib.json b/projects/foundations/tsconfig.lib.json new file mode 100644 index 000000000..af21f6f61 --- /dev/null +++ b/projects/foundations/tsconfig.lib.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "types": [] + }, + "exclude": [ + "**/*.test.ts", + "**/*.test.axe.ts", + "**/*.test.ssr.ts", + "**/*.test.visual.ts", + "**/*.test.lighthouse.ts", + "**/*.examples.ts" + ] +} diff --git a/projects/foundations/vite.config.ts b/projects/foundations/vite.config.ts new file mode 100644 index 000000000..4987e95ea --- /dev/null +++ b/projects/foundations/vite.config.ts @@ -0,0 +1,13 @@ +import { resolve } from 'path'; +import { UserConfig, defineConfig, mergeConfig } from 'vite'; +import { libraryBuildConfig } from '@internals/vite'; + +export default defineConfig(() => { + const config: UserConfig = { + resolve: { + alias: { '@nvidia-elements/foundations': resolve(import.meta.dirname, './src') } + } + }; + + return mergeConfig(libraryBuildConfig, config); +}); diff --git a/projects/foundations/vitest.axe.ts b/projects/foundations/vitest.axe.ts new file mode 100644 index 000000000..20b75ca2b --- /dev/null +++ b/projects/foundations/vitest.axe.ts @@ -0,0 +1,20 @@ +import { resolve } from 'path'; +import { mergeConfig } from 'vitest/config'; +import { libraryAxeTestConfig } from '@internals/vite/configs/axe.js'; + +export default mergeConfig(libraryAxeTestConfig, { + root: import.meta.dirname, + resolve: { + alias: { '@nvidia-elements/foundations': resolve(import.meta.dirname, './src') } + }, + test: { + include: ['./src/**/*.test.axe.ts'], + outputFile: { + junit: './coverage/axe/junit.xml' + }, + coverage: { + provider: 'istanbul', + reportsDirectory: './coverage/axe' + } + } +}); diff --git a/projects/foundations/vitest.config.ts b/projects/foundations/vitest.config.ts new file mode 100644 index 000000000..2ed3b888d --- /dev/null +++ b/projects/foundations/vitest.config.ts @@ -0,0 +1,26 @@ +import { resolve } from 'path'; +import { mergeConfig } from 'vitest/config'; +import { libraryTestConfig } from '@internals/vite/configs/test.js'; + +const config = mergeConfig(libraryTestConfig, { + root: import.meta.dirname, + resolve: { + alias: { '@nvidia-elements/foundations': resolve(import.meta.dirname, './src') } + }, + test: { + include: ['./src/**/*.test.ts'], + setupFiles: [], + coverage: { + thresholds: { + lines: 90, + branches: 90, + functions: 90, + statements: 90 + } + } + } +}); + +config.test.setupFiles = []; + +export default config; diff --git a/projects/foundations/vitest.lighthouse.html b/projects/foundations/vitest.lighthouse.html new file mode 100644 index 000000000..ca0745554 --- /dev/null +++ b/projects/foundations/vitest.lighthouse.html @@ -0,0 +1,19 @@ + + + + + + + + Lighthouse + + + +

Lighthouse Test

+ + diff --git a/projects/foundations/vitest.lighthouse.ts b/projects/foundations/vitest.lighthouse.ts new file mode 100644 index 000000000..58bf11a01 --- /dev/null +++ b/projects/foundations/vitest.lighthouse.ts @@ -0,0 +1,11 @@ +import { mergeConfig } from 'vitest/config'; +import { libraryLighthouseTestConfig } from '@internals/vite/configs/lighthouse.js'; + +export default mergeConfig(libraryLighthouseTestConfig, { + test: { + include: ['src/**/*.test.lighthouse.ts'], + outputFile: { + junit: './coverage/lighthouse/junit.xml' + } + } +}); diff --git a/projects/foundations/vitest.ssr.ts b/projects/foundations/vitest.ssr.ts new file mode 100644 index 000000000..d2781c447 --- /dev/null +++ b/projects/foundations/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/foundations': resolve(import.meta.dirname, './dist') } + }, + test: { + include: ['./src/**/*.test.ssr.ts'], + outputFile: { + junit: './coverage/ssr/junit.xml' + } + } +}); diff --git a/projects/foundations/vitest.visual.html b/projects/foundations/vitest.visual.html new file mode 100644 index 000000000..9f772650a --- /dev/null +++ b/projects/foundations/vitest.visual.html @@ -0,0 +1,35 @@ + + + + + + + + Visual + + + + diff --git a/projects/foundations/vitest.visual.ts b/projects/foundations/vitest.visual.ts new file mode 100644 index 000000000..58fed0d07 --- /dev/null +++ b/projects/foundations/vitest.visual.ts @@ -0,0 +1,12 @@ +import { mergeConfig } from 'vitest/config'; +import { libraryVisualTestConfig } from '@internals/vite/configs/visual.js'; + +export default mergeConfig(libraryVisualTestConfig, { + root: import.meta.dirname, + test: { + include: ['src/**/*.test.visual.ts'], + outputFile: { + junit: './coverage/visual/junit.xml' + } + } +}); diff --git a/projects/internals/metadata/package.json b/projects/internals/metadata/package.json index d58182445..920ab5a66 100644 --- a/projects/internals/metadata/package.json +++ b/projects/internals/metadata/package.json @@ -120,6 +120,8 @@ "../../monaco/dist/custom-elements.json", "../../viewport/package.json", "../../viewport/dist/custom-elements.json", + "../../foundations/package.json", + "../../foundations/dist/custom-elements.json", "../../patterns/package.json", "../../patterns/dist/custom-elements.json", "../../plot/dist/custom-elements.json", @@ -171,6 +173,10 @@ "script": "../../scene:build", "cascade": false }, + { + "script": "../../foundations:build", + "cascade": false + }, { "script": "../../viewport:build", "cascade": false diff --git a/projects/internals/metadata/src/tasks/api.utils.ts b/projects/internals/metadata/src/tasks/api.utils.ts index b0712ceaa..ad0a760f5 100644 --- a/projects/internals/metadata/src/tasks/api.utils.ts +++ b/projects/internals/metadata/src/tasks/api.utils.ts @@ -137,7 +137,8 @@ export async function getApi(): Promise<{ '../../../../monaco', '../../../../plot', '../../../../scene', - '../../../../viewport' + '../../../../viewport', + '../../../../foundations' ]; const types = getElementsStandardAPIProperties(); diff --git a/projects/site/package.json b/projects/site/package.json index 0eed1b5ec..fc7e23afc 100644 --- a/projects/site/package.json +++ b/projects/site/package.json @@ -108,6 +108,8 @@ "../scene/dist/**/*.examples.json", "../viewport/dist/**/*.js", "../viewport/dist/**/*.examples.json", + "../foundations/dist/**/*.js", + "../foundations/dist/**/*.examples.json", "../internals/design/dist/DESIGN.md", "../internals/metadata/static/**", "../internals/patterns/dist/**/*.js", @@ -179,6 +181,10 @@ { "script": "../viewport:build", "cascade": false + }, + { + "script": "../foundations:build", + "cascade": false } ], "output": [] @@ -344,6 +350,7 @@ "@nvidia-elements/code": "workspace:*", "@nvidia-elements/core": "workspace:*", "@nvidia-elements/forms": "workspace:*", + "@nvidia-elements/foundations": "workspace:*", "@nvidia-elements/lint": "workspace:*", "@nvidia-elements/markdown": "workspace:*", "@nvidia-elements/media": "workspace:*", diff --git a/projects/site/src/docs/foundations/component.md b/projects/site/src/docs/foundations/component.md new file mode 100644 index 000000000..8baec73d1 --- /dev/null +++ b/projects/site/src/docs/foundations/component.md @@ -0,0 +1,18 @@ +--- +{ + title: 'Foundations Component', + description: 'A slotted container component for NVIDIA Elements.', + layout: 'docs.11ty.js', + tag: 'nve-foundations' +} +--- + +# {{ title }} + +{% api 'nve-foundations' 'description' %} + +{% example '@nvidia-elements/foundations/foundations/foundations.examples.json' 'Default' %} + +## Slots + +{% api 'nve-foundations' 'slot' %}