This page gives an overview of the major concepts of Corset, and what the project is all about.

Progressive enhancement

Corset is a reactive library for building interactive user interfaces on the web. This puts it into the same category as popular libraries like React and Vue.

What sets Corset apart from other libraries is that it is not template based. Instead of having a template of what your HTML should look like, Corset is used to enhance HTML you already created. This is called progressive enhancement. Other libraries allow progressive enhancement as well, but they only enhance the HTML that was created using their library.

This means that when using libraries like React and Vue you have to use them both on your server and in the browser. This means you have to use Node.js as your web server.

<form class="login-form">
      placeholder="Enter your email address">
  <input type="submit" value="Continue">

Because Corset is powered by a CSS-like DSL, it is able to connect to HTML no matter how it is created. Corset works equally well in Node.js or PHP or Ruby on Rails powered backends. It works well inside of code demo tools like CodePen. You can even use React as your backend templating language and then Corset to make it reactive on the frontend!

We believe strongly that decoupling the backend from the frontend makes both better. The backend can be focused on the unique requirements of the application; the frontend can be smaller and leaner due to shedding the concerns of generating HTML (much of which is never interactive).

.login-form {
  event: submit var(--handler);

.login-form input[name=email] {
  event: input var(--validate-email);


Corset is a declarative library thanks to the CSS-like DSL. Corset supports many, but not all, features of CSS. One that it supports is custom properties. Using var() with custom properties allows complete reactive interfaces.

vars() in Corset give you "spreadsheet power". In a spreadsheet you have values and computed values. Whenever a value changes, its computed dependencies recalculate. In Corset, the same is true of vars().

Corset allows recomputing a sheet through JavaScript insertions in a top-level mount. A state object is passed into a mount and when that state object's properties change, the mount function is rerun.

import sheet, { mount } from '';

mount(document, class {
  constructor(props, { rebind }) {
    this.rebind = rebind;

  async onsubmit(user, ev) {
    await saveToDB();
    this.user = user;

  bind() {
    return sheet`
      .login-form {
        --user: "Wilbur";
        --submit: bind(${this.onsubmit}, var(--user));
        event: submit var(--submit);