import { css, html, LitElement } from 'lit'; import { customElement, property } from 'lit/decorators.js'; /** * A counter button — small on purpose, but it demonstrates the four things * almost every Lit component needs: a reactive property, scoped styles, a * custom event, and a slot. * * @fires count-changed - Dispatched when the count changes. `detail.count` * carries the new value. * @slot - Label rendered inside the button. * @csspart button - The underlying button element, styleable from outside. */ @customElement('counter-button') export class CounterButton extends LitElement { static override styles = css` :host { display: inline-block; } button { /* "inherit" lets the host page's font flow in past the shadow boundary. */ font: inherit; display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; border: 1px solid var(--app-color-border, #d4d4d8); border-radius: var(--app-radius, 0.5rem); background: var(--app-color-surface, #fff); color: var(--app-color-text, #18181b); cursor: pointer; transition: border-color 120ms ease, transform 120ms ease; } button:hover { border-color: var(--app-color-accent, #3b5bdb); } button:active { transform: translateY(1px); } /* Only show the focus ring for keyboard users. */ button:focus-visible { outline: 2px solid var(--app-color-accent, #3b5bdb); outline-offset: 2px; } .count { font-family: var(--app-font-mono, monospace); font-variant-numeric: tabular-nums; color: var(--app-color-accent, #3b5bdb); } `; /** * Current count. Declared with `@property` (not `@state`) so it is part of * the public API and can be set from markup as `count="3"`. */ @property({ type: Number, reflect: true }) count = 0; /** How much each click adds. */ @property({ type: Number, attribute: 'step-by' }) stepBy = 1; override render() { return html` `; } // Private class field syntax keeps the method off the public API surface. #increment = (): void => { this.count += this.stepBy; // `composed: true` is required for the event to escape the shadow root. this.dispatchEvent( new CustomEvent<{ count: number }>('count-changed', { detail: { count: this.count }, bubbles: true, composed: true, }), ); }; } declare global { interface HTMLElementTagNameMap { 'counter-button': CounterButton; } }