[WICG/webcomponents] Idea: HTML Module Imports and Exports (Issue #1059)

## Background

Extensive discussion on Declarative Custom Elements has brough the CG to a point of realizing that a good starting point would be HTML Modules. This will serve as a container for DCEs, but also other reusable HTML resources, such as templates.

This being the case, we need to focus in on exactly how exports AND imports would work. While the export side of HTML Modules has been explored previously, the import side largely undefined.

## Strawman

In order to get conversation going, I'll propose a few basic ideas.

### HTML Module Exports

I'll begin by submitting that there are five possible export types:

* Templates
* Fragments
* Styles
* Custom Elements
* Element Registries

To export any one of these, they must have an `id`, which will become the symbol under which the export is provided, and they must have the `export` attribute. Elements with only an `id` can be referenced within the module, but are not able to be imported. Both `id` and `export` are required for that.

Here are a few examples.

```html
<!-- Becomes an export named pricingCard of type HTMLTemplateElement -->
<template export id="pricingCard">
  <div class="pricing-card">
    <div class="header">
      <h4 class="name"></h4>
    </div>
    <div class="body">
      <h1 class="title"></h1>
      <ul class="features">
      </ul>
      <button type="button" class="subscribe-action"></button>
    </div>
  </div>
</template>

<!-- Becomes an export named sharedHeaderStyles of type CSSStyleSheet -->
<style export id="sharedHeaderStyles">
  .shared-header {
    display: flex;
    justify-content: center;
  }
</style>

<!-- Becomes an export named sharedHeader of type DocumentFragment -->
<header class="shared-header" export id="sharedHeader">
  <ul>
    <li><a href="./home">Home</a></li>
    <li><a href="./features">Features</a></li>
    <li><a href="./pricing">Pricing</a></li>
    <li><a href="./faq">FAQs</a></li>
    <li><a href="./about">About</a></li>
  </ul>
</header>
```

The above types exist today, but in the future we could also add `<element>` (or some other tag) for declarative custom elements and `<registry>` for a declarative custom element registry.

### Importing HTML Modules in JS

Given the above example, we could import in JavaScript, using import assertions.

```js
import { 
  pricingCard, 
  sharedHeaderStyles,
  sharedHeader
} from "./my-resources.html" with { type: "html" };
```

You should not be able to import the entire document, as that would break the encapsulation of the module. You should only be able to import elements with an `id` and an `export` attribute. We could also enable an element with an `export` and no `id` to be imported as the default export.

### Importing HTML Modules in HTML

Importing into HTML involves two separate tasks:

* Declaratively using a DCE, template, or fragment
* Defining a DCE

HTML already provides a way to declaratively use a custom element, but not a way to declaratively use a template or fragment. So, we'll need to add something new here.

We also have no way to declaratively define a custom element in a given HTML document or module.

First, let's tackly the issue of importing the resources from an HTML module. We could do that with an `<import>` element as such. Here are a few examples:

```html
<import from="./my-resources.html#sharedHeader">
<import from="./my-resources.html#pricingCard" as="productCard">
<import from="./design-system.html#MyButton" as="ui-button">
```

The `from` attribute provides the path to the HTML module and also includes a fragment identifying the specific export. The `as` attribute provides a way to name the import within the current document or module. In the case of a template or fragment, this creates something with an `id` denoted by `as`. In the case of a custom element, it will call `define` with the value of `as` for the tag name.

To use a custom element import, is no different than using any custom element:

```html
<ui-button>Click Me!</ui-button>
```

Using a template or fragment is different. I propose adding a new element to handle this, named `compose`. It would look something like this:

```html
<compose src="#sharedHeader"></compose>
<compose src="#productCard"></compose>
```

Note: We could enable the `src` attribute to both import and compose as a convenience. But, I think importing and using are two different things and minimally need what is shown above.

The above should all work when importing into documents or other HTML modules. One other scenario is worth mentioning: importing DCEs into a potential declarative custom element registry. That could look something like this:

```html
<registry export id="my-registry">
  <import from="./my-element.html" as="your-element">
  <import from="./my-module.html#NamedElementTwo" as="element-two">
  <import from="./everything-from-another-registry.html">
  
  <element tag="an-inline-element"></element>
</registry>
```

And of course, you could import this entire registry into a document as follows:

```html
<import from="./my-registry.html">
```

This would define all the contained elements in the current document, using their specified tag names. To change the names, we could add a mapping, similar to the way export parts work in shadow dom.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/WICG/webcomponents/issues/1059
You are receiving this because you are subscribed to this thread.

Message ID: <WICG/webcomponents/issues/1059@github.com>

Received on Friday, 3 May 2024 19:29:33 UTC