Fluor.js

Sprinkle interactivity on your design

on("click", "button", addClass("wobble", "h1"))
on("animationend", "h1", removeClass("wobble"))
GitHub

What is it?

Fluor.js is a tiny JavaScript library that provides you with a high-level language to easily add interactions and effects to your websites.

It is great for prototypes, UI and UX research and for all websites that do not require the cumbersome machinery of a full-fledged framework.

It is inspired by uilang and Alpine.js, has no dependencies and can be added to any page with a single line of HTML.

Quickstart

To load the latest version of Fluor.js in your webpage, all you need to do is drop this HTML snippet just before the </body> tag of your document:

<script type="module" src="//cdn.jsdelivr.net/npm/fluor@latest/dist/fluor.min.js"></script>

To use Fluor.js, add a <script type="fluor"> tag inside one of your HTML elements with your Fluor script code. Here's an example:

<!-- Your script executes only within its root element -->
<div>
  <p>
    <!-- We use the clicks variable contents as text of the strong element  -->
    <strong f-text="clicks"></strong>
  </p>
  <button>Click me</button>

  <!-- This is your Fluor script -->
  <script type="fluor">
    setup("clicks", 0)
    on("click", "button", set("clicks", n => n + 1))
  </script>
</div>

Guide

To use Fluor.js effectively, there are a few key concepts to grasp. Once that is done, you will be able to do simple things with very little amount of code.

Molecules

One key concept that you will encounter a lot in Fluor is a molecule. A molecule is an object that is created when you put a <script type="fluor"></script> inside an HTML element as shown in the Quickstart.

The created molecule creates an execution context for your Fluor code, it also registers the root HTML element as the molecule's $root. All HTML selectors that you will use with $ and $$ will be scoped to that root element as well unless you manually specify a different root.

You can also nest molecules and in that case you can use $parent to refer to the parent molecule.

Variables

Variables are defined using the setup function and updated with the set action. They can hold any type of JavaScript value: strings, numbers, arrays, objects and functions.

You can use variables in your Fluor scripts or in your HTML through the use of special directives that we will cover in the next section.

All the variables are internally stored in an object which you can access using the special $data variable.

Everytime you can write a variable name you can also use a dotted path to access nested objects. For example, if you have a variable user that contains an object {avatar: {url: "http://example.com/avatar.png"}} you can access the url part using user.avatar.url.

Directives

Directives are special HTML attributes that you can use to tie your Fluor code to your DOM. You can use them to print value of variables, conditionally show parts of your HTML and a lot of other things.

All Fluor directives start with f-. Because of the way HTML works, you cannot specify the same directive more than once (but you shouldn't have to anyway).

For a detailed documentation of each directive, please refer to the Directives API.

Actions

Most of your work when using Fluor.js is reacting to events that come from your DOM elements. To avoid the need to create event handlers, the Fluor language provides a set of predefined functions that create those handlers for you.

Throughout this documentation you will often encounter the term action which is simply a way to tell that these special functions that return functions that can be used as event handlers when using on.

The most common action you will likely use is set which is used to update variables as a reaction to an event.

You can obviously create your own actions too, it's just JavaScript in the end!

Directives API

f-bind-{attr}

Binds a variable to an attribute of the target DOM node. Replace {attr} with the name of the HTML attribute you want to bind, and use a dotted path to a variable as the value.

If the variable is a boolean, the given attribute is added or removed from the DOM node accordingly.

Binding a variable value to an attribute
<div>
  <button f-bind-class="buttonClass">Click me!</button>
  <style>
    .big-button { font-size: 2rem; }
  </style>
  <script type="fluor">
    setup("buttonClass", "big-button")
  </script>
</div>
More examples
Binding boolean variables
<div>
  <button f-bind-disabled="buttonIsDisabled">Click me!</button>
  <script type="fluor">
    setup("buttonIsDisabled", true)
  </script>
</div>

f-text

Display contents of a variable as the textContent of the target DOM node.

<div>
  <p f-text="message"></p>
  <script type="fluor">
    setup("message", "Hello, world!")
  </script>
</div>

f-html

Render HTML stored in a variable inside the target DOM node.

<div>
  <p f-html="message"></p>
  <script type="fluor">
    setup("message", "<b>Hello, world!</b>")
  </script>
</div>

f-if

Conditionally renders some HTML.

Due to the way Fluor.js works, you must use a <template> tag in order for this directive to work.

You can have many children in the <template> tag but you cannot put a Fluor script as a direct child.

<div>
  <template f-if="shown">
    <p>I'm visible!</p>
  </template>
  <button>Toggle paragraph</button>
  <script type="fluor">
    setup("shown", false)
    on("click", "button", toggle("shown"))
  </script>
</div>
More examples
<div>
  <template f-if="shown">
    <p>
      <strong>Click me!</strong>
      <script type="fluor">
        on("click", "strong", () => alert("Hello!"))
      </script>
    </p>
    <!-- You cannot put a Fluor script here! -->
  </template>
  <button>Toggle paragraph</button>
  <script type="fluor">
    setup("shown", true)
    on("click", "button", toggle("shown"))
  </script>
</div>

f-each

Loop over a collection of items. The value of the directive should be an expression of the form {iterator} in {list} where{iterator} is an arbitrary name that you will be able to use in the child template and {list} is a dotted variable path to an array of items you want to iterate over.

Due to the way Fluor.js works, you must use a <template> tag in order for this directive to work.

You can have many children in the <template> tag but you cannot put a Fluor script as a direct child.

<div>
  <ul>
    <template f-each="item in items">
      <li f-text="item"></li>
    </template>
  </ul>
  <script type="fluor">
    setup("items", ["Item 1", "Item 2", "Item 3"])
  </script>
</div>

Scripting API

setupfunction

  • setup(variable, value)
  • setup(variable, updater)
  • setup(object)

Initializes variables without calling render. As the name implies, this is mostly used for setting up state in your Fluor scripts as the system will perform an initial render after executing your script.

Please refer to the set documentation for details about the different ways to call it.

onfunction

  • on(eventName, selector, action)
  • on(eventName, selector, actions[])

Adds an event handler for eventName on all elements matched by selector. When the event occurs, the specified action is run.

In the second form, you can pass an array of actions to be run sequentially.

Fluor.js uses event delegation so you can add elements matching the selector and the event handlers will seamlessly apply to the new elements. The event object will be augmented with a$target property that will hold the actual DOM element matched by your selector.

Examples
Basic handler with a single action

Hello!

<div>
  <p class="p-2">Hello!</p>
  <script type="fluor">
    on("click", "p", toggleClass("text-red-900"))
  </script>
</div>
With an array of actions

Hello!

<div>
  <p class="p-2">Hello!</p>
  <script type="fluor">
    on("click", "p", [
      toggleClass("text-red-900"),
      toggleClass("bg-red-200"),
    ])
  </script>
</div>

everyfunction

  • every(interval, action)
  • every(interval, actions[])

Repeatedly execute an action, waiting a given time interval between each execution.

The interval should be given in seconds. You can use floating point number (e.g. 0.5) or fractions (e.g. 1/60) to use millisecond intervals.

You can also pass an array of actions to be run sequentially.

delayaction

  • delay(amoun, action)
  • delay(amoun, actions[])

Defers the execution of action by a given amount of time.

The delay should be given in seconds. You can use floating point number (e.g. 0.5) or fractions (e.g. 1/60) to use millisecond delays.

You can also pass an array of actions to be run sequentially.

setaction

  • set(variable, value)
  • set(variable, updater)
  • set(object)

The first form updates the variable named variable with value. You can also pass an updater that will receive the previous value and that must return a new value.

The third form allows you to pass an object where the keys are variable names and the values are either plain values or an updater function.

Examples
Calling with a value

<div>
  <p f-text="value"></p>
  <button>Set value to 2</button>
  <script type="fluor">
    setup("value", 1)
    on("click", "button", set("value", 2))
  </script>
</div>
Calling with an updater

<div>
  <p f-text="value"></p>
  <button>Increment</button>
  <script type="fluor">
    setup("value", 1)
    on("click", "button", set("value", previous => previous + 1))
  </script>
</div>
Calling with an object

<div>
  <p f-text="square"></p>
  <p f-text="cube"></p>
  <button>Next</button>
  <script type="fluor">
    setup({
      square: 1,
      cube: 1,
    })
    on("click", "button", set({
      square: prev => prev * 2,
      cube: prev => prev * 3,
    }))
  </script>
</div>

toggleaction

  • toggle(variable)

Alternate between true and false values on boolean variables.

Examples
Toggling an element's presence
<div>
  <template f-if="visible">
    <p>Hello!</p>
  </template>
  <button>Toggle</button>
  <script type="fluor">
    setup("visible", true)
    on("click", "button", toggle("visible"))
  </script>
</div>

addClassaction

  • addClass(className)
  • addClass(className, selector)

If called with one argument, adds the HTML class specified with className to the current action target.

If called with two arguments, adds the HTML class specified with className to all the elements matched by selector.

removeClassaction

  • removeClass(className)
  • removeClass(className, selector)

Works the same way as addClass but removes the class instead.

toggleClassaction

  • toggleClass(className)
  • toggleClass(className, selector)

Works the same way as addClass but toggles the class on and off instead.

appendaction

  • append(variable, item)
  • append(variable, builder)

Adds item at the end of the list stored atvariable.

In the second form, generate the appended value from the result of calling builder with the list as an argument.

prependaction

  • prepend(variable, item)
  • prepend(variable, builder)

Works the same way as append but inserts the item at the beginning of the list.

popaction

  • pop(variable)

Removes the last item from the list stored at variable.

shiftaction

  • shift(variable)

Works the same way as shift but removes the item from the beginning of the list.

withEventaction

  • withEvent(action)

This is an utility method that helps with consuming an event inside an action.

Examples
Changing state based on an event

Last keyCode:

<div>
  <input />
  <p>
    Last keyCode: <strong f-text="keyCode"></strong>
  </p>
  <script type="fluor">
    setup("keyCode", 0)
    on("keyup", "input", withEvent(e => set("keyCode", e.keyCode)))
  </script>
</div>

classesfunction

  • classes(strings[])
  • classes(object)

Generates HTML class names from an array of strings or an object. This works like the classnames or clsx libraries, but it handles less cases.

renderfunction

  • render()

Triggers a render of the current molecule and its children. You should not have to call this method manually.

$function

  • $(selector)
  • $(selector, root)

Returns all elements matching selector in the current molecule. Performs the search on root if given.

$$function

  • $$(selector)
  • $$(selector, root)

Returns the firrst element matching selector in the current molecule. Performs the search on root if given.

$dataobject

The current molecule state object. This can be used in functions or custom actions.

$idstring

The current molecule identifier. This is an internal property, messing with it is at your own risk!

$rootobject

The root DOM Node for the current molecule.

$parentobject

The parent molecule if it exists.