Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .agents/skills/authoring-documentation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,37 @@ You MUST review @projects/site/src/docs/internal/guidelines/documentation.md bef
## Key Principles

1. **Frontmatter Required**: All documentation files must include proper frontmatter with title and layout
2. **Eleventy Shortcodes**: Use built-in shortcodes for common patterns (code examples, callouts, etc.)
2. **Source-Owned API Documentation**: Write component guidance in source JSDoc and render it with Eleventy shortcodes so the site, CLI, and MCP share the same documentation.
3. **Clear Structure**: Use proper heading hierarchy and table of contents for long documents
4. **Code Examples**: Include working code examples with proper syntax highlighting
5. **Cross-References**: Link to related documentation and guidelines using proper paths

## Single Source of Truth for Component Documentation

Component JSDoc feeds the generated Custom Elements Manifest (CEM). The API catalog used by the CLI and Model Context Protocol (MCP) tools consumes that metadata. The documentation site renders the same descriptions through shortcodes. Example JSDoc feeds the separate example catalog used by the site, CLI, and MCP.

Guidance written only in a component's Markdown page does not automatically enter the CEM or those catalogs. A page can look complete while tools still lack the information needed to use the component correctly.

### Where Information Belongs

- Put component purpose and shared behavior in the class description. Document properties, attributes, slots, methods, and events on their source declarations or supported JSDoc tags. Include relevant defaults, constraints, accessibility behavior, units, and application responsibilities.
- Put an example's purpose, usage context, and benefits in its `@summary`. Use `@description` for additional explanation, mappings, or source references. These annotations travel with the example metadata.
- Keep component Markdown pages focused on frontmatter, headings, and `install`, `api`, and `example` shortcodes. Select the source description or property that owns the information instead of repeating its prose in Markdown.
- Keep conceptual guides and tutorials in Markdown when their content is not a component or example API contract. This convention does not require moving general documentation into code.

### Author and Verify at the Source

When adding or correcting component guidance, update the owning JSDoc first, then add or adjust the shortcode that presents it. Do not compensate for missing source annotations with a page-only explanation.

For example, render an annotated property and example with:

```liquid
{% api 'nve-joystick', 'property', 'i18n' %}
{% example '@nvidia-elements/core/joystick/joystick.examples.json' 'AccessibleFeedback' %}
```

For metadata changes, use the affected project's documented build or analyzer tasks and regenerate downstream metadata as needed. Inspect the relevant generated CEM description or member, and the API or example catalog consumed by the tools. Verify the site renders the content through its shortcodes. A successful site build alone does not prove that CLI and MCP consumers receive the guidance.

## Prose Linting (Vale)

### How Vale Runs
Expand Down
3 changes: 3 additions & 0 deletions projects/core/.visual/joystick.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions projects/core/.visual/joystick.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions projects/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,18 @@
"types": "./dist/input/define.d.ts",
"default": "./dist/input/define.js"
},
"./joystick": {
"types": "./dist/joystick/index.d.ts",
"default": "./dist/joystick/index.js"
},
"./joystick/index.js": {
"types": "./dist/joystick/index.d.ts",
"default": "./dist/joystick/index.js"
},
"./joystick/define.js": {
"types": "./dist/joystick/define.d.ts",
"default": "./dist/joystick/define.js"
},
"./logo": {
"types": "./dist/logo/index.d.ts",
"default": "./dist/logo/index.js"
Expand Down
2 changes: 2 additions & 0 deletions projects/core/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ 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/joystick/define.js';
import '@nvidia-elements/core/logo/define.js';
import '@nvidia-elements/core/menu/define.js';
import '@nvidia-elements/core/month/define.js';
Expand Down Expand Up @@ -114,6 +115,7 @@ 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/joystick';
export * from '@nvidia-elements/core/logo';
export * from '@nvidia-elements/core/menu';
export * from '@nvidia-elements/core/month';
Expand Down
1 change: 1 addition & 0 deletions projects/core/src/index.test.lighthouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ describe('lighthouse report', () => {
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/joystick/define.js';
import '@nvidia-elements/core/logo/define.js';
import '@nvidia-elements/core/menu/define.js';
import '@nvidia-elements/core/month/define.js';
Expand Down
13 changes: 13 additions & 0 deletions projects/core/src/joystick/define.ts
Original file line number Diff line number Diff line change
@@ -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/foundations/internal/define';
import { Joystick } from '@nvidia-elements/core/joystick';

define(Joystick);

declare global {
interface HTMLElementTagNameMap {
'nve-joystick': Joystick;
}
}
4 changes: 4 additions & 0 deletions projects/core/src/joystick/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

export * from './joystick.js';
145 changes: 145 additions & 0 deletions projects/core/src/joystick/joystick.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
/* SPDX-License-Identifier: Apache-2.0 */

:host {
--_size-scale: 1;
--width: calc(8.125rem * var(--_size-scale));
--height: calc(8.125rem * var(--_size-scale));
--background: var(--nve-sys-interaction-background);
--opacity: 0.6;
--border: var(--nve-ref-border-width-sm) solid var(--nve-ref-border-color);
--thumb-width: calc(var(--nve-ref-size-600) * var(--_size-scale));
--thumb-height: calc(var(--nve-ref-size-600) * var(--_size-scale));
--thumb-background: var(--nve-sys-text-muted-color);
--thumb-border: var(--nve-ref-border-width-sm) solid var(--nve-sys-layer-canvas-background);
--_axis-size: calc(3rem * var(--_size-scale));
display: inline-block;
width: var(--width);
height: var(--height);
user-select: none;
}

[internal-host] {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
border: var(--border);
border-radius: 50%;
cursor: grab;
}

.background {
position: absolute;
inset: 0;
z-index: 0;
border-radius: inherit;
background: var(--background);
pointer-events: none;
}

:host([opacity]) .background {
opacity: var(--opacity);
}

[internal-host]::before,
[internal-host]::after {
content: '';
position: absolute;
z-index: 1;
}

[internal-host]::before {
left: 50%;
top: 0;
bottom: 0;
border-left: var(--nve-ref-border-width-sm) solid var(--nve-ref-border-color);
}

[internal-host]::after {
top: 50%;
left: 0;
right: 0;
border-top: var(--nve-ref-border-width-sm) solid var(--nve-ref-border-color);
}

.travel {
position: absolute;
inset: calc(var(--thumb-height) / 2) calc(var(--thumb-width) / 2);
z-index: 2;
}

.thumb {
position: absolute;
box-sizing: border-box;
width: var(--thumb-width);
height: var(--thumb-height);
border: var(--thumb-border);
border-radius: 50%;
background: var(--thumb-background);
transform: translate(-50%, -50%);
}

:host([constraint='square']) [internal-host] {
border-radius: var(--nve-ref-border-radius-md);
}

:host([size='sm']) {
--_size-scale: 0.8;
}

:host([size='lg']) {
--_size-scale: 1.2;
}

:host([constraint='horizontal']) {
--height: var(--_axis-size);
}

:host([constraint='vertical']) {
--width: var(--_axis-size);
}

:host([constraint='horizontal']) [internal-host],
:host([constraint='vertical']) [internal-host] {
border-radius: var(--nve-ref-border-radius-md);
}

:host(:focus-visible) {
outline: none;
}

:host(:focus-visible) [internal-host] {
outline: var(--nve-ref-border-width-md) solid var(--nve-sys-accent-secondary-background);
outline-offset: var(--nve-ref-space-xs);
}

:host(:state(active)) {
--thumb-background: var(--nve-sys-accent-secondary-background);
}

:host(:state(active)) [internal-host] {
cursor: grabbing;
}

:host([disabled]) {
--background: var(--nve-sys-interaction-disabled-background);
--thumb-background: var(--nve-sys-interaction-disabled-color);
}

:host([disabled]) [internal-host],
:host([readonly]) [internal-host] {
cursor: default;
}

.announcement {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
Loading
Loading