// themes/light/[name].bootstrap.scss@use'styles/utilities' as *;
@use'themes' as *;
$theme: $bootstrap;
:host {
@include css-vars-from-theme(diff($base, $theme));
}
[!NOTE]
A brand-new component only has schemas once they are added to igniteui-theming. Until
then, declare the CSS variables directly in themes/shared/[name].common.scss and keep the
light/dark files empty rather than inventing values per theme.
4. Create the theme aggregator
themes/themes.ts is the only hand-written TypeScript file in the directory:
This generates a .css.ts next to each .scss (imported as .css.js). The generated files
are gitignored — never edit or commit them. Only files matching
*.{base,common,shared,material,bootstrap,indigo,fluent}.scss are picked up; anything else is
silently skipped.
6. Write the tests
src/components/[name]/[name].spec.ts:
import { elementUpdated, expect, fixture, html } from'@open-wc/testing';
import { defineComponents } from'#internals/definitions/defineComponents.js';
importIgc[Name]Componentfrom'./[name].js';
describe('[Name]', () => {
before(() => {
defineComponents(Igc[Name]Component);
});
it('passes the a11y audit', async () => {
const el = await fixture<Igc[Name]Component>(html`<igc-[name]></igc-[name]>`);
awaitexpect(el).shadowDom.to.be.accessible();
awaitexpect(el).to.be.accessible();
});
it('is initialized with the proper default values', async () => {
const el = await fixture<Igc[Name]Component>(html`<igc-[name]></igc-[name]>`);
expect(el.someProp).to.equal('default-value');
});
it('updates on property change', async () => {
const el = await fixture<Igc[Name]Component>(html`<igc-[name]></igc-[name]>`);
el.someProp = 'new-value';
awaitelementUpdated(el);
expect(el.someProp).to.equal('new-value');
});
});
Drive user interaction through the shared simulators (simulateClick, simulateKeyboard, …)
from #internals/testing/simulate.spec.js, and use
createFormAssociatedTestBed from #internals/testing/form-testbed.spec.js for form-associated
controls.
7. Create the Storybook story
stories/[name].stories.ts — the filename must match the tag name, and the generated block
must be fenced by // region default / // endregion:
npm run cem # custom-elements.json from the JSDoc
npm run build:meta # the `// region default` block of the story
9. Verify
npm run check # aliases, dependency rules, types
npm run lint # oxlint, lit-analyzer, oxfmt, stylelint
npm run test
Finally, add a CHANGELOG entry.
Documentation Conventions
Every JSDoc description on a public class, property, method, event, slot, CSS part or CSS
custom property is consumed verbatim by custom-elements.json, the generated story
metadata, the published API docs and the Angular / React / Blazor wrappers. Write product
documentation, not internal notes.
Never put igc- tag names in prose. Refer to components by their plain-English name — "the
carousel", "the tile manager", "toggle buttons".
// ❌ Wrong — tag names leak into the docs of every framework wrapper/**
* The `igc-carousel` presents a set of `igc-carousel-slide`s.
*
* @slot - Renders `igc-toggle-button` component.
* @csspartsvg - The igc-circular-progress SVG element.
*/// ✅ Right/**
* The carousel presents a set of slides.
*
* @slot - Renders the toggle buttons of the group.
* @csspartsvg - The circular progress SVG element.
*/
Tag names are allowed only in the @element tag, fenced @example blocks, literal
event/attribute names that contain igc- (e.g. the "igc-change-theme" window event), and
@internal/@hidden members or non-exported internals.
Describe the thing, not the attribute.@attr already says it is an attribute.
❌ Avoid
✅ Prefer
The label attribute of the control.
The label of the control.
The outlined attribute of the control.
Whether the control has an outlined appearance.
Gets/Sets the name for all child radios.
The name applied to all radio buttons in the group.
an empty value will return an empty string
an empty value returns an empty string
Booleans start with "Whether …" and describe the true state accurately — check the
implementation, don't trust the property name (hideIndicators is "Whether the carousel
should skip rendering of the indicator controls").
Use present tense; avoid "will".
Keep the description as the leading summary paragraph; don't append it to @element.
Public methods that return something get an @returns tag.
Validation Checklist
Component at src/components/[name]/[name].ts, single default export
tagName, styles and register() static members defined
Cross-cutting imports use #internals / #theming / #animations
Theming controller added in the constructor
JSDoc with @element, @slot, @csspart, @cssproperty, @event as applicable