{"id":20129393,"url":"https://github.com/ryanmorr/fusion","last_synced_at":"2025-09-21T04:32:21.740Z","repository":{"id":45465237,"uuid":"329985689","full_name":"ryanmorr/fusion","owner":"ryanmorr","description":"Reactive CSS-in-JS","archived":false,"fork":false,"pushed_at":"2024-11-26T17:07:28.000Z","size":386,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-26T23:15:47.780Z","etag":null,"topics":["css","css-in-js","javascript","reactive","signal"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ryanmorr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-15T17:59:44.000Z","updated_at":"2024-11-26T17:07:34.000Z","dependencies_parsed_at":"2024-11-13T20:35:38.511Z","dependency_job_id":"d7ae6df4-ec83-41c0-9f7a-ec8aa473712d","html_url":"https://github.com/ryanmorr/fusion","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmorr%2Ffusion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmorr%2Ffusion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmorr%2Ffusion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmorr%2Ffusion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ryanmorr","download_url":"https://codeload.github.com/ryanmorr/fusion/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233713001,"owners_count":18718326,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","css-in-js","javascript","reactive","signal"],"created_at":"2024-11-13T20:34:04.242Z","updated_at":"2025-09-21T04:32:16.393Z","avatar_url":"https://github.com/ryanmorr.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fusion\n\n[![Version Badge][version-image]][project-url]\n[![License][license-image]][license-url]\n[![Build Status][build-image]][build-url]\n\n\u003e Reactive CSS-in-JS\n\n## Install\n\nDownload the [CJS](https://github.com/ryanmorr/fusion/raw/master/dist/cjs/fusion.js), [ESM](https://github.com/ryanmorr/fusion/raw/master/dist/esm/fusion.js), [UMD](https://github.com/ryanmorr/fusion/raw/master/dist/umd/fusion.js) versions or install via NPM:\n\n```sh\nnpm install @ryanmorr/fusion\n```\n\n## Usage\n\nFusion is a tiny CSS-in-JS library that combines declarative CSS building with reactive stores to create data to CSS variable bindings:\n\n```javascript\nimport { css, store } from '@ryanmorr/fusion';\n\nconst color = store('red');\n\nconst style = css`\n    .foo {\n        color: ${color}\n    }\n`;\n\ndocument.head.appendChild(style);\n\ncolor.set('blue');\n```\n\n## API\n\n### `store(value?)`\n\nCreate a reactive store that encapsulates a value and can notify subscribers when the value changes:\n\n```javascript\nimport { store } from '@ryanmorr/fusion';\n\n// Create a store with an initial value\nconst count = store(0);\n\n// Get the store value\ncount.value(); //=\u003e 0\n\n// Set the store value\ncount.set(1);\n\n// Set the store value with a callback function\ncount.update((val) =\u003e val + 1);\n\n// Subscribe a callback function to be invoked when the value changes,\n// it returns a function to unsubscribe from future updates\nconst unsubscribe = count.subscribe((nextVal, prevVal) =\u003e {\n    // Do something\n});\n```\n\n------\n\n### `derived(...stores, callback)`\n\nCreate a reactive store that is based on the value of one or more other stores:\n\n```javascript\nimport { derived, store } from '@ryanmorr/fusion';\n\nconst firstName = store('John');\nconst lastName = store('Doe');\nconst fullName = derived(firstName, lastName, (first, last) =\u003e `${first} ${last}`);\n\nfullName.value(); //=\u003e \"John Doe\"\n\nfirstName.set('Jane');\n\nfullName.value(); //=\u003e \"Jane Doe\"\n\n// Subscribe to be notified of changes\nconst unsubscribe = fullName.subscribe((nextVal, prevVal) =\u003e {\n    // Do something\n});\n```\n\nIf 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:\n\n```javascript\nimport { derived, store } from '@ryanmorr/fusion';\n\nconst query = store();\n\n// Perform an ajax request when the query changes\n// and notify subscribers with the results\nconst results = derived(query, (string, set) =\u003e {\n    fetch(`path/to/server/${encodeURIComponent(string)}`).then(set);\n});\n```\n\n------\n\n### `css(strings, ...values?)`\n\nCreate CSS stylesheets declaratively via tagged template literals with support for nested rules:\n\n```javascript\nimport { css } from '@ryanmorr/fusion';\n\n// Create a \u003cstyle\u003e element\nconst stylesheet = css`\n    .foo {\n        color: red;\n\n        \u0026:hover {\n            color: blue;\n        }\n\n        @media (max-width: 750px) {\n            \u0026 {\n                color: purple;\n            }\n        }\n\n        .bar {\n            color: green;\n        }\n    }\n\n    .baz {\n        color: yellow;\n    }\n`;\n\n// Append styles to document\ndocument.head.appendChild(stylesheet);\n```\n\n#### Bindings\n\nWhen 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:\n\n```javascript\nimport { css, store } from '@ryanmorr/fusion';\n\nconst width = store('10px');\n\ndocument.head.appendChild(css`\n    .foo {\n        width: ${width};\n    }\n`);\n\nconst element = document.querySelector('.foo');\n\ngetComputedStyle(element).getPropertyValue('width'); //=\u003e \"10px\"\n\nwidth.set('50px');\n\ngetComputedStyle(element).getPropertyValue('width'); //=\u003e \"50px\"\n```\n\nSimilarly to stores, promises can also be interpolated into a `css` stylesheet, setting the value of the binding CSS variable when the promise resolves:\n\n```javascript\nimport { html } from '@ryanmorr/fusion';\n\nconst height = Promise.resolve('100px');\n\nconst style = css`\n    .foo {\n        height: ${height};\n    }\n`;\n```\n\nIf a store or promise returns a value of null or undefined, the binding CSS variable will be unset.\n\n------\n\n### `style(strings, ...values?)`\n\nCreate 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:\n\n```javascript\nimport { style, store } from '@ryanmorr/fusion';\n\nconst color = store('red');\n\n// Create a style declaration and return a class name\nconst className = style`\n    width: 100px;\n\n    \u0026:hover {\n        color: white;\n    }\n\n    .foo {\n        color: ${color};\n    }\n\n    @media only screen and (max-width: 30em) {\n        \u0026 {\n            width: 200px;\n        }\n    }\n`;\n\n// Add the unique class to an element\nelement.classList.add(className);\n```\n\n------\n\n### `keyframes(strings, ...values?)`\n\nCreate 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:\n\n```javascript\nimport { keyframes, css } from '@ryanmorr/fusion';\n\n// Create a keyframes animation\nconst slideIn = keyframes`\n    from {\n        transform: translateX(0%);\n    }\n    to {\n        transform: translateX(100%);\n    }\n`;\n\n// Add the animation to a `css` stylesheet\nconst stylesheet = css`\n    .foo {\n        animation: ${slideIn} 1s ease-in;\n    }\n`;\n```\n\n------\n\n### `fallback(...values)`\n\nAdd 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:\n\n```javascript\nimport { fallback, store, css } from '@ryanmorr/fusion';\n\nconst color = store();\n\n// Because the store is unset, it defaults to the fallback value of blue\nconst stylesheet = css`\n    .foo {\n        color: ${fallback(color, 'blue')};\n    }\n`;\n\n// When the reactive store is set, it takes precedence\ncolor.set('red');\n```\n\n------\n\n### `media(mediaQuery)`\n\nCreate a reactive store for a media query that can also be interpolated into a `css` stylesheet or `style` declaration:\n\n```javascript\nimport { media, css } from '@ryanmorr/fusion';\n\n// Create the media query store\nconst smallScreen = media('(max-width: 750px)');\n\n// Returns true if the media query currently matches\nconst isSmallScreen = smallScreen.value(); //=\u003e true/false\n\n// Interpolate the media query into a stylesheet\nconst style = css`\n    ${smallScreen} {\n        .foo {\n            color: green;\n        }\n    }\n`;\n\n// Subscribers are called when the status of the media query changes\nsmallScreen.subscribe((isSmallScreen) =\u003e {\n    // Do something\n});\n```\n\n------\n\n### `query(selector)`\n\nCreate 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:\n\n```javascript\nimport { query, css } from '@ryanmorr/fusion';\n\n// Create the element store\nconst fooElements = query('.foo');\n\n// Returns an array of elements that match the CSS selector\nconst elements = fooElements.value();\n\n// Interpolate the CSS selector into a stylesheet\nconst style = css`\n    ${fooElements} {\n        color: yellow;\n    }\n`;\n\n// Subscribers are called when elements matching the\n// CSS selector are added to or removed from the DOM\nfooElements.subscribe((nextElements, prevElements) =\u003e {\n    // Do something\n});\n```\n\n## DOM\n\nFor a DOM-based solution, refer to [reflex](https://github.com/ryanmorr/reflex), a similar library that brings reactivity to elements and attributes. It is also 100% compatible with fusion.\n\n## License\n\nThis project is dedicated to the public domain as described by the [Unlicense](http://unlicense.org/).\n\n[project-url]: https://github.com/ryanmorr/fusion\n[version-image]: https://img.shields.io/github/package-json/v/ryanmorr/fusion?color=blue\u0026style=flat-square\n[build-url]: https://github.com/ryanmorr/fusion/actions\n[build-image]: https://img.shields.io/github/actions/workflow/status/ryanmorr/fusion/node.js.yml?style=flat-square\n[license-image]: https://img.shields.io/github/license/ryanmorr/fusion?color=blue\u0026style=flat-square\n[license-url]: UNLICENSE","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanmorr%2Ffusion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fryanmorr%2Ffusion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanmorr%2Ffusion/lists"}