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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ryanmorr/fusion

v1.0.0

Published

Reactive CSS-in-JS

Downloads

7

Readme

fusion

Version Badge License Build Status

Reactive CSS-in-JS

Install

Download the CJS, ESM, UMD versions or install via NPM:

npm install @ryanmorr/fusion

Usage

Fusion is a tiny CSS-in-JS library that combines declarative CSS building with reactive stores to create data to CSS variable bindings:

import { css, store } from '@ryanmorr/fusion';

const color = store('red');

const style = css`
    .foo {
        color: ${color}
    }
`;

document.head.appendChild(style);

color.set('blue');

API

store(value?)

Create a reactive store that encapsulates a value and can notify subscribers when the value changes:

import { store } from '@ryanmorr/fusion';

// Create a store with an initial value
const count = store(0);

// Get the store value
count.value(); //=> 0

// Set the store value
count.set(1);

// Set the store value with a callback function
count.update((val) => val + 1);

// Subscribe a callback function to be invoked when the value changes,
// it returns a function to unsubscribe from future updates
const unsubscribe = count.subscribe((nextVal, prevVal) => {
    // Do something
});

derived(...stores, callback)

Create a reactive store that is based on the value of one or more other stores:

import { derived, store } from '@ryanmorr/fusion';

const firstName = store('John');
const lastName = store('Doe');
const fullName = derived(firstName, lastName, (first, last) => `${first} ${last}`);

fullName.value(); //=> "John Doe"

firstName.set('Jane');

fullName.value(); //=> "Jane Doe"

// Subscribe to be notified of changes
const unsubscribe = fullName.subscribe((nextVal, prevVal) => {
    // Do something
});

If the callback function defines an extra parameter in its signature, the derived store is treated as asynchronous. The callback function is provided a setter for the store's value and no longer relies on the return value:

import { derived, store } from '@ryanmorr/fusion';

const query = store();

// Perform an ajax request when the query changes
// and notify subscribers with the results
const results = derived(query, (string, set) => {
    fetch(`path/to/server/${encodeURIComponent(string)}`).then(set);
});

css(strings, ...values?)

Create CSS stylesheets declaratively via tagged template literals with support for nested rules:

import { css } from '@ryanmorr/fusion';

// Create a <style> element
const stylesheet = css`
    .foo {
        color: red;

        &:hover {
            color: blue;
        }

        @media (max-width: 750px) {
            & {
                color: purple;
            }
        }

        .bar {
            color: green;
        }
    }

    .baz {
        color: yellow;
    }
`;

// Append styles to document
document.head.appendChild(stylesheet);

Bindings

When a reactive store is interpolated into a css stylesheet, it is replaced with a unique CSS variable bound to that store and will be automatically updated when the internal store value changes:

import { css, store } from '@ryanmorr/fusion';

const width = store('10px');

document.head.appendChild(css`
    .foo {
        width: ${width};
    }
`);

const element = document.querySelector('.foo');

getComputedStyle(element).getPropertyValue('width'); //=> "10px"

width.set('50px');

getComputedStyle(element).getPropertyValue('width'); //=> "50px"

Similarly to stores, promises can also be interpolated into a css stylesheet, setting the value of the binding CSS variable when the promise resolves:

import { html } from '@ryanmorr/fusion';

const height = Promise.resolve('100px');

const style = css`
    .foo {
        height: ${height};
    }
`;

If a store or promise returns a value of null or undefined, the binding CSS variable will be unset.


style(strings, ...values?)

Create styles for an element and its descendants declaratively via tagged template literals and return a unique class name. Just like css, it supports nested rules and interpolating stores and promises:

import { style, store } from '@ryanmorr/fusion';

const color = store('red');

// Create a style declaration and return a class name
const className = style`
    width: 100px;

    &:hover {
        color: white;
    }

    .foo {
        color: ${color};
    }

    @media only screen and (max-width: 30em) {
        & {
            width: 200px;
        }
    }
`;

// Add the unique class to an element
element.classList.add(className);

keyframes(strings, ...values?)

Create a keyframes animation via tagged template literals and return a unique animation name that can be easily applied to a css stylesheet or style class name declaration:

import { keyframes, css } from '@ryanmorr/fusion';

// Create a keyframes animation
const slideIn = keyframes`
    from {
        transform: translateX(0%);
    }
    to {
        transform: translateX(100%);
    }
`;

// Add the animation to a `css` stylesheet
const stylesheet = css`
    .foo {
        animation: ${slideIn} 1s ease-in;
    }
`;

fallback(...values)

Add one or more fallback values for a CSS variable, supporting stores, promises, and CSS variable names. Moving left to right, if the value provided is null or undefined then precedence moves to the next fallback value:

import { fallback, store, css } from '@ryanmorr/fusion';

const store = store();
const promise = Promise.resolve('30px');

document.head.appendChild(css`
    .foo {
        width: ${fallback(store, promise, '--foo', '10px')};
    }
`);

const element = document.querySelector('.foo');

// The 3 previous values are unset, so precedence defaults to the right-most value
getComputedStyle(element).getPropertyValue('width'); //=> "10px"

// When the `--foo` CSS variable is set, it takes precedence
document.documentElement.style.setProperty('--foo', '20px');
getComputedStyle(element).getPropertyValue('width'); //=> "20px"

// Precedence movies to the promise when it resolves
await promise;
getComputedStyle(element).getPropertyValue('width'); //=> "30px"

// Finally, when the reactive store is set, it takes precedence over all the fallback values
store.set('40px');
getComputedStyle(element).getPropertyValue('width'); //=> "40px"

media(mediaQuery)

Create a reactive store for a media query that can also be interpolated into a css stylesheet or style declaration:

import { media, css } from '@ryanmorr/fusion';

// Create the media query store
const smallScreen = media('(max-width: 750px)');

// Returns true if the media query currently matches
const isSmallScreen = smallScreen.value(); //=> true/false

// Interpolate the media query into a stylesheet
const style = css`
    ${smallScreen} {
        .foo {
            color: green;
        }
    }
`;

// Subscribers are called when the status of the media query changes
smallScreen.subscribe((isSmallScreen) => {
    // Do something
});

query(selector)

Create a reactive store for a live array of DOM elements that match a CSS selector string. The store is automatically updated anytime one or more elements matching the CSS selector are added to or removed from the DOM. It can also be interpolated into a css stylesheet or style declaration:

import { query, css } from '@ryanmorr/fusion';

// Create the element store
const fooElements = query('.foo');

// Returns an array of elements that match the CSS selector
const elements = fooElements.value();

// Interpolate the CSS selector into a stylesheet
const style = css`
    ${fooElements} {
        color: yellow;
    }
`;

// Subscribers are called when elements matching the
// CSS selector are added to or removed from the DOM
fooElements.subscribe((nextElements, prevElements) => {
    // Do something
});

DOM

For a DOM-based solution, refer to reflex, a similar library that brings reactivity to elements and attributes. It is also 100% compatible with fusion.

License

This project is dedicated to the public domain as described by the Unlicense.