npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xaendar/core

v0.5.6

Published

A library containing core utils such as webcomponent base classes and theming support

Downloads

6,297

Readme

@xaendar/core

Core primitives for building Web Components with the Xaendar framework — including base classes, decorators, and reactive input signals.

npm version license


Installation

npm install @xaendar/core

Peer requirements — This package targets the browser and relies on the native Web Components APIs (customElements, ShadowRoot, CustomEvent) and the TC39 Signals proposal (Signal.State).


Overview

@xaendar/core gives you everything you need to author reactive, encapsulated Web Components:

| Primitive | Description | |-----------|-------------| | BaseWebComponent | Base class for every component — attaches a Shadow DOM and wires up lifecycle hooks | | @WebComponent | Class decorator that registers the element with the browser and binds observed attributes | | @Property | Accessor decorator that exposes a reactive InputSignal as an HTML attribute | | @Event | Accessor decorator that creates a typed CustomEvent emitter | | InputSignal | A Signal.State specialised for attribute-driven input, with optional value transformation |

Granular, signal-driven rendering — the view is never re-rendered as a whole. Every DOM node (element, attribute, or text node) is independently subscribed to the signal(s) it depends on. When a signal value changes, only the exact nodes that read that signal are updated — nothing else is touched.


Usage

1. Define a component

import { BaseWebComponent, WebComponent, Property, Event } from '@xaendar/core';
import type { InputSignal, Output } from '@xaendar/core';

@WebComponent({
  selector: 'my-button',
  templateUrl: './my-button.template.html',
  styleUrl: './my-button.styles.css',   // optional
})
class MyButton extends BaseWebComponent {

  /** Reactive label — set via HTML attribute or programmatically */
  @Property('Click me')
  public accessor label!: InputSignal<string>;

  /** Disabled state, transformed from the string attribute to a boolean */
  @Property(false, { 
    transform: (v: string) => v !== 'false' 
  })
  public accessor disabled!: InputSignal<boolean, string>;

  /** Emits when the button is clicked */
  @Event({ bubbles: true, composed: true })
  public accessor clicked!: Output<void>;
}
<!-- Use it anywhere in your HTML -->
<my-button label="Submit"></my-button>

2. BaseWebComponent

All Xaendar components must extend BaseWebComponent. It:

  • Attaches a ShadowRoot in open mode on construction.
  • Implements attributeChangedCallback to propagate attribute changes into the corresponding InputSignal.
  • Implements connectedCallback / disconnectedCallback to manage the component lifecycle.
import { BaseWebComponent } from '@xaendar/core';

class MyComponent extends BaseWebComponent {
  // your component logic
}

3. @WebComponent(options)

Registers the class as a Custom Element.

| Option | Type | Required | Description | |--------|------|----------|-------------| | selector | string \| string[] | ✅ | The custom element tag name(s) | | templateUrl | string | ✅ | Path to the HTML template | | styleUrl | string | — | Path to the component stylesheet |

@WebComponent({ 
  selector: ['x-card', 'xaendar-card'], 
  templateUrl: './card.xd.component.html' 
})
class Card extends BaseWebComponent { }

4. @Property(initialValue?, options?)

Marks an accessor field as a reactive attribute-bound property.

  • Registers the property key in the element's observedAttributes list.
  • Wraps the value in an InputSignal — read it like a signal via .get().
  • Supports an optional transform function to coerce the raw attribute string into a typed value.
// Optional property with a default value
@Property('hello')
accessor title: InputSignal<string>;

// Required-style with transform (string → number)
@Property(0, { transform: Number })
accessor count: InputSignal<number, string>;

Reading the value inside the component:

const current = this.count.get(); // number

5. @Event(options?)

Creates a typed CustomEvent emitter bound to the property name.

@Event({ 
  bubbles: true, 
  composed: true 
})
accessor valueChange: Output<string>;

// Emit from inside the component
this.valueChange.emit('new value');

// Override options per-emit
this.valueChange.emit('new value', { bubbles: false });

Listen from outside in pure HTML:

<my-input id="inp"></my-input>
<script>
  document.getElementById('inp').addEventListener('valueChange', e => {
    console.log(e.detail); // 'new value'
  });
</script>

6. InputSignal

A Signal.State extended with attribute-pipeline support. You rarely construct it directly — @Property handles that — but the type is exported for annotations.

import type { InputSignal } from '@xaendar/core';

// Type-only annotation
declare accessor name: InputSignal<string>;

// Reading the current value (Signal API)
this.name.get();

Granular DOM updates

Xaendar does not use a virtual DOM or component-level re-render cycles. The compiler analyses the template at build time and creates a direct subscription between each signal and every DOM node that consumes it:

  • Text nodes — only the text node whose content depends on a signal is updated.
  • Element attributes — only the specific attribute bound to a signal is written.
  • Structural nodes — only the nodes involved in a conditional or iteration driven by a signal are added/removed.

When a signal value changes, the update is surgical: no diff, no component subtree walk, no unnecessary repaints.

signal.set('new value')
         │
         ▼
  ┌──────────────────────────────────┐
  │  DOM nodes subscribed to signal  │
  │  ┌─────────────────────────────┐ │
  │  │ <span> text node ← updated  │ │
  │  └─────────────────────────────┘ │
  │  ┌─────────────────────────────┐ │
  │  │ [aria-label] attr ← updated │ │
  │  └─────────────────────────────┘ │
  └──────────────────────────────────┘
         │
  everything else → untouched

This makes updates O(1) per signal change regardless of the size of the component tree.


TypeScript configuration

Xaendar components rely on TC39 decorator metadata and accessor syntax. Make sure your tsconfig.json has at least:

{
  "compilerOptions": {
    "target": "ES2022",
    "experimentalDecorators": false,
    "useDefineForClassFields": true
  }
}

Standard (stage-3) decorators are used — not the legacy experimentalDecorators variant.


Related packages

| Package | Description | |---------|-------------| | @xaendar/signals | Reactive signal primitives | | @xaendar/types | Shared TypeScript utility types | | @xaendar/compiler | Template compiler |


License

MIT © Kaitenjo