A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

[Proposal] CHTML

ox-harris
2020-08-09

I envision a time when we can natively do what a UI component framework does now. Time has proven that this is beyond the scope of Web Components; we’ve got to develop new language primitives to support bindings and “reactivity”; we’ve got to develop more compositional powers in HTML! This is what I seek to push with CHTML.

CHTML is a (proposed) suite of new DOM features that brings language support for modern UI development paradigms: a component-based architecture, data binding, and reactivity, more composition. This will be helping us bank more on the platform and less on abstractions.

Here is the explainer: https://github.com/web-native/chtml/blob/master/explainer.md

Here is the current polyfill implementation: https://github.com/web-native/chtml

bob
2020-08-10

I whole-heartedly agree that we should bank more on the platform. Back in the 80s and 90s there was never this problem with developing in proprietary languages. They were stable. You knew where you stood. People got better and more in demand the more they learned the language. Web dev should be, ideally, a place where the more you can do, the more in demand you are. Ideally, the platform should be the main platform for web development. One’s skills should be built on native technology. It should be possible to become a master native programmer, revered by all, spoken about in whispers.

But clearly JavaScript is too low-level for a lot of companies - hence frameworks to aid productivity. There’s no point now, everyone writing their own libraries from native JavaScript and notably it makes no sense from a recruitment agency viewpoint. Companies want to employ coders in specific skills, like React, Vue, Angular, etc. - it’s not like the pioneer days where you can write your own library or learn someone else’s library from scratch. Try getting a job as a native developer and then listen to the reasons companies require that “real” programmers know frameworks. I’m in that situation right now. 49 years of age. Awesome developer. Wrote my own frameworks and libraries. Don’t know any popular frameworks. Having difficulty finding a new job.

Unless you are living under a rock, it is obvious that there needs to be a higher-level common ground for reactive and component functionality, and the browser platform itself should be the place where the magic happens at a high-enough level for people to be able to understand it without needing a CS degree. Ease-of-use is one of the purposes for the internet itself. So it needs to be easy to write UI for the internet. Else we’ll be stuck with static brochure websites forevermore.

The more this elephant in the room can be worked over, the better.

You hit the nail on the head with the platform enhancement. You should get a docs website up with examples, to show how it works in practice. As long as your suggestions work in a 100% backward-compatible way, are as flexible as a framework, and are easier to use than using a framework, then there is a chance. Anything that can be introduced natively that get rid of a framework need is a good thing. Having options for doing things on a web page natively are a good thing. But I would suggest that you get more case studies and examples up there.

I took the other approach to solving this issue of JavaScript being too low-level - extending CSS to be a fully event-driven, modern reactive framework. Sounds a bit weird, I concur. But equally weirdly, it does actually work in practice. It’s like a major extension based on the :hover command. It adds :click, :mouseover, and hundreds of other things. It can be added as-is to CSS as it stands right now, with no changes to any existing CSS specs (AFAIK).

Active CSS has the same scoped variableness of which you speak (great minds, clearly), essentially all the things you mention. is dramatically higher-level than JavaScript, which, let’s face it, is getting a bit beyond your novice web developer when it comes to components and that sort of thing (it takes at least a year to even understand what a shadow DOM is, and another year to stumble upon a valid need for isolated CSS, and then another 100 cups of coffee to grasp the API).

https://activecss.org. It could be incorporated into the browser in 100% backward-compatible fashion by design. I’m just right now sorting out sequential commands and nested loops to make it look more like a language, and hopefully then can start writing up proper tutorials. I keep getting distracted by missing bits in it and so feel the need to code more to make it complete before telling more people about it. But this post got my attention…

ox-harris
2020-08-11

I’m very delighted to see you share my values - banking more on the platform.

You made a valid point in the second paragraph about companies requiring being skilled in a framework. A little note here is that the status quo is based on current limitations and possibilities - which CHTML seeks to change. We’re having to get by with frameworks because we lack native features that help us keep the UI in sync with the state of an application.

With CHTML being a foundational technology, companies can always create higher-level tooling that is specifically adapted to their needs - this time, with less engineering and zero interoperability issues. With or without tooling, the question of building the UI from smaller-sized components and keeping these in sync with an application would have forever been based on a common ground.

I am certainly eager to work on more examples, and overall, a clearer documentation than what we have at https://docs.web-native.dev/chtml. The current proof of concept has been my priority the past many months. I should be back to work on seeing a good amount of interest in CHTML.

I’ll now be heading over to https://activecss.org to see what you have up there.

ox-harris
2020-08-17

So I thought to revise the EXPLAINER to put the discussion points in focus. So, here are the components of the CHTML suite:

  • Scoped HTML - Introduces namespaces into HTML to help us structure an HTML document as a hierarchy of scopes and subscopes. (Or components and sub components, to use the “component” terminology.)
  • Scoped CSS - Reproposes scoped stylesheets as a language feature to help us define styling as part of any element.
  • Scoped JS - Introduces scoped scripts into HTML - plain JavaScript that works as a “reactive” data-binding language - to enable us define behaviour as part of any element.
  • HTML Partials - Introduces Slots-Based composition into the “open” HTML and a means to define, access, and import reusable HTML snippets.

There is also the examples section that shows how the different parts of the suite fit together and how everything could work with any tool of choice.

trusktr
2020-08-21

Also it would be great to see examples of how this plays with Custom Elements. How would custom element authors use each of these features? I now CE authors don’t have to use these, and the features can be useful by themselves, but just curious to see what benefit the features may give to CE authors.

ox-harris
2020-09-02

This is a good question. I do apologise for a delayed response - it happend to be a time of vacation for me.

So, I would say that, while not drastically changing how we author web components, CHTML does offer “shortcuts”, consistency, and a good amount of “neatness” to how we code. These add up to improve the overall developer experience and maintainability of our work.

For example, the namespace API in the Scoped HTML specs can improve how we name the structural parts of our custom elements and how we access these parts in the JavaScript code. Here is an example of a “collapsible” component:

The markup (based on scoped IDs):

<my-collapsible namespace>
  <div>
    <div id="control"></div>
    <div id="content"></div>
  </div>
</my-collapsible>

The JavaScript:

customElements.define('my-collapsible', class extends HTMLElement {
  constructor() {
    super();
    this.namespace.control.addEventListener('click', () => {
      // Do something to this.namespace.content
    });
  }
});

Here, we are now coding against a structural API instead of CSS selectors.

  • This gives us loose coupling between the JavaScript and the HTML.

  • Using this.namespace instead of this.querySelector() does not only make everything neat, but also self-documenting - giving us the component’s semantics at a glance.

  • Also, with this.namespace being observable, it becomes easy to work with a component whose parts are dynamically added.

  • But the most significant win, arguably, is that we also avoid the specificity wars that using CSS selectors would bring at scale. For example, where this component is nested, we’d hit a terrible problem with a selector like .control at the parent component level:

    <my-collapsible>
      <div>
        <div class="control"></div>
        <div class="content">
    
          <my-collapsible>
            <div>
              <div class="control"></div>
              <div class="content"></div>
            </div>
          </my-collapsible>
    
        </div>
      </div>
    </my-collapsible>
    

Another way CHTML could improve how we author and consume custom elements is in how we define and access <template> elements for use in the shadow DOM. Currently, we query the document by CSS selectors to find a <template> element. But with the document.templates API, that wouldn’t be necessary.

Using the “collapsible” component above:

The HTML (defining the <template> for the component’s Shadow DOM):

<head>

  <template name="collapsible">
    <div>
      <div id="control"></div>
      <div id="content"></div>
    </div>
  </template>

</head>

The JavaScript (accessing the <template> for the component’s Shadow DOM):

customElements.define('my-collapsible', class extends HTMLElement {
  constructor() {
    super();
    let shadowRoot = this.attachShadow({mode: 'open'});
 
   shadowRoot.append(document.templates.collapsible.innerHTML);
  }
});

We could even “invert the control” of <template> selection by allowing the <template> element to be decided at component consumption time.

Here, the <template> to use for the Shadow DOM is defined at consumption time:

<my-collapsible template="collapsible"></my-collapsible>

For this, the JavaScript would use the Element.template API:

shadowRoot.append(this.template.innerHTML);

Lastly, CHTML has provided an elegant answer to the all-important subject of data binding and reactivity. Before now, you couldn’t approach this without a “tool” and a template language.

Using Scoped JS, here is how the collapsible component above could receive and remder data:

The HTML:

<my-collapsible namespace>

  <div>
    <div id="control"></div>
    <div id="content"></div>
  </div>

  <script scoped>
    this.namespace.content.innerHTML = content;
  </script>

</my-collapsible>

The JavaScript:

let data = {content: 'Collapsible content'};
document.querySelector('my-collapsible').bind(data);

Update data.content anytime and the UI is automatically updated.

The benefit of achieving this at the language level is that the modern UI can go without a tool. We shouldn’t really need specialised tools to do this.

Do let me know what you think.

Essang_Jesse
2020-09-03

:clap: :clap: :clap: :clap: Very impressive!!!

trusktr
2020-09-11

There’s no required timeline for free work. :slight_smile:

Those are some really great points that make it clear how custom element benefit. I see how they can be useful. Thanks!

If I understand correctly, Scoped JS could be used inside a custom element implementation like this:

class NameCard extends HTMLElement {
  _root = null
  _div = null

  connectedCallback() {
    if (!this._root) this._root = this.attachShadow({mode: 'open'})

    this._root.innerHTML = html`
      <div namespace>
        <div>Hello, my name is:</div>
        <div id="name"></div>
        <script scoped>
          this.namespace.name.innerHTML = name;
        </script>
      </div>
    `

    this._div = this._root.firstChild
  }

  _values = {}

  attributeChangedCallback(attr, oldVal, newVal) {
    if (this_div && attr === 'name') {
      this._values.name = newVal ?? 'John Doe'
      this._div.bind(this._values) 
    }
  }
}

customElements.define('name-card', NameCard)

And the end user consumption:

<name-card name="Omiron Centis"></name-card>

Are you implying that the code in <script scoped> is a “reactive computation” that gets re-run any time the values of variables (that are dependencies in the script’s code) bound to the containing namespace change?

Is there a declarative way to place the values into the DOM? (f.e. <div>The content: {content}</div>)?

For that particular use case, here’s how we can do it without ScopedJS, which is a little shorter:

class NameCard extends HTMLElement {
  _root = null
  _div = null

  connectedCallback() {
    if (!this._root) this._root = this.attachShadow({mode: 'open'})

    this._root.innerHTML = html`
      <div namespace>
        <div>Hello, my name is:</div>
        <div id="name"></div>
      </div>
    `

    this._div = this._root.firstChild
  }

  attributeChangedCallback(attr, oldVal, newVal) {
    if (this_div && attr === 'name') {
      this._div.innerHTML = newVal ?? 'John Doe'
    }
  }
}

customElements.define('name-card', NameCard)
ox-harris
2020-09-14

Thank you for this, and for the good thinking that went into it!

So, looking at the use case you gave, I see that another approach would be necessary! Problem with the first code is redundancy. Here, the <name-card> element is observing input data both via attribute-change detection and via Scoped JS’s reactivity. Actually, only one approach would do. Either of the two will be just fine. So, here are different ways I would do data-binding with the <name-card> element using Scoped JS alone, depending on the specific use-case:

  • If the shadow DOM isn’t really a requirement:

    <name-card namespace>
    
      <div>Hello, my name is:</div>
      <div id="name"></div>
    
      <script scoped>
        this.namespace.name.innerHTML = name;
        // If reflecting this value on an attribute is a requirement... say for SEO
        this.setAttribute('name', name);
        // If the component exposed some method... say toggle
        this.toggle(state);
      </script>
    
    </name-card>
    

    Now in the application:

    var data = {name: 'John Doe', state: 'active'};
    nameCard.bind(data);
    
  • If the shadow DOM is a requirement and much of the work happens inside, you want to send the data in by exposing some method that accepts and uses the data - say a setName() method (more about this method shortly):

    <name-card namespace>
    
      <script scoped>
        this.setName(name);
        // If the component exposed some other method... say toggle
        this.toggle(state);
      </script>
    
    </name-card>
    

    So, we are practically feeding the component - via its methods - from the reactive script, not the other way around

    Now, again, in the application:

    var data = {name: 'John Doe', state: 'active'};
    nameCard.bind(data);
    

    But, lest it seems that it would as well be fine for the application to call the component’s methods directly - nameCard.setName(data.name); nameCard.toggle(data.state) - instead of via the scoped script, we would be missing out on the reactivity of a scoped script and would have to be manually tracking changes.

    // This is a case where the data is coming from somewhere else
    var data = user.data;
    
    // Initial call
    nameCard.setName(data.name);
    nameCard.toggle(data.state);
    
    // On updates
    Observer.observe(data, 'name', update => {
        nameCard.setName(update.value);
    });
    Observer.observe(data, 'state', update => {
        nameCard.toggle(update.value);
    });
    

    This is essentially what a reactive script would do for us when we call nameCard.bind(data).

    In addition, we also get to avoid littering the application layer with much of the implementation details of the presentation layer (having to know the methods, attributes, and the intended behaviour of DOM elements). More on this shortly. For now, at least, we can see that the application code doesn’t have to worry about what happens with the data in the UI.

    Now, for the setName() method, here’s how we could implement it:

    class NameCard extends HTMLElement {
    
        connectedCallback() {
            if (!this._root) this._root = this.attachShadow({mode: 'open'})
    
            this._root.innerHTML = `
            <div namespace>
              <div>Hello, my name is:</div>
              <div id="name"></div>
            </div>`;
    
            this._tree = this._root.firstChild;
        }
    
        setName(name) {
            this._tree.namespace.name.innerHTML = name;
            // If reflecting this value on an attribute is a requirement... say for SEO
            this.setAttribute('name', name);
        }
    }
    

In all, notice that we are reflecting the value of name on the element’s attribute, not obtaining the value via the attribute!

The Sweet Spot of Scoped JS

Here are a few things about Scoped JS and its .bind() method:

  1. Separation of Concern: Two layers of code don’t have to know each other to be kept in sync. So, above, our application layer can just be concerned with data manipulation, while the DOM layer with data consumption. They are two separate layers that would be kept separate of each other. This principle becomes broken if the application layer were to be directly manipulating DOM attributes and methods as it would be having to know the implementation details of the DOM.

    So there is a down side to the pattern where the communication between the application layer and the DOM layer happens via attributes - element.setAttribute(name) in the application, attributeChangedCallback(attr, oldVal, newVal) {} in the element. But the issue is nothing in a little codebase where the design pattern doesn’t require a strict separation of concern.

  2. True Reactivity: True reactivity lets us work declaratively even with complex data types like nested objects and arrays - an obvious impossibility for attribute-based change-detection mechanisms.

    With Scoped JS, we can bind a nested data tree like this:

    var data = {
        user: {name: 'John Doe'},
        state: 'active',
    };
    nameCard.bind(data);
    

    then consume it like this:

    <script scoped>
      // We can either consume the "user" branch here...
      this.setName(user.name);
      // Or pass it down to a child component...
      this.namespace.user.bind(user);
      // If the component exposed some other method... say toggle
      this.toggle(state);
    </script>
    

    and everything will work reactively! For example, the application can update just the value of data.user.name, and this will trigger just the this.setName(user.name) statement in the entire script above. (Or if we actually did pass the user branch to a child component, only the child component responds to the update.)

    Granular DOM updates!

  3. Simplicity: Notice how much code is required to create a small behaviour using a custom element. We have to be working with a JavaScript class! We also have to ship the JavaScript code to the browser (often, in real life, involving some tooling - think minifiers). So… there is this bunch of extra work that goes into provisioning that small attributeChangedCallback(attr, oldVal, newVal) {} block!

    With scoped scripts, there is practically nothing to do upfront, nor afterward! No JavaScript classes, no loading of an external JavaScript file, just behaviours on the fly! Everything starts and ends right within the element markup!

One Last Thing - the Irony of Everything

Although Scoped JS can drastically improve how we write custom elements, it should actually help us reduce the amount of custom elements we have to write in the first place, as much of the custom elements we write is usually to emulate data-binding or some form of reactive data rending! There is a perfectly reactive data-binding language for that now!

Okay, your thoughts again…

Commentator
2020-09-22

I don’t know if CHTML will solve it, but I generally agree we need a decent state-ful GUI markup standard. Perhaps HTML browsers are too burdened already such that we should split web UI into three sub-groups: 1) Media/games/videos, 2) Documents (html may be good enough already), and 3) Productivity: CRUD and data-oriented GUI.

Perhaps one browser could render all three, but the point is to be able to focus on each so that a single standard doesn’t become Swiss Army Knife pasta. It would be 3 different “engines” but a single browser could potentially mix all three, but in different panels.

Perhaps the “GUI browser” could be based off the Tk or Qt tool kits, since those are the most mature open-source kits. The markup language would roughly resemble Microsoft’s XAML language, but be state-ful in that there is interaction.

Commentator
2020-09-22

We might not need gajillion web UI frameworks if browsers natively supported common GUI idioms and behaviors. I agree that if fashionable “eye candy” is your site’s goal, then having choice allows you to stay current on the latest UI fads. But a lot software is for getting work done and doesn’t need the UI widgets to change every 6 months. Intranet and niche business applications have a different need than public facing sites. I’m tired of social media polluting our standards for work software.

dy
2020-11-18

Nice and well-thought! I came to the same conclusions. scoped script polyfill (partial) is here https://gist.github.com/dy/2124c2dfcbdd071f38e866b85436c6c5

isiahmeadows
2020-11-23

With any standard, caution must be exercised, as I’ve seen this XKCD play out IRL far too many times to count. https://xkcd.com/927/

ox-harris
2020-11-23

Would it be possible to add details to this - to clarify the problem?

ox-harris
2020-11-23

Nice and succint, though partial. Really, the case for scoping anything is as clear as the case for modularity.

I am hoping we can soon scope scripts natively: where variables still don’t leak into the global scope. We’ll also have to decide whether to natively have scoped scripts as “reactive” scripts (where statements in the scripts are bound to the data (variables) they reference) or whether to have scoped scripts provide the low-level primitives to do so at a higher level. Meanwhile, the Scoped JS that CHTML proposes has both scoping and reactivity covered, and the polyfill has these implemented.

Personally, the more I explore the possibilities with this idea, the more compelling it feels. I’ll thus be spending some more time to publish our next iteration of the proposal, where we drop a few non-essential and build on key strengths.

trusktr
2021-01-09

Does nameCard.bind(data) patch the data object with accessors in order to observe the properties?

This is definitely a benefit dependency-based reactivity! :slight_smile:

I’m not sure that’s a benefit. I look at attributes as input sources, that a user should set, and the data flows into the component.

That is indeed a benefit: the app code (a pure-JS file) just binds the data. In another (HTML) file, the HTML author wires up the method calls. So now we have two different source codes, where the logic business logic just needs to know what the shape of the object to bind should be, based on what the HTML author says, and that object shape may not correlate to the shape of the elements that the HTML author used.

Yep, love dep-based reactivity! I like dependency-tracking reactivity where the dependencies are detected based on which variable a code expression uses.

I agree, this can be beneficial for quick reactivity without the need for custom elements. And custom elements can additionally be used to further ship re-usable DOM.

It would be great if this included a template syntax. I think that could make things simpler. For example:

Business logic:

const data = {name: "John Doe"}
el.bind(data)

HTML code:

<div namespace>
  Hi my name is: {name}
</div>

and then the script is not needed at all. That would really simplify things for people who learn first with HTML and CSS before JavaScript. I can imagine it being difficult to wrap one’s mind around the script parts.

If we could have both simple templating (no script tags) and a way to include HTML files in other HTML files, we’d be in a good shape for HTML devs to get work done without any JavaScript.

At the moment I’m guiding someone learn HTML/CSS. The very first problem they have is duplicating HTML everywhere. Especially for different pages. The web has no tool-less way to re-use HTML.

Imagine this:

<!-- namecard.html -->
<div>
  <span>My name is <b>{fullName}</b>.</span>
</div>
<style>...scoped css perhaps...</style>
<!-- app.html -->
<h1>Awesome site!</h1>
<include src="./namecard.html" name="Poe" />

If things were that simple, HTML specialists would have such a good time re-using HTML without writing JavaScript.

Something like your idea would then add an additional layer when script is needed:

<!-- namecard.html -->
<div namespace>
  <span>My name is <b>{this.fullName}</b>.</span>
  <script scoped>
    this.fullName = first + last
  </script>
</div>
<style>...scoped css perhaps...</style>
<!-- app.html -->
<h1>Awesome site!</h1>
<include src="./namecard.html" first="Poe" last="Jacobs" />

Or perhaps <include> has its own namespace (or there’s a lack of any namespace, however the vendor implementation wants to look at it, and:

<!-- namecard.html -->
<div>
  <span>My name is <b>{this.fullName}</b>.</span>
  <script scoped>
    // maybe this is an object for this file when it is `<include>`ed, or something.
    this.fullName = first + last
  </script>
</div>
<style>...scoped css perhaps...</style>
<!-- app.html -->
<h1>Awesome site!</h1>
<include src="./namecard.html" first="Poe" last="Jacobs" />

Reactivity is nice, but the main thing missing is re-usability in the first place (except with JavaScript which an HTML developer can’t avoid).

I think both ES Module and an HTML-imports-like thing should both exist. I think HTML imports should be super simple, just as the word include hints at.

I strayed off topic, but I couldn’t help but think: if I’m already writing custom elements (because I need code re-use), then I am totally fine with a class-based system housing my reactivity logic. I am going to write custom elements anyway because I need to re-use HTML (or JSX, or similar) to avoid pasting duplicate HTML everywhere. Because I’m already using Custom Elements for this purpose, the <script scoped> doesn’t add much for me (it’s reactivity without the re-usability). Let me know if I overlooked something.

Just to share, my Custom Element system looks like this:

import {Element, reactive, attribute} from '@lume/element'

@reactive
class NameCard extends Element {
  @reactive @attribute name = "Wolverine"
  span = null

  // This runs once (it's a reactive computation), not over and over (no vdom).
  template = () => {
    const div = <div>
      <span ref={this.span}>Hi, my name is: {this.name}</span>
    </div>
    jQuery.somePlugin(div)
    console.log(div instanceof HTMLDivElement) // true, only runs once.
    return div
  }

  connectedCallback() {
    super.connectedCallback()
    jQuery.otherPlugin(this.span)
    console.log(this.span instanceof HTMLSpanElement) // true
  }
}

I wrote it that way so some features of it are apparent. But it is cleaner without the references or effects within template:

import {Element, reactive, attribute} from '@lume/element'

@reactive
class NameCard extends Element {
  @reactive @attribute name = "Wolverine"

  // This runs once (it's a reactive computation), not over and over (no vdom).
  template = () => (
    <div ref={this.div}>
      <span ref={this.span}>Hi, my name is: {this.name}</span>
    </div>
  )

  connectedCallback() {
    super.connectedCallback()
    jQuery.somePlugin(div)
    console.log(this.div instanceof HTMLDivElement) // true
    jQuery.otherPlugin(this.span)
    console.log(this.span instanceof HTMLSpanElement) // true
  }
}

The template stuff is usable without custom elements. For example, and with an html template tag instead of JSX, and no build tools:

import {html, variable} from '@lume/element'

const name = variable('Wolverine')
let span
const p = document.createElement('p')
p.innerText = 'This is a paragraph.'

const div = html`
  <div>
    <span ref=${s => span = s}>${name}</span>
    ${p}
  </div>
`

jQuery.somePlugin(div)
jQuery.otherPlugin(span)

document.body.append(div)

// later...
name('Spiderman') // The DOM is updated

// later we're done...
div.remove()

What I’m trying to show is, because I need re-usability, I can already use JavaScript to do everything I need, so the script/namespace idea doesn’t add much for me because I already need to write JS files to re-use pieces of UI, and the declarative nature of the reactivity that I currently have is just what I like, whereas with <script scoped> I still have to write imperative JS code (even if its in HTML) in order to send data where it needs to go.

I think a declarative reactive solution would be great if it also provided HTML developers re-usability so that they aren’t required to know JavaScript to in order to re-use pieces of their UI. Then a JavaScript author (one who may or may not write Custom Elements), can provided the data for the HTML author’s UIs. A Custom Element author could even include an HTML author’s HTML files into a larger Custom Element that the HTML author could then re-use in another file too.

EDIT: I made a topic just for HTML re-usability.

ox-harris
2021-01-25

Does nameCard.bind(data) patch the data object with accessors in order to observe the properties?

No. Object accessors aren’t used. Proxies aren’t used either. The bind() method internally uses the Observer API. So, properties set or deleted using the Observer’s set() and deleteProperty() methods are observed on the fly. All other possibilites are documented with the Observer API.

I’m not sure that’s a benefit. I look at attributes as input sources, that a user should set, and the data flows into the component.

Well, it turns out that only string-type data can flow in. <name-card fname="John" lname="Doe"></name-card> What about data structures like objects and arrays? Flatten them into a thousand attribute names? And what about nested data structures? These are some cases where being able to pass in an (observable) object is useful. While not invalidating the other attribute-based pattern.

This would be a good idea. Certainly just maps JS values to HTML slots. But there are concerns I’ve battled with:

  • The {} curly braces, in any combination, are nothing but text characters in HTML. Repurposing them for a Domain-Specific Language would be very odd. One option could be hinting the HTML parser of some template variables ahead:

      <div vars="name, somethingelse">
        Hi my name is: {name}. This is {somethingelse}.
      </div>
    

    This was just some thoughts.

  • We may just realize that a simple way to map JS values to slots in HTML isn’t enough. Sometimes we want some little power at the HTML level to transform the value to render.

      <div vars="name, somethingelse">
        Hi my name is: {name.toUpperCase()}. This is {somethingelse}.
      </div>
    

    A lot of templating systems out there have to reinvent a new language for things like this. And we end up in more complexity than where we’re coming from. A new language to learn and debug.

These issues are already overcome by a Scoped Script. And a simplistic mapping pattern is also possible:

<div namespace>
    Hi my name is: <span id="name">name</span>
    <script type="scoped">
        this.namespace.name = value; // value.toUpperCase()
    </script>
</div>
ox-harris
2021-01-26

This is something I want to talk about as this has been the whole point of CHTML:

  • Being able to reuse HTML without having to involve JavaScript (entirely covered in the HTML Partials part of CHTML)
  • While, at the same time, having everything work reactively, as barely as without JavaScript classes or JS files, but with support for those when you want to still have them (the whole point of the Scoped JS part of CHTML)

You’ll quickly realize that if we had one without the other, the entire essence of trying to keep JavaScript where it belongs is missed! We would still be stranded! That is why I am proposing these features together as one suite - CHTML, instead of as standalone ideas. This cannot be over-emphasized: if we developed these ideas separately of each other, we would end up missing that one theme they share.

Now, once you begin threading the path away from an all-JS approach, you’ll quickly realize just how flat and non-modular HTML is by itself. You’d need JavaScript again even before you’re able to do something practical with the first two features - reusability, reactivity. Modularity becomes the next pain! And this is the next thing covered in the suite:

  • The ability to keep IDs scoped to a context other than the global scope (the Scoped HTML part of CHTML.)

This gives us at least three things to need together: reusability, reactivity, and modularity. And from my months of testing these assumptions, the results are that if we had these, the entire problem you highlighted would be delightfully addressed. We wouldn’t require JavaScript components just to reuse HTML or to make the UI modular. The guy at the HTML end can create markup independent of an application layer and still be able to author reactive UIs.

ox-harris
2021-01-26

One question about CHTML I’d like to answer is why a new proposal when we’ve had existing efforts in some of the areas covered in CHTML. The answer lies in these two factors: scope, timing, in the light of CHTML’s theme: the ability to create reusable, reactive, and modular HTML at HTML level.

  1. Scope: there is really no proposal with a scope as complete as CHTML’s. And it is difficult to make complete sense of individual flying ideas, let alone have them work as one when they eventually do ship. For example, what real benefit would we have if the platform shipped just one of reusability, reactivity, and modularity? Having developed the first independently of the others, how would we get it to naturally support those later on?

  2. Timing: some existing proposals were made at a time we were yet to realize all what would be involved to achieve the said goal. This explains the first.

CHTML’s theme is what underpins some of the existing proposals. But the limited individual scopes have made the directions different from CHTML’s.

mkay581
2021-01-26

I like this discussion but I’m confused. Everyone keeps referencing CHTML in the web-native/chtml repository but it says it’s been moved to WebQit/oohtml, which is named OOHTML. Which is it?