Skip to content

Commit b931ff5

Browse files
committed
chore(docs): fixup metadata issues
Signed-off-by: Cory Rylan <crylan@nvidia.com>
1 parent e8e3187 commit b931ff5

9 files changed

Lines changed: 151 additions & 23 deletions

File tree

projects/site/public/robots.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
User-agent: *
22
Allow: /
3-
Disallow: /assets/
43
Disallow: /.pagefind/
54

6-
Sitemap: https://nvidia.github.io/elements/sitemap.xml
5+
Sitemap: https://nvidia.github.io/elements/sitemap.xml

projects/site/src/_11ty/layouts/common.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import { escapeAttr, resolvePageMeta, renderJsonLd, BASE_URL } from './metadata.
1111
export const renderBaseHead = data => {
1212
const meta = resolvePageMeta(data);
1313
const ogType = meta.url === '/' ? 'website' : 'article';
14+
const robots = data.noindex ? 'noindex,follow' : 'all';
1415
return /* html */ `
1516
<meta charset="UTF-8">
1617
<meta name="viewport" content="width=device-width, initial-scale=1.0">
17-
<meta name="robots" content="all">
18+
<meta name="robots" content="${robots}">
1819
<base href="${BASE_URL}" />
1920
<title data-pagefind-meta="title">${escapeAttr(meta.title)}</title>
2021
<meta name="description" content="${escapeAttr(meta.description)}">

projects/site/src/_11ty/layouts/metadata.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,38 @@ function findElementByTag(tag) {
5757
return siteData.elements.find(e => e.name === tag) ?? null;
5858
}
5959

60+
function appendSiteName(title) {
61+
if (title === 'NVIDIA Elements' || title.endsWith(' | NVIDIA Elements')) return title;
62+
return `${title} | NVIDIA Elements`;
63+
}
64+
65+
function resolveSectionTitle(data, title) {
66+
const url = data.page?.url ?? '';
67+
if (url === '/examples/') return 'Example Gallery';
68+
if (url.startsWith('/docs/internal/guidelines/')) return `${title} Guidelines`;
69+
if (url.startsWith('/docs/integrations/')) return `${title} Integration`;
70+
if (url.startsWith('/docs/foundations/themes/')) return `Theme ${title}`;
71+
if (url.startsWith('/docs/labs/layout/responsive/patterns/')) return 'Responsive Layout Patterns';
72+
if (url.startsWith('/docs/labs/') && !url.startsWith('/docs/labs/layout/')) return `${title} Lab`;
73+
return title;
74+
}
75+
76+
function resolvePageTitle(data, title) {
77+
if (data.changelog?.title) return appendSiteName(`${data.changelog.title} Changelog`);
78+
if (data.isApiTab) return appendSiteName(`${title} API`);
79+
if (data.isExamplesTab) return appendSiteName(`${title} Examples`);
80+
return appendSiteName(resolveSectionTitle(data, title));
81+
}
82+
83+
function hasGeneratedPage(data, generatedUrls, url) {
84+
if (!generatedUrls.size) return true;
85+
return url === '/' || url === data.page?.url || generatedUrls.has(url);
86+
}
87+
6088
export function resolvePageMeta(data) {
6189
const url = data.page?.url ?? '/';
62-
const title = data.title ?? 'NVIDIA Elements';
90+
const rawTitle = data.title ?? 'NVIDIA Elements';
91+
const title = resolvePageTitle(data, rawTitle);
6392
const tag = data.tag ?? data.component?.data?.tag;
6493
const element = findElementByTag(tag);
6594
const componentDescription = element?.manifest?.description?.trim();
@@ -71,10 +100,12 @@ export function resolvePageMeta(data) {
71100
description = `${componentDescription} — API reference for <${tag}>.`;
72101
} else if (data.isExamplesTab && componentDescription) {
73102
description = `${componentDescription} — Interactive examples for <${tag}>.`;
103+
} else if (data.changelog?.description) {
104+
description = `Changelog for ${data.changelog.title}: ${data.changelog.description}`;
74105
} else if (componentDescription) {
75106
description = componentDescription;
76107
} else {
77-
description = `Documentation for ${title} in NVIDIA Elements, the framework-agnostic design system for AI/ML factories.`;
108+
description = `Documentation for ${rawTitle} in NVIDIA Elements, the framework-agnostic design system for AI/ML factories.`;
78109
}
79110

80111
const canonicalUrl = `${SITE_ORIGIN}${PATH_PREFIX}${url}`;
@@ -90,6 +121,7 @@ export function renderJsonLd(data, meta) {
90121
const isDocs = meta.url.startsWith('/docs/');
91122
const articleType = isDocs ? 'TechArticle' : 'WebPage';
92123
const date = data.page?.date instanceof Date ? data.page.date.toISOString() : new Date().toISOString();
124+
const generatedUrls = new Set(data.collections?.all?.map(entry => entry.url).filter(Boolean));
93125

94126
const article = {
95127
'@context': 'https://schema.org',
@@ -116,11 +148,12 @@ export function renderJsonLd(data, meta) {
116148
let cumulative = '';
117149
segments.forEach((seg, i) => {
118150
cumulative += `/${seg}`;
151+
const item = i === segments.length - 1 && !meta.url.endsWith('/') ? meta.url : `${cumulative}/`;
119152
itemListElement.push({
120153
'@type': 'ListItem',
121154
position: i + 2,
122155
name: titleCaseSegment(seg),
123-
item: `${SITE_ORIGIN}${PATH_PREFIX}${cumulative}/`
156+
...(hasGeneratedPage(data, generatedUrls, item) ? { item: `${SITE_ORIGIN}${PATH_PREFIX}${item}` } : {})
124157
});
125158
});
126159

projects/site/src/_11ty/plugins/llms-txt.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,47 @@ import markdownIt from 'markdown-it';
33
import { ApiService } from '@internals/tools/api';
44
import { ExamplesService } from '@internals/tools/examples';
55
import { skills } from '@internals/tools/skills';
6-
import { ELEMENTS_PAGES_BASE_URL } from '../utils/env.js';
6+
import { BASE_URL } from '../layouts/metadata.js';
7+
import { ELEMENTS_PAGES_BASE_URL, ELEMENTS_SITE_URL } from '../utils/env.js';
78

8-
const BASE = ELEMENTS_PAGES_BASE_URL || 'http://localhost:8082/elements';
9+
const SITE_ORIGIN = ELEMENTS_SITE_URL.replace(/\/$/, '');
10+
const PATH_PREFIX = BASE_URL.replace(/\/$/, '');
11+
const BASE = (ELEMENTS_PAGES_BASE_URL || `${SITE_ORIGIN}${PATH_PREFIX}`).replace(/\/$/, '');
912

1013
const md = markdownIt({ html: true, linkify: true, breaks: false });
14+
const relativeMarkdownUrlPattern = /\b(href|src)="((?!(?:[a-z][a-z\d+.-]*:|\/\/))[^"]+?)\.md(?=")/gi;
1115
const CSS = `:root{color-scheme:light dark}body{font:16px/1.6 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;max-width:48rem;margin:0 auto;padding:2rem 1rem 4rem;color:#1a1a1a;background:#fff}@media (prefers-color-scheme:dark){body{color:#e6e6e6;background:#111}a{color:#6ea8ff}code,pre{background:#222}hr,td,th{border-color:#333}}code,pre{background:#f3f3f3}nav.crumbs{margin-bottom:2rem;font-size:.875rem}nav.crumbs a{color:inherit;opacity:.7;text-decoration:none}nav.crumbs a:hover{opacity:1;text-decoration:underline}h1,h2,h3,h4,h5,h6{line-height:1.25;margin:2em 0 .5em}h1{font-size:2rem;margin-top:0}h2{font-size:1.5rem;border-bottom:1px solid currentColor;padding-bottom:.3em;opacity:.95}h3{font-size:1.2rem}ol,p,ul{margin:.75em 0}a{color:#0969da}code{font:0.9em/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;padding:.1em .3em;border-radius:3px}pre{padding:1rem;border-radius:6px;overflow-x:auto}pre code{background:0 0;padding:0}table{border-collapse:collapse;width:100%;margin:1em 0;font-size:.9em}td,th{border:1px solid #ddd;padding:.5em .75em;text-align:left;vertical-align:top}th{background:#f7f7f7}@media (prefers-color-scheme:dark){th{background:#1a1a1a}}blockquote{border-left:4px solid #ccc;margin:1em 0;padding:.25em 1em;opacity:.85}hr{border:0;border-top:1px solid #eee;margin:2em 0}`;
1216

17+
function escapeAttr(value) {
18+
return String(value ?? '')
19+
.replace(/&/g, '&amp;')
20+
.replace(/</g, '&lt;')
21+
.replace(/>/g, '&gt;')
22+
.replace(/"/g, '&quot;')
23+
.replace(/'/g, '&#39;');
24+
}
25+
26+
function getMarkdownMeta(markdown) {
27+
const tokens = md.parse(markdown, {});
28+
const titleIndex = tokens.findIndex(token => token.type === 'heading_open');
29+
const descriptionIndex = tokens.findIndex(token => token.type === 'paragraph_open');
30+
const title = titleIndex >= 0 ? tokens[titleIndex + 1]?.content : undefined;
31+
const description = descriptionIndex >= 0 ? tokens[descriptionIndex + 1]?.content : undefined;
32+
33+
return {
34+
title: title || 'Elements context',
35+
description: description || 'NVIDIA Elements context fragment for AI and LLM tools.'
36+
};
37+
}
38+
1339
async function writeDoc(path, markdown) {
40+
const meta = getMarkdownMeta(markdown);
41+
const htmlUrl = `${path.replace('./.11ty-vite/public', BASE)}.html`;
1442
const nav = path.endsWith('context/index')
1543
? ''
1644
: `<nav class="crumbs"><a href="${BASE}/context/index.html">← All context</a></nav>`;
17-
const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><link rel="alternate" type="text/markdown" title="Markdown version" href="${path.replace('./.11ty-vite/public', BASE)}.md" /><meta name="viewport" content="width=device-width, initial-scale=1"><title>Elements</title><style>${CSS}</style></head><body>${nav}<main>${md.render(markdown).replaceAll('.md"', '.html"')}</main></body></html>`;
45+
const renderedMarkdown = md.render(markdown).replace(relativeMarkdownUrlPattern, '$1="$2.html');
46+
const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex,follow"><meta name="description" content="${escapeAttr(meta.description)}"><link rel="canonical" href="${htmlUrl}"><link rel="alternate" type="text/markdown" title="Markdown version" href="${path.replace('./.11ty-vite/public', BASE)}.md" /><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeAttr(meta.title)} | NVIDIA Elements context</title><style>${CSS}</style></head><body>${nav}<main>${renderedMarkdown}</main></body></html>`;
1847
await fsp.writeFile(`${path}.md`, markdown, 'utf-8');
1948
await fsp.writeFile(`${path}.html`, html, 'utf-8');
2049
}

projects/site/src/_11ty/plugins/sitemap-xml.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ELEMENTS_SITE_URL } from '../utils/env.js';
55
const SITE_ORIGIN = ELEMENTS_SITE_URL.replace(/\/$/, '');
66
const PATH_PREFIX = BASE_URL.replace(/\/$/, '');
77
const EXCLUDED_PREFIXES = ['/docs/changelog/', '/docs/metrics/', '/examples/', '/404'];
8+
const ROBOTS_NOINDEX = /<meta\s+[^>]*name=["']robots["'][^>]*content=["'][^"']*\bnoindex\b/i;
89

910
function isPublishable(url) {
1011
if (!url) return false;
@@ -13,13 +14,17 @@ function isPublishable(url) {
1314
return true;
1415
}
1516

17+
function isPublishableResult(result) {
18+
if (!isPublishable(result.url) || result.data?.noindex || result.data?.component?.data?.hideExamplesTab) return false;
19+
return !ROBOTS_NOINDEX.test(result.content ?? '');
20+
}
21+
1622
export function sitemapPlugin(eleventyConfig) {
1723
eleventyConfig.on('eleventy.after', async ({ results } = {}) => {
18-
const urls = [...new Set((results ?? []).map(r => r.url).filter(isPublishable))].sort();
19-
const lastmod = new Date().toISOString();
24+
const urls = [...new Set((results ?? []).filter(isPublishableResult).map(result => result.url))].sort();
2025
const entries = urls.map(url => {
2126
const loc = `${SITE_ORIGIN}${PATH_PREFIX}${url}`;
22-
return ['<url>', `<loc>${loc}</loc>`, `<lastmod>${lastmod}</lastmod>`, '</url>'].join('\n');
27+
return ['<url>', `<loc>${loc}</loc>`, '</url>'].join('\n');
2328
});
2429

2530
const xml = [

projects/site/src/docs/about/migration.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616

1717
This guide covers migrating from the internal `@nve/*` packages to the new open source `@nvidia-elements/*` packages.
1818

19+
## Agent Skill
20+
21+
Agents can leverage the migration skill by calling the skill from the Elements CLI or MCP.
22+
23+
```shell
24+
nve skills.get migration
25+
```
26+
1927
## Overview
2028

2129
The Elements Design System is now hosted and developed in a public GitHub repository. Packages are now published to the public npm registry under the `@nvidia-elements` scope. The component APIs, tag names, and theming system remain the same. The primary changes are package names, import paths, and registry configuration.

projects/site/src/docs/elements/_tabs/examples.11ty.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export const data = {
2323
alias: 'component',
2424
addAllPagesToCollections: true
2525
},
26+
eleventyComputed: {
27+
noindex: data => data.component.data.hideExamplesTab || !data.component.data.tag
28+
},
2629
// Generate URLs in the format /docs/elements/{component-name}/examples/ or /docs/code/{component-name}/examples/ or /docs/monaco/{component-name}/examples/
2730
permalink: data => {
2831
const filePath = data.component.filePathStem;
@@ -46,13 +49,16 @@ export async function render(data) {
4649
const element = elements.find(e => e.name === componentData.tag);
4750
const exampleTemplates = examples.filter(example => example.element === componentData.tag);
4851

49-
if (data.component.data.hideExamplesTab || !element) return '';
50-
5152
data.tag = componentData.tag;
5253
data.title = componentData.title;
5354
data.page.fileSlug = componentData.page.fileSlug;
5455
data.isExamplesTab = true;
5556

57+
if (data.component.data.hideExamplesTab || !element) {
58+
data.noindex = true;
59+
return '';
60+
}
61+
5662
return /* html */ `
5763
<style scoped>
5864
.canvas-editable-container {

projects/site/src/examples/index.11ty.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,25 @@
55
import { PlaygroundService } from '@internals/tools/playground';
66
import { renderGlobalsScript } from '../_11ty/layouts/common.js';
77
import { siteData } from '../index.11tydata.js';
8+
import { ELEMENTS_SITE_URL } from '../_11ty/utils/env.js';
89

910
const { BASE_URL, examples } = siteData;
11+
const SITE_ORIGIN = ELEMENTS_SITE_URL.replace(/\/$/, '');
12+
const PATH_PREFIX = BASE_URL.replace(/\/$/, '');
13+
14+
function escapeAttr(value) {
15+
return String(value ?? '')
16+
.replace(/&/g, '&amp;')
17+
.replace(/</g, '&lt;')
18+
.replace(/>/g, '&gt;')
19+
.replace(/"/g, '&quot;')
20+
.replace(/'/g, '&#39;');
21+
}
22+
23+
function getCanonicalUrl(example) {
24+
const path = example.elementName ? `/docs/elements/${example.elementName}/examples/` : '/examples/';
25+
return `${SITE_ORIGIN}${PATH_PREFIX}${path}`;
26+
}
1027

1128
export const data = {
1229
title: 'Examples',
@@ -27,8 +44,11 @@ export async function render(data) {
2744
<head>
2845
<meta charset="UTF-8">
2946
<meta name="viewport" content="width=device-width, initial-scale=1.0">
47+
<meta name="robots" content="noindex,follow">
48+
<meta name="description" content="${escapeAttr(data.example.summary || data.example.description || `${data.example.name} example for NVIDIA Elements.`)}">
49+
<link rel="canonical" href="${getCanonicalUrl(data.example)}">
3050
<base href="${BASE_URL}" />
31-
<title data-pagefind-meta="title">Example - ${data.example.permalink}</title>
51+
<title data-pagefind-meta="title">${escapeAttr(data.example.name)} example | NVIDIA Elements</title>
3252
<style>
3353
@import '/examples/index.css';
3454
</style>

projects/site/src/index.md

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
---
22
{
33
title: 'Getting Started',
4-
description: 'Get started with NVIDIA Elements: install Web Components, themes, and starter templates for building framework-agnostic UI for AI/ML factories.',
4+
description: 'Get started with NVIDIA Elements Design System: framework-agnostic Web Components, design tokens, CLI, MCP, skills, and lint tooling for AV, robotics, and AI infrastructure.',
55
layout: 'docs.11ty.js'
66
}
77
---
88

99
<div nve-layout="column gap:lg pad-top:lg">
1010
<h1 nve-text="display sm">Elements</h1>
11-
<h2 nve-text="heading">The Design Language for AI/ML Factories, Robotics, and Autonomous Vehicles</h2>
11+
<h2 nve-text="heading">The Design Language and UI Agent Harness for AI/ML Factories, Robotics, and Autonomous Vehicles</h2>
1212
</div>
13-
13+
<nve-divider></nve-divider>
14+
<section nve-layout="grid gap:xl span-items:12 @md|span-items:6 @xl|span-items:3">
15+
<div nve-layout="column gap:sm">
16+
<p nve-text="label semibold">Built for AI Infrastructure</p>
17+
<p nve-text="body sm muted">Operational UI for AI/ML workloads, autonomous vehicle tools, and robotics consoles.</p>
18+
</div>
19+
<div nve-layout="column gap:sm">
20+
<p nve-text="label semibold">Framework Agnostic</p>
21+
<p nve-text="body sm muted">Web Components run in React, Angular, Vue, Svelte, Lit, plain HTML, server-rendered templates, and mixed stacks.</p>
22+
</div>
23+
<div nve-layout="column gap:sm">
24+
<p nve-text="label semibold">Agent-Ready Tooling</p>
25+
<p nve-text="body sm muted"><a href="docs/cli/" nve-text="link no-visit">CLI</a> and <a href="docs/mcp/" nve-text="link no-visit">MCP</a> expose component APIs, tokens, examples, imports, validation, and setup to terminals and AI assistants.</p>
26+
</div>
27+
<div nve-layout="column gap:sm">
28+
<p nve-text="label semibold">Stable API Contracts</p>
29+
<p nve-text="body sm muted"><a href="docs/mcp/#skills" nve-text="link no-visit">Skills</a> and <a href="docs/lint/" nve-text="link no-visit">lint</a> guide authoring best practices, common UI patterns and automated static analysis.</p>
30+
</div>
31+
</section>
1432
<nve-divider></nve-divider>
1533

1634
```shell
@@ -19,6 +37,9 @@ curl -fsSL {{ELEMENTS_PAGES_BASE_URL}}/install.sh | bash
1937

2038
# create a new project
2139
nve project.create
40+
41+
# configure dependencies and MCP tools
42+
nve project.setup
2243
```
2344

2445
<div nve-layout="grid gap:md span-items:12 @md|span-items:4">
@@ -57,22 +78,28 @@ import '@nvidia-elements/core/button/define.js';
5778

5879
<section nve-layout="row gap:sm align:center align:wrap pad-x:md">
5980
<nve-button>
60-
<a href="./docs/integrations/installation/"><nve-icon name="gear"></nve-icon> Installation</a>
81+
<a href="./docs/integrations/installation/"><nve-icon name="gear"></nve-icon> Install</a>
82+
</nve-button>
83+
<nve-button>
84+
<a href="docs/cli/"><nve-icon name="terminal"></nve-icon> CLI</a>
85+
</nve-button>
86+
<nve-button>
87+
<a href="docs/mcp/"><nve-icon name="sparkles"></nve-icon> MCP</a>
6188
</nve-button>
6289
<nve-button>
63-
<a href="docs/mcp/"><svg width="18" height="18"><use href="#cursor-svg"></use></svg> Add to Cursor</a>
90+
<a href="docs/mcp/"><svg width="18" height="18"><use href="#cursor-svg"></use></svg> Cursor</a>
6491
</nve-button>
6592
<nve-button>
66-
<a href="docs/mcp/"><svg width="18" height="18"><use href="#codex-svg"></use></svg> Add to Codex</a>
93+
<a href="docs/mcp/"><svg width="18" height="18"><use href="#codex-svg"></use></svg> Codex</a>
6794
</nve-button>
6895
<nve-button>
69-
<a href="docs/mcp/"><svg width="18" height="18"><use href="#claude-svg"></use></svg> Add to Claude</a>
96+
<a href="docs/mcp/"><svg width="18" height="18"><use href="#claude-svg"></use></svg> Claude</a>
7097
</nve-button>
7198
<nve-button>
7299
<a href="{{ELEMENTS_REPO_BASE_URL}}" target="_blank"><nve-icon name="fork"></nve-icon> GitHub</a>
73100
</nve-button>
74101
<nve-button>
75-
<a href="https://registry.npmjs.org" target="_blank"><nve-icon name="archive" style="--color: var(--nve-sys-accent-primary-background)"></nve-icon> npm Package</a>
102+
<a href="https://www.npmjs.com/package/@nvidia-elements/core" target="_blank"><nve-icon name="archive"></nve-icon> npm</a>
76103
</nve-button>
77104
<nve-button>
78105
<a href="./docs/integrations/typescript/"><svg width="18" height="18"><use href="#typescript-svg"></use></svg> TypeScript</a>

0 commit comments

Comments
 (0)