{"id":1456,"date":"2014-12-17T01:16:29","date_gmt":"2014-12-17T06:16:29","guid":{"rendered":"http:\/\/cbateman.com\/blog\/?p=1456"},"modified":"2015-05-13T22:16:03","modified_gmt":"2015-05-14T03:16:03","slug":"a-no-nonsense-guide-to-web-components-part-2-practical-use","status":"publish","type":"post","link":"https:\/\/cbateman.com\/blog\/a-no-nonsense-guide-to-web-components-part-2-practical-use\/","title":{"rendered":"A No-Nonsense Guide to Web Components, Part 2: Practical Use"},"content":{"rendered":"<p>This is Part 2 of a 3-part series.<\/p>\r\n<ul>\r\n<li><a href=\"http:\/\/cbateman.com\/blog\/a-no-nonsense-guide-to-web-components-part-1-the-specs\">Part 1: The Specs<\/a><\/li>\r\n<li>Part 2: Practical Use (Browser Support and Other Challenges)<\/li>\r\n<li><a href=\"http:\/\/cbateman.com\/blog\/web-components-in-angular-ember-and-react\/\">Part 3: Web Components in Angular, Ember, and React<\/a><\/li>\r\n<\/ul>\r\n<h2>Introduction<\/h2>\r\n<p>In Part 1 we learned how to code pure Web Components. But of course, adopting new web technologies is rarely painless, and Web Components are especially complicated. In this post, we&#8217;ll look at the current state of browsers, polyfills, performance, accessibility, progressive enhancement, and options for practical implementations.<\/p>\r\n\r\n<!--more-->\r\n\r\n<h2>Browser Support<\/h2>\r\n<p>For the latest status, check out <a href=\"http:\/\/jonrimmer.github.io\/are-we-componentized-yet\/\">Are We Componentized Yet<\/a> and <a href=\"http:\/\/caniuse.com\">caniuse<\/a>. But here&#8217;s the overall situation as of December 2014:<\/p>\r\n<p>All browsers (except for IE) have implemented HTML Templates. For the remaining specs:<\/p>\r\n<ul>\r\n<li><p><strong>Chrome<\/strong> completed and shipped all specs (enabled by default) by Chrome 36. <strong>Opera<\/strong> too.<\/p>\r\n<\/li>\r\n<li><p><strong>Firefox<\/strong> has implemented Custom Elements and Shadow DOM to some degree, but they are disabled by default behind the <code>dom.webcomponents.enabled<\/code> flag.<\/p>\r\n<p>Firefox will be shipping Custom Elements and Shadow DOM when they&#8217;re satisfied and confident in the specs. They will <strong>not<\/strong> be shipping HTML Imports, as they want to see what the landscape looks like after ES6 modules are shipped and utilized (<a href=\"https:\/\/hacks.mozilla.org\/2014\/12\/mozilla-and-web-components\/\">12\/15\/2014<\/a>).<\/p>\r\n<\/li>\r\n<\/ul>\r\n<!-- \"It is not being shipped because we are not confident in the current specification, we offer it as an experimental API to collect feedback from developers\" ([8\/15\/2014](https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=889230#c9))-->\r\n\r\n<ul>\r\n<li><p><strong>Safari<\/strong> removed Google&#8217;s Web Components code (in 2\/2014) leftover from before Blink forked, and has yet to begin any new implementations. While not opposed to implementing something in the future, they&#8217;re clearly not satisfied with the current specs.<\/p>\r\n<blockquote>\r\n<p>&#8220;There is no way I&#8217;m implementing the version [of Custom Elements] that&#8217;s incompatible with ES6 classes&#8221; (<a href=\"https:\/\/twitter.com\/ryosukeniwa\/status\/528774230532292609\">11\/1\/2014<\/a>)<\/p>\r\n<\/blockquote>\r\n<\/li>\r\n<li><p><strong>Internet Explorer<\/strong>&#8216;s <a href=\"https:\/\/status.modern.ie\/\">status for all specs<\/a> has been &#8220;under consideration&#8221; for about 6 months (as of 12\/2014). They&#8217;ve been pulling out some great stuff for <a href=\"http:\/\/blogs.msdn.com\/b\/ie\/archive\/2015\/01\/22\/project-spartan-and-the-windows-10-january-preview-build.aspx\">Spartan<\/a>, but so far it seems safe to say that Web Components won&#8217;t be there. However &ndash; they have said that they&#8217;re keeping it in mind as they build Spartan, which is pretty encouraging.<\/p>\r\n<\/li>\r\n<\/ul>\r\n<h3>What&#8217;s going on here?<\/h3>\r\n<p>When Chrome shipped Web Components \u2013 without a flag \u2013 <a href=\"http:\/\/w3cmemes.tumblr.com\/post\/75694930387\/googles-new-approach-to-standards-strives-for\">they<\/a> <a href=\"http:\/\/w3cmemes.tumblr.com\/post\/75701219807\/ghost-of-christmas-yet-to-come-hath-an-ultimatum\">kind<\/a> <a href=\"http:\/\/w3cmemes.tumblr.com\/post\/75681559100\/chrome-team-rapidly-evolving-into-superior-race\">of<\/a> <a href=\"http:\/\/w3cmemes.tumblr.com\/post\/75717243955\/live-from-the-uss-google-quick-update-on-shadow\">ticked<\/a> <a href=\"http:\/\/w3cmemes.tumblr.com\/post\/75719091769\/you-non-googlers-misunderstand-everything-we-say\">off<\/a> all the other browser makers, who felt that the specs weren&#8217;t done baking yet and that their feedback hadn&#8217;t been fully considered. They&#8217;re fairly diplomatic about the Chrome situation \u2013 routinely saying things like, &#8220;mail me privately for my feelings on the matter,&#8221; because their feelings probably involve a number of expletives.<\/p>\r\n<p>So Firefox and Safari aren&#8217;t shipping until they see the adjustments to the spec they want. Will those adjustments be possible when Chrome has already shipped? Will everyone get on the same page anytime soon? How&#8217;s it all going to end? I wish I had answers to these questions. Web standards are complicated things. Tune in next year (or the next) to find out!<\/p>\r\n<h2>Polyfills<\/h2>\r\n<p>In <a href=\"http:\/\/cbateman.com\/blog\/a-no-nonsense-guide-to-web-components-part-1-the-specs\">Part 1<\/a>, we looked at an example of a pure, native Web Component. If you were hoping that you could write code like that, plug in a polyfill, and be good to go \u2013 you better sit down, because I&#8217;ve got some bad news for you:<\/p>\r\n<p>It ain&#8217;t gonna happen. Some parts of Web Components simply aren&#8217;t possible\/reasonable to polyfill.<\/p>\r\n<p>But let&#8217;s examine each spec individually.<\/p>\r\n<p><a href=\"https:\/\/github.com\/webcomponents\/webcomponentsjs\">webcomponents.js<\/a> (which was previously part of <a href=\"https:\/\/www.polymer-project.org\/\">Polymer<\/a> as platform.js) is definitely the biggest game in town when it comes to polyfills, so that&#8217;s mainly what we&#8217;ll be talking about.<\/p>\r\n<h3>Custom Elements (<a href=\"http:\/\/caniuse.com\/#feat=custom-elements\">native support<\/a>)<\/h3>\r\n<p>Custom Elements is relatively easy to polyfill \u2013 down to IE 9 and Android 2.2, if you use <a href=\"https:\/\/github.com\/WebReflection\/document-register-element\">document-register-element<\/a> for the polyfill (and it&#8217;s only 3KB gzipped too!). The webcomponents.js polyfill works down to Android 4.1.<\/p>\r\n<p><strong>Caveat<\/strong>: No support for the <code>:unresolved<\/code> pseudo class.<\/p>\r\n<h3>HTML Templates (<a href=\"http:\/\/caniuse.com\/#feat=template\">native support<\/a>)<\/h3>\r\n<p>The webcomponents.js polyfill works down to IE 9 and Android 2 (and isn&#8217;t needed in other modern browsers).<\/p>\r\n<p><strong>Caveat<\/strong>: Polyfilled templates aren&#8217;t truly inert \u2013 resources like images will still download.<\/p>\r\n<h3>HTML Imports (<a href=\"http:\/\/caniuse.com\/#feat=imports\">native support<\/a>)<\/h3>\r\n<p>The webcomponents.js polyfill works down to IE 9 and Android 4.4 (some things like CSS references work down to Android 4.1, but there&#8217;s other bugginess), and in other modern browsers.<\/p>\r\n<p><strong>Caveat #1<\/strong>: Polyfilled imports load asynchronously, even if you didn&#8217;t add the <code>async<\/code> attribute. <\/p>\r\n<p><strong>Caveat #2<\/strong>: They load via XHR \u2013 which isn&#8217;t great for performance.<\/p>\r\n<p><strong>Caveat #3<\/strong>: <code>document.currentScript<\/code>, which is needed in the import to access templates (or other elements) in the import, can&#8217;t be polyfilled. It is, however, shimmed with <code>_currentScript<\/code>. So, to write code that works in both supported and polyfilled browsers, you must do this:<\/p>\r\n<pre class=\"pre\"><code class=\"lang-javascript\">document._currentScript || document.currentScript;<\/code><\/pre>\r\n<h3>Shadow DOM (<a href=\"http:\/\/caniuse.com\/#feat=shadowdom\">native support<\/a>)<\/h3>\r\n<p>It is not reasonable\/possible to polyfill Shadow DOM, thanks to its fancy encapsulation features. You just can&#8217;t fake the behavior of a shadow root.<\/p>\r\n<p>The webcomponents.js code <em>attempts<\/em> to shim <em>some<\/em> of the encapsulation features by way of rewriting your CSS. It polyfills Shadow DOM&#8217;s JavaScript API, querySelector and other DOM APIs to behave (hopefully) as they should, and it moves elements selected with the <code>&lt;content&gt;<\/code> tag to where the shadow root would be.<\/p>\r\n<p><strong>Caveats:<\/strong><\/p>\r\n<ul>\r\n<li>CSS rules in the page will still apply to elements in the (fake) shadow root. It&#8217;s like everything gets a <code>\/deep\/<\/code>.<\/li>\r\n<li>To use <code>::shadow<\/code> and <code>\/deep\/<\/code> CSS rules in the page, you must add the <code>shim-shadowdom<\/code> attribute to the <code>&lt;style&gt;<\/code> or <code>&lt;link&gt;<\/code>.<ul>\r\n<li>Even then, <code>::shadow<\/code> rules will behave like <code>\/deep\/<\/code> rules anyhow.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>To include CSS in a shadow root, you have to add some JS to the component to check whether the Shadow Dom shim is in effect, and if so, grab the css text, run it through a shimming function, add the resulting CSS text to the document, and delete the original CSS.<ul>\r\n<li>You don&#8217;t have to do all this if you&#8217;re using Polymer and its wrapper\/syntax. But native syntax doesn&#8217;t cut it.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<li><code>::content<\/code> rules in the shadow root will apply to everything in the shadow root \u2013 not just children of <code>&lt;content&gt;<\/code> elements.<\/li>\r\n<li>When using <code>&lt;content&gt;<\/code>, DOM hierchy will be different in polyfilled browsers. Normally, elements selected by <code>&lt;content&gt;<\/code> would be children of the root element, but in polyfilled browsers they will be children of the <code>&lt;content&gt;<\/code>&#8216;s parent. You can certainly work around this \u2013 but it&#8217;s an additional thing to keep in mind as you write your component&#8217;s JS.<\/li>\r\n<\/ul>\r\n<h3>Polyfill Sizes<\/h3>\r\n<p>webcomponents.js includes a &#8220;lite&#8221; file which includes Custom Elements, Templates, and Imports at 9KB, gzipped.<\/p>\r\n<p>Adding Shadow DOM brings the polyfill total up to 30KB, gzipped (it&#8217;s 103KB minified, without gzipping, by the way. TJ VanToll has written about <a href=\"http:\/\/developer.telerik.com\/featured\/web-components-arent-ready-production-yet\/\">why it&#8217;s so large<\/a>).<\/p>\r\n<h2>Performance &amp; HTML Imports<\/h2>\r\n<p>An HTML Import for a component contains individual links to all of the component&#8217;s dependencies. This really isn&#8217;t consistent with today&#8217;s practice of concatenating JS and CSS files, to keep the number of HTTP requests down.<\/p>\r\n<p>Imports were designed with HTTP\/2 in mind (which basically makes it fine to skip concatenation by way of multiplexing). Unfortunately, not every hosting provider, CDN, or server supports it yet. Browser support for SPDY (the predecessor to HTTP\/2&#8217;s multiplexing) <a href=\"http:\/\/caniuse.com\/#search=spdy\">isn&#8217;t too shabby<\/a>, but there are still some issues (mostly older IE, and IE 11 on Windows 7-). If you&#8217;re ready to go all HTTP\/2 \u2013 you&#8217;re in good shape to use HTML Imports.<\/p>\r\n<p>If not \u2013 Polymer does have a tool called <a href=\"https:\/\/github.com\/polymer\/vulcanize\">Vulcanize<\/a> that concatenates Import files \u2013 but there are some gotchas:<\/p>\r\n<ul>\r\n<li>Since all the Imports&#8217; contents will get lumped together, you must ensure there are no duplicated element IDs (mainly used for templates) between Imports.<\/li>\r\n<li><code>document.currentScript.ownerDocument<\/code> will point to the importing page&#8217;s document, rather than the (original) import document.<\/li>\r\n<li>Anything in the import other than templates, CSS, and JS will be removed. Which is probably fine if you&#8217;re just using Imports for Web Components.<\/li>\r\n<\/ul>\r\n<p><strong>Bottom line:<\/strong> The general point of Imports is to give you an <em>easy<\/em> way to get a component&#8217;s dependencies on a page. If you already have a solution for that \u2013 then you can just stick with it. You won&#8217;t have a good place to store HTML Templates, but that&#8217;s okay because <a href=\"http:\/\/tc39wiki.calculist.org\/es6\/template-strings\/\">ES6 template strings<\/a> will make templates in JS much less painful.<\/p>\r\n<h2>Accessibility<\/h2>\r\n<p>Accessibility with Web Components really isn&#8217;t any different than accessibility with any other kind of UI components. <\/p>\r\n<p>Yes, Web Components can make it convenient to reimplement native elements, but you certainly don&#8217;t <em>have<\/em> to, and people already do that without &#8220;Web Components&#8221; when the native element doesn&#8217;t provide the flexibility they want. We&#8217;ve seen custom dropdowns, range sliders, radios, checkboxes, buttons, and more. Some accessible, and some not so much.<\/p>\r\n<p>The same principle applies, whether it&#8217;s a &#8220;Web Component&#8221; or not: <strong>You must add the appropriate accessibility features to anything you build (whether you&#8217;re reimplementing a native element or not).<\/strong> And that often means <a href=\"http:\/\/www.paciellogroup.com\/blog\/2014\/08\/what-aria-does-not-do\/\">more work<\/a> than just adding an ARIA role. Here&#8217;s a <a href=\"http:\/\/www.paciellogroup.com\/blog\/2014\/09\/web-components-punch-list\/\">handy checklist<\/a>, courtesy of Steve Faulkner.<\/p>\r\n<p>And don&#8217;t forget \u2013 while Custom Elements lets you create new elements:<\/p>\r\n<pre class=\"pre\"><code class=\"lang-markup\">&lt;ul&gt;\r\n    &lt;crazy-li role=\"listitem\"&gt;First&lt;\/crazy-li&gt;\r\n&lt;\/ul&gt;<\/code><\/pre>\r\n<p style=\"text-decoration:line-through;\">It also allows you to extend native elements \u2013 saving you the trouble of reimplementing built-in accessibility features:<\/p>\r\n<pre class=\"pre\" style=\"text-decoration:line-through;\"><code class=\"lang-markup\">&lt;ul&gt;\r\n    &lt;li is=\"crazy-li\"&gt;First&lt;\/li&gt;\r\n&lt;\/ul&gt;<\/code><\/pre>\r\n<p><strong>Update 4\/2015:<\/strong> Extending with <code>is=<\/code> will most likely be <a href=\"https:\/\/lists.w3.org\/Archives\/Public\/public-webapps\/2015AprJun\/0515.html\">removed<\/a> from the spec. It may return in a different form, in a future version.<\/p>\r\n<p><strong>Bottom line:<\/strong> if you can write an accessible regular component, then you can write an accessible Web Component. Whatever you&#8217;re doing \u2013 make it accessible!<\/p>\r\n<h2>Progressive Enhancement<\/h2>\r\n<p>If you haven&#8217;t noticed yet \u2013 Web Components rely on JavaScript. Substantially. Imports is the only part that really works without JS. There are differing opinions on progressive enhancement, but let&#8217;s just examine a couple approaches from a high level, so that you can make the best decision for your situation:<\/p>\r\n<h3>Component renders its own internal HTML.<\/h3>\r\n<p>This is kind of the assumed default with Web Components.<\/p>\r\n<p><strong>Good:<\/strong> <\/p>\r\n<ul>\r\n<li>Page markup is clean, understandable, and simple<\/li>\r\n<li>Components&#8217; internal HTML can be easily updated on all pages<\/li>\r\n<\/ul>\r\n<p><strong>Bad:<\/strong> <\/p>\r\n<ul>\r\n<li>No JS = empty component<\/li>\r\n<li>A synchronous-loading component (in the <code>&lt;head&gt;<\/code>) will slow down the page&#8217;s initial render time<\/li>\r\n<li>An asynchronous-loading component will pop into existence after the initial render (Flash of Loaded Component &#8211; FOLC? Or FOCL?)<\/li>\r\n<\/ul>\r\n<h3>Server includes the component&#8217;s internal markup.<\/h3>\r\n<p>This still lets you take advantage of Custom Elements&#8217; lifecycle callbacks and element functions, while maintaining progressive enhancement.<\/p>\r\n<p><strong>Good:<\/strong><\/p>\r\n<ul>\r\n<li>Faster initial render<\/li>\r\n<li>No FOLC<\/li>\r\n<li>All markup is there if JS fails<\/li>\r\n<\/ul>\r\n<p><strong>Bad:<\/strong> <\/p>\r\n<ul>\r\n<li>Page markup is messy again<\/li>\r\n<li>Updating a component&#8217;s HTML means updating every page&#8217;s markup (unless you build something server-side to automate it)<\/li>\r\n<li>Shadow DOM will probably need to sit this one out<\/li>\r\n<li>The CSS needs to load in the <code>&lt;head&gt;<\/code>, of course. So if you&#8217;re using an Import, it&#8217;ll need to be synchronous (and maybe load its JS <em>asynchronously<\/em>, for performance).<ul>\r\n<li>But remember the Imports polyfill doesn&#8217;t do synchronous loading.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<h2>What to Do?<\/h2>\r\n<p>First off &ndash; the lack of consensus between the browser makers is concerning, all-around. Firefox and Safari (and probably IE) want to see changes made before they ship &ndash; but we don&#8217;t know what those changes will look like.<\/p>\r\n<p>Having said that, here are my overall conclusions:<\/p>\r\n<ul>\r\n<li><strong>Custom Elements<\/strong> are helpful, and fairly easy to polyfill.<\/li>\r\n<li><strong>HTML Imports<\/strong> have too many caveats right now (particularly around performance), polyfill browser support isn&#8217;t ideal, and Firefox isn&#8217;t going to do it (I kind of doubt Safari will either). They may be right that it&#8217;s too soon to try to finalize this solution. In the meanwhile \u2013 our current solutions for including resources will have to do.<\/li>\r\n<li>Polyfilled <strong>Shadow DOM<\/strong> has <em>way<\/em> too many caveats, and the polyfill is big (and especially slow on mobile devices). Shadow DOM will be useful someday when broad browser support is available.<\/li>\r\n<\/ul>\r\n<p>So I&#8217;m left with Custom Elements (this was TJ&#8217;s <a href=\"http:\/\/developer.telerik.com\/featured\/web-components-ready-production\/\">conclusion<\/a> as well). It&#8217;s the only spec that&#8217;s polyfillable on all the older platforms I&#8217;d like to support (IE 9, Android 4.3 and below, etc.) and I love the public API, the lifecycle callbacks, and the &#8220;semantic&#8221; and clean way you use them on a page.<\/p>\r\n<p>My suggestion is to give Custom Elements a try, and see if they make life easier for you. However, if you&#8217;re going to put Custom Elements into production &ndash; be sure to use a wrapper around the native API so that you can easily make changes if they become necessary. And it&#8217;s probably best to avoid extending native elements (using the <code>is<\/code> attribute).<\/p>\r\n\r\n<p>To learn about using Custom Elements in JS frameworks, head over to <a href=\"http:\/\/cbateman.com\/blog\/web-components-in-angular-ember-and-react\/\">Part 3<\/a>.<\/p>\r\n\r\n<h3>A note on frameworks\/libraries<\/h3>\r\n<p>Libaries like <a href=\"https:\/\/www.polymer-project.org\/\">Polymer<\/a> were developed to solve a number of common tasks related to Custom Elements (and the other specs). Things like easy attribute binding, smarter templating, and events.<\/p>\r\n<p>However, they almost seem to violate one of the objectives of Web Components, which is reusability. If you want to build a component that can be reused in a variety of environments, keeping your dependencies to a minimum is usually a good thing. I&#8217;m not sure I&#8217;m comfortable with forcing another largeish (Polymer is ~37KB, gzipped) dependency on everyone who might want to use my component. <\/p>\r\n<p>But if you want to develop components to be used in environments that <em>you<\/em> control, I&#8217;d feel much better about a library like Polymer &ndash; and it&#8217;d probably be fairly helpful. <a href=\"http:\/\/x-tags.org\/\">X-Tag<\/a> is another alternative which provides a neat wrapper for creating Custom Elements (and they don&#8217;t even bother with Shadow DOM, which is fine by me). And if you really want to start writing CSS for Shadow DOM, you might want to take a look at <a href=\"http:\/\/bosonic.github.io\/\">Bosonic<\/a>, which transpiles your CSS on the server (rather than in the browser, as Polymer does).<\/p>\r\n\r\n<p><strong>Meta:<\/strong> If I&#8217;ve gotten anything flat-out wrong, missed something, or if you have any suggestions, please leave a comment or let me know on Twitter!<\/p>\r\n\r\n<p><strong>Update 2\/2015:<\/strong> Browser makers are having a hard time coming to an agreement on how Custom Elements should work under the hood. Particularly around the extension of native elements (using the <code>is=<\/code> attribute). It&#8217;s possible that they may decide to remove that feature so that they can ship a spec they all agree on. Anne van Kesteren <a href=\"https:\/\/annevankesteren.nl\/2015\/01\/dom-custom-elements\">warns<\/a> that the updates &#8220;will likely be incompatible with what is out there today.&#8221; See <a href=\"https:\/\/wiki.whatwg.org\/wiki\/CustomElements\">here<\/a> for more details. There&#8217;s also been a lot of Shadow DOM discussion and not a ton of agreement &ndash; here&#8217;s the <a href=\"https:\/\/github.com\/w3c\/webcomponents\/wiki\/Shadow-DOM:-Contentious-Bits\">current status<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>This is Part 2 of a 3-part series. Part 1: The Specs Part 2: Practical Use (Browser Support and Other Challenges) Part 3: Web Components in Angular, Ember, and React Introduction In Part 1 we learned how to code pure Web Components. But of course, adopting new web technologies is rarely painless, and Web Components are especially complicated. In this&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1456","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/posts\/1456"}],"collection":[{"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/comments?post=1456"}],"version-history":[{"count":40,"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/posts\/1456\/revisions"}],"predecessor-version":[{"id":1578,"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/posts\/1456\/revisions\/1578"}],"wp:attachment":[{"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/media?parent=1456"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/categories?post=1456"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cbateman.com\/blog\/wp-json\/wp\/v2\/tags?post=1456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}