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

be-reflective

v0.0.3

Published

Custom element feature for reflecting properties to custom states via CSS declarations

Readme

be-reflective

NPM version How big is this package in your project?

This package contains the world's second custom element feature (following closely on the related https://github.com/bahrus/truth-sourcer which provides a pluggable feature for synchronizing attributes with properties in a kind of two-way binding mechanism).

This feature provides a pluggable way of reflecting properties.

Using this feature

The spawn definition can be either synchronous (eager) or asynchronous (lazy-loaded). The tradeoff:

  • Synchronous — the Reflector is available immediately, so custom states are reflected from the very first property change. No flash of unstyled state.
  • Asynchronous — smaller initial bundle. The implementation loads on first access. There's a brief window where states aren't reflected until the import resolves. For most UI this is invisible since custom states typically drive non-critical styling.

Both approaches are shown below. Pick whichever fits your priorities.

Synchronous (immediate reflection)

import 'assign-gingerly/assignFeatures.js';
import {Reflector} from 'be-reflective/Reflector.js';

class MyElement extends HTMLElement {
    /**
     * @type {EventTarget}
     **/
    propagator = new EventTarget();

    #internals;


    static supportedFeatures = {
        reflector: {
            fallbackSpawn: Reflector,
            getSharedContext(instance) {
                return { internals: instance.#internals };
            }
        }
    };

    /**
     * @type {string}
     **/
    #name = '';

    get name(){
        return this.#name
    }

    set name(nv){
        this.#name = nv;
        this.propagator.dispatchEvent(new Event('name'));
    }

    /** @type {boolean} */
    #disabled = false;

    get disabled() {
        return this.#disabled;
    }

    set disabled(nv) {
        this.#disabled = nv;
        this.propagator.dispatchEvent(new Event('disabled'));
    }

    constructor(){
        super();
        this.#internals = this.attachInternals();
        this.reflector.hostPropagator = this.propagator;
    }


}

customElements.assignFeatures(MyElement, {
    reflector: { spawn: Reflector }
});

customElements.define('my-element', MyElement);

Asynchronous (smaller initial footprint)

Uses ReflectorLazy which checks whether --custom-state-exports is actually declared in CSS before loading the full implementation. If no rules are present, the feature stays inert with zero overhead.

import 'assign-gingerly/assignFeatures.js';

class MyElement extends HTMLElement {
    /**
     * @type {EventTarget}
     **/
    propagator = new EventTarget();

    #internals;


    static supportedFeatures = {
        reflector: {
            fallbackSpawn: async () => (await import('be-reflective/ReflectorLazy.js')).ReflectorLazy,
            getSharedContext(instance) {
                return { internals: instance.#internals };
            }
        }
    };

    /**
     * @type {string}
     **/
    #name = '';

    get name(){
        return this.#name
    }

    set name(nv){
        this.#name = nv;
        this.propagator.dispatchEvent(new Event('name'));
    }

    /** @type {boolean} */
    #disabled = false;

    get disabled() {
        return this.#disabled;
    }

    set disabled(nv) {
        this.#disabled = nv;
        this.propagator.dispatchEvent(new Event('disabled'));
    }

    constructor(){
        super();
        this.#internals = this.attachInternals();
        this.reflector.hostPropagator = this.propagator;
    }


}

customElements.assignFeatures(MyElement, {
    reflector: { spawn: async () => (await import('be-reflective/ReflectorLazy.js')).ReflectorLazy }
});

customElements.define('my-element', MyElement);

Custom State Reflection

Custom state reflection is only available for properties of type boolean, number and string.

Booleans

For booleans, just specify each one individually:

<style>
    my-element {
        --custom-state-exports: name, disabled;
    }
    
</style>

Bonus benefit: This makes it really easy for another developer to "discover" what custom states are applicable, something that appears to be lacking with the current browser developer tools.

Strings

For strings, we can specify a mapping:

<style>
    alert-component {
        --custom-state-exports: 
            alertTypeIndicatesSuccess if alertType==success, 
            alertTypeIndicatesFailure if alertType==failure
        ;
    }
    
</style>

We can also specify wildcard matching:

<style>
    alert-component {
        --custom-state-exports: 
            alertTypeIndicatesSuccess if alertType*=success, 
            alertTypeIndicatesFailure if alertType$=failure
        ;
    }
    
</style>

We adopt the same symbols for the wildcard matching as is used for attribute selectors

Numbers

Finally for numbers, we can specify modulo checks, and greater than or less than checks

<style>
    alert-component {
        --custom-state-exports: 
            ticksInSecondQuarter if ticks % 4 == 1, 
            ticksInFourthQuarter if ticks % 4 == 3,
            ticksLessThan20 if ticks < 20,
            ticksGreaterThanOrEqualTo30 if ticks >= 30
        ;
    }
    
</style>

Viewing Demos Locally

  1. Install git
  2. Fork/clone this repo
  3. Install node.js
  4. Open command window to folder where you cloned this repo
  5. git submodule add https://github.com/bahrus/types.git types

  6. git submodule update --init --recursive

  7. npm install

  8. npm run serve

  9. Open http://localhost:8000/ in a modern browser

Running Tests

> npm run test

Using from ESM Module:

import 'be-reflective/be-reflective.js';

Using from CDN:

<script type=module crossorigin=anonymous>
    import 'https://esm.sh/be-reflective';
</script>