devpunks / snuggsi

snuggsi ツ - Easy Custom Elements in ~1kB
https://snuggsi.com
MIT License
395 stars 17 forks source link

Preload Reusable Component `<links>` #151

Closed snuggs closed 6 years ago

snuggs commented 6 years ago

Reusable Component <link>s

See github.com/whatwg/html/issues/2791#issuecomment-332403076 @brandondees could be on to something

<link rel=include href=path/to/include.html>

And what i love is no need for closing tag. Also there is support for multiple rel= attribute values. rel="preload include" Just might work as we'd expect!! 🎉

Thoughts...

brandondees commented 6 years ago

yeah this is what i thought was being discussed the whole time, we were just looking at a custom element as a poor man's alternative to attempt to demonstrate popular interest

snuggs commented 6 years ago

@brandondees how does this declaration feel?

<link id=foo-bar rel=preload href=/foo-bar.html>

We are basically abiding by the preloading spec and using convention of id=component-name as the link to custom elements of same tag name.

<!-- LATER IN MARKUP -->
<foo-bar></foo-bar>

I do realize this is a slippery slope between include and import (should we even be calling it that anymore?)

Just looking for a convention stat because need this sooner than later.

The bad side is rel=include isn't passing HTML parser validation (w3c so could also #FakeCare) The good side is our id= convention passes.

I kinda like having a preload where available out of the box #BatteriesAlmostIncluded capture d ecran 2017-09-28 a 17 16 43

brandondees commented 6 years ago

couple things:

what if i use <link id=baz-qux rel=preload href=/foo-bar.html> where foo-bar is the element defined in the include? do i get to alias the vendor's foo-bar element to my own name?

what if the include defines multiple custom elements? do i need to specify which to import into the parent document's namespace by using the id attribute?

how does preload get us what we want, exactly? does the spec make that available to the parent document's ES context for programmatic access?

any idea what we can rely on with future html modules? can it be polyfilled? given that imports have been declared dead by various parties, that seems to be what we are meant to be planning around, but i haven't seen any indication that it's in any way predictable yet. suppose whatever we decide to do involves designing our own non-standard interface which can wrap either the future standard implementation without breaking expectations, or just hack together something that fakes enough of the expected behavior to serve our purposes in the meantime, without relying on the specs being hammered out?

snuggs commented 6 years ago

@brandondees yes :-)

tmornini commented 6 years ago

@brandondees @snuggs K.I.S.S. 😄

If you want to "worry" about element names (which I'm not sure you should), prepend the include's ID to the included's ID...

brandondees commented 6 years ago

@tmornini for me, the semantics always matter, but for this, there could easily be namespacing issues to work around as well. prepending id might be sufficient for that, though, although in my ideal version I think I'd want the name attr to specify what the new element should be called in the parent document.

tmornini commented 6 years ago

@brandondees What if there are multiple elements in the include? 😄

brandondees commented 6 years ago

@tmornini snuggsi is convention over configuration, with the convention being one component per import, but presumably we can use some JS to re-map those manually if needed.

snuggs commented 6 years ago

True!!! @brandondees @tmornini also name isn't a valid attr for <link> elements. I've been working on this and arrived at the following convention. Will do a PR with documentation: https://github.com/devpunks/snuggsi/blob/master/index.html#L19-L29

The concept is called "Resource Hints" for preload prefetch prerender dns-connect preconnect https://www.keycdn.com/blog/resource-hints

This allows us to get HTML "include"s without even using Javascript (In "User Land")

Works SWEET TOO!!! We also get preloaded asynchronous non-blocking, HIGH PRIORITY download threads so when we "stamp" the custom elements with the template from the remote file it takes no time to respond from cache and works without even defining a custom element.

This also satisfies @rianby64, @btakita, @mrbernnz, & @halorium's request to have simple reusable components that can be embedded into an html page from a remote location. @brandondees has had the same request for over a decade (along with the rest of the web). Since Firefox is not implementing HTML Imports The concept is still necessary but lacking sane implementations. (First rule about HTML imports is we don't call them imports anymore. Even though they are "imports") This is about as sane and declarative as i can think of. And leaves us room to grow. Just use preload instead of import. and voila. 😎 Notice id attribute. Can you pick up the naming convention?

can see it in action here http://snuggsi.herokuapp.com/examples/header-group/example.html

foo-bar.html

<!-- This hints a migration to master document -->
<link rel=preload as=script href=script.es>
<link rel=preload as=style  href=style.css>

<template>
  <slot name=header>Default Header Text</slot>

  <p>Just a paragraph
</template>

index.html

<!doctype html>

<title>Linked Component Example</title>

<script src=//unpkg.com/snuggsi></script>

<link id=foo-bar rel=preload href=foo-bar.html>
<!--
  Any dependencies in foo-bar.html
  (i.e. <script> or <style> or <link type=stylesheet>)
  will be injected here after call site.

<script src=script.es></script><!-- script migration(s) -->
<link rel=stylesheet href=style.css><!-- style migration(s) -->

-->

<foo-bar><!-- contents from <template> in foo-bar.html will be stamped here --></foo-bar>
<foo-bar>
  <!-- can still replace slots within foo-bar.html without defining a custom element in javascript !!! -->
  <h1 slot=header>Slot replacement</h1>
</foo-bar>

<foo-bar><!-- multiple elements work too--></foo-bar>
snuggs commented 6 years ago

@brandondees @tmornini was kinda leaning towards the following hint.

<!-- would hint where to find "component" resources/links/template (singular)/etc. -->
<meta name=foo-bar content=foo-bar.html> <!-- so retro -->

But following caveats have to be overcome:

  1. <meta> tags aren't allowed in <body> only in <head> and links are (New with HTML5)
  2. We don't get the free resource fetching from greenfield browsers as we do with <link rel=preload>

But i like it better because it's more terse than:

<link
  as=fetch
  id=foo-bar
  rel=preload
  href=foo-bar.html>
<!--
  The platform actually does a lot of cool resource management for free.
  We merely add our own loading/stamping conventions to the final resulting DOM
-->

Which is the (upcoming) proposal. Your thoughts?

snuggs commented 6 years ago

@brandondees @btakita @rianby64 @janz93 @kurtcagle #ping ☝️

This feature is sorta a big deal...

@mrbernnz you and I worked on this back when the concept was <link id=tag-name rel=import href=...>

As soon as there is feedback can get docs prepared in a PR as this functionality is already implemented and being tested on a project @albertoponti & @cristhiandick are working on. Will also be able to be on track for example docs requested by @halorium of reusable components. This drastically changes documentation for our examples.

Repro Source Code - https://github.com/devpunks/snuggsi/tree/master/examples/header-group

See example and view source. (currently works in IE11 as well @robcole) http://snuggsi.herokuapp.com/examples/header-group/example.html

@rianby64 did not go the route of rel=include as that broke the W3C HTML Validator (see below). Which TECHNICALLY we don't have to give two shits about. However, the "bipartisan" way would be something we'd have to propose to the spec gods. And these components ain't gonna import by themselves in 2017! :-) That being said rel=prerender (spec here) i've been kicking around in my head for a week or two and is spec compliant. Ironically only IE supported but polyfillable for "HTML includes". We also would need ZERO JAVASCRIPT to pull it off. Merely conventions which is a win for developer ergonomics. My only discomfort is the spec states "NEXT navigation". Which feels a tad awkward for usage with "include"ing HTML within a call site. @brandondees may have more thoughts on this. For now can just use custom elements. (still no js needed. <slot> replacement batteries included!!!)

capture d ecran 2017-10-09 a 05 39 57

@brandondees what's interesting about this pattern is we implement the loadCSS pattern which actually creates async stylesheets. That's sorta a big deal. Downside is it introduces a FOUC which technically the onus should be on the developer to mitigate with inline styling. The upside is styles no longer block the HTML DOMParser which is a glorious win for the browser and a wanted feature since ancient Mosaic.

See extremely detailed blog post here (worth going past TL;DR;) https://jakearchibald.com/2016/link-in-body/

We were able to summarize and implement the strategy with just 1 LOC > https://github.com/devpunks/snuggsi/blob/master/html-link-element/index.es#L63-L69

brandondees commented 6 years ago

prerender is not appropriate for this type of thing, as it would render the remote document as if it is going to be the next full page navigation, for instant visual update on click. including a component in a parent doc wouldn't meet the browser's expectations and might achieve wasted resources with no user facing benefit. it happens at the browser's discretion, and in lowest priority mode (only when resources are idle, and the cached result might only be kept in memory for a short period, maybe up to a few minutes). it's intended specifically for situations where the user's workflow after loading the current page is very likely to be to immediately perform a subsequent navigation action and that next page can be predicted. the main intended use case for this is clicking through paginated content at a rapid pace, or pages that have a very clear funnel progression such as e.g. "home -> pricing -> contact us". preload is much more generally applicable as it is more about pre-caching the content to front-load the network activity without performing the rendering work eagerly. I believe the user agent still keeps this cache short-lived by default but because it's not using the CPU/GPU resources as aggressively it's likely to be tuned to be more general purpose. when both are combined in an optimal way, i believe the end result should be something approximating the UX of turbolinks. instant page transitions for the most likely navigation path, and very snappy transitions for the site overall due to pre-cached external content. in both cases, it's important to remember that these are "hints" not directives, the user agent is in control of the process and behavior may vary based on circumstances, so these are not dependable in any sense.

as for the usage API, i am not picky as long as it's not surprising/unintuitive, and as long as we allow for developer control where appropriate when we are applying convention over configuration defaults.

snuggs commented 6 years ago

@brandondees so the "proposal" is reasonable as far as developer ergonomics? What I love is that even if someone "divorces" the snuggsi library they still are in far better shape than they were before they picked up the library as far as the platform is concerned.

FWIW What i love the most is no need of a closing tag. #ItJustFeelsRight™

snuggs commented 6 years ago

@brandondees @halorium @albertoponti @cristhiandick @tmornini @btakita @scottmacdowell @rianby64 addressed and documented in README. https://github.com/devpunks/snuggsi#remote-components