igniteui/igniteui-webcomponents · Archived

add-component-property

Add a reactive property to an existing Lit web component with proper decorators, types, tests, and documentation

First seen Jun 17, 2026

Installation

$ npx skills add igniteui/igniteui-webcomponents --skill add-component-property

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from igniteui/igniteui-webcomponents.

npx skills add igniteui/igniteui-webcomponents

Browse all from igniteui/igniteui-webcomponents

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 167
License LICENSE
Default branch master
Open issues 23
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 8,006 B
  • docs SUMMARY.md 142 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Add Component Property

Adds a reactive property to an existing component, with the documentation, tests and generated metadata that go with it.

When to Use

  • "Add an 'orientation' property to the divider component"
  • "Add a 'variant' property with multiple options"

Related Skills

  • [create-new-component](../create-new-component/) - Create a component first
  • [update-component-styles](../update-component-styles/) - Style changes driven by the property

Required Context

  • Component: which component to modify
  • Name and type: camelCase property, kebab-case attribute
  • Default value: booleans must default to false
  • Reflection: only for primitives that affect styling or accessibility
  • Purpose: the description that ships in the public API docs

Steps

1. Declare the property

Place it in the //#region Public attributes and properties section, in the shape that matches its type:

/**
 * The style variant of the component.
 * @attr variant
 * @default 'primary'
 */
@property({ reflect: true })
public variant: StyleVariant = 'primary';

/**
 * Whether user interaction with the component is disabled.
 * @attr
 * @default false
 */
@property({ type: Boolean, reflect: true })
public disabled = false;

/**
 * The number of items rendered per page.
 * @attr items-per-page
 * @default 10
 */
@property({ type: Number, attribute: 'items-per-page' })
public itemsPerPage = 10;

/** The items rendered by the component. */
@property({ attribute: false })
public items: Array<Item> = [];

Rules that are easy to get wrong:

  • Booleans must default to false. An attribute's presence equates to true, so a

true default cannot be turned off from markup. Rename the property instead (enableddisabled).

  • Never reflect objects or arrays, and give them attribute: false so Lit doesn't try to

serialize them.

  • Lit derives the attribute name automatically, but spell it out for multi-word properties and

for HTML look-alikes (readOnlyreadonly, minLengthminlength).

  • A read-only value is a getter, not a readonly @property.

2. Write the description

The JSDoc is copied verbatim into custom-elements.json, the generated Storybook metadata and the Angular / React / Blazor wrapper docs.

  • No igc- tag names in prose — "the select component", not igc-select.
  • Don't restate that it is an attribute. @attr already says so.
  • No Gets/Sets. State what the value is; add a second sentence for side effects.
  • Booleans start with "Whether …" and must describe the true state accurately — verify

against the implementation, since hide/disable names invert the sentence.

  • Present tense, not "will".
// ❌ Wrong
/**
 * The outlined attribute of the control.
 * @attr
 */

// ✅ Right
/**
 * Whether the control has an outlined appearance.
 * @attr
 */

Full reference: [create-new-component → Documentation Conventions](../create-new-component/SKILL.md#documentation-conventions).

3. React to the change

If the property only affects the template, do nothing — just use it in render(). If it has side effects or feeds derived state, use the Lit lifecycle hooks, not @watch:

// Derived state, before rendering
protected override willUpdate(changedProperties: PropertyValues<this>): void {
  if (changedProperties.has('value')) {
    this._normalized = this.value.trim();
  }
}

// Side effects that need DOM access
protected override update(changedProperties: PropertyValues<this>): void {
  if (changedProperties.has('disabled')) {
    this._internals.setARIA({ ariaDisabled: `${this.disabled}` });
  }
  super.update(changedProperties);
}

Always guard with changedProperties.has() and call super.update() when overriding update.

For a form-associated control, a property that participates in constraint validation (min, pattern, maxLength, …) must call this._validate() from its setter.

4. Add tests

it('is initialized with the proper default value', async () => {
  const el = await fixture<IgcComponentComponent>(
    html`<igc-component></igc-component>`
  );

  expect(el.propertyName).to.equal(defaultValue);
});

it('updates on property change', async () => {
  const el = await fixture<IgcComponentComponent>(
    html`<igc-component></igc-component>`
  );

  el.propertyName = newValue;
  await elementUpdated(el);

  expect(el.propertyName).to.equal(newValue);
});

it('reflects to an attribute', async () => {
  const el = await fixture<IgcComponentComponent>(
    html`<igc-component property-name=${value}></igc-component>`
  );

  expect(el.propertyName).to.equal(value);
  expect(el.getAttribute('property-name')).to.equal(value);
});

If the property changes the rendered semantics, extend the a11y audit rather than adding a separate one.

5. Regenerate the story metadata

The argTypes, args and the args interface live inside a generated // region default … // endregion block in stories/[component-name].stories.ts. Never edit it by hand:

npm run cem        # custom-elements.json from the source JSDoc
npm run build:meta # the `// region default` block of each story

If the generated description reads badly, fix the JSDoc and regenerate. If the property doesn't appear at all, the story was skipped: the filename must match the tag name (igc-date-pickerdate-picker.stories.ts) and the region fence must be present — a missing fence is a silent no-op.

Then wire the property into the story templates, which are hand-written:

export const Basic: Story = {
  render: (args) => html`
    <igc-component .propertyName=${args.propertyName}>Content</igc-component>
  `,
};

6. Verify

npm run check
npm run test

Validation Checklist

  • Property declared in the public properties region with the right decorator options
  • Booleans default to false; complex types use attribute: false
  • @attr and @default tags present; description follows the

[description rules](#2-write-the-description)

  • Lifecycle hook used for side effects, super.update() called
  • _validate() called from setters affecting constraint validation
  • Tests cover default, change and reflection
  • npm run cem && npm run build:meta run; generated story region committed
  • Story template uses the new property
  • npm run check and npm run test pass
  • CHANGELOG updated if the property is part of a feature or fix

Common Pitfalls

Symptom Cause / Fix
Attribute can't be turned off from markup Boolean defaults to true — rename so the default is false
[object Object] in the DOM Complex type without attribute: false
String 'false' behaves as true Missing { type: Boolean } in the decorator
Attribute name is propertyname Multi-word property without an explicit attribute: 'property-name'
Story control missing after adding a property npm run build:meta not run, or the story is being skipped silently
Story description reverts The generated region was hand-edited — fix the JSDoc instead

Reference Examples

  • src/components/badge/badge.ts — reflected string, boolean and union-typed properties
  • src/components/input/input.ts — validation-affecting setters calling _validate()
  • src/components/combo/combo.ts — complex, non-attribute properties