Lit Web Component Approach

The <neem-button> is a standards-based custom element built with Lit 3. It uses Shadow DOM with CSS custom properties adopted from the global token sheet, so themes and dark mode work automatically.

Primary Button

Maps to Kotlin PrimaryButton. Full-width, 64dp height.

Secondary Button

Maps to Kotlin SecondaryButton. 2dp outlined border.

With Icons

Leading and trailing icon support via leadingIcon / trailingIcon properties.

Disabled State

Kotlin: enabled = falsesurface.disabledContainer.

Usage — HTML

Drop <neem-button> anywhere in your HTML after importing the module.

<!-- 1. Import the component once -->
<script type="module" src="/src/neem-button.ts"></script>

<!-- 2. Use it like native HTML -->
<neem-button
  label="Confirm Payment"
  variant="primary"
></neem-button>

<!-- Leading icon -->
<neem-button
  label="Secure Transaction"
  variant="primary"
  leadingIcon="<svg>...</svg>"
></neem-button>

<!-- Loading state -->
<neem-button label="Saving..." variant="primary" loading></neem-button>

<!-- Disabled state -->
<neem-button label="Not Allowed" variant="secondary" disabled></neem-button>

Source — src/neem-button.ts (Lit)

The key excerpt showing how all values come from tokens, not hardcoded numbers.

@customElement('neem-button')
export class NeemButton extends LitElement {
  @property() variant: 'primary' | 'secondary' = 'primary';
  @property() label = '';
  @property({ type: Boolean, reflect: true }) disabled = false;
  @property({ type: Boolean, reflect: true }) loading  = false;

  static styles = css`
    button {
      height:        var(--neem-spacing-extra-extra-large); /* 64px */
      border-radius: var(--neem-shapes-large);              /* 16px */
      font-size:     var(--neem-typography-label-large-size); /* 16px */
      transition:    all var(--neem-motion-duration-short) var(--neem-motion-easing-standard);
    }
    button.secondary {
      border: var(--neem-border-width-bold) solid var(--neem-border-secondary); /* 2px */
    }
    button:disabled {
      background: var(--neem-surface-disabled-container);
      color:      var(--neem-text-disabled);
    }
  \`;

  render() {
    return html\`
      <button class=\${this.variant} ?disabled=\${this.disabled || this.loading}>
        <div class="content \${this.loading ? 'hidden' : ''}">
          <slot name="leading-icon"></slot>
          <span>\${this.label}</span>
          <slot name="trailing-icon"></slot>
        </div>
        <!-- DottedLoadingIndicator -->
        <div class="dots">
          <div class="dot"></div>
          <div class="dot"></div>
          <div class="dot"></div>
        </div>
      </button>
    \`;
  }
}