<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bryan Ollendyke</title>
    <description>The latest articles on DEV Community by Bryan Ollendyke (@btopro).</description>
    <link>https://dev.to/btopro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F254952%2F8afc72b4-48bc-47b5-9535-293ac7d7990d.png</url>
      <title>DEV Community: Bryan Ollendyke</title>
      <link>https://dev.to/btopro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/btopro"/>
    <language>en</language>
    <item>
      <title>Web component Q&amp;A with.. well. me.</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Tue, 26 Aug 2025 18:30:24 +0000</pubDate>
      <link>https://dev.to/btopro/web-component-qa-with-well-me-10e4</link>
      <guid>https://dev.to/btopro/web-component-qa-with-well-me-10e4</guid>
      <description>&lt;p&gt;&lt;a href="https://hax.psu.edu" rel="noopener noreferrer"&gt;https://hax.psu.edu&lt;/a&gt; is built entirely using web components, as well as &lt;a href="https://github.com/haxtheweb" rel="noopener noreferrer"&gt;the whole hax ecosystem&lt;/a&gt;. All of our "apps" are web components, haxcms being the biggest one which is entirely web components. I often get asked, how is this possible. So here's the questions asked as well as my answers from a recent Slack message in Apereo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;how do you manage state across an app made up of plenty of web components?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We only use web components. hard stop. "apps" become:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;more and more web components
State is managed by MobX for a larger data store as in the case of hax editor or haxcms as app
router is vaadin-router so it's also a web component in a singleton pattern&lt;/li&gt;
&lt;li&gt;Singleton patterns are used for things that would be good to check in with. For example dialog, tooltip, toast as obvious ones but less obvious would be translation engine, text editor highlight selection menu. Screenshot shows several sub-system style singletons that are in haxcms&lt;/li&gt;
&lt;li&gt;Theme are in effect web components that boil up to a single tag, you can actually modify them in any hax site by inspecting the DOM and typing the name of another known element and watch the state management not be impacted. It is a lot of abstraction of theme from store to make work but it's awesome 🙂 . The CMS is in effect, storing all the mobx / state work and ensuring that the router is talked to to generate the CMS content for the site&lt;/li&gt;
&lt;li&gt;Theme development gets into mobx in our docs - &lt;a href="https://haxtheweb.org/documentation/developers/haxsite/data-store" rel="noopener noreferrer"&gt;https://haxtheweb.org/documentation/developers/haxsite/data-store&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;how do you manage communication, if a web component instance used by another which is used by another (e.g. an form in a feed shown in a content area) needs to communicate with a web component that is a deep decendant of another (e.g. a statusbar inside a footer)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If things need to talk to each other the two common patterns we use above come into play. If it's integral to the app then a prop in a mobx store. Singletons can be called directly via requestAvailability call on the global window, or we'll use events for bubble up / communication sorta stuff. Click a button, button emits the simple-modal-show event, the singleton is listening on the window and reacts to the event. This can also just be collapsed items that bubble up a message with parents listening. When the elements are completely disconnected from one another, singleton has worked great so that we can keep things abstracted.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can read more about our i18n singleton here -- &lt;a href="https://dev.to/btopro/i18n-in-web-components-2gii"&gt;https://dev.to/btopro/i18n-in-web-components-2gii&lt;/a&gt;
micro front-ends in our setup are also talked to via a singleton so - might be interesting stuff here -- &lt;a href="https://dev.to/btopro/building-micro-frontends-with-vercel-lit-45oe"&gt;https://dev.to/btopro/building-micro-frontends-with-vercel-lit-45oe&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;if you want to use lots of web components in your page and they are made up of web components, but 2 of them happen to have dependencies which use html snippets that use a  tag, but both web components mean different web components with that tag... basically a name clash&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You are correct. We have ensured we don't make clashing name space elements. There's patching to allow this in the spec and future spec work to make it native, but regardless we just don't get into name clashes. In part because of how we don't bundle our assets so we only ship 1 copy of (for example) lit. There are not multiple copies of lit in what sits on the CDN / local unbundled copy of assets.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;magic script talks about this process; we've enhanced it but use the same methodology invented in 2020 &lt;a href="https://dev.to/btopro/part-1-how-penn-state-unbundles-web-components-for-cdn-deployments-20di"&gt;https://dev.to/btopro/part-1-how-penn-state-unbundles-web-components-for-cdn-deployments-20di&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Our monorepo helps make sure everything keeps in sync as well as our build scripts which compile everything in place using a fork of polymer CLI tools (deprecated but only thing I can find that provides the functionality) and then terser to minify in place - &lt;a href="https://github.com/haxtheweb/webcomponents" rel="noopener noreferrer"&gt;https://github.com/haxtheweb/webcomponents&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Let's make a custom theme in hax</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Thu, 03 Apr 2025 19:22:30 +0000</pubDate>
      <link>https://dev.to/btopro/lets-make-a-custom-theme-in-hax-40n3</link>
      <guid>https://dev.to/btopro/lets-make-a-custom-theme-in-hax-40n3</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Gxbk43E1hB0"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
Site created in the tutorial: &lt;a href="https://haxcli-customthemetutorialfdsdfs.surge.sh/" rel="noopener noreferrer"&gt;https://haxcli-customthemetutorialfdsdfs.surge.sh/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial I'll walk through the process of building a custom theme in HAX. HAX allows you to visually edit web component based content in a flat-file site.&lt;/p&gt;
&lt;h1&gt;
  
  
  Get the CLI
&lt;/h1&gt;

&lt;p&gt;First let's install the &lt;code&gt;hax&lt;/code&gt; command-line interface tool&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; @haxtheweb/create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, run &lt;code&gt;hax start&lt;/code&gt; to see available options and select "Create a HAXsite"&lt;/li&gt;
&lt;li&gt;Answer where you want the project to live, it's name, author, and then select "Create Custom Theme"&lt;/li&gt;
&lt;li&gt;Name your theme, one will be suggested that starts with &lt;code&gt;custom-NAMEOFYOURSITE-theme&lt;/code&gt;. It is not required to start with &lt;code&gt;custom-&lt;/code&gt; or end in &lt;code&gt;-theme&lt;/code&gt; at this time but it recommended for name spacing.&lt;/li&gt;
&lt;li&gt;After this select the template you want to use. We recommend the &lt;code&gt;Vanilla Theme&lt;/code&gt; if this is your first time. Then Launch your project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your prompt should look like this for our tutorial:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jm6o4d4mshgs78040vn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jm6o4d4mshgs78040vn.png" alt="Tutorial with all options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next time if you wanted to script all that automatically you could have run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hax site customthemetutorial &lt;span class="nt"&gt;--theme&lt;/span&gt; &lt;span class="s2"&gt;"custom-theme"&lt;/span&gt; &lt;span class="nt"&gt;--custom-theme-name&lt;/span&gt; &lt;span class="s2"&gt;"custom-tutorial-theme"&lt;/span&gt; &lt;span class="nt"&gt;--custom-theme-template&lt;/span&gt; &lt;span class="s2"&gt;"base"&lt;/span&gt; &lt;span class="nt"&gt;--y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After it has finished creating the files you should get a browser window that opens and looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwor0w1gb8mjnkqra060f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwor0w1gb8mjnkqra060f.png" alt="HAXsite running"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Now the fun part
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Open VSCode to the location you created your site&lt;/li&gt;
&lt;li&gt;Open a new terminal window and do &lt;code&gt;cd custom&lt;/code&gt; followed by &lt;code&gt;npm start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You now have 2 terminals running. 1 that is monitoring files for changes to your custom theme (building it), and another that is serving the site so that you can edit it&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;custom/src/custom-tutorial-theme.js&lt;/code&gt; and edit as desired&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Things of note
&lt;/h1&gt;

&lt;p&gt;This is boilerplate, but is a custom theme. You can remove and add whatever as well as create additional elements if it makes it easier to develop your theme. &lt;a href="https://haxtheweb.org/documentation/developers/haxsite" rel="noopener noreferrer"&gt;The best way to learn theme development is to read through our documentation on theme development&lt;/a&gt;, especially around MobX, considerations for any HAXsite theme, and the available reusable &lt;code&gt;site-&lt;/code&gt; elements that already exist to simplify the process of creating new themes.&lt;/p&gt;

&lt;h1&gt;
  
  
  Publishing to surge.sh
&lt;/h1&gt;

&lt;p&gt;Due to this being so new, we have to manually edit 1 file in order to have our site work on surge.sh with it's custom theme. &lt;a href="https://haxtheweb.org/documentation/developers/haxsite/custom-theme-development#header-ccf54a40-a9cb-12db-cf31-10298e9917dc" rel="noopener noreferrer"&gt;You can find documentation on this here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;index.html&lt;/code&gt; and change the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      "@haxtheweb/": "./build/es6/node_modules/@haxtheweb/"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to say&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      "@haxtheweb/": "https://cdn.hax.cloud/cdn/build/es6/node_modules/@haxtheweb/"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;surge .&lt;/code&gt; If it says command not found, first install surge by running &lt;code&gt;npm install --global surge&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After this is published and you have an address, switch that line in the index.html back to what it was. This is a problem we are actively looking for a solution to but involves wrapping publishing into the CLI which is a larger task to script right now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Source of that file
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Copyright 2025 btopro
 * @license Apache-2.0, see License.md for full text.
 */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HAXCMSLitElementTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unsafeCSS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autorun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toJS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@haxtheweb/haxcms-elements/lib/core/HAXCMSLitElementTheme.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@haxtheweb/haxcms-elements/lib/ui-components/navigation/site-menu-button.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@haxtheweb/haxcms-elements/lib/ui-components/site/site-title.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@haxtheweb/haxcms-elements/lib/ui-components/active-item/site-active-title.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * `CustomTutorialTheme`
 * `CustomTutorialTheme based on HAXCMS theming ecosystem`
 * `This theme is an example of extending an existing theme component`
 *
 * @microcopy - language worth noting:
 *  - HAXcms - A headless content management system
 *  - HAXCMSLitElementTheme - A class that provides correct baseline wiring to build a new theme that HAX can use
 *
 * @documentation - see HAX docs to learn more about theming
 *  - Custom theme development - https://haxtheweb.org/documentation/developers/haxsite/custom-theme-development
 *  - Theme Blocks - https://haxtheweb.org/documentation/developers/theme-blocks
 *  - DDD - https://haxtheweb.org/documentation/ddd
 *  - Data Store - https://haxtheweb.org/documentation/developers/haxsite/data-store
 * @element custom-tutorial-theme
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomTutorialTheme&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HAXCMSLitElementTheme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Store the tag name to make it easier to obtain directly.
   * @notice function name must be here for tooling to operate correctly
   */&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom-tutorial-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// set defaults or tie into the store&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;autorun&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toJS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toJS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// properties to respond to the activeID and list of items&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;activeId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// allows for global styles to be set against the entire document&lt;/span&gt;
  &lt;span class="c1"&gt;// you can also use this to cascade styles down to the theme&lt;/span&gt;
  &lt;span class="c1"&gt;// but the more common reason is to influence the body or other things&lt;/span&gt;
  &lt;span class="c1"&gt;// put into the global index.html context by the system itself&lt;/span&gt;
  &lt;span class="nc"&gt;HAXCMSGlobalStyleSheetContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HAXCMSGlobalStyleSheetContent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
      :root {
        --my-theme-low-tone: var(--ddd-theme-default-slateMaxLight);
        --my-theme-high-tone: var(--ddd-theme-default-coalyGray);
      }
      body {
        padding: var(--ddd-spacing-0);
        margin: var(--ddd-spacing-0);
        background-color: var(--my-theme-low-tone);
      }
      body.dark-mode {
        background-color: var(--my-theme-high-tone);
      }
      `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;//styles function&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
        :host {
          display: block;
          padding: var(--ddd-spacing-10) var(--ddd-spacing-20);
          max-width: 960px;
          min-width: 400px;
          margin: var(--ddd-spacing-0) auto;
          border: var(--ddd-border-lg);
          border-width: var(--ddd-spacing-5);
          border-radius: var(--ddd-radius-lg);
          background-color: light-dark(var(--my-theme-low-tone), var(--my-theme-high-tone));
          color: light-dark(var(--my-theme-high-tone), var(--my-theme-low-tone));
        }
        .wrapper {
          border-radius: var(--ddd-radius-lg);
        }

        site-title {
          font-size: var(--ddd-font-size-l);
        }

        header {
          display: flex;
        }
        ul {
          margin: var(--ddd-spacing-0);
          padding: var(--ddd-spacing-0);
        }
        ul li {
          display: inline-block;
          margin: var(--ddd-spacing-0);
          padding: var(--ddd-spacing-0);
          list-style-type: none;
          vertical-align: top;
        }
        ul li a {
          display: block;
        }

        button {
          height: var(--ddd-spacing-8);
          width: var(--ddd-spacing-8);
          margin: var(--ddd-spacing-0);
          padding: 0;
          font-size: var(--ddd-font-size-sm);
          cursor: pointer;
        }

        .active button {
          background-color: light-dark(var(--my-theme-low-tone), var(--my-theme-high-tone));
          color: light-dark(var(--my-theme-high-tone), var(--my-theme-low-tone));
          font-weight: bold;
        }

        site-menu-button {
          display: inline-block;
          vertical-align: top;
        }
      `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;div class="wrapper"&amp;gt;
    &amp;lt;header&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;
          &amp;lt;site-menu-button
            type="prev"
            position="top"
          &amp;gt;&amp;lt;/site-menu-button&amp;gt;
        &amp;lt;/li&amp;gt;
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
        &amp;lt;li class="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeId&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
          &amp;lt;a href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;&amp;lt;button title="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/button&amp;gt;&amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
      `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
        &amp;lt;li&amp;gt;
          &amp;lt;site-menu-button
            type="next"
            position="top"
          &amp;gt;&amp;lt;/site-menu-button&amp;gt;
        &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/header&amp;gt;
    &amp;lt;main&amp;gt;
      &amp;lt;site-active-title&amp;gt;&amp;lt;/site-active-title&amp;gt;
      &amp;lt;article&amp;gt;
        &amp;lt;!-- this block and names are required for HAX to edit the content of the page. contentcontainer, slot, and wrapping the slot. --&amp;gt;
        &amp;lt;div id="contentcontainer"&amp;gt;&amp;lt;div id="slot"&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/article&amp;gt;
    &amp;lt;/main&amp;gt;
    &amp;lt;footer&amp;gt;
      &amp;lt;slot name="footer"&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;/footer&amp;gt;
  &amp;lt;/div&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CustomTutorialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CustomTutorialTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CustomTutorialTheme&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>haxtheweb</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>link-preview-card</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Tue, 01 Apr 2025 20:03:52 +0000</pubDate>
      <link>https://dev.to/btopro/link-preview-card-3502</link>
      <guid>https://dev.to/btopro/link-preview-card-3502</guid>
      <description>&lt;h1&gt;
  
  
  Web component
&lt;/h1&gt;

&lt;p&gt;link-preview-card&lt;/p&gt;

&lt;h1&gt;
  
  
  Scope
&lt;/h1&gt;

&lt;p&gt;Hit an open-api web service and render the metadata of the link in order to preview it. Common in text editors / social media sites.&lt;/p&gt;

&lt;h1&gt;
  
  
  Github Issue
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/haxtheweb/issues/issues/1764" rel="noopener noreferrer"&gt;https://github.com/haxtheweb/issues/issues/1764&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Demo
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://link-preview-card-one.vercel.app/" rel="noopener noreferrer"&gt;https://link-preview-card-one.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Student submission
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/AlexM-117/link-preview-card" rel="noopener noreferrer"&gt;https://github.com/AlexM-117/link-preview-card&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Accepted Solution
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/rich-text-editor/lib/open-apis/link-preview-card.js" rel="noopener noreferrer"&gt;https://github.com/haxtheweb/webcomponents/blob/master/elements/rich-text-editor/lib/open-apis/link-preview-card.js&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>api</category>
      <category>github</category>
      <category>webdev</category>
    </item>
    <item>
      <title>github-rpg-contributors</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Wed, 26 Mar 2025 20:59:16 +0000</pubDate>
      <link>https://dev.to/btopro/hax-sip-github-rpg-contributors-24ka</link>
      <guid>https://dev.to/btopro/hax-sip-github-rpg-contributors-24ka</guid>
      <description>&lt;h1&gt;
  
  
  Web component
&lt;/h1&gt;

&lt;p&gt;github-rpg-contributors&lt;/p&gt;

&lt;h1&gt;
  
  
  Scope
&lt;/h1&gt;

&lt;p&gt;hit the open github API and return a list of users that contribute to a project in an organization. Render these using the &lt;code&gt;@haxtheweb/rpg-character&lt;/code&gt; element&lt;/p&gt;

&lt;h1&gt;
  
  
  Github Issue
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/haxtheweb/issues/issues/1464" rel="noopener noreferrer"&gt;https://github.com/haxtheweb/issues/issues/1464&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Demo
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github-rpg-contributors-navy.vercel.app/" rel="noopener noreferrer"&gt;https://github-rpg-contributors-navy.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Student submission
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/Tushar1906/github-rpg-contributor" rel="noopener noreferrer"&gt;https://github.com/Tushar1906/github-rpg-contributor&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Video discussing the process and cleaning up
&lt;/h1&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/daO3Pt_mDDU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Accepted Solution
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/github-preview/lib/github-rpg-contributors.js" rel="noopener noreferrer"&gt;https://github.com/haxtheweb/webcomponents/blob/master/elements/github-preview/lib/github-rpg-contributors.js&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>haxtheweb</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>HAX Student Innovation Pipeline (11)</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Wed, 26 Mar 2025 20:53:11 +0000</pubDate>
      <link>https://dev.to/btopro/hax-student-innovation-pipeline-4oj4</link>
      <guid>https://dev.to/btopro/hax-student-innovation-pipeline-4oj4</guid>
      <description>&lt;p&gt;I teach modern web development at Penn State. As part of that, students learn about HTML,CSS and JS using the webcomponent standard as the ultimate vehicle for doing so. We start at the basic three and work our way into web components using the &lt;a href="https://haxtheweb.org/" rel="noopener noreferrer"&gt;HAX ecosystem and tooling as a backdrop&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; @haxtheweb/create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we learn the standards and do example projects to get a feel for modern development, we then turn toward solving real problems in the HAX ecosystem. These typically are small in scope, individual web components that we need to make using our design system or other criteria.&lt;/p&gt;

&lt;p&gt;This dev.to series will document the projects that get added to the HAX ecosystem primarily through teaching students how to do modern web development.&lt;/p&gt;

&lt;p&gt;You can learn more about the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://haxtheweb.org/hax-lab/student-innovation-pipeline" rel="noopener noreferrer"&gt;HAX Student Innovation Pipeline here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hax.cloud" rel="noopener noreferrer"&gt;HAX cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hax.psu.edu" rel="noopener noreferrer"&gt;HAX dot PSU&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>haxtheweb</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Web Component shadow roots x design system. Constructable style sheets?</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Thu, 06 Mar 2025 16:16:08 +0000</pubDate>
      <link>https://dev.to/btopro/web-component-shadow-roots-x-design-system-constructable-style-sheets-4g60</link>
      <guid>https://dev.to/btopro/web-component-shadow-roots-x-design-system-constructable-style-sheets-4g60</guid>
      <description>&lt;p&gt;Recently I fielded a question &lt;a href="https://github.com/haxtheweb/issues/issues/2237" rel="noopener noreferrer"&gt;issue&lt;/a&gt; from a student about how we handle "global styles" when working in the &lt;a href="https://playground.hax.cloud/site.html" rel="noopener noreferrer"&gt;HAXcms theme layer&lt;/a&gt;. This question is a common one I've found across developers of all experience levels due to the nature of two seemingly conflicting concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS. Cascading, Style, Sheets.&lt;/li&gt;
&lt;li&gt;Design systems leveraging the above&lt;/li&gt;
&lt;li&gt;ShadowRoot - a controversial aspect of web components that breaks the cascade to allow locally scoped styles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As HAX project lead, our platform works exclusively through the building and nesting of 1,000s of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components" rel="noopener noreferrer"&gt;Web Components&lt;/a&gt; and most (~95%) use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot" rel="noopener noreferrer"&gt;ShadowRoots&lt;/a&gt; via &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit / LitElement&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  How we manage local scoping vs global scoping
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet" rel="noopener noreferrer"&gt;Constructable style sheets&lt;/a&gt; is an API that allows for not just treating the style sheet as text which is then leveraged as text in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag to be applied as CSS; it treats the style sheet as a full JavaScript class / object.&lt;/p&gt;

&lt;p&gt;This allows for programmatically producing and sharing the style sheet around as a singular object. Lit / Google have some great articles about this so I'll link to these for background reading / more code detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lit.dev/articles/lit-cheat-sheet/#importing-styles-as-a-string" rel="noopener noreferrer"&gt;https://lit.dev/articles/lit-cheat-sheet/#importing-styles-as-a-string&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/constructable-stylesheets" rel="noopener noreferrer"&gt;https://web.dev/articles/constructable-stylesheets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  So what does it look like?
&lt;/h1&gt;

&lt;p&gt;In HAX, we have a designs system called &lt;a href="https://haxtheweb.org/documentation/ddd" rel="noopener noreferrer"&gt;Development, Design, Destroy (DDD)&lt;/a&gt;. This design system needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure that individual web components / blocks look like DDD&lt;/li&gt;
&lt;li&gt;Ensure our editing environment (h-a-x, a shadowRoot controlled authoring experience) looks like DDD&lt;/li&gt;
&lt;li&gt;Ensure that our theme layer looks like DDD&lt;/li&gt;
&lt;li&gt;Ensure the top level document looks like DDD (so things in the LightDom / entry file / HTML document)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Styles
&lt;/h1&gt;

&lt;p&gt;We have all of our styles as exported variables from a single file. These styles are decorated via &lt;a href="https://lit.dev/docs/api/styles/#css" rel="noopener noreferrer"&gt;Lit's css styling based template literate tag&lt;/a&gt;. You can see a sample of one here as well as the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  DDDStyles.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/d-d-d/lib/DDDStyles.js#L613" rel="noopener noreferrer"&gt;Full file&lt;/a&gt;, code sample below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DDDVariables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
  :root {
    color-scheme: light dark;
  }
  :root,
  html,
  body,
  :host {
    /* base colors */
    --ddd-theme-default-beaverBlue: #1e407c;
    --ddd-theme-default-beaver70: rgba(30, 64, 124, 0.7);
    --ddd-theme-default-beaver80: rgba(30, 64, 124, 0.8);
    --ddd-theme-default-landgrantBrown: #6a3028;
}
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then take this variable and &lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/d-d-d/lib/DDDStyles.js#L4342-L4359" rel="noopener noreferrer"&gt;all other style decorated variables&lt;/a&gt;, export them but then also combine them into an Array to be able to ingest the entire library. This allows for DX downstream of adopting a portion of DDD or the entire thing. If an element only needs access to our borders or font sizes, then it can leverage just &lt;code&gt;DDDBorders&lt;/code&gt; or &lt;code&gt;DDDFontSizing&lt;/code&gt; to obtain just these references.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code sample of the Array export
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// export that has all of them for easy stamping as a single sheet&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DDDAllStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;DDDVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;DDDDataAttributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDReset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDBreadcrumb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDExtra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDBorders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDMarginPadding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDLetterSpacing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDLineHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDBoxShadow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDBorderRadius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDBackground&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDFontClasses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDFontWeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDFontSizing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DDDAnimations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the comment at the top of the code block; we now have all of these style tokens in one Array so that we can build a style sheet that has all of them!&lt;/p&gt;

&lt;h2&gt;
  
  
  Individual element
&lt;/h2&gt;

&lt;p&gt;DDD then ships with two ways we can implement it in individual web components. &lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/d-d-d/d-d-d.js#L140-L150" rel="noopener noreferrer"&gt;Method one gets everything&lt;/a&gt; + our legacy color system (called Simple Colors) + LitElement. This is common for when we go "ya it's going to need everything" or especially when augmenting work that pre-dates DDD (so it used our old Color library, allowing us to transition).&lt;/p&gt;

&lt;p&gt;Method two, just applies the &lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/d-d-d/d-d-d.js#L112-L137" rel="noopener noreferrer"&gt;Reset via a SuperClass / mix-in approach&lt;/a&gt;. You'll note it also does a "is this awesome thing your making needing to be presented in the terrible things called Safari" ;)&lt;/p&gt;

&lt;p&gt;Right with that though &lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/d-d-d/d-d-d.js#L117" rel="noopener noreferrer"&gt;is this line&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDDSharedStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAvailability&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Global document / html / Constructable Style Sheet
&lt;/h2&gt;

&lt;p&gt;This &lt;code&gt;requestAvailability()&lt;/code&gt; approach is how we handle Singleton Paradigms, &lt;a href="https://dev.to/btopro/part-1-how-penn-state-unbundles-web-components-for-cdn-deployments-20di"&gt;especially important in an unbundled worldview&lt;/a&gt;. Singleton is 10 things need access to 1 thing, you don't know when they'll load, make a method that bridges that only one copy resides in the entire system  regardless.&lt;/p&gt;

&lt;p&gt;You can see the full call below which I'll then step through what it's doing:&lt;br&gt;
&lt;a href="https://github.com/haxtheweb/webcomponents/blob/master/elements/d-d-d/d-d-d.js#L157-L192" rel="noopener noreferrer"&gt;Code in question&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDDSharedStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestAvailability&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDDSharedStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// convert css into text content of arrays mashed together&lt;/span&gt;
    &lt;span class="c1"&gt;// this way we can inject it into a global style sheet&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;globalStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DDDAllStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;st&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adoptableDDD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CSSStyleSheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;adoptableDDD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalStyles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// THIS FLAG MAKES HAX LOAD IT IN ITS SHADOW ROOT!!!!&lt;/span&gt;
      &lt;span class="nx"&gt;adoptableDDD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// Combine the existing adopted sheets if we need to but these will work everywhere&lt;/span&gt;
      &lt;span class="c1"&gt;// and are very fast&lt;/span&gt;
      &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adoptedStyleSheets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adoptedStyleSheets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;adoptableDDD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nf"&gt;loadDDDFonts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dddCSSFeatureDetection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDDSharedStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;adoptableDDD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldStyleSafariBs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;oldStyleSafariBs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;globalStyles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldStyleSafariBs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;loadDDDFonts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dddCSSFeatureDetection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDDSharedStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldStyleSafariBs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDDSharedStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// self-appending on call&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DDDSharedStylesGlobal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDDSharedStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAvailability&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Steps / checks / happenings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do we already exist&lt;/li&gt;
&lt;li&gt;Look at all the styles, &lt;code&gt;.map&lt;/code&gt; the &lt;code&gt;Array&lt;/code&gt; and join together all the &lt;code&gt;cssText&lt;/code&gt; as a single string&lt;/li&gt;
&lt;li&gt;Use a &lt;code&gt;CSSStyleSheet()&lt;/code&gt; and add it to the document's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets" rel="noopener noreferrer"&gt;adoptedStyleSheets&lt;/a&gt; array, ensuring that DDD is applied last&lt;/li&gt;
&lt;li&gt;load fonts which get added as &lt;code&gt;link&lt;/code&gt;'s appended to &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You'll also note the "fun" Safari check to fallback and create a singular &lt;code&gt;style&lt;/code&gt; element. Whis should probably say "older browsers" but I digress in my trolling.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why do any of this?
&lt;/h1&gt;

&lt;p&gt;Performance. performance. performance! While what we are doing operates like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 sheet for the document made out of the &lt;strong&gt;textual representation of the other style objects&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1 object reference across &lt;strong&gt;n&lt;/strong&gt; number of elements to the same object in &lt;code&gt;LitElement&lt;/code&gt; based &lt;code&gt;shadowRoot&lt;/code&gt;s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It used to work like this prior to Lit and our own adoption of this methodology:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;css is text&lt;/li&gt;
&lt;li&gt;apply that css as text across &lt;strong&gt;n&lt;/strong&gt; number of elements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, this started to completely brick the performance of our ecosystem. Constructable stylesheets ensured that if there's a CSS variable called &lt;code&gt;--ddd-spacing-12&lt;/code&gt; that it at most is in memory 2x (1 for the global document, 1 &lt;strong&gt;across all elements that use DDD&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;In fact if you notice we put this in the global document which gives us 1 copy of the CSS variables, and then the default behavior of &lt;code&gt;DDDSuper&lt;/code&gt; is simply to apply a CSS reset. This implies none of the variables are loaded directly into these elements (even in memory) and thus will take on the css variable values from their global application.&lt;/p&gt;

&lt;p&gt;We are certainly not perfect in our usage of these things, but we've had great success with this approach. Our &lt;a href="https://cdn.hax.cloud/cdn/wc-registry.json" rel="noopener noreferrer"&gt;ecosystem of 658 web components&lt;/a&gt; loads with a high level of performance thanks to lit, the approaches mentioned here, and sweat equity.&lt;/p&gt;

&lt;h1&gt;
  
  
  HAX
&lt;/h1&gt;

&lt;p&gt;If you'd like to learn more about the HAX ecosystem and how Penn State is building a Ubiquitous Web Authoring platform hit up the links below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playground.hax.cloud/site.html" rel="noopener noreferrer"&gt;HAX Playground to try HAX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://haxtheweb.org" rel="noopener noreferrer"&gt;HAX docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hax.psu.edu" rel="noopener noreferrer"&gt;HAX @ Penn State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bit.ly/hax-discord" rel="noopener noreferrer"&gt;HAX Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>haxtheweb</category>
      <category>lit</category>
      <category>css</category>
      <category>webstandards</category>
    </item>
    <item>
      <title>hax cli: publish on surge.sh fast!</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Mon, 03 Mar 2025 17:18:34 +0000</pubDate>
      <link>https://dev.to/btopro/hax-cli-publish-on-surgesh-fast-4b3c</link>
      <guid>https://dev.to/btopro/hax-cli-publish-on-surgesh-fast-4b3c</guid>
      <description>&lt;p&gt;&lt;a href="https://playground.hax.cloud/site.html" rel="noopener noreferrer"&gt;HAX&lt;/a&gt; is a moonshot technology developed by &lt;a href="https://hax.psu.edu" rel="noopener noreferrer"&gt;Penn State&lt;/a&gt; faculty, students and staff to make web &lt;a href="https://haxtheweb.org/welcome/community/pillars" rel="noopener noreferrer"&gt;publishing and ownership ubiquitous&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Publish using &lt;code&gt;hax&lt;/code&gt; cli
&lt;/h1&gt;

&lt;p&gt;See the &lt;a href="https://github.com/haxtheweb/create/blob/main/examples.md" rel="noopener noreferrer"&gt;full list of example commands&lt;/a&gt; or copy and paste the blocks below to get building.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install the cli&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; @haxtheweb/create
&lt;span class="c"&gt;# create a new site called zombocom&lt;/span&gt;
hax site zombocom &lt;span class="nt"&gt;--theme&lt;/span&gt; &lt;span class="s2"&gt;"clean-one"&lt;/span&gt; &lt;span class="nt"&gt;--y&lt;/span&gt; &lt;span class="nt"&gt;--no-i&lt;/span&gt;
&lt;span class="c"&gt;# add a page&lt;/span&gt;
hax site &lt;span class="nt"&gt;--root&lt;/span&gt; ./zombocom node:add &lt;span class="nt"&gt;--title&lt;/span&gt; &lt;span class="s2"&gt;"My docs"&lt;/span&gt; &lt;span class="nt"&gt;--content&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p&amp;gt;This is all about our doc process&amp;lt;/p&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;--y&lt;/span&gt;
&lt;span class="c"&gt;# start publishing process to surge.sh, confirm name manually&lt;/span&gt;
hax site &lt;span class="nt"&gt;--root&lt;/span&gt; ./zombocom site:surge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command run &lt;a href="https://haxcli-zombocom.surge.sh/my-docs" rel="noopener noreferrer"&gt;produced the following site&lt;/a&gt;! There are many different themes with workflows coming to script and automate the creation of new ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  goals / features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;rapid publishing&lt;/li&gt;
&lt;li&gt;easy to move format&lt;/li&gt;
&lt;li&gt;click and build UI&lt;/li&gt;
&lt;li&gt;built in CDN to allow for publishing sites on static hosts without hosting all assets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;surge blocks indexing by search engines&lt;/li&gt;
&lt;li&gt;platform incentives multi-page / system visits over initial paint (always getting faster)&lt;/li&gt;
&lt;li&gt;SSR experimental / not possible w/ current reality&lt;/li&gt;
&lt;li&gt;Theme development workflows still being documented (Mar 2025)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Video tutorial
&lt;/h1&gt;

&lt;p&gt;Here's a video stepping through different commands in the CLI as well as building out and adding content through the UI tools.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Woo-mjKKe7k"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Learn more
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://haxtheweb.org" rel="noopener noreferrer"&gt;HAXTheWeb docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hax.cloud" rel="noopener noreferrer"&gt;HAX cloud playspace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hax.psu.edu" rel="noopener noreferrer"&gt;HAX @ Penn State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bit.ly/hax-discord" rel="noopener noreferrer"&gt;Join us today on Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our why
&lt;/h2&gt;

&lt;p&gt;While writing here on dev.to is great, and in a simple, clean format. It is still limited relative to what the web offers. It still uses "tokens" and code in order to bring content to the web. I can't take it and publish it on my own without knowing about how to leverage the web.&lt;/p&gt;

&lt;p&gt;We envision a world where you own every aspect of production of the web, without ever needing to know the web. Start on a PHP based system, move to CLI driven one, move to desktop or SaaS or nodejs driven one; you should not need to care about all these decision trees. Want to start in a Word / Google doc? Do that. Want to start from an existing site, copy the URL.&lt;/p&gt;

&lt;p&gt;We seek a world where publishing flows like water. &lt;a href="https://bit.ly/hax-discord" rel="noopener noreferrer"&gt;Join us today on Discord&lt;/a&gt;. One web component at a time, we will change web authoring forever.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>pennstate</category>
      <category>haxtheweb</category>
      <category>lit</category>
    </item>
    <item>
      <title>Github + Stickblitz + HAX = free web publishing</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Fri, 09 Aug 2024 17:41:37 +0000</pubDate>
      <link>https://dev.to/btopro/github-stickblitz-hax-publishing-ease-58nf</link>
      <guid>https://dev.to/btopro/github-stickblitz-hax-publishing-ease-58nf</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/C6gmrI-VQN4"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Video of the process followed below.&lt;/p&gt;

&lt;p&gt;In this tutorial I will show you how to Build and manage a web publishing workflow using free accounts and installing no tools by using Github, Stackblitz, and the haxtheweb/haxsite repo.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting started
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;First, start by getting an account on &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next an account on &lt;a href="https://stackblitz.com/" rel="noopener noreferrer"&gt;stackblitz&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 1: Setup Github Pages workflow
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Now navigate to the &lt;a href="https://github.com/haxtheweb/haxsite" rel="noopener noreferrer"&gt;haxtheweb/haxsite&lt;/a&gt; repo&lt;/li&gt;
&lt;li&gt;Click the &lt;code&gt;Create a new repository&lt;/code&gt; option found under the &lt;code&gt;Use this template&lt;/code&gt; button (image below)
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubewzvs37pgv6r4cq6zf.png" alt="Click the  raw `Create a new repository` endraw  option found under the  raw `Use this template` endraw  button" width="800" height="194"&gt;
&lt;/li&gt;
&lt;li&gt;Next, name your repository and select &lt;code&gt;Create repository&lt;/code&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6555eo0eqths0jr86st.png" alt="name your repository and select  raw `Create repository` endraw " width="800" height="480"&gt;
&lt;/li&gt;
&lt;li&gt;This will take a few seconds and then you have a copy of the repo!&lt;/li&gt;
&lt;li&gt;Now it's time for enabling Github's "Pages" capability. Click &lt;code&gt;settings&lt;/code&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftp43e0xzw6yd8vad7bx4.png" alt="Click settings button" width="800" height="147"&gt;
&lt;/li&gt;
&lt;li&gt;On the left under &lt;code&gt;Code and automation&lt;/code&gt; click the &lt;code&gt;Pages&lt;/code&gt; button&lt;/li&gt;
&lt;li&gt;Click the word that says "None" under &lt;code&gt;Branch&lt;/code&gt; and select the &lt;code&gt;main&lt;/code&gt; branch
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftff1ct4exa148uyudky1.png" alt="" width="800" height="738"&gt;
&lt;/li&gt;
&lt;li&gt;You should see main / (root); click "Save"
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv12x76tq3gltq5mni2cc.png" alt="main / (root); click " width="605" height="196"&gt;
&lt;/li&gt;
&lt;li&gt;Wait a minute or so and refresh the page and you should now see "Your site is live at"
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzjtl8neo7s1dfec4hln.png" alt="Your site is live" width="601" height="319"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the tutorial's live site at &lt;a href="https://btopro.github.io/jubilant-spoon/" rel="noopener noreferrer"&gt;https://btopro.github.io/jubilant-spoon/&lt;/a&gt; and now we've set things in motion so that when changes are made on github, it will automatically rebuid the site and our address will reflect the changes. Now it's time to use HAX to edit our files in the cloud!&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 2: Use HAX to edit the site on Stackblitz
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://stackblitz.com" rel="noopener noreferrer"&gt;https://stackblitz.com&lt;/a&gt; and click the "Open Github repository [beta]" button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6swnndcqddzs3hpg24y0.png" alt="stackblitz github connector" width="800" height="243"&gt;
&lt;/li&gt;
&lt;li&gt;Select your repository after authorizing the connection between github and stackblitz and click "Open repository"
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizlzxkvmrvki5vr6qgdv.png" alt="Connect repo" width="633" height="417"&gt;
&lt;/li&gt;
&lt;li&gt;It will think for a bit, do some installation and then you will see a broken window to the right like below (don't panic this is normal!)
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyde3tihoa6dxmhk8tt3.png" alt="Starting screen" width="800" height="387"&gt;
&lt;/li&gt;
&lt;li&gt;Click the Plug icon on the left for "Ports in use" and then click the "Open in browser" icon
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjtlcznij7u83eri6kwc6.png" alt="Open in browser" width="419" height="307"&gt;
&lt;/li&gt;
&lt;li&gt;A message will say "You're almost there!", click the "Connect to Project" button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxtw45asnpmmbkj6shxn.png" alt="Connect to Project" width="219" height="84"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 3: JACKPOT - HAX time
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lif7hd9gdi32csgc4nu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lif7hd9gdi32csgc4nu.gif" alt="JACKPOT" width="400" height="300"&gt;&lt;/a&gt;&lt;br&gt;
Congratulations, you now can edit a website in the cloud using HAX!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Edit" on the top bar to get started HAX'ing the web!
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobswmhafuvaq5mfq8ifa.png" alt="Click to edit page" width="800" height="372"&gt;
&lt;/li&gt;
&lt;li&gt;Add blocks, edit content, administer the site outline using the outline designer, go. to. town! You have a full working copy of HAX running on stackblitz
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcl5btr5uhej2mjjpkfcr.png" alt="HAX running on stackblitz" width="800" height="385"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 4 - HAX quick tutorial
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;After clicking Edit, click on the page content to activate it. The right panel will show you configuration options&lt;/li&gt;
&lt;li&gt;Markdown shorthand like &lt;code&gt;#&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt; can be used to write rapidly&lt;/li&gt;
&lt;li&gt;Merlin is the command discovery system. If you are trying to do something, start typing the word you are thinking of and merlin may be able to help jump you to that action. It's great for inserting Blocks in the page by writing and typing &lt;code&gt;/&lt;/code&gt; to start a blank paragraph&lt;/li&gt;
&lt;li&gt;Outline Designer allows you to rapidly administer the site hierarchy and can be found under Site actions -&amp;gt; Outline designer
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkp19ut0xlaxkt5lz78i.png" alt="Outline Designer" width="800" height="359"&gt;
&lt;/li&gt;
&lt;li&gt;You can change the theme and site details under Site actions -&amp;gt; Edit settings 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22t0jjchtb8tscqbxoyu.png" alt="Image description" width="173" height="346"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 5 - Publish changes to github
&lt;/h1&gt;

&lt;p&gt;By now you've modified content, added blocks, maybe some pages, and it's time to get our content updated on github. Easy Pease!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Close the browser tab that has HAX running in the cloud and go back to the Stackblitz repo&lt;/li&gt;
&lt;li&gt;Click "Source Control" on the left as shown
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76lo5c3aeht3jxhb7fip.png" alt="Git Source Control" width="800" height="419"&gt;
&lt;/li&gt;
&lt;li&gt;You will see a blue "Sync Changes" button with the number of changes. Thanks to HAX + Stackblitz, every operation you saved = a commit in the repo under the hood!&lt;/li&gt;
&lt;li&gt;It will ask you about pull and push, hit "OK" as shown
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjaagg8at10mml1btbmyp.png" alt="Image description" width="800" height="130"&gt;
&lt;/li&gt;
&lt;li&gt;The first time you do this you will be asked to authorize Stackblitz to talk to Github. Accept the permissions for this connection&lt;/li&gt;
&lt;li&gt;Go back to the github repo and you should see the build starting. In a minute or so, refresh the github pages link to see your changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  CONGRATULATIONS!
&lt;/h1&gt;

&lt;p&gt;You now have a HAX publishing workflow that requires installing nothing and paying nothing in order to host your content online!&lt;/p&gt;

&lt;p&gt;We'll get into the technical considerations of HAX in future posts but the tldr;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This site's CONTENT is stored with the site always under the &lt;code&gt;pages/&lt;/code&gt; directory so that it can all work as a stand alone package&lt;/li&gt;
&lt;li&gt;The JavaScript / system assets powering the site are coming from our network of CDNs using the &lt;a href="https://dev.to/btopro/uwc-part-3-the-magic-script-122a"&gt;Magic Script&lt;/a&gt;. If you want to run those assets locally you can &lt;a href="https://github.com/haxtheweb/psucdn/tree/master/cdn" rel="noopener noreferrer"&gt;copy wc-registry.json and the build directory from our CDN into the top level of your site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You are able to pull this site into other HAX tech like the Node-js, PHP, desktop, HAXiam and other implementations to work on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Learn more about HAX
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://oer.hax.psu.edu/bto108/sites/haxcellence/welcome" rel="noopener noreferrer"&gt;HAX docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bit.ly/hax-discord" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/haxtheweb" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mastodon.social/@btopro" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/company/haxtheweb" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/@haxtheweb" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hax.psu.edu/" rel="noopener noreferrer"&gt;HAX project home&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>haxtheweb</category>
      <category>webdev</category>
      <category>webcomponents</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>With the right tools, you can build anything</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Fri, 13 Oct 2023 15:32:50 +0000</pubDate>
      <link>https://dev.to/btopro/with-the-right-tools-you-can-build-anything-50i</link>
      <guid>https://dev.to/btopro/with-the-right-tools-you-can-build-anything-50i</guid>
      <description>&lt;p&gt;16 years in the making, the story of my thesis, turned into action and the platforms built. This is the backstory to HAX; where it started, when, and what I hope it will accomplish with my time here.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/G5yEgFIxzl0?start=1"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  HAX
&lt;/h1&gt;

&lt;p&gt;The authoring experience of HAX and the ability to make fast, static file backed websites rapidly.&lt;/p&gt;

&lt;p&gt;HAX seeks to be the smallest possible back-end CMS to make HAX work and be able to build websites with it. Leveraging JSON Outline Schema, HAX is able to author multiple pages, which it then writes onto the file system. This way a slim server layer is just for basic authentication, knowing how to save files, and placing them in version control.&lt;/p&gt;

&lt;p&gt;Watch and Read more about HAX here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Youtube channel - &lt;a href="https://www.youtube.com/@haxtheweb" rel="noopener noreferrer"&gt;https://www.youtube.com/@haxtheweb&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;HAXCellence &lt;a href="https://oer.hax.psu.edu/bto108/sites/haxcellence/what-is-hax" rel="noopener noreferrer"&gt;https://oer.hax.psu.edu/bto108/sites/haxcellence/what-is-hax&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Issues / Support / Community
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Discord Channel - &lt;a href="https://bit.ly/hax-discord" rel="noopener noreferrer"&gt;https://bit.ly/hax-discord&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Unified issue queue - &lt;a href="https://github.com/elmsln/issues/issues" rel="noopener noreferrer"&gt;https://github.com/elmsln/issues/issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Using Merlin directly in any HAX spaces and type "Issue" to jump start a report!&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Writing an HTML to HAXcms site importer with Vercel</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Thu, 21 Sep 2023 19:37:38 +0000</pubDate>
      <link>https://dev.to/btopro/writing-an-html-to-haxcms-site-importer-with-vercel-102e</link>
      <guid>https://dev.to/btopro/writing-an-html-to-haxcms-site-importer-with-vercel-102e</guid>
      <description>&lt;p&gt;This is a live coding session where I show the process of creating a new endpoint in Vercel and wiring it up for importing site content into HAXcms in order to build a brand new site! I've been overwhelmed by the interest in our importers, as well as how fast they've been to develop thanks to Vercel. When someone approached us recently about importing Drupal based content into HAXcms, I thought "what if it was just HTML on one page?". Sure enough, Drupal's book module, popular in education, can output the site's content for 'print' all on one page. This demonstrates me debugging and implementing end to end this new functionality to a beta state by reusing code we already have.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/LzO1u9KEFf0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How we automated storybook documentation for our web components</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Thu, 25 Aug 2022 14:59:33 +0000</pubDate>
      <link>https://dev.to/btopro/how-we-automated-storybook-documentation-for-our-web-components-52cb</link>
      <guid>https://dev.to/btopro/how-we-automated-storybook-documentation-for-our-web-components-52cb</guid>
      <description>&lt;p&gt;We recently &lt;a href="https://haxapi.vercel.app/" rel="noopener noreferrer"&gt;updated our storybook&lt;/a&gt; in a few dramatic ways that I wanted to share the process we used to improve them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I audited what we were communicating and removed dead leaves&lt;/li&gt;
&lt;li&gt;I added things we were missing&lt;/li&gt;
&lt;li&gt;I revamped our docs to &lt;a href="https://github.com/elmsln/lrnwebcomponents/blob/master/elements/storybook-utilities/storybook-utilities.js#L611-L692" rel="noopener noreferrer"&gt;include a simple way to supply developer shortcuts&lt;/a&gt; to get started with our elements&lt;/li&gt;
&lt;li&gt;I added in custom-elements.json details for advanced devs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's what it looks like if you didn't &lt;a href="https://haxapi.vercel.app/" rel="noopener noreferrer"&gt;navigate to the vercel driven storybook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdm6oyfy2l9uhvm403a55.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdm6oyfy2l9uhvm403a55.gif" alt="shows documentation page for our rpg character web component" width="640" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How we handle stories
&lt;/h2&gt;

&lt;p&gt;Most of how we've automated storybook stories can be attributed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My coworker &lt;a href="https://twitter.com/nikkimk" rel="noopener noreferrer"&gt;Nikki is an Evil Genius&lt;/a&gt; and syphons off the class properties to generate knobs automatically!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;custom-elements.json&lt;/code&gt; is auto built &lt;a href="https://wcfactory.js.org/" rel="noopener noreferrer"&gt;by our tooling&lt;/a&gt; using the &lt;a href="https://www.npmjs.com/package/web-component-analyzer" rel="noopener noreferrer"&gt;web-component-analyzer package&lt;/a&gt; with it's &lt;code&gt;wca&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;Our elements almost all have &lt;code&gt;static get tag()&lt;/code&gt; for the name of the element and &lt;code&gt;export&lt;/code&gt; their class to make it easier to build out&lt;/li&gt;
&lt;li&gt;We wire many elements to our &lt;a href="https://haxtheweb.org/documentation-1/hax-development/hax-schema" rel="noopener noreferrer"&gt;HAXSchema&lt;/a&gt; that supplies custom demo content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example of how our &lt;a href="https://haxapi.vercel.app/?path=/story/system-rpg-character--rpg-character-story" rel="noopener noreferrer"&gt;RPG character page&lt;/a&gt; works as it's a typical implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withKnobs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@open-wc/demoing-storybook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StorybookUtilities&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@lrnwebcomponents/storybook-utilities/storybook-utilities.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RpgCharacter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./rpg-character.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;System|RPG Character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rpg-character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;withKnobs&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;selectedPanel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;storybookjs/knobs/panel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;utils&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StorybookUtilities&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RpgCharacterStory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeUsageDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;RpgCharacter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeElementFromHaxDemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RpgCharacter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  storybook-utilities
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;npm install @lrnwebcomponents/storybook-utilities&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@lrnwebcomponents/storybook-utilities" rel="noopener noreferrer"&gt;NPM page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;makeElementFromHaxDemo&lt;/code&gt; can also be swapped out for &lt;code&gt;makeElementFromClass&lt;/code&gt; to be &lt;a href="https://github.com/elmsln/lrnwebcomponents/blob/master/elements/git-corner/git-corner.stories.js#L18-L26" rel="noopener noreferrer"&gt;used more broadly in any web component storybook&lt;/a&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeElementFromClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GitCorner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://github.com/elmsln/lrnwebcomponents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Our monorepo of all the things you see here&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;corner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our pattern follows that we pass in the class for the element (it should work with any library as our Vanilla and LitElement bases both work well)&lt;/p&gt;

&lt;h2&gt;
  
  
  makeUsageDocs
&lt;/h2&gt;

&lt;p&gt;This is where the quality stepped up a notch. By passing the template for our demo through this function, as well as &lt;code&gt;import.meta.url&lt;/code&gt; we've got everything we need in order to build our standard doc page that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to install via npm, yarn or pnpm&lt;/li&gt;
&lt;li&gt;how to use in your project&lt;/li&gt;
&lt;li&gt;deep API details from &lt;code&gt;custom-elements.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;links to github / npm&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How we get custom-elements.json
&lt;/h2&gt;

&lt;p&gt;So this probably has better ways of handling it but I went with a simple fetch relative to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta" rel="noopener noreferrer"&gt;import.meta.url&lt;/a&gt; and then stored it in a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" rel="noopener noreferrer"&gt;localStorage variable&lt;/a&gt;. Why? Because Storybook didn't enjoy rendering out async. As a result, there are 2 features that sorta "show up" as you use the stories more (we get our haxProperties in a similar way if its in a remote file).&lt;/p&gt;

&lt;p&gt;Here's what the code looks like for that specific custom-elements.json section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;entryFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;importPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/elements/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lrnwebcomponents/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.stories.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;packageName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;packageName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;importPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;importPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entryFile&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-description`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// pull from the custom-elements json file since our tooling rebuilds this&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/../custom-elements.json`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/lib/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ignore source versions&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;allD&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;allD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entryFile&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-description`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can see that we use &lt;code&gt;import.meta.url&lt;/code&gt; from the &lt;code&gt;rpg-character.stories.js&lt;/code&gt; file requesting. This gives us the path to the file that loaded the story so that we can then base the path to &lt;code&gt;custom-elements.json&lt;/code&gt; off of that. As our tooling runs in that same directory we just take the pathname from a &lt;code&gt;new URL()&lt;/code&gt;, go back a directory (&lt;code&gt;/../&lt;/code&gt;) which is a hack that can be used to move out of file names (could have replaced it but meh) and target custom-elements.json from there.&lt;/p&gt;

&lt;p&gt;After getting that file we walk through the json and use &lt;code&gt;pre&lt;/code&gt; to print the contents that we care about. &lt;code&gt;wca&lt;/code&gt; did all the hard work of documenting tags, descriptions, and properties; now we're just printing it out in-case people want to see that at a glance without digging into the source.&lt;/p&gt;

&lt;p&gt;I hope this helps give you ideas on how to automate your web component monorepos!&lt;/p&gt;

</description>
      <category>storybookjs</category>
      <category>javascript</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Targeting CSS Shadow Parts via URL search params</title>
      <dc:creator>Bryan Ollendyke</dc:creator>
      <pubDate>Tue, 23 Aug 2022 20:08:00 +0000</pubDate>
      <link>https://dev.to/btopro/targetting-css-shadow-parts-via-url-search-parameters-2m9</link>
      <guid>https://dev.to/btopro/targetting-css-shadow-parts-via-url-search-parameters-2m9</guid>
      <description>&lt;p&gt;Yeah I know; your jelly you didn't think to mash those words together for great SEO. No, I assure you it's a real thing courtesy of &lt;a href="https://github.com/elmsln/issues/issues/609" rel="noopener noreferrer"&gt;this fun issue&lt;/a&gt; came as an idea from Paul Hibbits, an educator who loves Doxify / GravCMS. The idea was to be able to let users (or the developer) send links to people with parts of the UI disabled. This has a few possible use-cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick A/B test of thing vs not thing&lt;/li&gt;
&lt;li&gt;Allow removing share links and other distractions&lt;/li&gt;
&lt;li&gt;Allow for removing parts of the UI when embedding in an iframe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to achieve this, I mixed together some interesting solutions. First, we allow a URL search parameter like this &lt;code&gt;welcome-page?disable-features=footer,site-menu-content&lt;/code&gt;. We can parse this into a string like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;disableFeatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disable-features&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;disableFeatures&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disableFeatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;disableFeatures&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to set the internal &lt;code&gt;disableFeatures&lt;/code&gt; property in our web component based on the URL's search location. In LitElement we then &lt;code&gt;reflect&lt;/code&gt; that attribute so that we can do CSS selectors like this &lt;code&gt;haxcms-site-builder[disable-features="thing"]&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding *=
&lt;/h2&gt;

&lt;p&gt;Reading the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" rel="noopener noreferrer"&gt;Moz Doc page on attribute selectors&lt;/a&gt; we see a &lt;code&gt;*=&lt;/code&gt; selector which says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Represents elements with an attribute name of attr whose value contains at least one occurrence of value within the string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means that if we have &lt;code&gt;things,and,stuff&lt;/code&gt; we can match on &lt;code&gt;*="and"&lt;/code&gt; successfully. Using this knowledge, We can create a selector like: &lt;code&gt;haxcms-site-builder[disable-features*="site-menu-content"]&lt;/code&gt; and successfully target that URL condition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the Shadow Parts
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::part" rel="noopener noreferrer"&gt;::part syntax + attribute&lt;/a&gt;, is a relative newcomer to HTML spec. It allows for targeting parts of elements that lay within shadowRoots of other elements in order to provide specific styling.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;haxcms-site-builder[disable-features*="site-menu"] .haxcms-theme-element::part(site-menu)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Combining these approaches we get a solution like so which can successfully allow (smart) users to target and disable parts of the UI via the URL parameter while safely only allowing the targeting of ones we allow.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR the fun video
&lt;/h2&gt;

&lt;p&gt;You got it&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/kMMIcnOTuAI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The full CSS selector
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"site-menu"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;site-menu&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"page-breadcrumb"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;page-breadcrumb&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"print-branch-btn"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;print-branch-btn&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"rss-btn"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;rss-btn&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"git-corner-btn"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;git-corner-btn&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"search-btn"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;search-btn&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"site-menu-content"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;site-menu-content&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"footer"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"qr-code-btn"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;qr-code-btn&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"right-col"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;right-col&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nt"&gt;haxcms-site-builder&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;disable-features&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"left-col"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.haxcms-theme-element&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;left-col&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>css</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
