Skip to content

What is CWCO, why it was build and what is the plan for the future?

Elson Correia edited this page Jan 3, 2022 · 1 revision

What is CWCO?

CWCO stands for Contextful Web Components and it is pronounced "Cuoco". It is a library that exposes classes that allows you to create vanilla web components without the pain point of state or attribute management or DOM manipulation among many others.

In a way, it is an enhancement or addition to the native Web Components API. The reason is, it tries to follow Web Standards and not modify what you can natively do with HTML, CSS, and Javascript but it does introduce new ideas, while still using Web Standards, to enhance the web components building experience;

What are the pain points of native Web Components API?

Although web components API are a great improvement over the traditional DOM manipulation, it does not save you from tedious stuff. In way, it introduces new problems which require a developer to manage in order to work fine.

Let's take for example this "simple" button component and how it handles label and type update

class MyButton extends HTMLElement {
  static observedAttributes = ['label', 'type'];
  
  get label() {
    return this.getAttribute('label');
  }
  
  set label(value) {
    return this.setAttribute('label', value);
  }
  
  get type() {
    return this.getAttribute('type') || 'button';
  }
  
  set type(value) {
    return this.setAttribute('type', value);
  }
  
  constructor() {
    super();
    
    this.attachShadow({mode: 'open'});
    
    this.shadowRoot.innerHTML = `${this.style}<button type="${this.type}">${this.label}</button>`;
  }
  
  attributeChangedCallback(name, oldVal, newVal) {
    if(name === 'label') {
      this.shadowRoot.querySelector('button').innerHTML = newVal;
    } else if(name === 'type') {
      this.shadowRoot.querySelector('button').type = newVal;
    }
  }
  
  get style() {
    return `<style>
      button {
        padding: 10px;
        color: #222;
        background: #fff;
      }
    </style>`
  }
}

customElements.define('my-button', MyButton);

const btn = new MyButton();

btn.label = 'click me';

document.body.appendChild(btn);

setTimeout(() => {
  btn.setAttribute('label', 'do something')
}, 1500)

You still need to manually update DOM on changes

To update a specific element with a detected change you must query it and manually do the change where it applies. This is fine for this small example but you could have components that have a lot of observed attributes or many elements that need to be updated when a specific attribute changes. This can become really painful to maintain as the component grows in complexity or size;

Manual Attribute-to-property mapping

This is actually a recommended practice to mimic native HTML element instances having properties that match the attributes and having to map all attributes to properties is a robust and tedious work. One that observedAttributes does not automatically do

Can't pass complex data via attribute

All attribute values are either string or boolean natively. If you start working with complex data, using getters and setters is fine if you have an instance but then you get into the complexity of if having to get a reference of element every time in order to pass complex data. This also means if you have a component that renders another, the containing component needs to make the distinction between simple vs complex data to update the right elements. To support complex data as an attribute you will need to stringify it first and then parse it when you receive it.

Boilerplate code

For some reason, shadow root feels like an internal API and you have to deal with it every time you want to create a web component.

Style is just another element to manage

There is nothing special with how styling is done. There is nothing new there to improve it and it is just another element to manage.

What are the pain points it is solving?

Here is the same component without all the tedious manual work.

class MyButton extends WebComponent {
  static observedAttributes = ['label', 'type'];
  type = 'button'; // default
  
  template = `
    <button type="{type}">{label}</button>
  `;
  
  stylesheet = `<style>
      button {
        padding: 10px;
        color: #222;
        background: #fff;
      }
    </style>`;
}

MyButton.register();

const btn = new MyButton();

btn.label = 'click me';

document.body.appendChild(btn);

setTimeout(() => {
  btn.setAttribute('label', 'do something')
}, 1500);

Optional DOM manipulation

CWCO is designed in such a way that you hardly need to do DOM manipulation. Simply provide the template, which is an HTML web standard, with a placeholder for data and all the DOM elements will be updated when there is a change in properties or attributes.

Automatical Attribute-to-properties mapping

When you define the observedAttributes you automatically get equivalent camel-cased properties in the class that when changed will trigger DOM updates as well.

Complex data handling via attribute

When you have a property with complex data(object array for example), you simply reference it in curly braces as attribute value and it will automatically get passed to the component. This is true even for non-JSON friendly objects like Map, function etc.

Data binding in CSS

You can bind data directly into CSS which means when data change, the style for your component also will which means, you don't always need to target particular selectors for styling. This helps minimize the styling needed for different states.

Minimal boilerplate code

It is super easy to deal with boilerplate code. They become just static properties you set on the class.

So much more

CWCO did not stop at just solving the pain point of native web components API. It also introduced new concepts to it like:

  • directives: Natively, these are known as global attributes in HTML and it is available via directives building;
  • context: The idea of concept eliminates the need to pass data deeply into your application. This is great for common things in website building like localization, theming, help system, global data accessing, etc;
  • parsing directly on the HTML: The good stuff of CWCO components does not only need to happen inside the components. Templates can be defined outside the components and are still handled the same way.
  • event binding: Putting event attributes in HTML tags is often frowned upon and CWCO automatically converts them into event listeners before removing them from HTML tags which makes them much more appealing.
  • context provider components: these are components whose sole job is to provide a specific type of data for any children it wraps. this is an awesome way to declare everything in one place and plug it where it is needed.

What's next?

The current focus now is to increase adoption by building good documentation and tutorials to help new adopters but there are other things that are desired for the future like:

  • functional approach to build components to attract FP lovers;
  • support for extending specific HTML elements;
  • stronger, simpler, and more integration with other features of the DOM;
  • server-side rendering better support in different languages like PHP, Node, Deno, etc;
  • a large and powerful UI library that can be used with any library or framework which addresses common native elements tags limitations right now in HTML.
  • external template file support;
  • improved safety and bad practice detection integrated;
  • local browser storage integration;
  • canvas and SVG improved integration;
  • and more;

There is no limit to what CWCO can bring and the needs will arise with adoption and they will be targeted and solved so we can continue to simplify the web.

Learn more with documentation and do not hesitate in helping out.


Thank You! 😊

Elson Correia