Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defining a Non-Script Native HTML Component Model #9815

Open
brandonmcconnell opened this issue Sep 30, 2023 · 1 comment
Open

Defining a Non-Script Native HTML Component Model #9815

brandonmcconnell opened this issue Sep 30, 2023 · 1 comment
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest

Comments

@brandonmcconnell
Copy link

brandonmcconnell commented Sep 30, 2023

The problem

Modern web development heavily relies on component-based architectures, allowing for modular and reusable code. However, currently, this necessitates the use of JavaScript libraries or frameworks, which can result in performance overhead, complex build setups, and a steeper learning curve for newcomers to web development. There exists a gap in native HTML/CSS for defining, importing, and using reusable components in a modular fashion.

** This does not solve the same problem that web components solve. They are both useful, but needfully separate.

Existing solutions

JavaScript Frameworks and Libraries

  • Libraries such as React, Vue, and Angular provide robust component models, allowing for the creation, import, and use of reusable components with encapsulated logic and styles.
  • Custom Elements and Shadow DOM are part of the Web Components standard, allowing developers to define new HTML elements and encapsulate styles.
  • However, these solutions require JavaScript and in many cases a build-step, which can add complexity and potential performance overhead.

CSS Preprocessors

  • Solutions like SASS or LESS allow for modular styling, but they do not provide a mechanism for encapsulating HTML structure or logic within components.

Server-Side Includes (SSI)

  • SSI (e.g. PHP) allows for including reusable HTML snippets, but lacks the ability for encapsulation, data-binding, and is not a client-side solution.

HTML Templates

  • The <template> tag in HTML allows for the definition of inert HTML templates that can be activated via JavaScript, yet they require JavaScript for manipulation and rendering.

Proposed solution

I would propose a solution that expands upon the existing HTML/CSS syntax to introduce native HTML components with the following characteristics:

Declarative Syntax

  • A new HTML element like <component> could be introduced, which allows for the definition, import, and use of reusable components directly within HTML files.

CSS Custom Properties

  • Utilizing CSS custom properties to pass styles into components, allowing for style overrides and theming.

Attribute Binding

  • Extending the existing <slot> comoponent to support binding content to an attribute on the slotted element instead using a named slot. Alternatively, a new element like <attr> could be introduced which would operate as an element solely bound to the named attribute on the component usage's root and would share the same general syntax as the <slot> element.

Content Slotting

  • Extending the existing <slot> element to enable content slotting within components using named and default slot(s).

Component Nesting

  • Allowing components to be nested within other components, promoting modularity and reusability. This could even potentially support recursion, but that would likely require some additional syntax and consideration and is not a requirement for this proposal.

Native Import/Export

  • Introducing a native import/export mechanism to enable the sharing of components across HTML files without relying on JavaScript.

Additional considerations

Direct DOM Injection Over Shadow DOM

  • A direct DOM injection approach ensures components are seamlessly integrated within the existing DOM structure, enhancing accessibility, SEO, and ease of styling without the added complexity and encapsulation barriers introduced by Shadow DOM.

Backward Compatibility

  • Ensuring that the proposed solution is backward compatible or gracefully degradable on older browsers is crucial for adoption.

Performance

  • Assessing the performance impact of the proposed solution, especially in terms of rendering speed and resource utilization.
@brandonmcconnell brandonmcconnell added addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest labels Sep 30, 2023
@brandonmcconnell
Copy link
Author

brandonmcconnell commented Sep 30, 2023

For the sake of this PR brief, I sought to follow the WHATWG guidelines laid out here and intentionally did not go deep on syntax in the brief.

Regarding syntax specifics, I have conducted some research and gathered some thoughts here for further discussion, but I'm fully open to tossing these in favor of something better. Hopefully, this can help to stir some fruitful discussion.

  

Native HTML Component Model

Introduction

Enhancements in HTML and CSS have tackled challenges traditionally handled by JavaScript. A native component model within HTML/CSS, without JavaScript reliance, would simplify web development, and promote better performance and maintainability.

Objective

Propose a native component model in HTML/CSS for creating, importing, and using reusable components without JavaScript or external libraries.

Feature Details

Component Definition

  • New element <component> to encapsulate HTML, CSS, and other assets.
  • The <component> element should have a name attribute to uniquely identify the component, which will match the component property value on the <use> element that creates a new instance of the component.
  • New components can be composed in separate files and imported, or crafted in the current HTML file, where they can then also be imported into other HTML files (will export/import components only, not the entire document).
  • When a component instance does not require any slotted content, it can optionally be self-closing.
<!-- file: components.html -->
<component name="card">
    <div class="card">
        <div class="card-title">Static Content</div>
    </div>
</component>

<!-- file: index.html -->
<component name="fancy-button">
    <button>
        <slot />
    </button>
</component>

<head>
    <link rel="component" href="components.html" />
</head>

<body>
    <use component="card" />
    <use component="button">Click me</button>
</body>

Attribute Passing

I've considered 2 options…

  • Extend the existing <slot> element to support binding content to an attribute on the slotted element using a new attr attribute on the slot element instead of name
  • Introduce a new element like <attr> to operate as an element solely bound to the named attribute on the component usage root (sharing the same general syntax as the element)

With either option, the element may need to be self-closing if we want to enforce that the default content, like any attribute passed value is a string type and does not include any nodes, which a slot's default content typically supports. For this reason, using a new element like the <attr> idea here may be better.

<!-- file: components.html -->
<component name="card">
    <div class="card">
        <div class="card-title">
            <slot attr="text" default="Default Title" />
            <!-- or -->
            <attr name="text" default="Default Title" />
        </div>
    </div>
</component>

<!-- file: index.html -->
<body>
    <use component="card" text="Hello, World!" />
</body

Style Passing

  • Styles can be passed to components using CSS custom properties.
  • Styles are also naturally inherited via the cascade where applicable, which is one key difference between this native component model and ShadowDOM-based web components.
<!-- file: components.html -->
<component name="card">
    <style>
        .card {
          color: var(--txtColor);
          background-color: var(--bgColor, #fff);
        }
    </style>
    <!-- ... -->
</component>

<!-- file: index.html -->
<body style="--txtColor: #ff0000">
    <use component="card" style="--bgColor: #f0f0f0" />
</body>

Content Slotting

  • Utilize the default (unnamed) or any named <slot> element(s) for content slotting, allowing default content to be overridden.

  • Slots without default content can optionally be self-closing.

  • New <fragment> element to pass multiple elements into a slot without a wrapping element.

    Many JS frameworks emphasize the importance of being able to pass fragments into components this way:

<!-- file: components.html -->
<component name="card">
    <slot />
    <div class="card-content">
        <slot name="content" />
    </div>
</component>

<!-- file: index.html -->
<body>
    <use component="card">
        Some content here for the default slot
        <fragment slot="content">
            <p>Text 1</p>
            <p>Text 2</p>
        </fragment>
    </use>
</body>

Component Nesting

  • Components should be able to nest other components within them.
<!-- file: components.html -->
<component name="card">
    <div class="card-actions">
        <use component="button" label="Action" />
    </div>
</component>
<component name="button">
    <button><attr name="label" default="Click Me" /></button>
</component>

<!-- file: index.html -->
<body>
    <use component="card" />
</body>

Component Import

  • New rel value component for the <link> element to facilitate component import.
<!-- file: index.html -->
<head>
    <link rel="component" href="components.html" />
</head>

<body>
    <use component="card" />
</body>

Implementation Considerations

  • Alternatively to the familiar and repurposed <use> element, it would arguably provide a better developer experience to be able to reference one's own components as the tag name, similar to how web components work, but we would need to ensure there would be no conflict there or decide how those conflicts are handled, should they arise (i.e. web components take priority, vice versa, etc.).

  • For privacy and better maintainability, it may be best to require whitelisting components with a public attribute allowing them to be imported by other files/components. Without this, a component would be local by default and only usable within the current document.

    <component name="card" public />

    It may be worth exploring the possibility of cross-origin reference blocking as well, so HTML files—or the components therein—can opt into sharing across other domains or only on the same origin/domain.

    • If a component does not use public, it is not importable on any domain
    • If a component does use public and the file/component does not opt into cross-origin resource sharing, the component can be imported only by other HTML files on the same origin/domain
    • If a component does use public and the file/component does opt into cross-origin resource sharing, the component can be imported by all other HTML files on any domain, or those whitelisted.
      • This could be dangerous if malicious content owners bake scripts into their publicly shared components. It may even be worth considering not allowing <script> elements inside components at all, for security, or at the very least warn against importing from any untrusted sources, the same caution someone should consider when importing a 3rd party script.
  • Following up on the previous concern, it may be best to always (or optionally) import components by name in the <link> file to better avoid name collisions. Something like this:

     <link rel="component" href="components.html" name="card,button" />
  • Would the component names be looser than web component names if we were to use the <use> approach? Should we prudently observe the same restrictions from web components (required hyphen), or even some other new convention (e.g. <@button>, <%button>, <$button>, etc.)

    • Component instances (the root reference) themselves are not targetable via CSS as selectors, so there is no issue using a new tag name format that does not comply with CSS, at least in that regard.

Conclusion

The proposed native HTML/CSS component model seeks to leverage existing web standards while introducing minimal new syntax for creating, importing, and using reusable components. This proposal aims to further a declarative, less imperative web, reducing JavaScript dependency for component-based architecture.

@brandonmcconnell brandonmcconnell changed the title Defining a Native HTML/CSS Component Model Defining a Non-Script Native HTML Component Model Sep 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest
Development

No branches or pull requests

2 participants
@brandonmcconnell and others