<?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>Forem: Yao-Hui Chua</title>
    <description>The latest articles on Forem by Yao-Hui Chua (@yaaooo).</description>
    <link>https://forem.com/yaaooo</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%2F208420%2F45226dbf-f795-4b74-ad03-5a13d0257c6e.jpeg</url>
      <title>Forem: Yao-Hui Chua</title>
      <link>https://forem.com/yaaooo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/yaaooo"/>
    <language>en</language>
    <item>
      <title>How to Build and Scale Design Systems: Starting with the Right Framework</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Tue, 28 Apr 2026 05:04:30 +0000</pubDate>
      <link>https://forem.com/yaaooo/how-to-build-and-scale-design-systems-starting-with-the-right-framework-29lf</link>
      <guid>https://forem.com/yaaooo/how-to-build-and-scale-design-systems-starting-with-the-right-framework-29lf</guid>
      <description>&lt;p&gt;I've been spending the past few months juggling product engineering work with the building and scaling of &lt;a href="https://www.figma.com/blog/design-systems-101-what-is-a-design-system/" rel="noopener noreferrer"&gt;design systems&lt;/a&gt;. While I can't reveal much about how frontends at Amazon work behind the scenes, I can share some generalizable learnings that I believe will be useful to anyone trying to establish a reusable component ecosystem of their own.&lt;/p&gt;

&lt;p&gt;To start, I'll just focus on one specific topic: Picking the right front-end rendering framework for your design system.&lt;/p&gt;

&lt;h2&gt;
  
  
  React Is a (Very) Sensible Default
&lt;/h2&gt;

&lt;p&gt;I've been working with React for about a decade now. I've seen the framework go through various phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The early era of class components, accompanied by the somewhat awkward lifecycle methods API (e.g. &lt;code&gt;componentWillMount()&lt;/code&gt;, &lt;code&gt;componentWillReceiveProps()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The emergence of React Native and the &lt;a href="https://medium.com/airbnb-engineering/building-a-cross-platform-mobile-team-3e1837b40a88" rel="noopener noreferrer"&gt;tempering of some of the "write once, use everywhere" hype&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The push towards composition with &lt;a href="https://youtu.be/dpw9EHDh2bM?si=FA1cYvIEYyXaJuV7" rel="noopener noreferrer"&gt;functional components, hooks, concurrent mode, etc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For any web application that involves rich client-side interactions, the perks of using React and having access to the ecosystem of tooling built around it (e.g. Redux Toolkit, React Native, TanStack, Next.js, as well as component libraries like &lt;a href="https://mantine.dev/" rel="noopener noreferrer"&gt;Mantine&lt;/a&gt;) are undeniable. Its declarative API for turning state into UI is also easy enough to pick up for most (even if it requires experience to master).&lt;/p&gt;

&lt;p&gt;As indicated by &lt;a href="https://2025.stateofjs.com/en-US/libraries/front-end-frameworks/" rel="noopener noreferrer"&gt;2025's State of JavaScript survey&lt;/a&gt;, React continues to lead in terms of adoption. I've had the chance to work with Angular and Vue 2/3 at various periods of my career, but I've always preferred using React to ship features.&lt;/p&gt;

&lt;p&gt;Without having context on the specific needs of an organization, I would strongly recommend React as a safe, reliable default framework of choice for getting started with building and shipping reusable components.&lt;/p&gt;

&lt;h2&gt;
  
  
  ... But Your Situation May Be Different
&lt;/h2&gt;

&lt;p&gt;If you work at a startup and are just looking to ship fast, you're probably not pondering the question: &lt;em&gt;Which framework is best for our design system?&lt;/em&gt;. Really, you just want to know: &lt;em&gt;Which framework should we use to ship our &lt;a href="https://www.uxglossary.com/glossary/minimum-lovable-product" rel="noopener noreferrer"&gt;Minimum Lovable Product&lt;/a&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The real demand for a design system often arises months or years into development, when your organization's product and engineering scope grows to a point where simply letting individual teams drive their own UI development doesn't scale well. &lt;/p&gt;

&lt;p&gt;Such growing pains can manifest in several practical ways. It usually means needing to engage multiple teams just to ensure that: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Atomic UI Components&lt;/strong&gt; (e.g. Buttons, Alerts) are rendered consistently with the same &lt;strong&gt;Design Tokens&lt;/strong&gt; (e.g. colors, font, spacing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite UI Components&lt;/strong&gt; (e.g. Product Cards, Chat Widgets, Payment Widgets) don't require teams to do duplicate work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App-level and Page-level UI Patterns&lt;/strong&gt; (e.g. Page Shell Templates, Form Data Management, Navigation) are upheld consistently, such that the complex interactions &lt;em&gt;within&lt;/em&gt; and &lt;em&gt;between&lt;/em&gt; screens don't throw off your customers&lt;/li&gt;
&lt;/ul&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%2Fg0ukepz0ot6wb5k9jgj3.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%2Fg0ukepz0ot6wb5k9jgj3.png" alt="A design system pyramid featuring tokens, atomic elements, composite components, and patterns" width="681" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In particular, in companies where technical decision making is hyper-decentralized (i.e. "loosely coupled &lt;em&gt;and&lt;/em&gt; loosely aligned"), these types of problems can easily take form. &lt;/p&gt;

&lt;p&gt;The cost of migrating to React (or unifying towards any framework, for that matter) may be too much to bear for some. Organizational challenges and historical decisions can seriously impede the adoption of a design system.&lt;/p&gt;

&lt;p&gt;It's one thing to convince clients to migrate from one version of React to another, but it's another thing to navigate more entrenched blockers (e.g. resource constraints, content management and rendering systems that don't support React, legacy product flows that are stitched together with server-rendered markup and jQuery, etc).&lt;/p&gt;

&lt;p&gt;With this in mind, it is sometimes helpful to not fixate on React (or any specific front-end framework, for that matter). Rather, one can turn to the browser spec.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Components Are Great for Interoperability
&lt;/h2&gt;

&lt;p&gt;While adoption of React has surged over the years, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components" rel="noopener noreferrer"&gt;Web Components&lt;/a&gt; have also come a long way. &lt;a href="https://caniuse.com/?search=web+components" rel="noopener noreferrer"&gt;Most browsers now support the Web Components V1 spec&lt;/a&gt;. We've also seen the emergence of Stencil and the replacement of Polymer with Lit.&lt;/p&gt;

&lt;p&gt;Using Web Components instead of React to ship design system components may help you navigate interoperability challenges. Web Components are registered to the Window object and accessible to the global DOM by default:&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;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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-custom-element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyCustomElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the front-end landscape of your organization, different teams may have their UI templates defined using different technologies (e.g. React, PHP, JSP, etc). The neat thing about Web Components is that they are built to the browser spec, which makes them reusable across these frameworks.&lt;/p&gt;

&lt;p&gt;Here's an example of a React component using a custom web component internally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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="s1"&gt;./user-card.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Ensure the component is registered&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;React Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* React passes data via attributes */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;card&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;This is a React-managed bio.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's an example of a JSP file also using the same custom web component internally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;--&lt;/span&gt; &lt;span class="na"&gt;profile.jsp&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%@&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt; &lt;span class="na"&gt;contentType=&lt;/span&gt;&lt;span class="s"&gt;"text/html;charset=UTF-8"&lt;/span&gt; &lt;span class="na"&gt;language=&lt;/span&gt;&lt;span class="s"&gt;"java"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"user-card.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;JSP Dashboard&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;--&lt;/span&gt; &lt;span class="na"&gt;Assume&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="na"&gt;bean&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt; &lt;span class="na"&gt;available&lt;/span&gt; &lt;span class="na"&gt;in&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;request&lt;/span&gt; &lt;span class="na"&gt;scope&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;user-card&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"${user.name}"&lt;/span&gt; &lt;span class="na"&gt;theme=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Logged in via Java/Spring Security.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/user-card&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with most tools, there are always tradeoffs. In my opinion, the key thing with using Web Components over React is that you trade &lt;em&gt;ease of development&lt;/em&gt; for &lt;em&gt;interoperability&lt;/em&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  ... But Web Components Are Also Finnicky
&lt;/h2&gt;

&lt;p&gt;Yes, Web Components require a lot of manual plumbing. This is natural, given they are built to match the browser spec. Lit and Stencil reduce this to some extent, but you still don't get the full range of development abstractions from frameworks like React and Vue (e.g. virtual DOM).&lt;/p&gt;

&lt;p&gt;A lot of the complexity of working with Web Components stems from the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM" rel="noopener noreferrer"&gt;Shadow DOM&lt;/a&gt;. You will need to deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Shadow DOM's encapsulation of subtree nodes and styles&lt;/strong&gt;: A generic &lt;code&gt;document.querySelector&lt;/code&gt; call will not return nodes inside a custom element. Global CSS classes will not influence their styles either, though there are exceptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using &lt;code&gt;open&lt;/code&gt; and &lt;code&gt;closed&lt;/code&gt; modes&lt;/strong&gt;: The short (and perhaps over-simplified) answer is that &lt;code&gt;open&lt;/code&gt; mode is usually desirable, since it exposes the &lt;code&gt;.shadowRoot&lt;/code&gt; and allows you to re-run queries within the subtree. This gives you access to the nodes and their properties (e.g. &lt;code&gt;innerText&lt;/code&gt;), which can be useful for testing. Consumers will have to be intentional about overriding subtree nodes if they want. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relating Properties to Attributes&lt;/strong&gt;: In React, we mostly only care about &lt;code&gt;props&lt;/code&gt; and &lt;code&gt;state&lt;/code&gt; and don't have to think too much about defining them as class properties. With Web Components, we are dealing with HTML Elements that have both &lt;strong&gt;properties&lt;/strong&gt; and &lt;strong&gt;attributes&lt;/strong&gt;, and we need to have an idea of how to relate them (e.g. whether to use two-way or one-way binding for data, such as a &lt;code&gt;value&lt;/code&gt; attribute that gets updated by an input event). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slot Change Handling&lt;/strong&gt;: While child updates may trigger re-renders in React, the same cannot be said for Web Components. Any coupling between the slot container and the slotted container has to be managed with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/slotchange_event" rel="noopener noreferrer"&gt;slotchange handlers&lt;/a&gt;, since the slot container typically operates in the Shadow DOM even while the slotted content remains in the Light DOM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing Encapsulated CSS&lt;/strong&gt;: You will need to apply the &lt;code&gt;:host&lt;/code&gt; selector to your CSS and have an understanding of which styles are scoped to the Shadow DOM, and which are not. Escape hatches, such as an exemption of properties like &lt;code&gt;font-family&lt;/code&gt;, and CSS variables, allow for styles to be passed from the Light DOM to the Shadow DOM, which is useful for consistency and theming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility Gotchas&lt;/strong&gt;: Given that &lt;code&gt;id&lt;/code&gt;s are unique within the Shadow DOM, you will have trouble using the &lt;code&gt;for&lt;/code&gt; attribute to relate &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;s to &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;s, unless they exist within the same subtree. Communication between &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; elements in the Light DOM and &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;s within the Shadow DOM is also limited.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumer Plumbing&lt;/strong&gt;: As a consumer, event handling may not be as simple as passing &lt;code&gt;onXyzChange&lt;/code&gt; directly to the Web Component, as you would do with a React "prop". You will need write your own event listeners. A few things help to address this: Lit and Stencil both offer helpers that &lt;a href="https://lit.dev/docs/frameworks/react/" rel="noopener noreferrer"&gt;wrap your Web Components as React components&lt;/a&gt;, while React 19 &lt;a href="https://react.dev/blog/2024/12/05/react-19#support-for-custom-elements" rel="noopener noreferrer"&gt;fully supports Web Components&lt;/a&gt; such that you can directly pass your complex data and event handlers directly to Web Components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In essence, the Shadow DOM helps to provide a layer of encapsulation over our custom elements, but this encapsulation comes with gotchas that developers without experience can easily trip on. LLM-assisted development will go a long way in mitigating these blindspots. &lt;/p&gt;

&lt;h2&gt;
  
  
  Consider a Hybrid Approach
&lt;/h2&gt;

&lt;p&gt;In an ideal world, teams would share the same dependency tree and the need to address interoperability challenges would be minimal.&lt;/p&gt;

&lt;p&gt;Clearly, we don't live in an ideal world, so some amount of interoperability is typically required. In the event that your organization uses different front-end frameworks to generate different types of templates (PHP, JSP, React), a hybrid approach may be ideal. This usually means two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creating stateless UI elements using Web Components.&lt;/strong&gt; You would maintain, in a distinct package, a set of Web Components for stateless UI primitives like your Button, Modals, and Alerts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handling the assembly of these stateless UI elements in React, or any other team's framework of choice.&lt;/strong&gt; You would let your consumers import your reusable components package. Your consumers can then reference the HTML tags corresponding to your stateless elements in their respective UI templates (&lt;code&gt;&amp;lt;my-button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;my-modal&amp;gt;&lt;/code&gt;, etc). &lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fz437r21i4jiux4ptgsga.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%2Fz437r21i4jiux4ptgsga.png" alt="A design system pyramid featuring atomic custom elements and composite React components" width="681" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A middle of the road approach like this allows you to make firm decisions on core design system concerns like typography, colors, and sizing, while giving the consumers of your components the room to own their &lt;em&gt;stateful&lt;/em&gt; logic for matters such as user interactions (e.g. form inputs) and side effects (e.g. API requests). &lt;/p&gt;

&lt;h2&gt;
  
  
  ... But Consolidate If You Can
&lt;/h2&gt;

&lt;p&gt;Personally, I would still advise teams to unify their front-end development around a single framework. For large engineering organizations, in particular, the merits of using a consistent set of tools far outweigh the potential upside of experimentation. Your code and patterns become more predictable, which also makes it easier to build custom AI agents to do larger-scale refactors.  &lt;/p&gt;

&lt;p&gt;There &lt;em&gt;is&lt;/em&gt; a time and place for experimentation. This can be limited to &lt;a href="https://medium.com/walmartglobaltech/walmart-labs-our-journey-to-change-the-way-the-world-shops-788a1908cf92" rel="noopener noreferrer"&gt;"Labs teams"&lt;/a&gt; working on special projects or pages with unique data requirements. Upgrades to new major versions are also worthy of consideration. Experimenting with entirely new frameworks, on the other hand, is seldom worth it — the development benefits need to be extremely compelling to justify the long-term cost.&lt;/p&gt;

&lt;p&gt;All that said, I know that the reality on the ground is often different for many organizations. This is where the value of a hybrid approach lies.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Picking the right front-end framework is just one piece of the puzzle. There are so many considerations you'll need to be mindful of when buiding a design system: Token schemas, theming, CSS vending, tree shaking, layered testing, FOUC/FOUT, AI usage, etc. In a future post, I'll provide a high-level overview of some of these topics and suggest a mental model that developers can use.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>designsystem</category>
      <category>react</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>The Cognitive Costs of AI Chatbots and a Framework for Better Design</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Thu, 12 Mar 2026 02:44:01 +0000</pubDate>
      <link>https://forem.com/yaaooo/the-cognitive-costs-of-ai-chatbots-and-a-framework-for-better-design-533l</link>
      <guid>https://forem.com/yaaooo/the-cognitive-costs-of-ai-chatbots-and-a-framework-for-better-design-533l</guid>
      <description>&lt;p&gt;As engineers, when we talk about the "cost" of using AI, we tend to focus on billing costs — the cost of running OpenSearch clusters, invoking models through Bedrock, provisioning instances, etc. It's easy to forget that a frustrating user experience is often the most expensive cost to an organization.&lt;/p&gt;

&lt;p&gt;At a time when companies are trying to aggressively incorporate AI experiences into their products, it may be tempting to want to hop on the bandwagon and package every new product requirement as a single-threaded, AI-powered chat experience. This approach, when applied to the wrong customer need, can wind up incurring a tax on the customer's mental bandwidth.&lt;/p&gt;

&lt;p&gt;To be clear, I don't have professional experience as a UX designer or a product manager. That said, I'd like to think that I've been in enough cross-stakeholder discussions to take notice of design anti-patterns when they occur. I can't talk specifics, so I'll just illustrate this trend with a hypothetical example (and with the help of images generated using Nano Banana 2 ✨).&lt;/p&gt;

&lt;h2&gt;
  
  
  Product Use Case
&lt;/h2&gt;

&lt;p&gt;Suppose you are building a B2B platform that enterprise employees use to book business travel. A key product requirement would be to have a "Policy Exception" process. Specifically, when an employee needs to book a flight or hotel that is over the company's budget (perhaps due to a last-minute project or limited availability), we'll need to ensure that certain conditions are met before the employee can get an exception.&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%2F9ib8udgwkedrctdkqf6i.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%2F9ib8udgwkedrctdkqf6i.png" alt="B2B Travel Booking Dashboard" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On paper, this looks like a perfect use case for an AI-powered chat assistant. You might conceive of an "AI Concierge" which chats with the employee, asks for justifications, checks the company handbook, and approves the booking. However, we begin to see cracks in this model once we try to scale this out to complex tasks that require fine-grained user input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cognitive Overload
&lt;/h2&gt;

&lt;p&gt;Chats are, by their very nature, "single-threaded experiences" — the user submits a query, and the agent responds.&lt;/p&gt;

&lt;p&gt;When a user manages their travel plans, they aren't necessarily just looking at a single data point, or even a single series of data points. Instead, they may very well be making different decisions across different entities (e.g. making adjustments to multiple trips, with each trip being associated with a flight, a hotel, and a car rental).&lt;/p&gt;

&lt;p&gt;An AI agent might ask: "I see your hotel is $100 over budget. Can you provide a justification?" As the employee works through their hotel budget, they might realize that changing their flight date would make a cheaper hotel available, or that the car rental for a separate trip needs to be revised.&lt;/p&gt;

&lt;p&gt;In a chat window, context management and backtracking can be tough for the user. In a dashboard, the UI holds the state; in a chat, the user's brain fills this role. Specifically, customers would have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep the information that they've been presented with fresh in their mind&lt;/li&gt;
&lt;li&gt;Scroll back up to find previous details if they've forgotten them&lt;/li&gt;
&lt;li&gt;Interrupt the current thread if backtracking is required&lt;/li&gt;
&lt;/ol&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%2Fb97hkz4m4fybd7cigghm.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%2Fb97hkz4m4fybd7cigghm.png" alt="Travel Booking Dashboard" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For complex tasks involving multiple configurable items, a spatial UI (e.g. a dashboard), may very well be superior to a linear UI (e.g. a chat interface). Dashboards allow the user to navigate as they wish, whereas a chat "locks" them in the current discussion and makes it difficult for them to retain a bird's eye view of the current state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deterministic vs Non-Deterministic Workflows
&lt;/h2&gt;

&lt;p&gt;It is easy for product, design, and engineering leaders to conflate &lt;em&gt;agentification&lt;/em&gt; with &lt;em&gt;automation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Returning to our travel booking use case — in this example, the rules for approving a flight are usually deterministic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;flight &amp;gt; 6 hours&lt;/code&gt;, then &lt;code&gt;Business Class = Allowed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;booking is &amp;lt; 24 hours away&lt;/code&gt;, then &lt;code&gt;Price Cap = +50%&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LLMs are fundamentally probabilistic. Using a non-deterministic agent to handle rules that are strictly deterministic introduces risk, latency, and "hallucination" potential. If the rules are already well-defined, it may be wiser to prioritize building out your business logic behind a deterministic, client-facing API first. You can worry about exposing this as a tool to an AI agent later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Playbook for Product and Design Teams
&lt;/h2&gt;

&lt;p&gt;Before committing to building an agentic chat interface for a core workflow, product managers and designers should ask themselves the following questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Information Complexity:&lt;/strong&gt; Are we dealing with multiple entities that each require a series of user decisions? If yes, does the user need to be able to review the state of all entities, or is this more of a "select-and-forget" type of workflow? &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Visual Real Estate:&lt;/strong&gt; Is there sufficient space to reasonably present the decisions at stake in a single-threaded conversation? In particular, do you have mobile users and have you considered their needs?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic vs Non-Deterministic:&lt;/strong&gt; Are the rules governing your new workflow largely binary (e.g. if X, then Y), or do they require some amount of subjective, human-like judgment?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficiency of User Input:&lt;/strong&gt; Is it actually faster for a user to address their issue with a sentence, or to click a button?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see where I'm going with this. A use case that has &lt;strong&gt;high information complexity&lt;/strong&gt;, &lt;strong&gt;limited real estate&lt;/strong&gt;, &lt;strong&gt;strictly deterministic rules&lt;/strong&gt; and a need for &lt;strong&gt;efficient user input&lt;/strong&gt; is likely best served by a static user experience as the &lt;em&gt;primary&lt;/em&gt; interface. An AI chatbot can serve as the &lt;em&gt;secondary&lt;/em&gt; interface which handles the "long-tail" of subjective questions that a static UI can't predict.&lt;/p&gt;

&lt;p&gt;It goes without saying that the two types of experiences are not mutually exclusive. Your product team can absolutely build a static UX alongside an agentic one. However, it is important to prioritize the experience that makes the most sense for your customers. To return to my original example — a "Policy Exception" customer journey should be built with a dashboard first and supplemented with a chatbot later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Best of Both Worlds: A Hybrid Approach
&lt;/h2&gt;

&lt;p&gt;With this in mind, it's worth noting that there are ways to blend deterministic workflows with non-deterministic ones. In fact, I would argue that most products should strive to offer this type of hybrid UX, so that customers can select the UX journey that best addresses their needs.&lt;/p&gt;

&lt;p&gt;One option is to have your AI agent as a supplementary assistant that talks users through the main experience. Several products, such as TurboTax, do this well:&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%2Fz410yw39iqdbjri1zqtm.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%2Fz410yw39iqdbjri1zqtm.png" alt="Travel Booking Dashboard with a Chat Side Panel" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows your user to have a bird's eye view of what's at stake, take actions as needed, while being able to communicate with an agent to get deeper insight.&lt;/p&gt;

&lt;p&gt;Alternatively, you can also program your AI chat experience in a way that "locks users" into deterministic workflows when needed:&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%2F2e8a0i50lnqs7wxz0ss1.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%2F2e8a0i50lnqs7wxz0ss1.png" alt="Travel Booking Dashboard with Deterministic Widgets" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this approach, customers have to complete a bounded, deterministic workflow contained within a UI widget before continuing to speak with the agent. If we go back to the "Policy Exception" use case I described, I wouldn't be a fan of this approach, because it gives the user less agency and prevents them from interjecting with questions.&lt;/p&gt;

&lt;p&gt;That said, there's a definitely a good time and place for this type of UX. For example, surfacing an "Add to Cart" button in the chat experience is an excellent choice — Information Complexity in this situation is somewhat low and the user saves on having to type out their request (e.g. "Please add this item to my cart").&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In a nutshell, don't blindly hop on the AI bandwagon! Customer-facing AI chatbots are a powerful tool and can elevate your core user experience. However, depending on your use case, you may need to build out a reliable static experience first before supplementing it with an agentic one.&lt;/p&gt;

&lt;p&gt;As a front-end engineer, there are many exciting things to look out for in the realm of developing robust "static-agentic" hybrid user experiences. For example, Google has previewed plans to define a specialized UI protocol called &lt;a href="https://github.com/google/A2UI/" rel="noopener noreferrer"&gt;A2UI&lt;/a&gt; that bridges the gap between the traditional text responses from agents and the need for highly interactive UI widgets. In the coming months and years, I also expect we'll see plenty of new patterns emerge around how parent web applications keep their state in sync with their child chatbot widgets.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>uxdesign</category>
      <category>uidesign</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AWS Cognito and IAM for Front-End Developers</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Fri, 16 Sep 2022 03:12:26 +0000</pubDate>
      <link>https://forem.com/yaaooo/aws-cognito-and-iam-for-front-end-developers-29fc</link>
      <guid>https://forem.com/yaaooo/aws-cognito-and-iam-for-front-end-developers-29fc</guid>
      <description>&lt;p&gt;Whenever you build a web application, you'll often need to provision accounts for your users. By &lt;em&gt;users&lt;/em&gt;, I'm specifically referring to &lt;em&gt;customers&lt;/em&gt; who interact with your services via your JavaScript/TypeScript frontend.&lt;/p&gt;

&lt;p&gt;Managing these user accounts comes with plenty of challenges. Not only will you need to persist user profiles, you'll need to figure out how users can be organized into groups, how to store credentials, what kind of auth mechanisms would be suitable, and so on. &lt;/p&gt;

&lt;p&gt;If you're a front-end developer who mainly cares about building enjoyable web experiences, these concerns might seem tedious to deal with. Fortunately, &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html" rel="noopener noreferrer"&gt;AWS Cognito&lt;/a&gt; handles them for you.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://yao.page/posts/identity-and-access-management-aws/" rel="noopener noreferrer"&gt;previous blog post&lt;/a&gt;, I covered some basic concepts around &lt;a href="https://aws.amazon.com/iam/" rel="noopener noreferrer"&gt;Identity and Access Management (IAM)&lt;/a&gt;. Now, I'd like to build on these concepts by explaining how front-end developers can easily use AWS' Cognito and IAM services to set up authentication and authorization for customer-facing applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Cognito
&lt;/h2&gt;

&lt;p&gt;As mentioned, AWS Cognito handles all the nasty user management work. When using Cognito, you really just need to be familiar with two offerings: &lt;strong&gt;User pools&lt;/strong&gt; and &lt;strong&gt;Identity pools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To pull a quote from the Cognito console: &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%2F9ms4mqzg7uz8zadpc5s9.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%2F9ms4mqzg7uz8zadpc5s9.png" alt="A summary of user pools and identity pools" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User pools are user directories that provide sign-up and sign-in options for your app users. Identity pools provide AWS credentials to grant your users access to other AWS services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  User Pools
&lt;/h3&gt;

&lt;p&gt;The term "user pool" is an intuitive one, as it refers to a collection of users (which can be organized into groups).&lt;/p&gt;

&lt;p&gt;Cognito provides plenty of features for customizing how this collections of users can be managed. Amongst other things, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa.html" rel="noopener noreferrer"&gt;Enable multi-factor authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-federation-with-social-idp.html" rel="noopener noreferrer"&gt;Set up federation with identity providers&lt;/a&gt; such as Google or Facebook&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html" rel="noopener noreferrer"&gt;Run customized logic&lt;/a&gt; as users progress through their auth-related user journeys (e.g. signing up, signing in).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a user from your user pool logs into your application successfully, they'll have access to an &lt;a href="https://docs.amplify.aws/lib/auth/overview/q/platform/js/#accessing-aws-services" rel="noopener noreferrer"&gt;ID (JWT) token from Cognito&lt;/a&gt;. This token can be used to obtain a set of AWS credentials corresponding to a specific &lt;a href="https://yao.page/posts/identity-and-access-management-aws/#roles" rel="noopener noreferrer"&gt;IAM role&lt;/a&gt;. At this point, you might be wondering: How do we relate Cognito users to IAM identities at all? &lt;/p&gt;

&lt;h3&gt;
  
  
  Identity Pools
&lt;/h3&gt;

&lt;p&gt;This is where identity pools come in.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/tAUmz94O2Qo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Identity pools are a bit trickier to understand. The identities we're dealing with &lt;em&gt;are&lt;/em&gt; IAM identities, which means their settings (e.g. permission policies) are defined in IAM, not Cognito.&lt;/p&gt;

&lt;p&gt;I prefer to think of an identity pool as a &lt;strong&gt;collection of &lt;em&gt;associations&lt;/em&gt; between IAM roles and Cognito user pools&lt;/strong&gt;. For example, given a user pool &lt;code&gt;MyUserPool&lt;/code&gt;, we may want to define an identity pool which assigns one IAM role (e.g. &lt;code&gt;MyAuthenticatedRole&lt;/code&gt;) for users who are authenticated and another IAM role (e.g. &lt;code&gt;MyUnauthenticatedRole&lt;/code&gt;) for users who are not.&lt;/p&gt;

&lt;p&gt;In other words, these identity pools specify the relationships between user pools and IAM roles. As such, Cognito users are able to &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html" rel="noopener noreferrer"&gt;exchange Cognito tokens for AWS credentials&lt;/a&gt;. Front-end clients &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html" rel="noopener noreferrer"&gt;receive these credentials from STS&lt;/a&gt;, which allows Cognito users to interact with AWS services. &lt;/p&gt;

&lt;h2&gt;
  
  
  In Practice
&lt;/h2&gt;

&lt;p&gt;The steps for getting started with Cognito and IAM are relatively straightforward. On your JavaScript/TypeScript frontend, I recommend using the &lt;a href="https://docs.amplify.aws/lib/q/platform/js/" rel="noopener noreferrer"&gt;Amplify SDK&lt;/a&gt;, which supports many common authentication and authorization use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Console TODOs
&lt;/h3&gt;

&lt;p&gt;On the AWS Console, you'll have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Cognito user pool with an app client representing your front-end web application.&lt;/li&gt;
&lt;li&gt;Create a Cognito identity pool with roles for authenticated and unauthenticated users. As you create the identity pool, be sure to link it to your user pool and app client by listing Cognito as an  &lt;strong&gt;Authentication provider&lt;/strong&gt;. Also, ensure your authenticated and unauthenticated roles have the necessary IAM policies in place for them to interact with your AWS services, such as &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt; and &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;S3&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Front-End Application Code TODOs
&lt;/h3&gt;

&lt;p&gt;In your front-end application code, you'll need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store the region, user pool, identity pool id, and app client id in a configuration object (e.g. &lt;code&gt;config&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Initialize Amplify with this config object (e.g. &lt;code&gt;Amplify.configure(config)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Use Amplify's &lt;code&gt;Auth&lt;/code&gt; module for your auth-related operations. For example, you can simply invoke &lt;code&gt;Amplify.Auth.signUp(...)&lt;/code&gt; to register a user and add them to your user pool.&lt;/li&gt;
&lt;li&gt;Use Amplify's other modules for interacting with other services. For example, you can use &lt;code&gt;Amplify.API&lt;/code&gt; to speak with your API Gateway endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing worth calling out with Amplify's &lt;code&gt;API&lt;/code&gt; module is that it handles a lot of the IAM-based authorization work for you. &lt;/p&gt;

&lt;p&gt;Under the hood, when you invoke a method such as &lt;code&gt;Amplify.API.post(...)&lt;/code&gt;, the module derives temporary AWS credentials corresponding to the appropriate role (e.g. &lt;code&gt;MyAuthenticatedRole&lt;/code&gt;). These credentials are used to sign the frontend request payload before sending it to API Gateway. See &lt;a href="https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html" rel="noopener noreferrer"&gt;this article on SigV4&lt;/a&gt; for details on the signing process.&lt;/p&gt;

&lt;p&gt;For an overview of what the architecture I've described looks like, check out &lt;a href="https://catalog.us-east-1.prod.workshops.aws/workshops/bc60f0b2-991f-4df9-933c-234a67e75179/en-US/module-2/opt-exts" rel="noopener noreferrer"&gt;this section from an AWS Identity Workshop&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Note that using Cognito with IAM is not strictly necessary. For example, you can set up your endpoints on API Gateway to &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html" rel="noopener noreferrer"&gt;directly rely on Cognito user pools for authorization&lt;/a&gt;. Personally, I prefer the IAM-based approach, because it aligns "how Cognito users interact with APIs" with "how AWS services generally interact with each other".&lt;/p&gt;

&lt;p&gt;I'm constantly impressed by how much AWS is able to abstract out these software building blocks to help developers scaffold their applications quickly. For anyone looking to get their hands dirty with the tools covered, here are some useful references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A Beginner's Guide to Cognito from &lt;a href="https://www.youtube.com/c/BeABetterDev" rel="noopener noreferrer"&gt;one of my favorite channels&lt;/a&gt;:   &lt;iframe src="https://www.youtube.com/embed/QEGo6ZoN-ao"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://catalog.us-east-1.prod.workshops.aws/workshops/bc60f0b2-991f-4df9-933c-234a67e75179/en-US" rel="noopener noreferrer"&gt;Workshop: Using Amazon Cognito for Serverless Apps&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/building-an-application-with-aws-amplify-amazon-cognito-and-an-openid-connect-identity-provider/" rel="noopener noreferrer"&gt;Building an application with AWS Amplify, Amazon Cognito, and an OpenID Connect Identity Provider&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/security-iam.html" rel="noopener noreferrer"&gt;Identity and Access Management for Amplify&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Werewolf: A New Neo-Noir VSCode Theme</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Wed, 02 Jun 2021 18:48:33 +0000</pubDate>
      <link>https://forem.com/yaaooo/werewolf-a-new-vscode-theme-2l5b</link>
      <guid>https://forem.com/yaaooo/werewolf-a-new-vscode-theme-2l5b</guid>
      <description>&lt;p&gt;Telltale's &lt;em&gt;The Wolf Among Us&lt;/em&gt; was released in 2013 and is arguably one of the company's strongest entries in its catalog of story-driven games. The series isn't perfect, but it does have a very cinematic quality to it. The art design and narrative elements blend together really well to create a world which oscillates between fantasy and reality.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_htfCzTjCpA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The game's thoughtful use of colors, in particular, stood out to me so much that I felt compelled to port some of its neo-noir vibes over to my workspace. A brand new VSCode theme was the perfect way to capture the spirit of this masterpiece.&lt;/p&gt;

&lt;p&gt;You can find the final product &lt;a href="https://marketplace.visualstudio.com/items?itemName=yaaooo.werewolf" rel="noopener noreferrer"&gt;on the VSCode Marketplace&lt;/a&gt; and its corresponding repository &lt;a href="https://github.com/yaaooo/werewolf-vscode-theme" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful Resources
&lt;/h2&gt;

&lt;p&gt;Before I started working on my new theme, I searched for tutorials that could lead me through this process.&lt;/p&gt;

&lt;p&gt;To save you time, the best guide out there at this moment is &lt;a href="https://css-tricks.com/creating-a-vs-code-theme/" rel="noopener noreferrer"&gt;Sarah Drasner's article on CSS Tricks&lt;/a&gt;. It's a handy resource that walks you through the initial setup, configuration basics, and even some important points on accessibility. Alternatively, you can also take a peek at VSCode's official documentation for &lt;a href="https://code.visualstudio.com/api/get-started/your-first-extension" rel="noopener noreferrer"&gt;getting started with your first extension&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Throughout the theming process, I had some takeaways that I'll briefly share with you in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencing Art
&lt;/h2&gt;

&lt;p&gt;If you're basing your color palette on some art you've discovered, you might want to find a couple of expressive images that capture the look and feel you're gunning for. While working on my theme, I used the following piece of concept art pulled from ArtStation by &lt;a href="https://www.artstation.com/artwork/N9v45" rel="noopener noreferrer"&gt;Gray Rogers&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%2Fk402kr3vn3ela7q3gtfo.jpg" 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%2Fk402kr3vn3ela7q3gtfo.jpg" alt="Fabletown at Night from ArtStation by Gray Rogers" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This depiction of the cityscape was important in helping me determine the kind of mood I wanted to evoke with my choice of colors. I understood that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The theme's backdrop had to be a mix of darker shades (variants of purple and red) &lt;/li&gt;
&lt;li&gt;Points of interest could be highlighted with bursts of neon (variants of blue and green) or yellow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This eventually led to combinations of colors like the one you see below:&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%2Fy5ukwqg3pucx0i1wh9ub.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%2Fy5ukwqg3pucx0i1wh9ub.png" alt="A glimpse of my theme in Python" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each time I spun up VSCode to iterate on my theme's colors, I adjusted my IDE's window and positioned it right next to my reference image:&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%2Fzwnubnc0i52fjltq255o.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%2Fzwnubnc0i52fjltq255o.png" alt="Placing my IDE and my reference image side by side" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This provided me with a sanity check of sorts. I was able to look at the current state of my theme and compare it to the art piece: If my theme felt like an extension of its source of inspiration, then I knew I was on the right track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calibrating Colors
&lt;/h2&gt;

&lt;p&gt;As you might've noticed in some of the code snippets, not every noteworthy piece of syntax can be differentiated with single versions of blue, green, and yellow.&lt;/p&gt;

&lt;p&gt;Thus, I had to expand my selection of colors by exploring different compositions of RGB values.&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%2Fpgpurazy9cromwiqqqie.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%2Fpgpurazy9cromwiqqqie.png" alt="A glimpse of my theme in React" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, I used &lt;a href="https://www.color-hex.com/color/43bf90" rel="noopener noreferrer"&gt;#43BF90&lt;/a&gt; for &lt;strong&gt;integers/floats&lt;/strong&gt; and &lt;a href="https://www.color-hex.com/color/7accc9" rel="noopener noreferrer"&gt;#7ACCC9&lt;/a&gt; for &lt;strong&gt;strings&lt;/strong&gt;. Both hex codes have a substantial amount of green in them (which makes them appear somewhat similar) but the latter also has a substantial amount of blue (which gives string primitives a more tealish glow and helps them stand out slightly from their numerical counterparts).&lt;/p&gt;

&lt;p&gt;You can imagine extending this approach to type annotations as well. In my case, I used a darker orange for primitive type references (e.g. &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;) and used a color resembling gold for complex object types (e.g. &lt;code&gt;Props&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To me, it makes sense to use RGB values that are slight variations of each other when coloring tokens that are functionally similar, because it feels like an intuitive way of classifying related chunks of information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theming the UI before the Syntax
&lt;/h2&gt;

&lt;p&gt;To be clear, building a VSCode theme mostly just involves assigning hex values to keys in a JSON file that permits common violations such as trailing commas and comments:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="s2"&gt;Werewolf&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="s2"&gt;type&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="s2"&gt;dark&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="s2"&gt;colors&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;activityBar.background&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="s2"&gt;#220033&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="s2"&gt;editor.background&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="s2"&gt;#1D0622&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ... VSCode interface colors&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tokenColors&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="s2"&gt;Comment&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="s2"&gt;scope&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment&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="s2"&gt;punctuation.definition.comment&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="s2"&gt;settings&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foreground&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="s2"&gt;#6464B3&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;// ... Syntax highlighting colors &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;There are two major sections here: &lt;code&gt;colors&lt;/code&gt; and &lt;code&gt;tokenColors&lt;/code&gt;.  &lt;code&gt;colors&lt;/code&gt; manages the different components in your VSCode interface (e.g. your editor, sidebar, terminal, etc), while &lt;code&gt;tokenColors&lt;/code&gt; handles syntax highlighting. &lt;/p&gt;

&lt;p&gt;My suggestion is to work through your preferred &lt;code&gt;colors&lt;/code&gt; first by going through the &lt;a href="https://code.visualstudio.com/api/references/theme-color" rel="noopener noreferrer"&gt;list of available options in the VSCode docs&lt;/a&gt;. You can think of these &lt;code&gt;color&lt;/code&gt; settings as being responsible for composing the "frame" within which your code will be displayed. Updating the background colors of your editor, sidebar, and terminal is enough to radically change the overall appearance of your IDE.&lt;/p&gt;

&lt;p&gt;Configuring &lt;code&gt;tokenColors&lt;/code&gt;, on the other hand, is a trickier task that I wouldn't want to front-load. &lt;a href="https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide" rel="noopener noreferrer"&gt;Syntax highlighting&lt;/a&gt; can be as involved as you'd like it to be; you can easily find yourself tinkering with minor details that demand a lot of consideration (e.g. your preferred colors for brackets, comments, etc).&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Back
&lt;/h2&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%2Fos1zzcwb04iquzezl7vz.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%2Fos1zzcwb04iquzezl7vz.png" alt="Custom banner by Natalie Christian Tan" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I named this theme &lt;em&gt;Werewolf&lt;/em&gt; (after the main character of the series) and &lt;a href="https://marketplace.visualstudio.com/items?itemName=yaaooo.werewolf" rel="noopener noreferrer"&gt;shared it publicly&lt;/a&gt; a few months ago. Some minor administration was required too; I had to set up proper channels and templates for submitting pull requests and issue reports on the &lt;a href="https://github.com/yaaooo/werewolf-vscode-theme" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Looking back, I'm not sure this project taught me substantial things about web development, but spending those hours playing around with a bunch of colors like a kid was a pretty fun and relaxing experience.&lt;/p&gt;

&lt;p&gt;My only regret is not shipping this out earlier because &lt;em&gt;The Wolf Among Us&lt;/em&gt; is a pretty dated game at this point. If there's a certain visual language out there that inspires you, I encourage you to take a couple of evenings off to give theming your VSCode a shot!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Warcraft III and Web Development</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Tue, 20 Oct 2020 02:06:23 +0000</pubDate>
      <link>https://forem.com/yaaooo/warcraft-iii-and-web-development-5151</link>
      <guid>https://forem.com/yaaooo/warcraft-iii-and-web-development-5151</guid>
      <description>&lt;p&gt;I ended my stint as a web engineer at &lt;a href="https://www.carousell.com/" rel="noopener noreferrer"&gt;Carousell&lt;/a&gt; a few weeks ago. With the extra time I had on my hands, I dusted off a couple of old laptops and wandered through various files and folders from my teenage life.&lt;/p&gt;

&lt;p&gt;One of the gems I came across was a custom Warcraft III map I created in the mid-2000s that I never got around to finishing. I had planted units (i.e. character models) in their respective groups and had shaped up the terrain to look fairly immersive, but the bulk of the gameplay was still incomplete.&lt;/p&gt;

&lt;p&gt;I figured I owed it my younger self to see the project through, so I dug up my old Warcraft III keys, re-installed the game, and started hacking away on the &lt;a href="https://en.wikipedia.org/wiki/Warcraft_III_World_Editor" rel="noopener noreferrer"&gt;World Editor&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Back In Time
&lt;/h2&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%2Fi%2F0iru7gg5l0wq4zm3frik.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%2Fi%2F0iru7gg5l0wq4zm3frik.png" alt="A view of Warcraft III's World Editor" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was definitely strange revisiting the World Editor after more than a decade. Back in the day, I didn't have the slightest idea about the basics of programming, so I sort of fumbled my way through variables and conditionals. I usually tell people that my first experience with programming was Harvard's CS50, but perhaps Warcraft –– yes, Warcraft –– prepared me in ways I never realised.&lt;/p&gt;

&lt;p&gt;There are a number of similarities between map editing and web development. This isn't exactly a profound insight, as you'd expect to find overlapping patterns between most types of user-facing applications anyway. Still, I think it'll be fun to explore this space and draw some comparisons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triggers / Event Listeners
&lt;/h2&gt;

&lt;p&gt;Warcraft's World Editor is composed of multiple editors such as the Object Editor, the Sound Editor, and the Trigger Editor. The Trigger Editor, in particular, manages a collection of &lt;em&gt;triggers&lt;/em&gt; which are each responsible for coordinating a specific in-game behaviour.&lt;/p&gt;

&lt;p&gt;Suppose you want to kickstart a cinematic sequence when a unit enters a particular area. The corresponding trigger might look something 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%2Fi%2Ff86i0jdorha408fr3xq7.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%2Fi%2Ff86i0jdorha408fr3xq7.png" alt="A trigger in the Trigger Editor" width="660" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every trigger is divided into three components: Events, Conditions, and Actions. There are several &lt;a href="https://world-editor-tutorials.thehelper.net/triggers.php" rel="noopener noreferrer"&gt;online tutorials&lt;/a&gt; which do a solid job of explaining the rudiments, but experienced programmers will have an easy time understanding the terms: Whenever an Event occurs, the Warcraft runtime confirms if all specified Conditions have been satisfied before it runs a series of defined Actions.&lt;/p&gt;

&lt;p&gt;As web developers, a huge part of our job involves working with event listeners, which are functionally identical to these triggers. We watch for browser events (such as &lt;code&gt;resize&lt;/code&gt; and &lt;code&gt;keydown&lt;/code&gt;) and respond to them by executing handlers which may subsequently invoke web APIs (such as timers and XMLHTTPRequests).&lt;/p&gt;

&lt;p&gt;In any event-based system, a common mistake people make is forgetting to unsubscribe listeners after they've become irrelevant. Allowing a trigger to repeat itself more than once in a Warcraft map can make for a disjointed gaming experience. Similarly, in the case of web applications, failing to remove listeners can easily lead to memory leaks and unwanted side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layers / Browser Tools
&lt;/h2&gt;

&lt;p&gt;Deciding how to place units, designate regions, angle cameras, and curate the terrain — these are all important aspects of a custom Warcraft map.&lt;/p&gt;

&lt;p&gt;In the World Editor, each of these concerns are owned by a specific &lt;em&gt;layer&lt;/em&gt;. For example, we can only move units around the map with the cursor if the Unit Palette has been selected. Likewise, if you wanted to modify the texture of the ground, you'd have to engage the Terrain Palette. We can't select items across layers and modify them simultaneously.&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%2Fi%2Fbhuhkq5txjaaw112s4yg.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%2Fi%2Fbhuhkq5txjaaw112s4yg.png" alt="Viewing the map through the lens of the Camera Palette" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the realm of web development, we often find ourselves switching between the templating (HTML), styling (CSS), and interactivity (JavaScript) tools available to us on the browser. The boundaries between them are a lot fuzzier thanks to inlined styles/scripts in HTML and the fluidity of frameworks such as React and Vue which allow us to blend the different syntaxes together (e.g. JSX, MDX).&lt;/p&gt;

&lt;p&gt;Using triggers, map authors can direct elements from one layer to interact with elements from another (e.g. creating a specific type of unit at region X). This is kind of similar to how web developers use JavaScript to make changes to the DOM or CSSOM. Although the separation of concerns takes a fair bit of getting used to in both cases, it goes a long way providing people with a more cohesive development experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Player Journeys / User Journeys
&lt;/h2&gt;

&lt;p&gt;At the risk of stating the obvious: As the complexity of a Warcraft map grows, the number of triggers involved naturally increases. Hence, it's important to put some thought into how triggers are classified, so that map authors have an easier time reviewing and modifying them.&lt;/p&gt;

&lt;p&gt;For single-player maps with multiple acts or chapters, it can be difficult to keep track of the triggers used at each point of the player's journey. I've found it appropriate to group triggers by the &lt;em&gt;Quests&lt;/em&gt; they are associated with:&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%2Fi%2Fpogr622s3dfao2qu75df.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%2Fi%2Fpogr622s3dfao2qu75df.png" alt="Grouping triggers by their associated Quests" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each quest can be thought of as an independent slice of the game. One quest might require the player to bring character A to point X, while another might task the player to survive enemy attacks for a specified amount of time. Unless you go out of your way to design a map that heavily relies on choice and consequence, the triggers of one quest usually don't have much bearing on the triggers of another. Thus, it makes sense to bunch up triggers together based on the quests they belong to.&lt;/p&gt;

&lt;p&gt;In web applications, many user journeys can span multiple pages. For instance, making a purchase on an e-commerce platform can take a user from the product details page, to the cart, and then to the checkout screen. In the same vein, I've found it helpful to modularise frontends by grouping data operations by their corresponding pages.&lt;/p&gt;

&lt;p&gt;In that sense, one might say that the relationship between quests and player journeys in Warcraft maps is somewhat analogous to the relationship between pages and user journeys in large web applications. &lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding Thoughts
&lt;/h2&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%2Fi%2Fxmbsqf26axb5vruqywac.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%2Fi%2Fxmbsqf26axb5vruqywac.png" alt="An in-game screenshot" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ultimately, the challenges of map editing are still very different from web development. This is especially the case if you're creating a custom map with narrative elements. Beyond designing the in-game logic, you'll also have to get your hands dirty with other domains such as cinematography, writing, and pacing.&lt;/p&gt;

&lt;p&gt;There are plenty of details I haven't gotten into in this article. The Warcraft Trigger Editor, at its core, is just a GUI for defining scripted behaviours. Blizzard's JASS language allows map authors to tap into all kinds of in-game possibilities.&lt;/p&gt;

&lt;p&gt;Today, the presence of platforms such as &lt;a href="[http://dreams.mediamolecule.com/](http://dreams.mediamolecule.com/)"&gt;Dreams&lt;/a&gt; lowers the barriers to entry for people to create games without fussing too much over the lower-level details. It's exciting to see where this field is headed and what kinds of fancy creations will emerge. Personally, I'm keeping an eye out for updates from &lt;a href="https://youtu.be/-ir9Yc0ezNo" rel="noopener noreferrer"&gt;this Avatar remake&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/SAEyhW_D1Ms"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Feel free to watch the finished product on YouTube! It's not terribly exciting and it pales in comparison to some of the more impressive works out there, but I'm satisfied with what I got out of the creative process. My goal with this exercise was to simply reconnect with my past self while dipping my toes in as many aspects of map editing as possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing and directing cinematic sequences&lt;/li&gt;
&lt;li&gt;Managing quests and their associated triggers&lt;/li&gt;
&lt;li&gt;Playing with music, environment effects, and sounds&lt;/li&gt;
&lt;li&gt;Importing and using custom models made by the community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've played games like Warcraft in the past, I hope this post inspires you to have fun in a similar fashion!&lt;/p&gt;

</description>
      <category>sideprojects</category>
      <category>watercooler</category>
    </item>
  </channel>
</rss>
