CSS-Only Custom Elements
The built-in CSS detector finds UNdefined custom elements that are intended to work without JavaScript. It scans project .css files and emits a CEM declaration
when a custom-element selector is preceded by a JSDoc-style comment.
Documenting An Element
Put a JSDoc style comment (/** ... */) immediately before the selector:
/** * A badge styled entirely with CSS. */my-badge { /** Inner spacing. */ --badge-padding: 4px;
/** Surface color. */ --badge-bg-color: lightgray;
/** Text color. */ --badge-fg-color: black;}The generated declaration uses my-badge as both its name and tagName, and
the selector comment becomes its description.
The element comment can also use @cssprop or @cssproperty tags. These tags
are merged into the generated cssProperties collection and override matching
values discovered from the stylesheet:
/** * A documented badge. * @cssprop [--badge-padding=8px] - Override the default spacing. * @cssprop --badge-outline-color - Optional outline color. */my-badge { /** Base spacing. */ --badge-padding: 4px;}Selectors without a preceding JSDoc-style comment are ignored. A regular CSS comment is not enough:
/* Not detected. */my-undocumented-element { --color: red;}CSS Custom Properties
Custom-property declarations inside a CSS-only element must also have their own
preceding JSDoc-style comment. This prevents implementation-only variables from
being added to the public CEM API. Use @cssprop in the element comment for
documented properties that are not declared directly in the rule.
@property rules are always included when they declare a custom property. Their
syntax and initial-value are emitted as syntax and default; a preceding
JSDoc comment is emitted as the property’s description when present.
/** Corner radius. */@property --badge-radius { syntax: "<length>"; initial-value: 4px; inherits: false;}Nested variant rules can continue to override the element’s styles without changing the base metadata:
my-badge { /** Base surface color. */ --badge-bg-color: lightgray;
&[variant="danger"] { --badge-bg-color: red; }}Conservative Attribute Detection
The detector recognizes simple attribute selectors attached directly to the
custom-element selector, including nested &[...] selectors:
/** A badge with variants. */my-badge { &[variant="danger"] { --badge-bg-color: red; }}This emits an attribute named variant. Add @attr or @attribute to the
element comment to provide its description or other metadata:
/** * A badge with variants. * @attr variant - Selects the badge style. */my-badge[variant] { /** Base surface color. */ --badge-bg-color: lightgray;}Exact values in simple selectors are combined into a string-literal union in
the attribute type. For example, variant="danger" and
variant="success" produce "danger" | "success". Presence-only selectors
such as [disabled] do not produce an inferred type. An explicit type in an
@attr {Type} tag takes precedence.
Presence-only selectors are not automatically treated as booleans because CSS presence matching does not define how the component interprets the value. If an attribute is a boolean API, declare that explicitly:
/** * A dismissible alert. * @attr {boolean} dismissible - Whether the alert can be dismissed. */alert-box[dismissible] { display: block;}This emits dismissible with type.text set to boolean.
The conservative detector only handles simple attribute selectors attached to
the custom-element selector, or nested selectors beginning with &. Descendant
selectors and complex selector functions are intentionally not inferred.
File Filtering
CSS files are scanned automatically when include is omitted or empty. If
include is provided, a CSS file must match one of its patterns. exclude
patterns always take precedence.
const manifest = generateCem({ include: ["src/components/**"], exclude: ["**/*.test.css"],});