Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 69e3ed5

Browse files
committed
chore(docs): update agent documentation and component creation guidelines
- Revised descriptions in AGENTS.md for clarity and consistency. - Updated component creation guidelines to reflect the use of ButtonFormControlMixin instead of BaseButton. - Enhanced metadata handling in various files, including eslint configurations and internal documentation. - Added tests for new eslint rules regarding primitive properties and inheritance. Signed-off-by: Cory Rylan <[email protected]>
1 parent 05c73b9 commit 69e3ed5

22 files changed

Lines changed: 634 additions & 103 deletions

File tree

.agents/skills/component-creation/SKILL.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: component-creation
3-
description: Guide for creating new Elements components with all required files, base classes, metadata patterns, and test boilerplate. Use this skill whenever the user wants to create, scaffold, or set up a new component from scratch, needs to understand the required 10-file structure, asks about base classes (LitElement vs BaseButton), define.ts vs index.ts patterns, static metadata, component registration, or sub-component parent relationships. Also trigger when the user mentions creating test boilerplate for all 5 test types.
3+
description: Guide for creating new Elements components with all required files, base classes, metadata patterns, and test boilerplate. Use this skill whenever the user wants to create, scaffold, or set up a new component from scratch, needs to understand the required 10-file structure, asks about base classes and mixins (LitElement vs ButtonFormControlMixin), define.ts vs index.ts patterns, static metadata, component registration, or sub-component parent relationships. Also trigger when the user mentions creating test boilerplate for all 5 test types.
44
---
55

66
# Component Creation
@@ -12,7 +12,7 @@ You MUST review the component creation guideline before creating or modifying co
1212
- Creating a new component from scratch
1313
- Understanding the required file structure for components
1414
- Setting up test boilerplate for all 5 test types
15-
- Choosing the appropriate base class (BaseButton vs LitElement)
15+
- Choosing the appropriate base class or mixin (ButtonFormControlMixin vs LitElement)
1616
- Understanding component metadata and registration patterns
1717
- Setting up JSDoc comments for components
1818

@@ -22,8 +22,8 @@ Read the [component creation guide](projects/site/src/docs/internal/guidelines/c
2222

2323
## Workflow
2424

25-
1. **Gather requirements**:confirm the component name (kebab-case), purpose, and which base class to use (`LitElement` for most components, `BaseButton` for button-like interactive components).
26-
2. **Study a reference**:read the closest reference component listed below to understand the pattern in practice.
25+
1. **Gather requirements**: confirm the component name (kebab-case), purpose, and whether to use `LitElement` directly or apply `ButtonFormControlMixin` for button-like interactive components.
26+
2. **Study a reference**: read the closest reference component listed below to understand the pattern in practice.
2727
3. **Create the 10 required files** in `projects/core/src/<component-name>/`:
2828
- `component-name.ts`:component class with metadata, JSDoc, and render method
2929
- `component-name.css`:styles using CSS custom properties
@@ -35,10 +35,10 @@ Read the [component creation guide](projects/site/src/docs/internal/guidelines/c
3535
- `component-name.test.lighthouse.ts`:lighthouse tests
3636
- `define.ts`:registration using `define()` helper with `HTMLElementTagNameMap`
3737
- `index.ts`:side-effect-free export
38-
4. **Update bundle**:add `import '@nvidia-elements/core/<component-name>/define.js'` to `projects/core/src/bundle.ts` in alphabetical order so the component is registered in the bundle.
39-
5. **Verify**:confirm all files follow the templates in the component creation guide, run `pnpm run lint` and `pnpm run test` from the elements project.
38+
4. **Update bundle**: add `import '@nvidia-elements/core/<component-name>/define.js'` to `projects/core/src/bundle.ts` in alphabetical order so the bundle registers the component.
39+
5. **Verify**: confirm all files follow the templates in the component creation guide, run `pnpm run lint` and `pnpm run test` from the elements project.
4040

41-
## Additional References
41+
## References
4242

4343
- [Component Creation Guide](/projects/site/src/docs/internal/guidelines/component-creation.md) - Complete component creation workflow
4444
- [TypeScript Guidelines](/projects/site/src/docs/internal/guidelines/typescript.md) - Type safety patterns
@@ -49,6 +49,6 @@ Read the [component creation guide](projects/site/src/docs/internal/guidelines/c
4949

5050
### Reference Components
5151

52-
- `/projects/core/src/button/` - BaseButton pattern example
52+
- `/projects/core/src/button/` - ButtonFormControlMixin pattern example
5353
- `/projects/core/src/badge/` - LitElement pattern example
5454
- `/projects/core/src/card/` - Composition with slots example

AGENTS.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,21 @@ pnpm run lint:fix
111111

112112
### Monorepo Structure
113113

114-
The repository is organized as a top-level workspace with individual project directories:
114+
The repository contains a top-level workspace with individual project directories:
115115

116116
- `/projects/core` - Core Elements Web Components library (Lit-based)
117+
- `/projects/forms` - Form control utilities, mixins, and schema validation
117118
- `/projects/themes` - Theme tokens and CSS custom properties
118119
- `/projects/styles` - CSS utilities for layout and typography
119-
- `/projects/starters` - Starter templates for various frameworks (React, Angular, Vue, Svelte, etc.)
120-
- `/projects/labs` - Experimental packages (forms, CLI, markdown, code, brand, etc.)
120+
- `/projects/starters` - Starter templates for supported frameworks, including React, Angular, Vue, and Svelte
121+
- `/projects/cli` - Command-line tooling for Elements development and project scaffolding
122+
- `/projects/create` - `npm create @nvidia-elements` wrapper around CLI project creation
123+
- `/projects/code` - Code authoring components, including syntax-highlighted code blocks
124+
- `/projects/markdown` - Markdown components and utilities
125+
- `/projects/media` - Media playback UI components
126+
- `/projects/lint` - Elements lint configurations and custom rules
121127
- `/projects/monaco` - Monaco editor integration
128+
- `/projects/pages` - GitHub Pages deployment project
122129
- `/projects/site` - Documentation site (11ty)
123130
- `/projects/internals` - Internal tooling (vite configs, eslint, patterns, metadata)
124131

@@ -128,7 +135,7 @@ Each component in `/projects/core/src/` follows this structure:
128135

129136
```
130137
component-name/
131-
├── component-name.ts # Main component class (extends BaseButton, BaseElement, etc.)
138+
├── component-name.ts # Main component class (extends LitElement or applies a forms mixin)
132139
├── component-name.css # Component styles
133140
├── component-name.examples.ts # Example templates for documentation
134141
├── component-name.test.ts # Unit tests
@@ -140,9 +147,9 @@ component-name/
140147
└── define.ts # Registers component to customElementsRegistry
141148
```
142149

143-
Components typically extend Lit's `LitElement` directly. Shared behavior is provided via base classes (e.g., `BaseButton`) and reactive controllers (keynav, state management, i18n) from `@nvidia-elements/core/internal`. Components use Lit decorators for properties, CSS custom properties for theming, and follow ARIA Authoring Practices Guide patterns.
150+
Components typically extend Lit's `LitElement` directly. Import forms mixins from `@nvidia-elements/forms/mixins`; reactive controllers and utilities remain under `@nvidia-elements/core/internal` for shared behavior such as keynav, state management, and i18n. Components use Lit decorators for properties, CSS custom properties for theming, and follow ARIA Authoring Practices Guide patterns.
144151

145-
When adding a new component, also add its `define.js` import and `export *` to `projects/elements/src/bundle.ts` (alphabetical order). This file is the entry point for the CDN bundle. A `lint:bundle` script validates completeness in CI.
152+
When adding a new core component, also add its `define.js` import and `export *` to `projects/core/src/bundle.ts` (alphabetical order). This file is the entry point for the core CDN bundle. The `no-missing-bundle-registration` lint rule validates completeness in CI.
146153

147154
### Component Definition
148155

@@ -181,18 +188,18 @@ declare global {
181188
- **Vite** - Build tool for compiling TypeScript and bundling
182189
- **Semantic Release** - Automated versioning and publishing based on conventional commits
183190

184-
Dependencies between projects are defined in Wireit configurations in each `package.json`. The build system intelligently rebuilds only what changed.
191+
Wireit configurations in each `package.json` define dependencies between projects. The build system intelligently rebuilds only what changed.
185192

186193
### Release Process
187194

188195
Releases are fully automated:
189196

190197
1. Commits follow conventional commit format: `type(scope): message`
191198
2. Types: `fix` (patch), `feat` (minor), `chore` (no release)
192-
3. Scopes map to projects: `elements`, `themes`, `labs-forms`, etc.
199+
3. Scopes map to project names: `core`, `themes`, `forms`, `cli`, `code`, etc.
193200
4. Semantic Release analyzes commits and publishes packages
194201
5. Changelogs are auto-generated from commit messages
195-
6. Multiple packages can release in a single merge with dependency ordering
202+
6. More than one package can release in a single merge with dependency ordering
196203

197204
Releases happen automatically after CI passes on merge to `main`. No manual version bumping.
198205

@@ -216,7 +223,7 @@ git commit -m "feat(themes): add dark mode color tokens"
216223
git commit -m "chore(docs): update component examples"
217224
```
218225

219-
**Important:** The subject line (first line after `type(scope):`) must be entirely lowercase. Avoid starting with proper nouns or using uppercase letters (e.g., use "add feature" not "Add feature", "update api" not "Update API").
226+
**Important:** the subject line (first line after `type(scope):`) must be entirely lowercase. Avoid starting with proper nouns or using uppercase letters. For example, use "add feature," not "Add feature," and "update api," not "Update API."
220227

221228
**Commit types:**
222229

@@ -227,11 +234,19 @@ git commit -m "chore(docs): update component examples"
227234
**Common scopes:**
228235

229236
- `docs` - 11ty docs site and landing page (`/projects/site`)
230-
- `elements` - Core Elements library (`/projects/core`)
237+
- `core` - Core Elements library (`/projects/core`)
231238
- `themes` - Theme tokens (`/projects/themes`)
232239
- `styles` - CSS utilities (`/projects/styles`)
233240
- `starters` - Starter templates (`/projects/starters`)
234-
- `labs-*` - Lab projects (e.g., `labs-cli`, `labs-forms`, `labs-code`)
241+
- `cli` - Command-line tooling (`/projects/cli`)
242+
- `code` - Code authoring components (`/projects/code`)
243+
- `create` - `npm create @nvidia-elements` wrapper (`/projects/create`)
244+
- `forms` - Form control utilities (`/projects/forms`)
245+
- `lint` - Lint configurations and custom rules (`/projects/lint`)
246+
- `markdown` - Markdown components and utilities (`/projects/markdown`)
247+
- `media` - Media playback UI components (`/projects/media`)
248+
- `monaco` - Monaco editor integration (`/projects/monaco`)
249+
- `pages` - GitHub Pages deployment project (`/projects/pages`)
235250
- `ci` - Build/CI tooling (`/projects/internals`)
236251

237252
### Prose Linting (Vale)

NOTICE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ This product includes the following bundled third-party software:
3535
- highlight.js v11.11.1 [BSD-3-Clause] (used by: @nvidia-elements/code)
3636
Copyright: Josh Goebel <[email protected]>
3737

38-
- lit v3.3.2 [BSD-3-Clause] (used by: @nvidia-elements/code, @nvidia-elements/core, @nvidia-elements/forms, @nvidia-elements/markdown, @nvidia-elements/media, @nvidia-elements/monaco)
38+
- lit v3.3.2 [BSD-3-Clause] (used by: @nvidia-elements/code, @nvidia-elements/core, @nvidia-elements/markdown, @nvidia-elements/media, @nvidia-elements/monaco)
3939
Copyright: Google LLC
4040

4141
- lit-html v3.3.2 [BSD-3-Clause] (used by: @nvidia-elements/code, @nvidia-elements/core, @nvidia-elements/monaco)

pnpm-lock.yaml

Lines changed: 12 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

projects/internals/eslint/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Applied to `src/**/*.ts`, `src/**/*.tsx`, test files, and `*.examples.ts`.
5656
- **`require-composed-events`**. Any `CustomEvent` dispatched with `bubbles: true` must also set `composed: true`. Shadow DOM boundary crossing stays opt-in.
5757
- **`reserved-property-names`**. Disallows `@property` / `@state` / `@event` names that collide with `HTMLElement` prototype keys, ARIA attributes, or native event handlers (case-insensitive).
5858
- **`reserved-event-names`**. Disallows custom event names that collide with native HTMLElement events (`click`, `change`, `load`, …).
59-
- **`primitive-property`**. Public `@property` members must use primitive types. The rule rejects `Array` and `Object` except for the `data`, `i18n`, and `stepSizes` conventions.
59+
- **`primitive-property`**. Public `@property` members must use primitive types. The rule rejects `Array` and `Object` except for the `commandForElement`, `data`, `i18n`, and `stepSizes` conventions.
6060
- **`stateless-property`**. The rule disallows `this.<publicProperty> = ...` assignments so public API stays read-only inside the component. `hidden` and `value` can still mutate for standard-element parity.
6161

6262
**Lifecycle resource cleanup**

projects/internals/eslint/src/configs/typescript.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ const config = {
110110
'local-typescript/no-dead-code': ['warn'], // todo, this should be migrated to the internal playground template config
111111
'local-typescript/no-deep-class-inheritance': [
112112
'error',
113-
{ maxDepth: 2, allowedRoots: ['HTMLElement', 'LitElement', 'FormControlMixin', 'BaseButton'] }
113+
{ maxDepth: 2, allowedRoots: ['HTMLElement', 'LitElement', 'FormControlMixin', 'ButtonFormControlMixin'] }
114114
],
115115
'local-typescript/require-listener-cleanup': 'error',
116116
'local-typescript/require-observer-cleanup': 'error',
@@ -154,6 +154,9 @@ const config = {
154154
slot: { name: true },
155155
cssprop: { name: true },
156156
csspart: { name: true },
157+
attr: { name: true },
158+
attribute: { name: true },
159+
reflect: { name: false },
157160
command: { name: true },
158161
entrypoint: { name: true },
159162
tags: { name: true },

projects/internals/eslint/src/local/no-deep-class-inheritance.test.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ test('valid: allows direct and depth-2 class inheritance', () => {
4242
code: `
4343
declare class LitElement {}
4444
45-
class BaseButton extends LitElement {}
46-
class SortButton extends BaseButton {}
45+
class ButtonBase extends LitElement {}
46+
class SortButton extends ButtonBase {}
4747
`
4848
},
4949
{
@@ -67,8 +67,8 @@ test('valid: supports custom maxDepth and allowedRoots options', () => {
6767
code: `
6868
declare class LitElement {}
6969
70-
class BaseButton extends LitElement {}
71-
class SortButton extends BaseButton {}
70+
class ButtonBase extends LitElement {}
71+
class SortButton extends ButtonBase {}
7272
class ToolbarSortButton extends SortButton {}
7373
`
7474
},
@@ -95,8 +95,8 @@ test('invalid: reports classes deeper than maxDepth', () => {
9595
code: `
9696
declare class LitElement {}
9797
98-
class BaseButton extends LitElement {}
99-
class SortButton extends BaseButton {}
98+
class ButtonBase extends LitElement {}
99+
class SortButton extends ButtonBase {}
100100
class ToolbarSortButton extends SortButton {}
101101
`,
102102
errors: [
@@ -106,7 +106,7 @@ test('invalid: reports classes deeper than maxDepth', () => {
106106
className: 'ToolbarSortButton',
107107
depth: '3',
108108
maxDepth: '2',
109-
chain: 'ToolbarSortButton -> SortButton -> BaseButton -> LitElement'
109+
chain: 'ToolbarSortButton -> SortButton -> ButtonBase -> LitElement'
110110
}
111111
}
112112
]

projects/internals/eslint/src/local/no-single-consumer-internal-base.test.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,23 +103,23 @@ test('valid: counts default import subclasses for default-exported bases', () =>
103103
});
104104

105105
test('valid: internal base consumed through package internal barrel', () => {
106-
const filename = createFile('src/internal/base/button.ts', 'export class BaseButton {}');
106+
const filename = createFile('src/internal/base/widget.ts', 'export class WidgetBase {}');
107107
createFile('package.json', JSON.stringify({ name: '@nvidia-elements/core' }));
108-
createFile('src/internal/index.ts', "export * from './base/button.js';");
108+
createFile('src/internal/index.ts', "export * from './base/widget.js';");
109109
createFile(
110110
'src/button/button.ts',
111-
"import { BaseButton } from '@nvidia-elements/core/internal';\nexport class Button extends BaseButton {}"
111+
"import { WidgetBase } from '@nvidia-elements/core/internal';\nexport class Button extends WidgetBase {}"
112112
);
113113
createFile(
114114
'src/icon-button/icon-button.ts',
115-
"import { BaseButton } from '@nvidia-elements/core/internal';\nexport class IconButton extends BaseButton {}"
115+
"import { WidgetBase } from '@nvidia-elements/core/internal';\nexport class IconButton extends WidgetBase {}"
116116
);
117117

118118
tester.run('no-single-consumer-internal-base', noSingleConsumerInternalBase, {
119119
valid: [
120120
{
121121
filename,
122-
code: 'export class BaseButton {}',
122+
code: 'export class WidgetBase {}',
123123
options: [{ rootDir }]
124124
}
125125
],
@@ -128,23 +128,23 @@ test('valid: internal base consumed through package internal barrel', () => {
128128
});
129129

130130
test('valid: infers package root from filename', () => {
131-
const filename = createFile('src/internal/base/button.ts', 'export class BaseButton {}');
131+
const filename = createFile('src/internal/base/widget.ts', 'export class WidgetBase {}');
132132
createFile('package.json', JSON.stringify({ name: '@nvidia-elements/core' }));
133-
createFile('src/internal/index.ts', "export * from './base/button.js';");
133+
createFile('src/internal/index.ts', "export * from './base/widget.js';");
134134
createFile(
135135
'src/button/button.ts',
136-
"import { BaseButton } from '@nvidia-elements/core/internal';\nexport class Button extends BaseButton {}"
136+
"import { WidgetBase } from '@nvidia-elements/core/internal';\nexport class Button extends WidgetBase {}"
137137
);
138138
createFile(
139139
'src/tag/tag.ts',
140-
"import { BaseButton } from '@nvidia-elements/core/internal';\nexport class Tag extends BaseButton {}"
140+
"import { WidgetBase } from '@nvidia-elements/core/internal';\nexport class Tag extends WidgetBase {}"
141141
);
142142

143143
tester.run('no-single-consumer-internal-base', noSingleConsumerInternalBase, {
144144
valid: [
145145
{
146146
filename,
147-
code: 'export class BaseButton {}'
147+
code: 'export class WidgetBase {}'
148148
}
149149
],
150150
invalid: []

projects/internals/eslint/src/local/primitive-property.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const properties = new Map();
2+
const propertyExceptions = new Set(['commandForElement', 'data', 'i18n', 'stepSizes']);
23

34
export default {
45
meta: {
@@ -20,7 +21,7 @@ export default {
2021
properties.forEach(node => {
2122
const props = node.expression.arguments.flatMap(args => args.properties);
2223
const propName = node.parent.key.name;
23-
const noExceptions = !['data', 'i18n', 'stepSizes'].find(i => i === propName);
24+
const noExceptions = !propertyExceptions.has(propName);
2425

2526
props.forEach(prop => {
2627
const propType = prop.value.name;

0 commit comments

Comments
 (0)