Documenting Components
@slot, @csspart, @cssprop, @fires, @attr) are an alternative, and remain the preferred choice for @fires and @attr. See examples below.
Use JSDoc comments to document your custom elements for the manifest . The manifest powers LSP features like autocomplete and validation, enables AI assistants to understand your components, and drives the dev server’s interactive controls .
JSDoc Tags
Use these tags in your element class and member JSDoc comments. See the generate command reference for the complete list.
Basic Example
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
/**
* Displays a personalized greeting message
*
* @summary A simple greeting component
* @slot - Default slot for custom content
* @csspart greeting - The greeting text container
* @cssprop --greeting-color - Text color (default: currentColor)
*/
@customElement('hello-world')
export class HelloWorld extends LitElement {
/**
* The name to greet
*/
@property() name = 'World';
render() {
return html`
<div id="greeting" part="greeting">
Hello, ${this.name}!
<slot></slot>
</div>
`;
}
}Specifying Tag Names
When the tag name can’t be detected automatically, use @customElement, @element, or @tagName:
/**
* A vanilla custom element
*
* @customElement vanilla-element
*/
class MyElement extends HTMLElement {
static is = 'vanilla-element';
static {
customElements.define(this.is, this);
}
}All three tags are aliases and work identically:
@customElement vanilla-element(recommended)@element vanilla-element@tagName vanilla-element
The generator automatically detects tag names from the @customElement
decorator and customElements.define('tag-name', Class) calls with static
strings. However, when using dynamic patterns like
customElements.define(this.is, this) where the tag name is stored in a
variable, you must use one of the JSDoc tags above to specify the tag name
explicitly.
Documenting Slots and Parts
cem automatically detects <slot> elements and part attributes in your
template. You can add descriptions using JSDoc tags or inline HTML comments.
@slot and @csspart tags for documenting slots and parts. HTML comments stay co-located with the markup they describe, making them easier to maintain.
Plain Comments
A plain HTML comment immediately before an element is the simplest way to add a description. This works for both slots and parts:
<!-- Button label content. SHOULD contain text. -->
<slot></slot>
<!-- The native button element -->
<button part="button">Single-Key YAML
When you need a short summary label (shown in tooling previews) rather than a
full description, use a single-key YAML comment:
<!-- summary: Button label content -->
<slot></slot>
<!-- summary: The native button element -->
<button part="button">YAML Metadata
When you need separate summary and description fields, or want to mark
something as deprecated, use multi-key YAML syntax inside the comment:
<!--
summary: The main slot for content
description: |
This slot displays user-provided content.
Supports multiline **markdown**.
deprecated: true
-->
<slot></slot>Combined Slot and Part
When the same element has both a slot and part attribute and you want
separate documentation for each, use nested slot: and part: keys.
A scalar string is shorthand for description, so you can mix scalar and
object forms when one side only needs a description and the other needs
full metadata:
<!-- slot:
summary: The `info` slot
part:
summary: The `info-part` part -->
<slot name="info" part="info-part"></slot>
<!-- slot: The info slot
part:
summary: Short label
description: Longer description of the part -->
<slot name="info" part="info-part"></slot>
<!-- part: The overlay container
slot:
summary: Overlay
description: Content shown in the overlay -->
<slot name="overlay" part="overlay"></slot>`code` in lit-html templates, escape the backticks in the comment.
Documenting CSS Custom Properties
@cssprop tags on the class. CSS comments keep the documentation co-located with the property definitions.
Document CSS variables using JSDoc-style comments in your CSS:
:host {
/**
* A property defined on the host
* @summary The host's custom property
*/
--host-property: red;
color:
/**
* Custom color for use in this element
* @summary color
* @deprecated Use the `color` property instead
*/
var(--custom-color);
border:
1px solid
/** Border color of the element */
var(--border-color);
}Position comments correctly when both LHS and RHS contain CSS custom properties:
/** Comment for --a */
color: var(--a);
/** Comment for --b */
--b: blue;
/** Comment for --c */
--c:
/** Comment for --d */
var(--d);Design Token Integration
Use the --design-tokens flag to integrate
DTCG-format
design tokens:
cem generate --design-tokens npm:@my-ds/tokens/tokens.json --design-tokens-prefix my-dsnpm: and jsr: specifiers resolve from node_modules first. If the package
isn’t installed locally, cem fetches it from
esm.sh
automatically.
The prefix should not include leading dashes — use my-ds, not --my-ds.
When both user comments and design token descriptions exist for the same property, both are included (user description first, then design token description).
Documenting Demos
JSDoc @demo Tag
Link to demos directly from your element class:
/**
* @demo https://example.com/my-element-plain/
* @demo https://example.com/my-element-fancy/ - A fancier demo
*/
@customElement('my-element')
class MyElement extends LitElement { }Automatic Demo Discovery
Configure automatic discovery in .config/cem.yaml:
sourceControlRootUrl: "https://github.com/your/repo/tree/main/"
generate:
demoDiscovery:
fileGlob: "src/**/demos/*.html"
urlPattern: "/src/:tag/demos/:demo.html"
urlTemplate: "https://example.com/elements/{{.tag | alias}}/{{.demo | slug}}/"Template functions:
alias- Apply element alias mappingslug- Convert to URL-friendly formatlower- Convert to lowercaseupper- Convert to uppercase
See Working with Demos for organization strategies.
Code Examples
Use the @example tag for code examples:
/**
* @example Basic usage
* ```html
* <my-element></my-element>
* ```
*/
@customElement('my-element')
class MyElement extends LitElement { }With explicit caption:
/**
* @example
* <caption>Advanced usage with properties</caption>
* ```html
* <my-element color="primary" size="large"></my-element>
* ```
*/Multiple examples:
/**
* A flexible element
* @example Simple case
* ```html
* <my-element></my-element>
* ```
* @example With attributes
* ```html
* <my-element foo="bar"></my-element>
* ```
*/Examples with captions are wrapped in <figure>/<figcaption> elements in the generated manifest.
Documenting Methods
Document public methods using @param and @returns tags:
/**
* Updates the element's theme
*
* @summary Apply a new theme
* @param {string} themeName - Name of the theme to apply
* @param {boolean} [persist=false] - Whether to save theme preference
* @returns {boolean} True if theme was applied successfully
* @example
* ```typescript
* element.setTheme('dark', true);
* ```
*/
setTheme(themeName: string, persist = false): boolean {
// implementation
}Parameter syntax:
- Required:
@param {type} name - description - Optional:
@param {type} [name] - description - With default:
@param {type} [name=default] - description
Monorepo Setup
For npm or yarn workspaces, create a .config/cem.yaml file in each package:
Root package.json:
{
"scripts": {
"generate": "npm run generate --workspaces"
},
"workspaces": ["./core", "./elements"]
}core/.config/cem.yaml:
generate:
files:
- './**/*.ts'core/package.json:
{
"scripts": {
"generate": "cem generate"
}
}Repeat for each workspace package.
See Also
- Development Workflow - When to regenerate the manifest
- Generate Command - Complete JSDoc tag reference and command options
- Working with Demos - Demo organization strategies
- Effective Writing for AI - AI-friendly documentation patterns