<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Thinkmill</title><description>A depth of experience in software design and engineering that few can match – from research and UX/UI Design, to Node.js, React, TypeScript, and GraphQL.</description><link>https://www.thinkmill.com.au/</link><language>en-au</language><item><title>What AI use looks like in five enterprise design teams</title><link>https://www.thinkmill.com.au/blog/what-ai-use-looks-like-in-five-enterprise-design-teams</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/what-ai-use-looks-like-in-five-enterprise-design-teams</guid><description>Listening to design teams at Stripe, Shopify, Ramp, Atlassian, and Intercom, three ingredients become clear: a place to prototype, good context, and someone helping people put both to use.</description><pubDate>Mon, 07 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;At Thinkmill, we’ve been helping &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;design system teams&lt;/a&gt; adopt &lt;a href=&quot;https://www.thinkmill.com.au/services/ai&quot;&gt;AI-assisted design and development&lt;/a&gt;. These days, a lot of that work centres around one key question: &lt;em&gt;how do you set up an AI agent to produce something genuinely useful?&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Most of what’s been written about AI focuses on shiny demos or big predictions. I wanted to know how teams are &lt;em&gt;actually&lt;/em&gt; working today. I scoured the web and watched many hours of interviews with designers and engineers at Stripe, Shopify, Ramp, Atlassian and Intercom. Each participant walked through their design process now that AI is part of the mix.&lt;/p&gt;&lt;p&gt;I found that each team had invested in three key areas:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Somewhere to prototype&lt;/li&gt;&lt;li&gt;A way of providing AI with the right context&lt;/li&gt;&lt;li&gt;The work of helping people adopt both&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Here’s a quick snapshot of each team’s approach, based on publicly available information, at the time of publication:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=CqMZTg7L-wE&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Atlassian&lt;/strong&gt;&lt;/a&gt; uses off-the-shelf tools including Figma Make and Replit, alongside fine-tuned design system documentation.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=_9OdGDjFrCw&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Intercom&lt;/strong&gt;&lt;/a&gt; prototypes directly in their production codebase with Claude Code, Figma Code Connect and the Figma MCP server.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=hQFEAZK__q0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Stripe&lt;/strong&gt;&lt;/a&gt; built their own prototyping environment, &lt;a href=&quot;https://owenwillia.ms/work/protodash/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Protodash&lt;/a&gt;, which runs in its own environment outside of production.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aVDAhJ3PtLg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Shopify&lt;/strong&gt;&lt;/a&gt; uses an internal toolkit called &lt;a href=&quot;http://shopify.engineering/quick&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Quick&lt;/a&gt; together with a playground that mirrors their admin interface.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=mH5qIPx47jw&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Ramp&lt;/strong&gt;&lt;/a&gt; built a custom platform called &lt;a href=&quot;https://builders.ramp.com/post/why-we-built-our-background-agent&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Inspect&lt;/a&gt; to action bug fixes and customer feedback in production.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Somewhere to prototype&lt;/h2&gt;&lt;h3&gt;Diverging approaches&lt;/h3&gt;&lt;p&gt;Two things distinguish the approach to prototyping amongst the five teams.&lt;/p&gt;&lt;h4&gt;Hand-rolled vs off-the-shelf tools&lt;/h4&gt;&lt;p&gt;The first is whether a team &lt;strong&gt;rolled their own prototyping tool or reached for an existing one&lt;/strong&gt;. Stripe and Ramp have invested in internal platforms. Atlassian leans more heavily on products such as Figma Make and Replit. Intercom and Shopify sit somewhere in the middle, with their own workflows and integrations.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/jmho6mcol1d6/build-vs-buy?width=848&quot; alt=&quot;There&apos;s been a mix of investing in off-the-shelf configuration and custom-made tooling.&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;figcaption&gt;There&apos;s been a mix of investing in off-the-shelf configuration and custom-made tooling.&lt;/figcaption&gt;&lt;/figure&gt;&lt;h4&gt;Sandboxed vs wired to production&lt;/h4&gt;&lt;p&gt;The second is &lt;strong&gt;how close the prototype is to production&lt;/strong&gt;. Stripe appears to keep Protodash isolated in a sandbox, but builds it from the real design system, so what a designer ships in a review is the same code an engineer can pick up. Ramp goes even further and prototypes directly on a branch of production. Both approaches result in a prototype real enough for people to trust it.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/qnxalb2kjdl9/sandbox-vs-production?width=848&quot; alt=&quot;Design teams have different postures when it comes to integrating prototyping into development pipelines.&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;figcaption&gt;Design teams have different postures when it comes to integrating prototyping into development pipelines.&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;Converging features&lt;/h3&gt;&lt;p&gt;Despite these diverging approaches to prototype setups, many teams and tools are converging on similar features. Features which all work to make design conversations easier, help people align earlier, and explore ideas quickly.&lt;/p&gt;&lt;h4&gt;Shareable, hosted URLs and CLI commands &amp;amp; helpers&lt;/h4&gt;&lt;p&gt;All five teams have created a safe space for designers to store and share prototypes. This enables anyone in the org to view a prototype in their browser without installing anything or setting up a local environment.&lt;/p&gt;&lt;p&gt;Intercom and Shopify have optimised the prototype publising process by providing CLI commands to prototype creators.&lt;/p&gt;&lt;h4&gt;Control panel UI&lt;/h4&gt;&lt;p&gt;Another common feature was a UI panel to control application state. This lets prototype creators and reviewers switch between empty and populated states, test with messy data, preview a different language, or see how the interface behaves for a startup compared to an enterprise customer, without rebuilding the prototype. That makes the prototype more useful because it can quickly test the robustness of the design against common user scenarios.&lt;/p&gt;&lt;h4&gt;Fidelity control and rules-based output adjustment&lt;/h4&gt;&lt;p&gt;Stripe and Shopify deliberately signal when work is unfinished. Early concepts are shown in greyscale or using off-brand UI components so reviewers naturally treat them as early prototypes rather than polished ready-to-ship designs.&lt;/p&gt;&lt;h4&gt;In prototype comments&lt;/h4&gt;&lt;p&gt;To help facilitate design review, Stripe and Shopify both built commenting directly into the prototype. AI summarises the feedback, then acts on the requested changes. Atlassian leverages similar features with Figma Make.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/jxtgh7ecc40q/prototype-features?width=848&quot; alt=&quot;A feature comparison of prototype tools and custom-built tools. Atlassian omitted above as they use Figma Make.&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;figcaption&gt;A feature comparison of prototype tools and custom-built tools. Atlassian omitted above as they use Figma Make.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Each one of these ideas removes a little friction from the design prototyping process.&lt;/p&gt;&lt;h2&gt;Context is what makes the difference&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;[An LLM] will just imagine the entire design system without telling you when it’s not there. – &lt;strong&gt;Owen Williams&lt;/strong&gt;, Stripe Engineer&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;If the prototyping environment is where the work happens, context is what determines the quality of the result.&lt;/p&gt;&lt;p&gt;This was the strongest pattern across the five teams. &lt;strong&gt;The quality of the AI-generated work depended on the quality of the information it was given.&lt;/strong&gt; Each team had invested in providing context across the design system, the project, and the customer.&lt;/p&gt;&lt;h3&gt;Design system context&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://www.thinkmill.com.au/blog/why-your-design-system-is-the-most-important-input-to-claude-code&quot;&gt;Design system context&lt;/a&gt; is where most of the effort has gone, and for good reason. The challenge is hallucination. As Stripe’s Owen Williams put it, an LLM “will just imagine the entire design system without telling you when it’s not there.” The output often looks believable, even when it’s built on components that don’t exist. The trap is that the work looks complete, so teams start building on it, then hit the gap when someone tries to ship the real thing and the component isn&apos;t there or it doesn&apos;t work that way.&lt;/p&gt;&lt;p&gt;Several teams had run into the same limitation. Design tokens translate reasonably well to AI tools. Composing real components is much harder.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Atlassian&lt;/strong&gt; generates instruction files directly from their component packages, but only exposes a carefully selected subset to the model. They narrow it down to 20 to 30 components with clear examples for each. More context, they found, often produced worse results. They also built a “translating from Tailwind” table for every component. Since language models have seen so much Tailwind and React, Atlassian lets the model think in familiar terms before mapping those concepts back to the correct design system components.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Intercom&lt;/strong&gt; uses Figma Code Connect across their component library. Instead of recreating components from scratch, the AI receives the real import paths from the production codebase and works with what’s already there.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Stripe&lt;/strong&gt; exposes their design system through an MCP server and requires the AI to query it before writing any code. Just as importantly, it defines what should happen if that server isn’t available, so the model fails honestly instead of confidently inventing an answer.&lt;/p&gt;&lt;p&gt;The principle behind each of these approaches is consistent. &lt;strong&gt;AI performs better when it can rely on a trusted source of truth rather than trying to fill in the gaps itself.&lt;/strong&gt; My colleague &lt;a href=&quot;https://www.thinkmill.com.au/authors/joe-prisk&quot;&gt;Joe Prisk&lt;/a&gt; explored this in his article, &lt;a href=&quot;https://www.thinkmill.com.au/blog/why-your-design-system-is-the-most-important-input-to-claude-code&quot;&gt;Why your design system is the most important input to Claude Code&lt;/a&gt;. Despite coming from different directions, each of these teams leveraged their existing design system as context for their AI prototyping tools.&lt;/p&gt;&lt;h3&gt;Project context&lt;/h3&gt;&lt;p&gt;Project context is much simpler. It’s the brief, the requirements and the background to the work. Stripe’s designers paste a product requirements document directly from Google Docs into their prototype. Some Shopify designers keep a small collection of markdown files alongside each project so the model starts with the right context before generating anything.&lt;/p&gt;&lt;h3&gt;Customer context&lt;/h3&gt;&lt;p&gt;At Ramp, designers begin by understanding what customers are experiencing. They review session recordings in LogRocket, use internal tools that summarise where people struggle, and query research transcripts with Notion AI. Those insights shape both the prototype and the prompts that generate it.&lt;/p&gt;&lt;p&gt;Most of that work is still manual. Designers read the research, identify the patterns and bring those insights into the prototyping process themselves.&lt;/p&gt;&lt;p&gt;The same discipline that’s emerging around design systems and project context can also be applied to customer understanding. As these workflows mature, AI won’t just have better information about how an interface should be built. It’ll have better information about the people it’s being designed for.&lt;/p&gt;&lt;h2&gt;Supporting adoption&lt;/h2&gt;&lt;p&gt;The least visible part of these workflows may be the most important.&lt;/p&gt;&lt;p&gt;Design teams bring together people with very different technical backgrounds. Some designers have never opened a code editor, while others are comfortable working in Git and the command line. Even small hurdles, like setting up permissions, installing tools, or deploying a prototype, can stop someone before they get to the work itself. The teams I observed don’t treat this as an afterthought. &lt;strong&gt;They invest in training just as deliberately as they invest in tooling&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;At Shopify, AI adoption accelerated after its CEO, Tobi Lütke, set the expectation that &lt;a href=&quot;https://x.com/tobi/status/1909251946235437514&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;using AI would become part of how people work and how performance is assessed&lt;/a&gt;. Intercom’s push came from &lt;a href=&quot;https://ideas.fin.ai/p/2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;its CTO and design leadership&lt;/a&gt;. Atlassian ran &lt;a href=&quot;https://www.atlassian.com/blog/ai-at-work/a-design-technologists-take-on-ai-builders&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a company-wide “AI Builder Week”&lt;/a&gt; where, by their account, thousands of employees paused their usual work for a week to learn AI prototyping.&lt;/p&gt;&lt;p&gt;The strongest adopters also &lt;strong&gt;made it easy for people to learn from each other&lt;/strong&gt;. Intercom created a Prototyping Hub with practical resources, including a plain-language “Code 101 for designers” guide and step-by-step videos. Shopify maintains an internal gallery of prototypes alongside a shared library of prompts that others can reuse. Atlassian even discovered that inviting people into a Slack channel wasn’t particularly effective, while sending a group direct message prompted much stronger engagement. They built a small bot around that insight.&lt;/p&gt;&lt;p&gt;The broader lesson is that &lt;strong&gt;the investment in tools and the investment in people need to move together&lt;/strong&gt;. A sophisticated prototyping environment isn’t much use if only a handful of people feel confident enough to use it.&lt;/p&gt;&lt;h2&gt;What to get right first&lt;/h2&gt;&lt;p&gt;The teams making the fastest progress had one thing in common; &lt;strong&gt;they already had a strong &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;design system&lt;/a&gt; before AI entered the workflow&lt;/strong&gt;. A design system has always been about making one team’s decisions clear and reusable for everyone else. Tokens, components, documentation, and guidance exist to help people build consistently. We’ve been doing that work for well over a decade. AI agents are just another audience for the same clarity. If you’re exploring what this means for your own team, &lt;a href=&quot;https://www.thinkmill.com.au/contact&quot;&gt;we’re always happy to talk it through&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/cs32dlgo92ur/ai-round-up-poster?width=1200" length="83259" type="image/jpeg"/></item><item><title>From Vibes to Durable Context</title><link>https://www.thinkmill.com.au/blog/from-vibes-to-durable-context</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/from-vibes-to-durable-context</guid><description>AI can speed up implementation, but it does not remove the need for good engineering. This article looks at how planning, human judgment, useful documentation, and a well-structured codebase can help coding agents make better decisions with less guesswork.</description><pubDate>Thu, 20 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;In my article on &lt;a href=&quot;https://www.thinkmill.com.au/blog/where-ai-actually-helps-quality-engineering&quot;&gt;where AI can help with quality engineering&lt;/a&gt;, I wrote about planning, testing, review, and the parts of software delivery where faster feedback can improve the result.&lt;/p&gt;&lt;p&gt;Since then, I have been thinking about the other side of that argument. AI can make implementation much faster, but the more I work with coding agents, the more I find that &lt;a href=&quot;https://www.thinkmill.com.au/blog/why-great-engineering-still-wins&quot;&gt;engineering discipline&lt;/a&gt; matters.&lt;/p&gt;&lt;p&gt;The structure of a codebase shapes what an agent can understand from it. Clear patterns and boundaries reduce the amount it has to infer or guess. When it does have to guess, those assumptions can turn into code very quickly.&lt;/p&gt;&lt;h2&gt;Agents will fill the gaps&lt;/h2&gt;&lt;p&gt;It is common to pick up a ticket and find something nobody has decided yet.&lt;/p&gt;&lt;p&gt;Maybe the design covers the happy path but says nothing about a partial failure. Maybe the API contract leaves room for interpretation. Or the change crosses a boundary that looks arbitrary in the code but exists for a reason that is no longer obvious.&lt;/p&gt;&lt;p&gt;In a large codebase, the ticket only tells you so much. You might need to dig through an old discussion, ask product what the expected behaviour should be, or talk to someone who remembers why a particular choice was made. Sometimes finding that missing context is the engineering work.&lt;/p&gt;&lt;p&gt;Coding agents often handle that differently. When a requirement has two plausible interpretations, an agent will often choose one and keep going. The choice may be perfectly reasonable given what it can see, even when the missing context is the part that matters most.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/tb1yhjtg1ycf/from-vibes-to-durable-context-agents-will-fill-the-gaps?width=848&quot; alt=&quot;Ambiguity still needs judgment.&quot; width=&quot;848&quot; height=&quot;565&quot; /&gt;&lt;figcaption&gt;Ambiguity still needs judgment.&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Engineering still happens before implementation&lt;/h2&gt;&lt;p&gt;I have found &lt;a href=&quot;https://www.thinkmill.com.au/services/ai&quot;&gt;planning with agents useful&lt;/a&gt;. Before implementation starts, I want the agent to show me where the task is still unclear. What assumptions is it making? Which behaviours are underspecified? Are there decisions that could materially change the solution?&lt;/p&gt;&lt;p&gt;Sometimes a short conversation is enough. Larger changes may benefit from a more structured approach such as spec-driven development.&lt;/p&gt;&lt;p&gt;Planning surfaces decisions that might otherwise be made implicitly during implementation. From there, someone still needs to decide the correct behaviour, weigh the trade-offs, and understand which constraints are intentional.&lt;/p&gt;&lt;p&gt;AI speeds up development, and that can make it easier to skip some of the decision-making because plausible assumptions can quickly turn into code.&lt;/p&gt;&lt;h2&gt;More documentation is not always more context&lt;/h2&gt;&lt;p&gt;That kind of planning can also produce useful artefacts before implementation starts. Specs and implementation plans make important decisions easier to review and communicate with stakeholders.&lt;/p&gt;&lt;p&gt;The concern is what happens over time. LLMs make it cheap to produce specs, architecture notes, and instructions for future agents, but keeping them accurate is much harder. Code changes, designs evolve, and behaviour shifts.&lt;/p&gt;&lt;p&gt;If the documentation does not change with the implementation, it can keep describing an older version of the system while still looking authoritative. That creates another source of truth, and stale documentation can be especially confusing for coding agents because it is still usable context.&lt;/p&gt;&lt;p&gt;Prose is also difficult to validate against the implementation as the system changes. A document can say that copied records must stay independent, or that a dependency boundary must be preserved, but nothing fails when those rules are broken.&lt;/p&gt;&lt;p&gt;The reasoning may still be worth documenting. If something needs to remain true, though, prose alone is a weak place to keep it.&lt;/p&gt;&lt;h2&gt;Make the decisions executable&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/wrms6nw3zk6z/from-vibes-to-durable-context-make-the-decisions-executable?width=848&quot; alt=&quot;Context comes from many places.&quot; width=&quot;848&quot; height=&quot;565&quot; /&gt;&lt;figcaption&gt;Context comes from many places.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Humans build up context over time through reviews, incidents, conversations, and experience with the codebase. An agent starts without that history, so it has to rely on the task, the context we give it, and what it can work out from the repository.&lt;/p&gt;&lt;p&gt;That makes the &lt;a href=&quot;https://www.thinkmill.com.au/blog/optimise-your-context-not-your-prompts&quot;&gt;context in the codebase&lt;/a&gt; important. When a constraint matters enough to shape the implementation, I want to see whether the codebase can carry it directly.&lt;/p&gt;&lt;p&gt;A behaviour that must not regress can become a test. Types or validation can rule out invalid states. Linting or dependency rules can make architectural boundaries enforceable. These &lt;a href=&quot;https://www.thinkmill.com.au/services/frontend-engineering&quot;&gt;ordinary engineering practices&lt;/a&gt; give an agent immediate feedback when it gets something wrong, rather than relying on it to find and correctly interpret a Markdown file at the right moment.&lt;/p&gt;&lt;p&gt;Product reasoning, trade-offs, and historical context still need prose. Documentation is most useful when it captures what the code cannot, instead of becoming a second implementation of the system in English.&lt;/p&gt;&lt;h2&gt;Faster implementation raises the stakes&lt;/h2&gt;&lt;p&gt;I do not think coding agents require us to rethink software engineering from scratch. Faster implementation puts more pressure on the decisions around it: what gets clarified up front, what still needs human judgment, what is worth documenting, and what the codebase should enforce.&lt;/p&gt;&lt;p&gt;Good AI-assisted development comes from making the right context available where it matters, so fewer important decisions are left to guesswork.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Is Your Design System Ready for AI?</title><link>https://www.thinkmill.com.au/blog/is-your-design-system-ready-for-ai</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/is-your-design-system-ready-for-ai</guid><description>AI can generate convincing interfaces, but it struggles when a design system’s decisions live in team knowledge rather than its components, documentation and defaults. Preparing a design system for AI means making those decisions discoverable, explicit and verifiable.</description><pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;We recently used AI to build a new piece of UI using an existing design system. The first result looked good. It compiled, broadly matched the design and used familiar patterns from the product.&lt;/p&gt;&lt;p&gt;Then the design departed slightly from the existing system. It introduced a new border colour and a small variation on an existing component. Instead of treating them as extensions of existing patterns, the AI created new components from scratch.&lt;/p&gt;&lt;p&gt;Nothing it produced was obviously broken. The interface looked reasonable in isolation, but it was slowly building a design system of its own.&lt;/p&gt;&lt;p&gt;AI can generate interfaces, but getting it to generate interfaces that belong in your product is much harder.&lt;/p&gt;&lt;h2&gt;A component library is not a design language&lt;/h2&gt;&lt;p&gt;Most design systems give engineers a collection of components, tokens and documentation. They tell us what exists. The decisions around how those pieces should be used together are often less visible.&lt;/p&gt;&lt;p&gt;One of our form inputs had an option to preserve space for validation messages. The team had decided to always enable it because the form shifted whenever a message appeared beneath an input.&lt;/p&gt;&lt;p&gt;The component defaulted to leaving the space out. Engineers on the project knew to override the default. The decision had become part of the team’s shared knowledge, passed between people during implementation and code review. It wasn’t documented anywhere the AI could find.&lt;/p&gt;&lt;p&gt;Whenever the AI added a form, it used the default. It found the correct component and used its API correctly. It had no way to know that the team had established a convention beyond what the component expressed.&lt;/p&gt;&lt;p&gt;This is where a component library stops short of describing the full design language.&lt;/p&gt;&lt;p&gt;Components and tokens define the available vocabulary. Teams also make decisions about composition, hierarchy and behaviour. Which variants are preferred. How components should respond to loading and error states. Which optional properties should be used in almost every implementation.&lt;/p&gt;&lt;p&gt;People absorb these decisions over time. AI needs somewhere to find them.&lt;/p&gt;&lt;h2&gt;What makes a design system AI-ready?&lt;/h2&gt;&lt;p&gt;An AI-ready design system makes its &lt;a href=&quot;https://www.thinkmill.com.au/blog/design-system-kickoff-checklist&quot;&gt;components, rules and examples&lt;/a&gt; discoverable without relying on someone remembering whom to ask.&lt;/p&gt;&lt;p&gt;Components in design map cleanly to their production equivalents. Tokens are structured and consistently used. Component APIs guide consumers towards expected behaviour. Documentation stays close enough to the implementation to remain accurate. Examples show components working together in realistic situations. Tests verify some of these expectations.&lt;/p&gt;&lt;p&gt;These are familiar &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;design system concerns&lt;/a&gt; and AI gives them a new consumer.&lt;/p&gt;&lt;p&gt;An engineer faced with a confusing API can search the codebase, inspect existing implementations or ask another engineer what the team normally does. An AI agent works from the &lt;a href=&quot;https://www.thinkmill.com.au/blog/optimise-your-context-not-your-prompts&quot;&gt;context it has been given&lt;/a&gt;. If it finds three competing form patterns and incomplete examples, it has to choose between them.&lt;/p&gt;&lt;p&gt;It will usually choose something plausible. That can be difficult to catch because the resulting code often compiles, passes a quick visual check and looks reasonable in isolation. The inconsistency only becomes obvious when you understand the wider system.&lt;/p&gt;&lt;p&gt;An AI-ready design system makes those wider decisions visible.&lt;/p&gt;&lt;h2&gt;Access is getting better&lt;/h2&gt;&lt;p&gt;The tools around design systems are starting to expose more useful context to AI.&lt;/p&gt;&lt;p&gt;Figma’s MCP server can provide structured information about a design instead of requiring an agent to infer everything from a screenshot. Code Connect can link a component in Figma to its production implementation. Storybook can expose component APIs, stories and documentation. Its MCP integration also gives agents a way to render components and run tests. Component registries make existing implementations directly available.&lt;/p&gt;&lt;p&gt;An agent has a better chance of finding your &lt;code&gt;Button&lt;/code&gt; when it can discover the real component and see how it is used, however that access only solves part of the problem.&lt;/p&gt;&lt;p&gt;It won’t explain why your team always preserves validation-message space unless the decision appears somewhere in the system. A Storybook story that demonstrates the default behaviour may even reinforce the wrong pattern. Several discoverable components that solve the same problem still leave the agent to decide which one to use.&lt;/p&gt;&lt;p&gt;The quality of the result depends on the quality of the system being exposed.&lt;/p&gt;&lt;h2&gt;Defaults carry more weight now&lt;/h2&gt;&lt;p&gt;Our form-input problem could have been addressed through documentation. The component API offered another place to capture the decision. If every implementation should preserve message space, the default should reflect that.&lt;/p&gt;&lt;p&gt;Component APIs already guide engineers towards particular outcomes. AI agents follow that guidance very literally. They reach for the available component, accept its defaults and continue building. A good API makes the expected behaviour the easiest path.&lt;/p&gt;&lt;p&gt;That might mean changing a default, removing an option with few valid uses or introducing a higher-level composition that encodes the team’s preferred layout. A recurring component variation might belong in the design system instead of being recreated with local styles each time.&lt;/p&gt;&lt;p&gt;Some decisions will always depend on product context and need explanation. Others can be captured directly in components, tokens and tests.&lt;/p&gt;&lt;p&gt;Every decision encoded in the system is one less decision an engineer or AI agent has to rediscover.&lt;/p&gt;&lt;h2&gt;Start with the mistakes&lt;/h2&gt;&lt;p&gt;Teams can start by looking at what AI already gets wrong. Ask it to implement a small workflow using the existing design system. Watch where it invents a component, reaches for a raw colour value or chooses an unwanted variation. Pay attention to the corrections an experienced engineer makes during review. Those corrections point towards specific &lt;a href=&quot;https://www.thinkmill.com.au/services/design-system-audit&quot;&gt;gaps in the design system&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The agent may create a component because the existing one was difficult to discover. It may use a raw value because the appropriate token was unclear. It may choose the wrong variant because every documented example used the default. It may assemble the right components incorrectly because the expected composition lived only in the team’s shared knowledge.&lt;/p&gt;&lt;p&gt;Each mistake gives you something concrete to improve. Take the component and connect it to its implementation. Update the default, encode the common composition and add a test that captures the expected behaviour. Then run the same task again.&lt;/p&gt;&lt;p&gt;The useful measure is whether the system helped the agent make a better decision without another carefully worded prompt.&lt;/p&gt;&lt;h2&gt;AI exposes the gaps&lt;/h2&gt;&lt;p&gt;The real design system is usually spread across Figma libraries, production components, documentation, code review comments and the memories of people who have worked on the product for years.&lt;/p&gt;&lt;p&gt;AI has none of that shared history unless we make it available.&lt;/p&gt;&lt;p&gt;It will use the default. It will choose the first plausible component. It will recreate a pattern when the existing one is difficult to find. Those choices reveal where the system depends on knowledge it has never captured.&lt;/p&gt;&lt;p&gt;Preparing a design system for AI means making those decisions clear, discoverable and verifiable. That helps AI generate interfaces that belong in the product. It also helps the next engineer understand why they were built that way.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Build vs buy: AI code review for teams that have opinions</title><link>https://www.thinkmill.com.au/blog/build-vs-buy-ai-code-review-for-teams-that-have-opinions</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/build-vs-buy-ai-code-review-for-teams-that-have-opinions</guid><description>There’s a good chance that somewhere in your org, someone is trialling an AI code review tool right now, and at the same time someone else is pasting agent output into PRs under their own name.</description><pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;The trial is the visible part. Pick a tool like &lt;a href=&quot;https://www.coderabbit.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CodeRabbit&lt;/a&gt;, &lt;a href=&quot;https://www.greptile.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Greptile&lt;/a&gt;, &lt;a href=&quot;https://www.qodo.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Qodo&lt;/a&gt; or &lt;a href=&quot;https://github.com/features/code-review&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Copilot&lt;/a&gt;, install the GitHub App, and within an hour it’s leaving comments on your pull requests.&lt;/p&gt;&lt;p&gt;For anyone leading a medium-to-large engineering team, the demo only answers the easy question. The harder one is who controls the rules tomorrow – and that’s the question that splits build from buy. But before weighing either option, the second engineer deserves a closer look.&lt;/p&gt;&lt;h2&gt;Your team is already doing AI review&lt;/h2&gt;&lt;p&gt;That engineer is not an outlier. While you’re evaluating vendors, or if you’ve outgrown your current tool, people across most orgs are piping diffs through the desktop agent they use for everything else, skimming the output, and pasting the good comments into the PR.&lt;/p&gt;&lt;p&gt;It’s understandable, and it breaks the one thing PR approval is supposed to mean. When a reviewer approves a change, the org is recording that a human weighed it. Once model comments ship under human names, that record is unreliable. You can’t tell a considered approval from a laundered one, and neither can the incident review six months later that’s trying to work out who actually looked at the migration.&lt;/p&gt;&lt;p&gt;The secondary costs follow from there. Each engineer’s agent applies whatever conventions live in their local setup, so &quot;review standards&quot; fragment per laptop, and none of it accumulates anywhere the org can see, version, or correct.&lt;/p&gt;&lt;p&gt;This is the status quo any build-vs-buy decision replaces: ad-hoc, unattributed AI review, fragmenting one laptop at a time. The bought tools fix the attribution problem on day one, which is a genuine point in their favour. Where their judgement comes from is the more complicated part.&lt;/p&gt;&lt;h2&gt;What buying gets you&lt;/h2&gt;&lt;p&gt;The bought tools are good at what they do. Fast setup, polished PR integration, cost controls, comments that arrive under the bot’s name, and review quality that keeps improving as the underlying models improve. For a team of ten with no written conventions, they’re probably the right call and you can stop reading here.&lt;/p&gt;&lt;p&gt;But look at where their judgement comes from. Out of the box, an off-the-shelf reviewer evaluates your code against the model’s training data: the aggregate opinion of public GitHub. That opinion knows nothing about your architecture decision docs, the naming convention you settled after a three-week argument, or the incident last quarter that led you to ban a whole class of query pattern.&lt;/p&gt;&lt;p&gt;The vendors know this, which is why most now offer custom rules. CodeRabbit has a config file, Greptile learns from your reviewers’ comments. It helps. But your rules live inside their system, expressed in their format, evaluated by their harness. You’re configuring someone else’s product rather than encoding your organisation’s knowledge somewhere you own it. At ten engineers that distinction is academic. At fifty, it is the decision itself.&lt;/p&gt;&lt;h2&gt;Centralised rules for the win&lt;/h2&gt;&lt;p&gt;At 30, 50, 100 engineers, review consistency stops coming from individuals and has to come from encoded organisational knowledge, living in one versioned place that every review reads on every PR.&lt;/p&gt;&lt;p&gt;And rules, here, doesn’t mean lint rules. Formatting and static checks are a solved problem with deterministic tooling. It means the judgement layer above them: the reasoning behind your style guide, your security policies, decision docs, the incident learnings no linter can express. That layer is written as prose, and the model reads it alongside every diff.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.thinkmill.com.au/blog/rules-source-build-vs-buy.svg&quot; alt=&quot;Diagram: two flows side by side. Built: PR → rules repo + model (via aggregator) → review comment, with the rules repo shown as a git repo your team commits to. Bought: PR → vendor platform (black box containing &apos;vendor&apos;s rules format + vendor’s model choice) → review comment&quot; /&gt;&lt;/p&gt;&lt;p&gt;When the rules source is yours, three things follow:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Rules changes ship like code.&lt;/strong&gt; An engineer proposes a new convention as a PR to update the review skill. It gets reviewed, merged, and lands in every subsequent code review across every team. No admin console, no per-repo config drift.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The same source feeds everything.&lt;/strong&gt; The rules that drive AI review also feed your coding agents. The convention that tells the reviewer &quot;flag raw SQL in service handlers&quot; is the same file that tells Claude Code not to write it in the first place. Vendors can’t do this, because their rules only exist inside their review product.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Nothing is trapped.&lt;/strong&gt; When you leave a bought platform, your accumulated rules and reviewer feedback stay behind. When your rules are markdown files in a repo, they outlive any tool decision you make.&lt;/p&gt;&lt;h2&gt;Built means model-agnostic. Bought means resold.&lt;/h2&gt;&lt;p&gt;The second argument is about the models themselves.&lt;/p&gt;&lt;p&gt;A bought platform picks the model for you, wraps it, and resells it inside their subscription. When a better model ships, and one ships every few months, you wait for the vendor to evaluate it, integrate it, and pass it through on their schedule and their margin. You’ve bought code review. What you’re paying for is someone else’s model procurement.&lt;/p&gt;&lt;p&gt;Build behind an aggregator and the relationship inverts. Route your review harness through &lt;a href=&quot;https://openrouter.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenRouter&lt;/a&gt;, &lt;a href=&quot;https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-mantle.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bedrock&lt;/a&gt;, &lt;a href=&quot;https://www.cloudflare.com/products/ai-gateway/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cloudflare&lt;/a&gt;, &lt;a href=&quot;https://www.litellm.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LiteLLM&lt;/a&gt;, or &lt;a href=&quot;https://vercel.com/ai-gateway&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vercel’s AI Gateway&lt;/a&gt; and switching from Claude to GPT to Gemini is a one-line config change. New frontier model ships on a Tuesday, you’re reviewing PRs with it on Wednesday. You can also A/B models against your own rules, which no vendor benchmark tells you: their evals measure generic bug-catching, not how well a model applies &lt;em&gt;your&lt;/em&gt; conventions.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.thinkmill.com.au/blog/model-agnostic-harness-vs-vendor.svg&quot; alt=&quot;Diagram: left side, a thin &apos;review harness&apos; box connecting a rules repo and a CI webhook to an aggregator layer, which fans out to Gemini / Claude / GPT  with a toggle switch. Right side, the bought chain: your team → vendor platform → vendor’s chosen model, each link labelled with markup and lag.&quot; /&gt;&lt;/p&gt;&lt;p&gt;Model quality is the fastest-moving variable in the equation. A vendor’s release cycle means paying a margin to move slower on exactly that variable.&lt;/p&gt;&lt;h2&gt;What the build &quot;costs&quot;&lt;/h2&gt;&lt;p&gt;The harness is thin: a CI hook that fires on PR events, a loader that pulls the relevant rules for the changed files, a prompt that combines diff, context/rules, and an aggregator call.&lt;/p&gt;&lt;p&gt;The ongoing cost is real but it’s the useful kind. Someone owns the rules repo. Prompts get tuned when reviews miss things. Rules get pruned when they generate noise. Call it maintenance if you like, but it amounts to your organisation deliberately curating its own engineering standards, which is work a team your size should be doing anyway. The rules then show up as comments on every PR, rather than sitting in a document nobody opens.&lt;/p&gt;&lt;h2&gt;My recommendation&lt;/h2&gt;&lt;p&gt;Small team, no written standards, want review lift this week: buy. CodeRabbit or Greptile will serve you well. Better yet, if you’re on GitHub + Claude Code leverage &lt;a href=&quot;https://code.claude.com/docs/en/code-review&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Claude Code Review&lt;/a&gt; and land somewhere in the middle.&lt;/p&gt;&lt;p&gt;Medium-to-large team with real conventions, decision docs, and opinions about how your code should look: build the thin layer. Record your rules, route the models through an aggregator, and treat review intelligence the way you treat the rest of your infrastructure: rented compute, owned configuration.&lt;/p&gt;&lt;p&gt;There&apos;s a strong case for owning the rules while renting the models. But whichever path you choose, the unattributed AI reviews already happening on your team deserve some daylight – and of all the trust problems you could tackle, attribution is probably the cheapest.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Optimise Your Context, Not Your Prompts</title><link>https://www.thinkmill.com.au/blog/optimise-your-context-not-your-prompts</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/optimise-your-context-not-your-prompts</guid><description>AI makes better engineering decisions when it can understand a project’s architecture, conventions and shared knowledge. Making that context clear and discoverable helps it produce software that fits the wider system.</description><pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;On a recent project, we were rebuilding parts of an application using an established &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;design system&lt;/a&gt;. Some of the designs deviated from the system, often in subtle ways. One example was a border token that the existing component didn’t support through its API.&lt;/p&gt;&lt;p&gt;An engineer looking at this would see a decision that needed to be made. Was the difference intentional? Should the component support a new variant? Could the existing token be used? Or had the design drifted away from the system without anyone noticing?&lt;/p&gt;&lt;p&gt;The AI didn’t see a decision. It saw that the design couldn’t be reproduced with the existing component, so it built a new one from scratch. Viewed in isolation, the resulting code was perfectly reasonable. It matched the design as specified, but it had also bypassed the design system entirely and introduced another component for the team to maintain.&lt;/p&gt;&lt;h2&gt;The prompt wasn’t the problem&lt;/h2&gt;&lt;p&gt;The obvious thing to do would be to add more instructions to the prompt: always use components from the design system; never create a new component when one already exists; follow the patterns established in the codebase.&lt;/p&gt;&lt;p&gt;That may have prevented this particular component from being created, but it still wouldn’t have resolved the conflict between the design and the design system.&lt;/p&gt;&lt;p&gt;Sometimes a new component is the right choice. Sometimes the existing component needs to support another variant. Sometimes the design has drifted and needs to be brought back into line. The important thing is recognising when there is a decision to make instead of blindly implementing what’s in front of you.&lt;/p&gt;&lt;p&gt;The AI couldn’t make that distinction because it didn’t have the context behind those decisions. Much of that knowledge existed in the heads of the people working on the project. We could keep adding that knowledge to the prompt, but at some point the prompt becomes a substitute for documenting how the organisation builds software.&lt;/p&gt;&lt;h2&gt;Improving the environment&lt;/h2&gt;&lt;p&gt;As the project progressed, we started documenting playbooks for common situations. We captured how to build a new page layout, how forms should be structured and which patterns we wanted engineers to follow.&lt;/p&gt;&lt;p&gt;We also brought the design system into the application’s monorepo. This made its source code, component APIs and existing usage easier for AI to discover alongside the application consuming it. The AI could inspect the system directly instead of relying on our prompts to explain how it worked.&lt;/p&gt;&lt;p&gt;It began following the established patterns more consistently. The playbooks and design-system changes also gave engineers clearer examples to work from and reviewers a shared understanding of what the implementation should look like. The whole project became easier to work with.&lt;/p&gt;&lt;h2&gt;Let’s not get carried away&lt;/h2&gt;&lt;p&gt;It’s tempting to solve this by giving AI access to everything. More documentation, more files, more examples and a larger context window. More context only helps when it is relevant, current and consistent.&lt;/p&gt;&lt;p&gt;If the documentation recommends one pattern, the codebase contains three others and the design system supports something else entirely, AI has to decide which source to trust. It will usually make that decision without telling you there was any ambiguity.&lt;/p&gt;&lt;p&gt;The codebase, design system, architecture documentation and project playbooks all contribute to how AI understands a system. The goal is to make the intended way of building software clear enough that both engineers and AI can follow it.&lt;/p&gt;&lt;h2&gt;Look at what your team keeps correcting&lt;/h2&gt;&lt;p&gt;A good place to start is with the mistakes your team repeatedly corrects in AI-generated code.&lt;/p&gt;&lt;p&gt;If AI keeps recreating components that already exist, adding another instruction to the prompt may stop it happening next time. However, it’s more useful to ask why the existing component was difficult to discover in the first place.&lt;/p&gt;&lt;p&gt;The answer tells you what needs to become clearer or easier to access. Whatever the fix, the repeated correction has exposed a gap in the context surrounding the work.&lt;/p&gt;&lt;p&gt;Closing that gap makes the right decision easier to reach before any code is generated.&lt;/p&gt;&lt;h2&gt;Workarounds become part of the system&lt;/h2&gt;&lt;p&gt;Teams get very good at navigating their own systems. Experienced engineers know which documentation is current, where the reliable examples live and who to ask when the code doesn’t tell the whole story.&lt;/p&gt;&lt;p&gt;After a while, those workarounds become part of how the team operates. AI exposes this hidden context because it doesn’t share the team’s history. When it struggles to find the right component or understand why the system is structured a certain way, it may be surfacing friction that engineers have simply learned to work around.&lt;/p&gt;&lt;h2&gt;Optimise your context&lt;/h2&gt;&lt;p&gt;The component from our project wasn’t a spectacular AI failure. It compiled, matched the design and looked reasonable in isolation. The problem only became visible when we considered how it fit into the wider system.&lt;/p&gt;&lt;p&gt;The next time AI produces reasonable code that still feels wrong, pay attention to the correction your team makes. Ask what information would have helped it recognise that a decision was needed, then put that information somewhere the next engineer or AI assistant can find it.&lt;/p&gt;&lt;p&gt;Repeated corrections will show you where the project’s architecture, conventions and shared knowledge are unclear or difficult to access. Start there. Choose one source, make it more reliable and easier to find, then see whether the same mistake happens again.&lt;/p&gt;&lt;p&gt;These gaps can be difficult to see when your team has learned to work around them. An &lt;a href=&quot;https://www.thinkmill.com.au/services/design-system-audit&quot;&gt;outside perspective&lt;/a&gt; can help make them visible and identify which foundations are worth improving first.&lt;/p&gt;&lt;p&gt;The next prompt is one interaction. The context around it shapes every decision that follows.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Where AI Actually Helps Quality Engineering</title><link>https://www.thinkmill.com.au/blog/where-ai-actually-helps-quality-engineering</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/where-ai-actually-helps-quality-engineering</guid><description>AI-assisted QA is not just about generating tests faster. By carrying product context through planning, development, review and release, AI can shorten feedback loops, surface risks earlier and help teams make better decisions, provided its output is supported by clear specifications, guardrails and human judgement.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;AI can produce convincing work quickly, but confidence in its output has to be earned. The value of AI-assisted QA goes beyond faster test generation: it helps teams build and carry context from planning through to release, using checks and guardrails to validate the work along the way. This article takes a high-level view of how AI can support quality throughout that process.&lt;/p&gt;&lt;p&gt;On a recent project, we used AI to explore a codebase that had evolved over several years to uncover undocumented variations in fields and business rules. Surfacing them early gave us concrete behaviour to review and turn into testable requirements before those gaps became costly to fix. That understanding then carried into testing, review and release.&lt;/p&gt;&lt;h2&gt;Making behaviour testable&lt;/h2&gt;&lt;p&gt;Findings and decisions can be captured in many forms. In my experience, &lt;a href=&quot;https://cucumber.io/docs/bdd/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;BDD&lt;/a&gt; scenarios make specifications easier for both the team and AI to understand and use because they describe what the product should do without prescribing how it should be built.&lt;/p&gt;&lt;p&gt;That shared language allows product, design, engineering and QA to work from the same expectations. The scenarios also document product behaviour, giving AI clearer constraints and a reliable source of context when it starts work on a new ticket.&lt;/p&gt;&lt;p&gt;They can become end-to-end tests and guide unit, integration and manual testing. Paired with Test-Driven Development (TDD), they create a continuous feedback loop for checking AI-assisted implementation against the intended behaviour. This increases confidence in the output, while human judgement remains essential for filtering out low-value or irrelevant tests that can make the coverage appear stronger than it is.&lt;/p&gt;&lt;h2&gt;Shortening feedback loops&lt;/h2&gt;&lt;p&gt;As those specifications move into implementation, AI can also help check the work against approved designs, accessibility requirements and acceptance criteria. Many of these checks are traditionally deferred until review or QA, but an AI agent with access to the relevant artefacts can perform an earlier first pass.&lt;/p&gt;&lt;p&gt;It can flag potential accessibility issues, design drift, missed requirements, likely bugs and code that no longer matches the intended behaviour before a pull request is opened or a task is marked complete. These findings are leads, not decisions; reviewers still determine whether they are valid and what action to take.&lt;/p&gt;&lt;p&gt;This shifts human attention away from routine checks and towards decisions that require experience, product knowledge and context. Running that first pass earlier and more often shortens feedback loops and gives the next stage fewer avoidable issues to resolve.&lt;/p&gt;&lt;h2&gt;Carrying context into release and beyond&lt;/h2&gt;&lt;p&gt;Once specifications and BDD scenarios are in place, AI can reuse them to generate smoke tests, release checklists and release criteria. Instead of starting from a generic template, these checks can reflect the behaviour, risks and edge cases already identified during planning and development.&lt;/p&gt;&lt;p&gt;AI can also help prioritise critical workflows, so the most important paths are validated first and release decisions are based on agreed behaviour rather than memory or assumptions.&lt;/p&gt;&lt;p&gt;After release, the same specifications provide a clear baseline during an incident. AI can compare the expected behaviour with logs, recent changes and production behaviour to help identify where the system diverged and narrow the investigation before detailed debugging begins. Human review remains essential: teams still need to validate the generated release checks, assess the incident findings and decide what action to take.&lt;/p&gt;&lt;h2&gt;AI as a force multiplier&lt;/h2&gt;&lt;p&gt;AI works best as a force multiplier, not a replacement for human judgement. With the right context, checks and guardrails, it can surface problems earlier and give teams greater confidence in their work. Used well, it helps teams anticipate risks, make better decisions and deliver higher-quality software.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Why Great Engineering Still Wins</title><link>https://www.thinkmill.com.au/blog/why-great-engineering-still-wins</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/why-great-engineering-still-wins</guid><description>AI can write code faster than ever, but coding was never the hardest part. The organisations seeing the biggest gains are investing in engineering fundamentals that help both people and AI make better decisions.</description><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;AI is getting ridiculously good at smashing out code. Not long ago it was a juiced-up autocomplete tool. Now it&apos;ll happily build an entire feature, write the tests, and explain what it just did.&lt;/p&gt;&lt;p&gt;Impressive? Absolutely. Surprising? Not anymore. Either way, it&apos;s made people wonder what happens to software engineers once the code writes itself.&lt;/p&gt;&lt;p&gt;Writing code has never been the thing most engineering teams struggle with. It&apos;s the one skill most of us have spent our careers sharpening. Projects usually stall for entirely different reasons. Someone has to figure out the problem worth solving. Product and engineering have to agree on what a good outcome looks like. Architects have to decide where a new feature fits into an existing system. Everyone involved has different priorities, and they rarely align.&lt;/p&gt;&lt;p&gt;Then there are the things nobody plans for. Undocumented legacy behaviour. Third-party APIs that don&apos;t do what the docs promised. Performance issues that only show up once real customer data hits the system. Security requirements nobody mentioned until the last minute.&lt;/p&gt;&lt;p&gt;None of this disappears because AI can whip up a React component in a few seconds. These are the problems that have always defined software engineering. AI can produce the code. It still can&apos;t sit in a room with product, engineering and design and work through a messy problem together.&lt;/p&gt;&lt;h2&gt;From implementation to judgement&lt;/h2&gt;&lt;p&gt;AI has shifted where engineers spend their time. Less time writing boilerplate, more time reviewing it. Less time trawling documentation, more time deciding which approach fits. A CRUD endpoint used to eat an afternoon; now that afternoon goes into figuring out how the feature fits the &lt;a href=&quot;https://www.thinkmill.com.au/services/api-platforms&quot;&gt;wider architecture&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The best engineers I&apos;ve worked with have always known when &lt;em&gt;not&lt;/em&gt; to write code. They spot problems before they happen and recognise patterns others miss. Cleverness loses to consistency, and they&apos;ve always known it.&lt;/p&gt;&lt;p&gt;AI has freed engineers to focus on the decisions that actually shape good software. The value has changed from implementation to judgement, collaboration, and technical leadership.&lt;/p&gt;&lt;h2&gt;Better context, better outcomes&lt;/h2&gt;&lt;p&gt;There&apos;s an old saying that content is king. In software engineering today, context is king. As engineers spend less time writing code and more time deciding, the quality of that context carries more weight.&lt;/p&gt;&lt;p&gt;Clean architecture. A &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;well-maintained design system&lt;/a&gt;. Documentation people actually want to read. None of this is new. AI has just raised the cost of getting it wrong.&lt;/p&gt;&lt;p&gt;Watch an AI assistant confidently generate code that ignores your team&apos;s conventions, or duplicates something that already exists, and you&apos;ve seen this play out. The model isn&apos;t the problem. It doesn&apos;t have enough context to make the call an experienced engineer would make.&lt;/p&gt;&lt;p&gt;It&apos;s no coincidence that the organisations &lt;a href=&quot;https://www.thinkmill.com.au/services/ai&quot;&gt;getting the most out of AI&lt;/a&gt; are also investing in architecture and technical leadership. They&apos;re building the strongest possible foundation for both engineers and AI assistants.&lt;/p&gt;&lt;h2&gt;Great engineering still wins&lt;/h2&gt;&lt;p&gt;It’s so easy to slip into the same old technology rollout thinking with AI. Pick a model. Give everyone access. Measure how much faster the pull requests come in. That might lift productivity for a while, but it&apos;s not where the real gains come from.&lt;/p&gt;&lt;p&gt;The organisations pulling ahead are using AI as a multiplier to &lt;a href=&quot;https://www.thinkmill.com.au/services/product-design-engineering&quot;&gt;improve how software gets built&lt;/a&gt;. They&apos;re developing practices that make good decisions easier, codebases people can actually understand and architecture that evolves with the product. They&apos;re not chasing whatever model sits at the bleeding edge. They&apos;re building the environment where engineers and AI can both make good calls, consistently.&lt;/p&gt;&lt;p&gt;AI has made writing code faster and more accessible than it&apos;s been before. That was never the hardest part of building software. Great software engineers navigate ambiguity, balance competing priorities and solve the right problems. That’s not going anywhere.&lt;/p&gt;&lt;p&gt;The AI will keep getting better. Great engineering will always matter.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Designing beyond the median user</title><link>https://www.thinkmill.com.au/blog/designing-beyond-the-median-user</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/designing-beyond-the-median-user</guid><description>For seventy years software has shipped one shape to every user. The confluence of design systems, headless platforms, and agent-native exposure has coalesced to make that constraint optional. A retrospective of the median-user problem and the new architectural horizon now opening.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;In the 1950s, US Air Force statistician Gilbert Daniels &lt;a href=&quot;https://apps.dtic.mil/sti/citations/tr/AD0010203&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;measured&lt;/a&gt; 4,063 active pilots across ten anthropometric dimensions: neck, chest, sleeve, leg, you name it. None of them fell within the average range on all ten. And when he narrowed the test to three dimensions, fewer than 3.5% of pilots met the average. Daniels “landed” with the following conclusion:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;...the “average [hu]man” is a misleading and illusory concept as a basis for design criteria, and is particularly so when more than one dimension is being considered.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The Air Force had been designing cockpits around the average pilot for the better part of two decades. Daniels’ finding explained why so many pilots were dying in them. The military’s response was adjustable cockpits sized to the 5th–95th percentile range of each dimension. The shape of the cockpit became whatever shape any one pilot needed it to be. Accident rates plummeted.&lt;/p&gt;&lt;p&gt;It’s taken software a while to inherit Daniels’ lesson…&lt;/p&gt;&lt;p&gt;For most of its history, software has remained the inflexible cockpit: one interface, designed against a notional median user, shipped to everyone. It has no physical reason to be inflexible, but for the longest time the working assumption was that one shape per platform was both possible and good.&lt;/p&gt;&lt;h2&gt;Designing for nobody&lt;/h2&gt;&lt;p&gt;Platform documentation started the work of homogenising the end user. Apple’s original &lt;a href=&quot;https://archive.org/details/applehumaninterf00appl/page/n1/mode/2up&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Human Interface Guidelines&lt;/a&gt; codified a vocabulary (menu bar, window controls, modal dialog) and laid out its rationale. Microsoft soon followed. Their existence produced more usable software than the chaotic alternative of every application inventing its own conventions. But what these guidelines also reified was a single monolithic shape, applied to every user.&lt;/p&gt;&lt;p&gt;Alan Cooper named the consequence in 1999. Without a specific person in mind, every stakeholder projects their own assumptions onto “the user”, and every feature becomes defensible because some user might want it. He called this fictional person the &lt;em&gt;elastic user&lt;/em&gt;: the one who bends and stretches to fit whatever decision needs justifying. The elastic user is the median user’s quieter twin – the one who survives because nobody is being specific about who you’re designing for.&lt;/p&gt;&lt;h2&gt;The near-solutions&lt;/h2&gt;&lt;p&gt;Cooper’s solution was the &lt;em&gt;persona&lt;/em&gt;: a named archetype that had concrete goals. Over time Cooper and others formalised the persona hierarchy into a primary persona (the design target, one per interface) surrounded by secondary, supplemental, customer, served, and negative personas. The axiom was that the primary persona’s needs and goals should be &lt;em&gt;“completely and happily satisfied by a &lt;strong&gt;single interface&lt;/strong&gt; without disenfranchising any of the other personas”&lt;/em&gt; (&lt;a href=&quot;https://www.brightec.co.uk/blog/using-personas&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Single interface&lt;/strong&gt;&lt;/em&gt; is the clincher here. Personas force specificity about who you’re designing for, but the interface is still one shape, and the shape still has to accommodate everyone else. Cooper’s own framing that secondary personas receive &lt;em&gt;“small additions that should not negatively affect the experience of the primary persona”&lt;/em&gt; names the compromise without quite admitting it. It’s the cockpit sized for one, and a sprinkling of affordances for the rest of us.&lt;/p&gt;&lt;h3&gt;Jobs to the rescue?&lt;/h3&gt;&lt;p&gt;Where Personas reframed &lt;em&gt;who&lt;/em&gt; we were designing for, a parallel methodology reframed the &lt;em&gt;what&lt;/em&gt;. &lt;a href=&quot;https://anthonyulwick.com/jobs-to-be-done/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jobs to Be Done&lt;/a&gt; (Ulwick and Christensen) argued that people don’t buy products so much as hire them to do a &lt;em&gt;job&lt;/em&gt;. And that the job, being more durable than any demographic, is the better unit to design around. JTBD fixed the weakness in Cooper’s framework but it left the architecture untouched. A product built around a primary job still ships one interface, optimised for that job, compromising on the rest. The decomposition improved, but the constraint stuck around.&lt;/p&gt;&lt;p&gt;Neither Cooper nor the jobs school was wrong. Each named something real, and each gave us tools that improved what came before. What neither could do was build a system where the interface itself adjusts.&lt;/p&gt;&lt;h3&gt;A framework for the fringe dwellers&lt;/h3&gt;&lt;p&gt;On most projects of any complexity, a lot of design time gets spent attending to &lt;em&gt;edge cases&lt;/em&gt;: the fields, paths, and decisions that your average user is unlikely to make or take. The thing is, these aren’t actually edges, they’re just not the average. For some other person using the same software, those edge cases are the whole experience. The interface holds them; but the design discipline may not.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://medium.com/@jutta.trevira&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jutta Treviranus&lt;/a&gt;, the field’s leading voice in &lt;em&gt;inclusive design&lt;/em&gt;, built a design philosophy on that exact observation. Designing from the edges tends to produce better outcomes for everyone, because the edges are where the real constraints surface. Instead of sizing the cockpit to the 5th-to-95th percentile and accept the tails as casualties, design for the fringe dwellers and let the middle look after itself. She nailed the principle. But the thing nobody had was a different interface for each person. To get past it, you had to &lt;strong&gt;make the interface itself adapt&lt;/strong&gt;.&lt;/p&gt;&lt;h2&gt;The failed escape&lt;/h2&gt;&lt;p&gt;The HCI field tried just this. Ernest Edmonds opened the door in 1981 with an essay called “Adaptive Man-Computer Interfaces,” and by 1990 the field had its first major synthesis in Browne, Totterdell and Norman’s &lt;a href=&quot;https://api.pageplace.de/preview/DT0400.9781483294254_A23889020/preview-9781483294254_A23889020.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Adaptive User Interfaces&lt;/a&gt;. The argument was straightforward: if the system can observe what the user is doing, it can also adapt the interface to match.&lt;/p&gt;&lt;p&gt;The argument became a public debate at CHI 1997, when Ben Shneiderman and Pattie Maes &lt;a href=&quot;https://www.cs.umd.edu/~ben/papers/Maes1997Intelligent.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;debated&lt;/a&gt; the competing paradigms of direct manipulation versus interface agents. Maes argued that as software environments grew more complex, users would need intelligent delegation; the system would need to do some of the work of inferring what they wanted. Shneiderman posited that users need control, predictability, and comprehensibility; and that adaptive systems are inherently unpredictable, because they change underneath you for reasons you can’t see.&lt;/p&gt;&lt;p&gt;Maes’ thesis crash-landed in the form of Lumière, Microsoft’s research project that incarnated as Clippy, Office 97’s desktop assistant. The research was technically sound, and Clippy’s underlying Bayesian model was capable of inferring what users were likely doing. But the user experience was widely loathed. &lt;strong&gt;Because the inference happened invisibly, Clippy’s behaviour felt unpredictable&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Embedded media — view this post on thinkmill.com.au.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Shneiderman’s argument largely prevailed for the next twenty-five years. Adaptive systems couldn’t reliably model intent, so users couldn’t predict what would happen. The cure was worse than the disease.&lt;/p&gt;&lt;h2&gt;Three things converged&lt;/h2&gt;&lt;p&gt;Since Clippy’s demise in 1997 three independent developments have occurred that together make it worth revisiting the median-user problem.&lt;/p&gt;&lt;h3&gt;1. The atoms became reliable&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;Design systems&lt;/a&gt; started out as documentation: rules for designing the one shape, well. Twitter’s Bootstrap took consistency to industrial scale. Then along came Brad Frost’s &lt;a href=&quot;https://atomicdesign.bradfrost.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Atomic Design&lt;/a&gt; which gave the practice a mental model of atoms, molecules, and organisms. Jina Anne followed up with &lt;a href=&quot;https://www.jina.me/work/salesforce-lightning-design-system&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;design tokens&lt;/a&gt; to encode the subatomic particles of UI.&lt;/p&gt;&lt;p&gt;But the shift that mattered most for what comes next was when components stopped imposing their own visual style. Tools like Headless UI, Radix UI, and React Aria ushered in an era where atoms were standardised, accessibility-correct, and behaviour-complete in a way they had never been before. The atoms became reliable enough to compose across brands at configuration time. &lt;strong&gt;The interface stopped being a single thing the design team locks in. It became a space of possible shapes, bounded by the design system, that configuring users move within&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;We’ve watched this play out in our own client work. &lt;a href=&quot;https://www.thinkmill.com.au/work/anywhere-works&quot;&gt;Earth&lt;/a&gt;, the design system we built with AnywhereWorks, has a token architecture designed to let small business owners reshape the booking page (palette, logos, button shapes) to match their brand. This flexible configuration approach carries through our work with &lt;a href=&quot;https://www.thinkmill.com.au/work/rugby&quot;&gt;Rugby Australia&lt;/a&gt; and &lt;a href=&quot;https://www.thinkmill.com.au/work/auscycling&quot;&gt;AusCycling&lt;/a&gt;: where themability unlocked scale for content managers.&lt;/p&gt;&lt;h3&gt;2. The data became reachable&lt;/h3&gt;&lt;p&gt;Headless content APIs are the second integral pillar. The design principle behind headless is to enable rendering flexibility from a single source of truth, so many frontends could consume the same content and present it in myriad ways. We open-sourced &lt;a href=&quot;https://keystonejs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Keystone&lt;/a&gt; in 2013 as the first Node-based headless CMS of its kind. Contentful, Sanity, and others soon followed with their own unique spin on headless. A decade on, these platforms expose their data through schema-driven, self-documenting APIs and power many of the world’s interfaces. While they weren’t built with LLMs in mind, they happen to be exactly the kind of structured, front-end-agnostic context that LLMs thrive on.&lt;/p&gt;&lt;h3&gt;3. The platforms met a new consumer&lt;/h3&gt;&lt;p&gt;The third thread is the newest. The &lt;a href=&quot;https://modelcontextprotocol.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Model Context Protocol&lt;/a&gt; makes any platform agent-native so it is readable and operable by a language model, not just developer-written code. The &lt;a href=&quot;https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MCP Apps Extension&lt;/a&gt; (late 2025) extends the pattern further, standardising how servers ship interactive UI back to clients.&lt;/p&gt;&lt;p&gt;The assumption that humans are the only consumers of the platform, implicit in seventy years of HCI, drops.&lt;/p&gt;&lt;p&gt;None of these three developments were aimed at where they’ve collectively arrived. Design systems matured so teams could ship faster without losing quality. Platforms went headless for channel flexibility. Agent-native exposure landed because the agents arrived. Nobody was coordinating any of it. Looking back, the through-line for adaptive UI is clear.&lt;/p&gt;&lt;h2&gt;A new architectural response to the old debate&lt;/h2&gt;&lt;h3&gt;Bounded generation&lt;/h3&gt;&lt;p&gt;The convergence of these three pieces asks new questions about what might be possible with composition. When:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The user states what they want to see, in plain language.&lt;/li&gt;&lt;li&gt;A language model reads a typed schema and a typed component catalogue, and emits a typed spec.&lt;/li&gt;&lt;li&gt;A deterministic renderer turns the spec into UI.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The LLM never writes the UI code in an unbound manner. It selects from the catalogue, binds to the schema, and describes the result.&lt;/p&gt;&lt;p&gt;Several independent versions of this bounded-generation pattern (&lt;a href=&quot;https://github.com/microsoft/portal-ux-agent&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Portal UX Agent&lt;/a&gt;, &lt;a href=&quot;https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;A2UI&lt;/a&gt;, &lt;a href=&quot;https://www.infoq.com/news/2026/03/vercel-json-render/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;json-render&lt;/a&gt;, &lt;a href=&quot;https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MCP Apps&lt;/a&gt;) all landed within a four-month window in late 2025. It’s the &lt;strong&gt;same underlying principle&lt;/strong&gt; each time: the model composes, the schema constrains, and the renderer executes.&lt;/p&gt;&lt;h3&gt;Anchored intent&lt;/h3&gt;&lt;p&gt;If we pair the productivity of bounded on-system UI generation with the tried-and-tested &lt;a href=&quot;https://uxdesign.cc/pinning-is-not-saving-saving-is-not-favoriting-favoriting-is-not-flagging-c3941c444bd5#5a16&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pinning&lt;/a&gt; metaphor where users save the composed view as a stable artefact they can return to, share, or evolve — the 1997 Shneiderman–Maes debate reframes in an interesting light.&lt;/p&gt;&lt;p&gt;Shneiderman’s primary concern was &lt;em&gt;invisible inference&lt;/em&gt;: the system changing under you for reasons you can’t see. Today, when the user states intent (prompts for the view they want) that hidden and confusing inference moves into the open. His comprehensibility and predictability concerns find their footing in a composed interface where stating intent is visible and the result is pinnable. Maes’s case for adaptive delegation finds a new friend in the LLM who’s capable of doing the (bounded) composition at prompt time. Neither side won. But the terms certainly changed.&lt;/p&gt;&lt;p&gt;It’s worth notiing thay while architectural availability is one thing, the shape of such a paradigm &lt;em&gt;in production&lt;/em&gt; is another. End-user just-in-time composition at this scale hasn’t yet shipped in any SaaS products I’m tracking. But what’s adjacent is real:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;companies like Figma, Canva, and Box are shipping &lt;a href=&quot;https://youtu.be/bluAmTHoEow?si=fToHS5uKAsvwadPu&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;official components that render inside chat clients like Claude&lt;/a&gt;&lt;/li&gt;&lt;li&gt;vibe-coding sessions are producing hyperfunctional design artefacts that access platform data and solve for UX in ways that are truly centred around unique, non-average use cases&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Some of the architectural responses to the median-user problem are now possible in a way they weren’t five years ago, even if whether they ship widely turns on things that aren’t architectural: economics, governance, accountability, or the cold-start problem of not knowing what to ask for.&lt;/p&gt;&lt;h2&gt;What might change going forward&lt;/h2&gt;&lt;p&gt;This shift changes the work of designing. The unit has been &lt;em&gt;the interface&lt;/em&gt; for seventy years — what we’ve shipped, argued over, recruited for, charged for.&lt;/p&gt;&lt;p&gt;When composition at the moment of intent is possible, the unit moves toward the schema, the primitives, and the boundaries of &lt;em&gt;what can be composed&lt;/em&gt;. The default interface a platform ships still matters because it bootstraps everyone, but it stops being the &lt;em&gt;only&lt;/em&gt; shape your users will ever meet. Persona work aimed the whole effort at one representative user; our discipline can now hold the full spread of them, edge cases and all.&lt;/p&gt;&lt;p&gt;That’s a different design surface, and arguably a bigger one.&lt;/p&gt;&lt;p&gt;If you ship software, three questions start to matter:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;What does your schema look like to a language model that’s never read your documentation?&lt;/li&gt;&lt;li&gt;Which of your &quot;edge cases&quot; stop being edges once users can compose what they need?&lt;/li&gt;&lt;li&gt;What’s the shape of the space your users should be able to move within?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The cockpit never had to be one shape. For the first time, it doesn’t have to be.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/fejzpztp4216/embed-post-designing-beyond-the-median-user?width=1200" length="578209" type="image/png"/></item><item><title>What engineering teams owe for the AI speed boost</title><link>https://www.thinkmill.com.au/blog/what-engineering-teams-owe-for-the-ai-speed-boost</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/what-engineering-teams-owe-for-the-ai-speed-boost</guid><description>AI-assisted development genuinely accelerated engineering teams, but many organisations stopped at the velocity gains and ignored the accumulating debt. The costs: code nobody fully understands, weakened code review culture, bloated dependency trees, and architectural decisions made without real judgement about tradeoffs...</description><pubDate>Fri, 29 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;The productivity gains were real. Worth saying that upfront, because a lot of the conversation right now sounds like revisionism – like the people who moved fast were somehow naive, and the people who stayed cautious were wise. That&apos;s not what happened. Teams shipped faster. Backlogs shrank. A mid-level engineer with decent prompting skills could scaffold a new service in an afternoon that would have taken a senior dev most of a week. Stakeholders loved it, of course they did.&lt;br /&gt;&lt;br /&gt;The problem isn&apos;t that organisations moved fast. The problem is that some of them stopped there.&lt;br /&gt;&lt;br /&gt;Here&apos;s what didn&apos;t show up in the velocity charts: code that nobody fully understood. Not &quot;bad&quot; code, necessarily – plausible-looking code, code that passed review, code that worked fine in staging. But when the 2am production incident happened, or the security review came around, the engineer who &quot;wrote&quot; it couldn&apos;t walk you through it. That&apos;s a different kind of risk than the ones most teams were tracking.&lt;br /&gt;&lt;br /&gt;Review culture took a quiet hit too. PR turnaround times dropped, which looked like efficiency, and sometimes it was. But the second pair of eyes – the one that catches the auth bug, the injection vector, the edge case that only shows up under load – that pair of eyes started skimming. You can&apos;t really blame people. The volume went up, the review expectations didn&apos;t change, something had to give.&lt;br /&gt;&lt;br /&gt;And then there&apos;s the dependency problem. AI-generated code reaches for libraries. Not always the right ones, not always maintained ones, not always the ones already in your stack. Over twelve months of moving fast, a lot of codebases got significantly wider and harder to reason about. Nobody planned for it. Nobody really noticed until they tried to upgrade something.&lt;br /&gt;&lt;br /&gt;The architectural decisions are the most concerning. Using AI to generate a utility function is fine. Using it to design your data model or your service boundaries is a different proposition – one that requires understanding tradeoffs and consequences that LLMs don&apos;t reliably have. &quot;It worked in the prototype&quot; is doing a lot of heavy lifting in some engineering orgs right now.&lt;br /&gt;&lt;br /&gt;None of this is a mystery in hindsight. You give people a powerful new tool and tell them to go faster, and they go faster. The debt accumulates quietly until it doesn&apos;t. Security incidents are often the first sign – not always dramatic ones, sometimes it&apos;s credentials in a repo, an open endpoint, an unvalidated input that sat there for months before someone noticed. Maintenance slows down. Engineers are slower in codebases they didn&apos;t really build, full of patterns that don&apos;t match the team&apos;s conventions. Abstractions that seemed clean in isolation that don&apos;t compose well.&lt;br /&gt;&lt;br /&gt;There&apos;s also a talent problem that nobody wants to say out loud: junior engineers who spent the last year prompting rather than building are missing some foundational mental models. The ones that let you catch what the model gets wrong. That&apos;s not a gap you can hire your way out of quickly, and it&apos;s not really their fault – it&apos;s the environment they were put in.&lt;br /&gt;&lt;br /&gt;So what now? There&apos;s no clean undo.&lt;br /&gt;&lt;br /&gt;The teams I&apos;ve seen handle this well aren&apos;t doing anything heroic. They&apos;re auditing their codebases without turning it into a blame exercise. They&apos;re asking which AI-assisted code hasn&apos;t had a real senior review, where the test coverage gaps are, what&apos;s in the dependency tree that shouldn&apos;t be there. They&apos;re reinvesting in review – not as a slowdown, but as a recognition that speed without it is just faster debt accumulation. They&apos;re being deliberate about the line between AI for implementation (good) and AI for architecture (milage may vary). And they&apos;re having the cross-functional conversation — engineering, security, and product in the same room, looking at the same numbers, instead of each optimising for their own metrics.&lt;br /&gt;&lt;br /&gt;This is all easy to say in hindsight, the last twelve months weren&apos;t a mistake. The speed was real, the competitive pressure was real, and organisations that didn&apos;t move lost ground to ones that did. But there&apos;s a difference between &quot;we moved fast&quot; and &quot;we&apos;re done.&quot; The bill for the speed is arriving now – not as punishment, just as the completely predictable consequence of how complex systems work. The organisations that come out of this well are the ones honest enough to look at what they actually built.&lt;br /&gt;&lt;br /&gt;If any of this sounds familiar and you find yourself needing a little extra help let&apos;s talk.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>A behavioural framework for the monsters in your team</title><link>https://www.thinkmill.com.au/blog/a-behavioural-framework-for-the-monsters-in-your-team</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/a-behavioural-framework-for-the-monsters-in-your-team</guid><description>The AI in Design 2026 report captures a profession mid-mutation. The &quot;Frankenjob&quot; label the coverage put on it misses the seam where the real work happens.</description><pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Much of what the AI in Design 2026 report describes (designers shipping code, broader role definitions, the search for new team structures) isn’t new everywhere. We’ve been refining a cross-disciplinary practice for over a decade. The report charts its emergence in the mainstream, but one metaphor that’s followed it into the coverage (&quot;Frankenjobs&quot;) doesn’t quite fit what the practice actually looks like.&lt;/p&gt;&lt;h2&gt;What the report unearths&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://stateofaidesign.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AI in Design 2026 report&lt;/a&gt;, from Designer Fund in partnership with Foundation Capital, surveyed over 900 designers across 60+ countries. It catalogues a profession mid-mutation. 91% of respondents now use AI weekly, up from 54% last year. Half of them have shipped AI-generated code to production. The average designer toolstack has more than doubled, from three to seven tools.&lt;/p&gt;&lt;p&gt;The shape of design work itself is also shifting. Towards systems thinking, code, and cross-functional ownership:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Half of the leaders we surveyed report that when they hire designers now, they’re placing greater emphasis on AI fluency, followed by systems thinking and strategic skills. 22% are placing more emphasis on technical or coding skills.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Fast Company picked up the report under the headline &lt;a href=&quot;https://www.fastcompany.com/91543989/ai-in-design-design-jobs-2026&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AI imagineer. Design crafter. Builder. Why design is suddenly full of Frankenjobs&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&quot;Frankenjob&quot; puts a name to the discomfort of role-blurring vividly: parts forced into one body, stitched together, the results more contradiction than coherence. Hideous new titles are emerging from the laboratory floor: &lt;em&gt;designer engineer&lt;/em&gt;, &lt;em&gt;AI imagineer&lt;/em&gt;, &lt;em&gt;builder&lt;/em&gt;, &lt;em&gt;design crafter&lt;/em&gt;. Designers really are doing PM and engineering work, and vice versa...&lt;/p&gt;&lt;h2&gt;Who’s the monster here?&lt;/h2&gt;&lt;p&gt;In Mary Shelley’s epic tome, Frankenstein is the creator, not the monster. Victor built his creature and walked away from it:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Oh! no mortal could support the horror of that countenance. A mummy again endued with animation could not be so hideous as that wretch. I had gazed on him while unfinished; he was ugly then; but when those muscles and joints were rendered capable of motion, it became a thing such as even Dante could not have conceived.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The story’s horror lives equal parts within Victor’s abandonment as it does the unorthodox form of his nameless progeny.&lt;/p&gt;&lt;p&gt;Designer Fund’s Ben Blumenrose, in the same Fast Company piece, described the prior orthodoxy:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;For the past two decades, the way we built software was the same for the most part. Someone came up with a concept of what they wanted to build, they’d work with a PM [project manager] to figure that out, they’d bring on a designer to give the visuals to that thing, then pass it to the engineer to build.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Stepping out of the laboratory: AI is clearly making designers more capable and fast, and the report captures that clearly.&lt;/p&gt;&lt;p&gt;What’s harder to see is the work that doesn’t get easier: building shared context, shared artefacts, and &lt;a href=&quot;https://www.thinkmill.com.au/blog/shared-understanding&quot;&gt;shared understanding&lt;/a&gt; between the people who now bring broader skills to the table. The &quot;Frankenjob&quot; feeling lands at that seam, not in the new roles themselves. That practice doesn’t take a multi-year cycle to form, but it does take intention.&lt;/p&gt;&lt;p&gt;The Fast Company article makes it sound like these monsters never existed, and that AI (Victor) is the sole progenitor of these cross-disciplinary forms. In truth, some teams of designers, engineers, and product people have been working this way for quite a while now. AI didn’t create the practice; it just made it visible at scale. We’re one of those teams.&lt;/p&gt;&lt;h2&gt;How to do better than Victor&lt;/h2&gt;&lt;p&gt;In 2017 we brought a credentialed designer into what was an engineering-led company. By 2020, our then-design director &lt;a href=&quot;https://www.thinkmill.com.au/authors/lauren-argenta&quot;&gt;Lauren Argenta&lt;/a&gt; had distilled our practice into a five-behaviour framework called &lt;a href=&quot;https://www.thinkmill.com.au/blog/engineering-led-design&quot;&gt;Engineering-led design&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Context together.&lt;/strong&gt; Everyone attends early discovery, regardless of discipline. Designers learn the technical constraints in real time. Engineers see what designers see in user research.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Create a dictionary.&lt;/strong&gt; Define shared vocabulary as you go. Acronyms get unpacked. Practice-specific jargon gets reserved meanings. The dictionary becomes a bridge between disciplines.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Teach as you work.&lt;/strong&gt; Short, structured asides between disciplines. Thirty-minute sessions on graph theory, spatial localisation, or whatever the team needs to unlock. Framed as a first-class part of daily work, not a distraction.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Play it back to validate.&lt;/strong&gt; Reveal your thinking before you commit to it. Surface hidden assumptions. The team catches what an individual would miss.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Break down language barriers.&lt;/strong&gt; The majority discipline translates for the minority. In an engineering-led environment, that means engineers do the translating.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These five behaviours became the seed of a &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;broader framework&lt;/a&gt;: the cross-disciplinary principles, mindsets, and approaches we use when helping &lt;a href=&quot;https://www.thinkmill.com.au/work&quot;&gt;clients&lt;/a&gt;. In a &lt;a href=&quot;https://www.thinkmill.com.au/blog/what-we-learned-applying-the-thinkmill-method-to-a-complex-accounting-app&quot;&gt;2023 retrospective on a complex accounting app project&lt;/a&gt;, Ben Derham and Chris Dale described what happens when a team sets up using this framework: a core team of Product owner, UX lead, Tech lead, and Design-engineer, all co-piloting with equal responsibility. They worked directly in the browser wherever possible, and used a schema-first approach to surface dependencies and build a shared vocabulary. This way of working sounds eerily like the franken-practices that are emerging today!&lt;/p&gt;&lt;h2&gt;Lab notes for the curious&lt;/h2&gt;&lt;p&gt;If you’re running a design team and beginning to understand the value that this &lt;em&gt;monstrous&lt;/em&gt; way of working offers teams like yours, here’s a few practical takeaways to help navigate the shift that we found meaningful:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Check out Shamsi Brinn’s &lt;a href=&quot;https://nohandoff.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;No Handoff&lt;/a&gt; framework.&lt;/strong&gt; A manifesto for replacing waterfall handovers with synchronous, cross-disciplinary collaboration.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Read the &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;Thinkmill Method&lt;/a&gt;.&lt;/strong&gt; It’s our synthesis and magnum opus of all things cross-disciplinarity.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Talk to engineers, often.&lt;/strong&gt; Cross-disciplinary practice depends on small, frequent, low-stakes conversations more than formal handovers.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Work together in real time.&lt;/strong&gt; The biggest gains we’ve seen come from designing and engineering in the same room (or the same call), in the same artefact, in the same conversation, not handing assets back and forth. As AI gets faster, and AI companies define better multi-player experiences (thanks &lt;a href=&quot;https://maggieappleton.com/zero-alignment&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Maggie&lt;/a&gt;!), this is becoming a lot easier to achieve.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Every team’s path to cross-disciplinary practice will look different. But the shape of the practice itself is familiar: people working together in shared language, building in parallel, learning from each other over time.&lt;/p&gt;&lt;p&gt;If you’re navigating this shift in your team and want a second pair of eyes (or hands), &lt;a href=&quot;https://www.thinkmill.com.au/contact&quot;&gt;reach out for a chat&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/n14yhjdutd6t/a-behavioural-framework-for-the-monsters-in-your-team?width=1200" length="517415" type="image/png"/></item><item><title>Why your design system is the most important input to Claude Code</title><link>https://www.thinkmill.com.au/blog/why-your-design-system-is-the-most-important-input-to-claude-code</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/why-your-design-system-is-the-most-important-input-to-claude-code</guid><description>Design systems are critical inputs for AI-assisted development tools like Claude Code because they constrain the AI’s micro-decisions around colours, spacing, and components – preventing &quot;drift at speed&quot; and ensuring generated UI remains consistent with your product’s visual language.</description><pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Claude Code can generate a working UI in seconds. Whether that UI is &lt;em&gt;coherent&lt;/em&gt; with the rest of your product is a different question entirely, and the answer lives in your design system.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;At Thinkmill, we’ve spent years thinking about the gap between how designers specify things and how developers implement them. Design systems were our answer to that gap. Now there’s a new actor in the room: AI-assisted development tools like Claude Code. And it turns out, design systems matter &lt;em&gt;more&lt;/em&gt; in this context, not less.&lt;/p&gt;&lt;p&gt;Here’s why.&lt;/p&gt;&lt;h2&gt;The problem with fast generation&lt;/h2&gt;&lt;p&gt;Claude Code is remarkably good at producing UI from a short description. Ask it to build a user profile card and it will. Ask it to add a danger button and it will do that too. The problem is that without constraints, every one of those generation tasks involves Claude making dozens of micro-decisions: What shade of red? What padding? What border radius? What font size for the label?&lt;/p&gt;&lt;p&gt;Each individual decision is reasonable. Across fifty components generated over the course of a sprint, you end up with fifty islands of slightly different visual language. Buttons that are almost the same. Spacing that’s close but not quite. A red that’s near your brand red but isn’t.&lt;/p&gt;&lt;p&gt;This is what we think of as &lt;strong&gt;drift at speed&lt;/strong&gt;. It’s not new, developers have always introduced inconsistency, but AI tools can produce it an order of magnitude faster.&lt;/p&gt;&lt;h2&gt;What a design system actually gives Claude&lt;/h2&gt;&lt;p&gt;A design system’s job, in the context of AI-assisted development, is to collapse the decision space. Instead of inventing, Claude composes.&lt;/p&gt;&lt;p&gt;This works across several layers:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Design tokens&lt;/strong&gt; give Claude named values instead of raw numbers. When your CLAUDE.md says &quot;never use raw hex, use CSS custom properties from &lt;code&gt;@acme/tokens&lt;/code&gt;&quot;, Claude stops generating &lt;code&gt;background-color: #dc2626&lt;/code&gt; and starts generating &lt;code&gt;background-color: var(--color-surface-danger)&lt;/code&gt;. That one change means every piece of generated UI inherits your theming system for free.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Component APIs&lt;/strong&gt; give Claude a vocabulary. When &lt;code&gt;Button&lt;/code&gt; has &lt;code&gt;variant=&quot;destructive&quot;&lt;/code&gt; as a defined prop, Claude reaches for it. When it doesn’t exist, Claude invents something, usually a one-off styled &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; that will diverge from everything else. The specificity of your component API is the specificity of Claude’s output.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Layout primitives&lt;/strong&gt; like &lt;code&gt;Stack&lt;/code&gt;, &lt;code&gt;Box&lt;/code&gt;, &lt;code&gt;Grid&lt;/code&gt; mean Claude isn’t reasoning from scratch about flexbox every time it needs to position things. It picks from a set of named spatial patterns you’ve already sanctioned.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Storybook stories&lt;/strong&gt; are, in practice, the most underrated input. Every story Claude can read is a worked example of correct usage. Think of them less as documentation for humans and more as training context for the AI.&lt;/p&gt;&lt;h2&gt;The output quality difference is stark&lt;/h2&gt;&lt;p&gt;Here’s an example of the same prompt, run with and without a design system in context.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Without:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;button
  style={{
    backgroundColor: &apos;#dc2626&apos;,
    color: &apos;white&apos;,
    padding: &apos;8px 16px&apos;,
    borderRadius: &apos;4px&apos;,
    border: &apos;none&apos;,
    fontSize: &apos;14px&apos;,
  }}
&amp;gt;
  Delete Account
&amp;lt;/button&amp;gt;
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;With:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;Button variant=&quot;destructive&quot; size=&quot;md&quot; onClick={handleDelete}&amp;gt;
  Delete Account
&amp;lt;/Button&amp;gt;
&lt;/pre&gt;&lt;p&gt;The second version is themeable, consistent with every other destructive action in the product, and has accessibility handled inside the component. Claude didn’t do less work, it did better work, because it had a better vocabulary to draw from.&lt;/p&gt;&lt;h2&gt;How to wire this up with CLAUDE.md&lt;/h2&gt;&lt;p&gt;Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt; files up the directory tree as persistent context. This is the bridge between your design system and Claude’s behaviour.&lt;/p&gt;&lt;p&gt;For a design system consumed as an external package, a minimal but effective &lt;code&gt;CLAUDE.md&lt;/code&gt; section looks like this:&lt;/p&gt;&lt;pre&gt;## Design System

We use `@acme/design-system`. Always import from this package.
Never write inline styles or raw Tailwind classes.

### Imports
- Components: `import { Button, Input, Stack } from &apos;@acme/design-system&apos;`
- Tokens: `import { tokens } from &apos;@acme/design-system/tokens&apos;`

### Component variants
Button: primary | secondary | ghost | destructive (sizes: sm | md | lg)
Input: default | error | disabled
Badge: neutral | positive | warning | critical

### Rules
- Use design tokens for all colour, spacing, and type values
- Prefer layout primitives (Stack, Cluster, Grid) over custom flexbox
- All form inputs must use DS components, not native elements
&lt;/pre&gt;&lt;p&gt;That’s it. Claude will read this before generating any UI and will constrain itself accordingly.&lt;/p&gt;&lt;p&gt;If you’re in a monorepo where the design system lives in &lt;code&gt;packages/design-system&lt;/code&gt;, add a &lt;code&gt;CLAUDE.md&lt;/code&gt; inside that directory as well. Claude picks it up automatically and gets direct access to your component docs, token definitions, and any guidance you want to give about the system’s intent.&lt;/p&gt;&lt;h2&gt;The design system is now the spec&lt;/h2&gt;&lt;p&gt;There’s a wider shift worth naming here. In traditional product development, designers produce detailed specs (annotated Figma files, redlines, component documentation) that developers implement. The handoff is where things go wrong.&lt;/p&gt;&lt;p&gt;In a Claude Code workflow, that handoff changes shape. You write intent: &lt;em&gt;&quot;build a user settings page&quot;&lt;/em&gt;. Claude looks up your design system, understands the vocabulary, and composes. The detailed decisions (spacing, type scale, component selection) are handled by the system, not the spec.&lt;/p&gt;&lt;p&gt;This doesn’t mean you stop designing. It means the design system &lt;em&gt;is&lt;/em&gt; the specification. The quality of your design system directly determines the quality of Claude’s output. Investing in clear token naming, well-documented component APIs, and good Storybook coverage isn’t just good hygiene anymore — it’s the highest-leverage thing you can do for AI-assisted development velocity.&lt;/p&gt;&lt;h2&gt;What this means in practice&lt;/h2&gt;&lt;p&gt;A few things shift when you take this seriously:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Write your design system docs as if Claude will read them&lt;/strong&gt;, because it will. Be explicit about what variants exist. Name things semantically, not visually. &lt;code&gt;destructive&lt;/code&gt; is better than &lt;code&gt;red&lt;/code&gt;. &lt;code&gt;surface.subtle&lt;/code&gt; is better than &lt;code&gt;gray-100&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Enforce tokens with lint rules.&lt;/strong&gt; Add an ESLint rule that flags raw hex values and hardcoded &lt;code&gt;px&lt;/code&gt; strings. If Claude generates something invalid, your tooling catches it immediately.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Keep components small and composable.&lt;/strong&gt; Claude is better at assembling ten clear primitives than reasoning about one complex component with forty props. If you’ve been deferring a refactor of a monolithic component, this is a good reason to move it up the list.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Review intent, not pixels.&lt;/strong&gt; When you’re reviewing Claude’s output, you shouldn’t be checking whether the button is the right shade of blue. The design system handles that. You’re reviewing whether the right components were chosen, whether the interaction model makes sense, whether the data flow is correct. This is a better use of your attention.&lt;/p&gt;&lt;p&gt;Design systems have always been about reducing the cost of consistency at scale. AI-assisted development just raised the throughput, which makes a well-maintained design system more valuable than ever.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Faster CI Pipelines: Share build artifacts across independent jobs</title><link>https://www.thinkmill.com.au/blog/faster-ci-pipelines-share-build-artifacts-across-independent-jobs</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/faster-ci-pipelines-share-build-artifacts-across-independent-jobs</guid><description>Optimising CI pipelines is critical for scale. By leveraging `actions/cache` with commit-specific keys, we can share build artifacts across parallel jobs, significantly reducing build times and compute costs.</description><pubDate>Wed, 14 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;As engineering teams scale, Continuous Integration (CI) pipelines often become a bottleneck. The feedback loop slows down, and compute costs rise linearly with the number of jobs. This challenge is particularly acute in &lt;a href=&quot;https://monorepo.guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;monorepos&lt;/a&gt;, where a single commit often triggers a matrix of linting, testing, and analysis jobs across multiple packages. A common denominator I’ve noticed in many slower workflows is often either the redundancy of build steps across parallel jobs or the lack of parallel jobs to avoid redundant build steps.&lt;/p&gt;&lt;p&gt;Here’s an example of two workflows that demonstrate the impact of sharing build artifacts between independent jobs.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ixn5rwvvld48/w-cache?width=848&quot; alt=&quot;GitHub Actions workflow: Jobs with cached build artifacts&quot; width=&quot;848&quot; height=&quot;404&quot; /&gt;&lt;figcaption&gt;GitHub Actions workflow: Jobs with cached build artifacts&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/obiffe6sv1r2/wo-cache?width=848&quot; alt=&quot;GitHub Actions workflow: Jobs without cached build artifacts&quot; width=&quot;848&quot; height=&quot;404&quot; /&gt;&lt;figcaption&gt;GitHub Actions workflow: Jobs without cached build artifacts&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;In a distributed workflow, you might have separate jobs for Unit Tests, E2E Tests, and Bundle Analysis. If these jobs run in parallel, they typically function as isolated environments. While modern package managers (like pnpm or npm) have excellent mechanisms for caching downloaded dependencies based on lockfiles, they don’t solve the redundancy of the &lt;strong&gt;build process&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;If three parallel jobs need the compiled application to run their tests, they often end up running the build script three times. This redundancy introduces:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Wasted compute&lt;/strong&gt;: compiling the same TypeScript/codebase multiple times.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Increased cost&lt;/strong&gt;: linearly increasing billable minutes for CPU-intensive tasks.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Non-determinism&lt;/strong&gt;: slight timing or environment differences could theoretically yield different build outputs.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;The architectural approach: Build once, consume everywhere&lt;/h2&gt;&lt;p&gt;The optimal architecture treats the build process as a distinct, upstream producer. We build the application once, package the generated artifacts (e.g., &lt;code&gt;dist/&lt;/code&gt;, &lt;code&gt;.next/&lt;/code&gt;, &lt;code&gt;build/&lt;/code&gt;), and distribute them to downstream consumer jobs.&lt;/p&gt;&lt;p&gt;While standard dependency caching relies on &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; (which rarely changes), &lt;strong&gt;artifact caching&lt;/strong&gt; needs to be strictly tied to the source code state. By using the specific git commit SHA as our cache key, we can safely share build outputs between jobs running on the same commit.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/b119m15i3op5/actions-cache-workflow?width=848&quot; alt=&quot;Diagram: Workflow Architecture&quot; width=&quot;848&quot; height=&quot;364&quot; /&gt;&lt;figcaption&gt;Diagram: Workflow Architecture&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;A contrived example&lt;/h2&gt;&lt;p&gt;To demonstrate this, I set up a pnpm monorepo containing a Vite+React application and a Playwright test suite. The goal is to build in one job and test in another, without rebuilding.&lt;/p&gt;&lt;p&gt;Here’s the link to the repo: &lt;a href=&quot;https://github.com/Thinkmill/demo-actions-cache&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/Thinkmill/demo-actions-cache&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Step 1: The producer (Build job)&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;Build&lt;/code&gt; job is responsible for the heavy lifting: installing dependencies and compiling the application.&lt;/p&gt;&lt;p&gt;We use &lt;code&gt;actions/cache&lt;/code&gt; with a &lt;strong&gt;commit-specific key&lt;/strong&gt; (&lt;code&gt;${{ github.sha }}&lt;/code&gt;). This is the critical distinction from dependency caching. We aren’t just caching what we &lt;em&gt;downloaded&lt;/em&gt;; we are caching what we &lt;em&gt;generated&lt;/em&gt;.&lt;/p&gt;&lt;pre&gt;# Build Job configuration
- name: Cache Build Artifacts
  uses: actions/cache@v3
  with:
    # We cache the build output (dist)
    # Dependencies are automatically cached using pnpm store cache
    path: |
      apps/example-react-app/dist
      node_modules
    # The commit SHA ensures a unique cache entry for this run
    key: ${{ runner.os }}-build-${{ github.sha }}
&lt;/pre&gt;&lt;h3&gt;Step 2: The consumers (Test &amp;amp; Analysis jobs)&lt;/h3&gt;&lt;p&gt;Downstream jobs declare a &lt;code&gt;needs: build&lt;/code&gt; dependency. They don’t need to run &lt;code&gt;pnpm install&lt;/code&gt; or &lt;code&gt;pnpm build&lt;/code&gt;. They simply restore the &quot;hydrated&quot; workspace state—containing both the compiled artifacts and the runtime dependencies—and start testing immediately.&lt;/p&gt;&lt;pre&gt;# E2E Tests Job configuration
needs: build
steps:
  - uses: actions/checkout@v3

  - name: Restore Build Artifacts
    uses: actions/cache@v3
    with:
      path: |
        apps/example-react-app/dist
        node_modules
      # Matching the key from the build job
      key: ${{ runner.os }}-build-${{ github.sha }}
      # Fail fast if the cache is missing, indicating an upstream failure
      fail-on-cache-miss: true

  - name: Run Playwright
    # Direct execution of tests without build overhead
    run: pnpm exec playwright test
&lt;/pre&gt;&lt;p&gt;Using &lt;code&gt;fail-on-cache-miss: true&lt;/code&gt; is a good practice here. It ensures that if the artifact transfer fails, the job fails immediately rather than attempting to run tests against a non-existent build, providing clearer failure signals.&lt;/p&gt;&lt;h2&gt;Wrapping up&lt;/h2&gt;&lt;p&gt;Adopting this &lt;em&gt;&quot;Build Once, Consume Everywhere&quot;&lt;/em&gt; pattern yields immediate benefits:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Cost savings&lt;/strong&gt;: eliminating redundant build minutes directly reduces the billable time on GitHub Actions.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Determinism&lt;/strong&gt;: every test job runs against the &lt;em&gt;exact&lt;/em&gt; same binary artifacts, eliminating &quot;flaky build&quot; issues between parallel jobs.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Pipeline clarity&lt;/strong&gt;: CI job logs are cleaner. Test jobs show only test output, making debugging significantly easier.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Building a faster CI pipeline is an iterative process and architectural changes like artifact caching often have highest impact on CI time and expense. By decoupling the build process from verification steps, you create a pipeline that is both faster and more reliable. We have seen upwards of 40% reduction in CI minutes in our client projects by sharing build artifacts between independent jobs, amongst other improvements.&lt;/p&gt;&lt;p&gt;For those looking to implement this in their own monorepos, the pattern is highly adaptable. I encourage you to audit your current workflows for redundant build steps, as it is often the lowest hanging fruit for CI optimisation.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Inclusive user testing with browser-based prototypes</title><link>https://www.thinkmill.com.au/blog/inclusive-user-testing-with-browser-based-prototypes</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/inclusive-user-testing-with-browser-based-prototypes</guid><description>Last year, we partnered with the Queensland Government and Meld Studios to design a website that helps people find tools and information to improve their digital literacy. This was a great chance to apply our inclusive design skills and build a site that’s usable and accessible to as many people as possible.</description><pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;At Thinkmill we care a lot about &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;web accessibility&lt;/a&gt; and are lucky to have many &lt;a href=&quot;https://www.thinkmill.com.au/blog/64k-question-more-accessible-inclusive-experiences&quot;&gt;accessibility experts&lt;/a&gt; on our team. This project provided a wonderful opportunity to deepen our accessibility understandings by designing and testing with Queenslanders who live with a disability, speak English as a second language, live in remote areas, or have varying levels of confidence in technology.&lt;/p&gt;&lt;p&gt;When we set out to design a site that is as inclusive as possible, I quickly realised that many of our go-to user testing tools — workshops, sticky notes, wireframes, Figma prototypes, and even Zoom calls — can unintentionally exclude people. So I had to adjust my design process to make the entire experience more accessible.&lt;/p&gt;&lt;h2&gt;An accessible prototype&lt;/h2&gt;&lt;p&gt;Before we started user testing we identified a series of questions our research should answer, such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;“Can users achieve their goals on a website, or would a mobile app be better?”&lt;/li&gt;&lt;li&gt;“What accessibility features does the website need?”&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Testing a prototype seemed like the best way to find out. But how do you create an accessible prototype?&lt;/p&gt;&lt;h3&gt;The Swiss-Stack to the rescue&lt;/h3&gt;&lt;p&gt;Thankfully, my teammates had already introduced me to a set of tools we have come to call the &lt;em&gt;Swiss-Stack&lt;/em&gt;, which we affectionately named after Thinkmill alumni &lt;a href=&quot;https://simonswiss.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Simon Swiss&lt;/a&gt;.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/qaeh3u0o2h44/dih-tech-stack?width=848&quot; alt=&quot;Logos of tailwindcss, Astro and Keystatic&quot; width=&quot;848&quot; height=&quot;226&quot; /&gt;&lt;p&gt;The &lt;em&gt;Swiss-Stack&lt;/em&gt; includes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://astro.build/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Astro&lt;/strong&gt;&lt;/a&gt;: A powerful javascript framework used to build and deploy websites.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Tailwind&lt;/strong&gt;&lt;/a&gt;: A popular and efficient tool for styling websites.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://keystatic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Keystatic&lt;/strong&gt;&lt;/a&gt;: A lightweight, open-source content management system (developed by us).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;After successfully using these tools to rebuild &lt;a href=&quot;https://jordangeizer.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my personal website&lt;/a&gt;, I felt confident using them to create a prototype directly in the browser. Instead of testing pictures of a website in Figma, we would test an actual website experience in a real browser.&lt;/p&gt;&lt;h3&gt;Designing and building the prototype&lt;/h3&gt;&lt;p&gt;Our team mapped the user experience with &lt;a href=&quot;https://www.thinkmill.com.au/method/playbook/functional-wireframing&quot;&gt;wireframes&lt;/a&gt; and outlined the content structure with a &lt;a href=&quot;https://www.thinkmill.com.au/blog/visualising-a-schema-led-approach-using-figjam&quot;&gt;data schema&lt;/a&gt;.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rowm2zm3c9o3/dih-schema-v2?width=848&quot; alt=&quot;We used the Schema Nodes FigJam widget to create a diagram of the proposed data schema for the website.&quot; width=&quot;848&quot; height=&quot;636&quot; /&gt;&lt;figcaption&gt;We used the Schema Nodes FigJam widget to create a diagram of the proposed data schema for the website.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;I set up the schema in &lt;a href=&quot;https://keystatic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Keystatic&lt;/a&gt;, wrote some content, built the experience in Astro, and styled it to look like a wireframe with Tailwind. In just over a week, we had a low-fidelity, browser-based prototype ready for user testing.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/z34pxev6n1bf/dih-low-fidelity-v2?width=848&quot; alt=&quot;The low-fidelity web prototype captured at the mobile (left) and desktop (right) breakpoints.&quot; width=&quot;848&quot; height=&quot;636&quot; /&gt;&lt;figcaption&gt;The low-fidelity web prototype captured at the mobile (left) and desktop (right) breakpoints.&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;An accessible user testing environment&lt;/h2&gt;&lt;p&gt;To capture feedback from a wide range of perspectives, we recruited a diverse group of Queenslanders — people with vision and hearing impairments, those who are non-verbal, neurodivergent, wheelchair users, and more.&lt;/p&gt;&lt;p&gt;We contacted participants in advance to understand their support needs and opted for one-on-one sessions, either in-person or over Zoom. The in-person sessions were held across four towns in accessible venues, and translators or support workers assisted when needed. Participants used their own devices, navigating the prototype with their assistive technologies and preferences.&lt;/p&gt;&lt;h2&gt;Increasing prototype fidelity&lt;/h2&gt;&lt;p&gt;We ran multiple rounds of testing. The initial low-fidelity prototype helped shape the project’s direction. Since the Queensland Government had a &lt;a href=&quot;https://www.design-system.health.qld.gov.au/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;design system&lt;/a&gt;, we increased the design fidelity over time.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/lr6s0govetgu/dih-increasing-fidelity-v2?width=848&quot; alt=&quot;We used the same content model to render the website at two different levels of fidelity.&quot; width=&quot;848&quot; height=&quot;848&quot; /&gt;&lt;figcaption&gt;We used the same content model to render the website at two different levels of fidelity.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Astro made it easy to integrate the Queensland Government’s current and future design systems. We could quickly switch between different versions during testing, expanding the research to assess the accessibility of both systems and provide valuable feedback that could improve all Queensland Government websites which use the design systems.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/accessible-user-testing-with-browser-based-prototypes/dih-increasing-fidelity-2160.mp4&quot;&gt;For the high-fidelity round of testing, we were able to switch dynamically between the Queensland Government’s two design systems.&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Concluding thoughts&lt;/h2&gt;&lt;p&gt;Testing a browser-based prototype on a participant’s device was an invaluable method for obtaining rich, detailed feedback. If we had used a prototyping tool like Figma, the ability for our participants to engage meaningfully in testing would have been greatly diminished, and we would have missed out on many of the valuable insights we needed to design the right experience.&lt;/p&gt;&lt;p&gt;With a design system and open-source tools, browser-based prototypes can be a powerful, inclusive addition to the design process.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Progressive Web Apps (PWAs): A stepping stone towards a native mobile application</title><link>https://www.thinkmill.com.au/blog/progressive-web-apps-pw-as-a-stepping-stone-towards-a-native-mobile-application</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/progressive-web-apps-pw-as-a-stepping-stone-towards-a-native-mobile-application</guid><description>Progressive Web Apps (PWAs) are now a viable option for providing a mobile experience that meets users’ high expectations. This article provides an introduction to the history of PWAs for those new to the topic, as well as what can be expected from them in 2024. We also look at where PWAs sit in the mobile landscape and how they can be a practical stepping stone towards a native application experience in the future.</description><pubDate>Fri, 16 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;A Progressive Web App (PWA) is a type of web application, which means it’s accessible through a browser but can be installed onto the device to utilise modern web technologies to provide a rich and reliable experience. Features previously reserved for native mobile apps are now available to PWAs, including push notifications, offline functionality, and access to device features like the camera or GPS. This makes them a unique option for products or services looking to provide an amazing experience, without sacrificing the ease of access via the browser.&lt;/p&gt;&lt;p&gt;Because PWAs are an extension of an existing web application, they build off the same code, with a little bit of additional effort to take advantage of the native features of a device. Just like a website, they are accessible cross-device and cross-platform.&lt;/p&gt;&lt;p&gt;However, because they are still relatively unknown to users, the process of installing a PWA can be a hurdle. Though the power of PWAs has increased greatly over the last five years, this gap in understanding has had a substantial impact on their adoption.&lt;/p&gt;&lt;h2&gt;History of PWAs&lt;/h2&gt;&lt;p&gt;Progressive Web Apps have been around &lt;a href=&quot;https://infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;since 2015&lt;/a&gt; and have been evolving ever since. In 2016, Google adapted PWAs as a new web development standard, including the ability to add an icon to the home screen, push notifications and background syncing. In 2017, Twitter/X launched its PWA, which loads faster than its native app and uses less data. With the release of Windows 10 in 2018, PWAs are now supported by Windows 10 devices. By 2019, Pinterest and Uber had both launched PWAs that showcased app-like experiences like real-time messaging and access to data offline. In 2021, Apple finally added support for PWAs in the Safari browser on iOS and macOS, allowing users to add them to their home screen and access them offline. Over this time, the user experience has become better and the gap between native mobile apps and PWAs has continuously closed.&lt;/p&gt;&lt;h2&gt;State of PWAs&lt;/h2&gt;&lt;p&gt;The term ‘Progressive Web App’, may not bring the smoothest of digital experiences to mind. However, as of 2024, PWAs have reached new levels of functionality and operating system support that means they can now be considered passable as native mobile applications. This is due to several key changes:&lt;/p&gt;&lt;h3&gt;Hiding the URL bar&lt;/h3&gt;&lt;p&gt;The URL bar is a telltale sign of a browser, and also breaks the immersive user experience by allowing the user to navigate away from your website. Hiding the URL bar is a simple feature that can be utilised to more closely mimic a native mobile application, and maintain the immersion of a PWA.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/f8dvuur69cd0/progressive-web-apps-pw-as-a-stepping-stone-towards-a-native-mobile-application-twitter-nourlbar?width=848&quot; alt=&quot;Screenshot of the X (Twitter) PWA with no URL bar visible&quot; width=&quot;848&quot; height=&quot;1838&quot; /&gt;&lt;figcaption&gt;Screenshot of the X (Twitter) PWA with no URL bar visible&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;Retaining the sign-in&lt;/h3&gt;&lt;p&gt;Historically, PWAs could not retain an account sign-in and a user would have to re-enter their details each time they launched the web app. As a poor user experience, &lt;em&gt;this&lt;/em&gt; deterred many from using the PWA option. This has since been resolved, and a user’s sign-in remains when quitting and relaunching the PWA.&lt;/p&gt;&lt;h3&gt;Easier installation&lt;/h3&gt;&lt;p&gt;One of the main drawbacks of a PWA is that many users are not aware of their existence or how to install them. &lt;a href=&quot;https://web.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Web.dev&lt;/a&gt; have created a great development resource which details the &lt;a href=&quot;https://web.dev/promote-install/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;recommended patterns for promoting a PWA&lt;/a&gt;, via placement of a straightforward ‘Install’ button. This button can be placed in a number of convenient locations such as on the landing screen, in the navigation menu, or at the completion of the user’s primary journey where they may be interested in coming back to the website. Pressing the ‘Install’ button will trigger the necessary modals for the user to confirm the installation, making this the most simple method for adding the PWA to the home screen of the mobile device.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/i9nnfw0tfdfu/progressive-web-apps-pw-as-a-stepping-stone-towards-a-native-mobile-application-pwa-installation?width=848&quot; alt=&quot;‘Install’ button conveniently located in the navigation menu of a responsive mobile website. &quot; width=&quot;848&quot; height=&quot;1184&quot; /&gt;&lt;figcaption&gt;‘Install’ button conveniently located in the navigation menu of a responsive mobile website. &lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;Offline experience&lt;/h3&gt;&lt;p&gt;Another difference in expectation from users between websites and mobile applications is support for an offline experience. When a website cannot connect to the internet, users are fine seeing nothing, however this is not the case for a mobile app. For many apps like Instagram or Facebook, when the device is unable to connect, cached data is displayed along with a connection error. Luckily, PWAs offer similar functionality where the offline experience can take advantage of caching and storage of data on the device to provide an experience when there is no connectivity.&lt;/p&gt;&lt;h3&gt;Push notifications&lt;/h3&gt;&lt;p&gt;As of &lt;a href=&quot;https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Safari 16.4&lt;/a&gt;, Apple supports push notifications for PWAs. Push notifications are a powerful feature that enables PWAs to effectively communicate with users and provide real-time updates on their tasks and activities. Unlike traditional websites which often rely on email notifications, PWAs have the ability to leverage push notifications and thereby significantly enhance their usefulness. This recent decision from Apple improves the viability of PWAs cross-platform.&lt;/p&gt;&lt;h3&gt;Splash screen&lt;/h3&gt;&lt;p&gt;An often forgotten part of the mobile application experience is the presence of a splash screen which shows upon the tapping of the app’s icon on the home screen. When creating a PWA, developers are able to specify the splash screen behaviour to mimic a more native experience.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/progressive-web-apps-pw-as-a-stepping-stone-towards-a-native-mobile-application/twitter-splash.MP4&quot;&gt;The X (Twitter) PWA’s splash screen experience.&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Apple support on iOS&lt;/h3&gt;&lt;p&gt;Finally, in February of 2024, Apple announced plans to remove the Home Screen web app capability for users in the European Union. After receiving pressure and feedback from developers and users alike, Apple reverted to its original stance of supporting Home Screen web apps in the EU. They emphasised that PWAs will be built directly on its WebKit browser engine, and align with the existing security and privacy models for native apps on iOS.&lt;/p&gt;&lt;h2&gt;Making a responsive website into a PWA&lt;/h2&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rqtl72t7yv96/progressive-web-apps-pw-as-a-stepping-stone-towards-a-native-mobile-application-effort-to-pwa?width=848&quot; alt=&quot;Bar graph showing that with a little extra work, a responsive website can be made into a PWA.&quot; width=&quot;848&quot; height=&quot;309&quot; /&gt;&lt;p&gt;One of the biggest benefits of PWAs is they can piggyback off the work already done for a responsive website. The steps involved with turning a responsive website into a PWA are minimal and include creating a manifest file which lets the browser know about your Progressive Web App, as well as some additional information about how it should behave when installed on a user’s device. The main components of a typical manifest file include the app name, the app icon, and the URL that should open when the app is launched.&lt;/p&gt;&lt;pre&gt;{
  &quot;name&quot;: &quot;Example App&quot;,
  &quot;short_name&quot;: &quot;App&quot;,
  &quot;start_url&quot;: &quot;/&quot;,
  &quot;display&quot;: &quot;standalone&quot;,
  &quot;background_color&quot;: &quot;#ffffff&quot;,
  &quot;description&quot;: &quot;An example of a Progressive Web App&quot;,
  &quot;icons&quot;: [
    {
      &quot;src&quot;: &quot;icon-192x192.png&quot;,
      &quot;sizes&quot;: &quot;192x192&quot;,
      &quot;type&quot;: &quot;image/png&quot;
    },
    {
      &quot;src&quot;: &quot;icon-256x256.png&quot;,
      &quot;sizes&quot;: &quot;256x256&quot;,
      &quot;type&quot;: &quot;image/png&quot;
    },
    {
      &quot;src&quot;: &quot;icon-384x384.png&quot;,
      &quot;sizes&quot;: &quot;384x384&quot;,
      &quot;type&quot;: &quot;image/png&quot;
    },
    {
      &quot;src&quot;: &quot;icon-512x512.png&quot;,
      &quot;sizes&quot;: &quot;512x512&quot;,
      &quot;type&quot;: &quot;image/png&quot;
    }
  ],
}
&lt;/pre&gt;&lt;p&gt;From there, additional work to enable offline functionality and the caching of essential assets can be done to improve the feel of the PWA.&lt;/p&gt;&lt;h2&gt;PWA vs. React Native vs. Native&lt;/h2&gt;&lt;p&gt;Mobile phones offer a convenient and on-the-go experience for users. As user interaction on these devices continues to grow, it is important to provide a great mobile experience. However, it can be difficult to decide which avenue to take. There are three tiers of mobile experiences: Progressive Web Apps, React Native apps, and fully native apps.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ljd0eayse3a1/progressive-web-apps-pw-as-a-stepping-stone-towards-a-native-mobile-application-pwa-reactnative-native?width=848&quot; alt=&quot;Line depicting progression between PWA, React Native and Native Apps.&quot; width=&quot;848&quot; height=&quot;227&quot; /&gt;&lt;table&gt;&lt;thead&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Progressive Web Apps&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;React Native Apps&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Native Apps&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Codebase&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Uses the same code as a responsive website (HTML/CSS/Javascript).&lt;/td&gt;&lt;td&gt;Uses React Native which can be deployed across iOS and Android with minimal changes.&lt;/td&gt;&lt;td&gt;The codebase is specific to the operating system of the device, Java for Android and Swift for iOS.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Complexity of user interactions&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Limited to user interactions available on the web, though these are getting more advanced each year.&lt;/td&gt;&lt;td&gt;Quite complex user interactions can be achieved, getting close to the fully native experience.&lt;/td&gt;&lt;td&gt;Provides the smoothest user experience and can handle the most complex user interactions.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Discoverability&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Easy discovery for users through the web which can be accessed from any browser.&lt;/td&gt;&lt;td&gt;Users must download the app from the iOS or Android app store.&lt;/td&gt;&lt;td&gt;Users must download the app from the iOS or Android app store.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Updates are deployed to users immediately, same as a website.&lt;/td&gt;&lt;td&gt;Users must perform an app update.&lt;/td&gt;&lt;td&gt;Users must perform an app update.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Device feature integration&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Progressive enhancement approach to feature detection. You should not expect your app to work the same way on every device. Good support for basic features via JavaScript APIs, but becomes more limited as you venture into hardware sensors and operating system features.&lt;/td&gt;&lt;td&gt;React Native offers a very similar level of integration to a truly native application. This comes at the expense of performance in more complex applications.&lt;/td&gt;&lt;td&gt;Highest level of integration with device features such as camera, GPS and fingerprint reader.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Same security options as any web application. Must run under &lt;code&gt;https&lt;/code&gt; to ensure a secure server/client connection.&lt;/td&gt;&lt;td&gt;Added benefit to include operating system/hardware security measures i.e biometric auth. Must pass the app store’s security standards before publication.&lt;/td&gt;&lt;td&gt;Added benefit to include operating system/hardware security measures i.e biometric auth. Must pass the app store’s security standards before publication.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Browser support&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Not available on some browsers e.g. Firefox&lt;/td&gt;&lt;td&gt;Unrelated to the browser.&lt;/td&gt;&lt;td&gt;Unrelated to the browser.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Perceived trust&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Less perceived trust from users.&lt;/td&gt;&lt;td&gt;Because these apps have to go through the app review process by iOS or Android, users tend to trust them more, especially for tasks that require higher-level of security like banking apps.&lt;/td&gt;&lt;td&gt;Because these apps have to go through the app review process by iOS or Android, users tend to trust them more, especially for tasks that require higher-level of security like banking apps.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;One huge benefit of PWAs is that they leverage the investment made to build a mobile-responsive website. Building a robust and intuitive responsive website can be expensive, so adding PWA functionality can be a cost-effective option to provide a richer experience for early adopters.&lt;/p&gt;&lt;h2&gt;Where do PWAs have a place?&lt;/h2&gt;&lt;p&gt;All users have an existing mental model of how to access their digital products and services. Typically, this is done in one of two ways; via websites accessed through browsers such as Safari and Chrome, or via the icon of a mobile application which has been accessed through an app store and installed onto their device. Progressive Web Apps (PWAs) exist outside of this mental model, and although it is now simpler than ever to install a PWA onto a device, the process will be unfamiliar to most users. This is why it’s important to understand where PWAs sit within the mobile experience landscape and to have clarity on what offering this alternative will mean to users.&lt;/p&gt;&lt;p&gt;This group is less likely to mind the PWA installation process to enjoy a more app-like experience including an icon on their home screen for quick access to a web application they already navigate to often via their browser.&lt;/p&gt;&lt;p&gt;Additionally, building a native mobile application is more expensive, not just in terms of cost, but also because finding market fit is not usually a straightforward process. A PWA offers a unique solution for testing out ideas and integrating user feedback at a lower cost, making it a great stop-gap before moving to a more conventional application offering. This gives the team time to understand the problem space and the best way to direct development efforts.&lt;/p&gt;&lt;p&gt;Finally, because a PWA looks and feels so similar to a native mobile application, they are great at giving insight into what the future-state of the experience might look like. This makes them a powerful sales tool to help potential clients visualise where the product is headed, without having to invest in a separate native app up-front.&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;In conclusion, Progressive Web Apps have come a long way but still suffer from sitting outside the typical user’s mental model of how to access their digital products and services. However, in some select cases, they can be a practical way of leveraging the work done to build a mobile-responsive website to provide a more app-like experience for early adopters, until a native application can be built. Because they are easy to update and use the exact same codebase as the responsive website, they can empower teams to integrate user feedback quickly and find market fit. Additionally, they can be extremely effective as a low-cost sales tool to help visualise a future React Native or fully native mobile application.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Building a Multi Brand Design System with Tailwind: Tips, Tricks and Tradeoffs</title><link>https://www.thinkmill.com.au/blog/building-a-multi-brand-design-system-with-tailwind-tips-tricks-and-tradeoffs</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/building-a-multi-brand-design-system-with-tailwind-tips-tricks-and-tradeoffs</guid><description>I recently wrapped up a project to help build a multi-brand design system. This article explores why we chose Tailwind CSS for styling and discusses the pros and cons of using it in this context.</description><pubDate>Tue, 04 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article stems from a &lt;a href=&quot;https://www.thinkmill.com.au/blog/talk-building-a-multi-brand-design-system-with-tailwind-tips-tricks-and-tradeoffs&quot;&gt;talk I gave at SydJS in 2024&lt;/a&gt;. This version has all the links and code snippets for you to follow along at home.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Why we chose Tailwind&lt;/h2&gt;&lt;p&gt;As a consultant, one of my goals when helping clients build any product is to pick a set of tools that suit its specific needs. These need to fit the applications they’ll be used in and match the skills of the people who will maintain them after I leave. In this project, we aimed to create a design system that supports multiple brands for a team with developers of all skill levels.&lt;/p&gt;&lt;p&gt;After carefully considering several different styling solutions, we decided on Tailwind for a few key reasons:&lt;/p&gt;&lt;h3&gt;Lowering the barrier to entry&lt;/h3&gt;&lt;p&gt;For styling design systems, I usually reach for &lt;a href=&quot;https://emotion.sh/docs/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Emotion&lt;/a&gt;. It’s powerful, flexible, and battle-tested, allowing me to define CSS styles directly in the same file as my JSX. This co-location of markup, functionality, and styles also fits really well with React’s component model.&lt;/p&gt;&lt;p&gt;Still, Emotion on its own doesn’t really help when it comes to using design tokens, which is something you use a lot in a design system! Developers need to create their own system for using these tokens. Usually, you need to call a hook to get the theme out of context and map the token to the CSS property every time. Tailwind makes this a lot easier because you pull in your design tokens in the Tailwind config file and do the mapping upfront. Then you have classes made available to you with the tokens baked in. There’s also no runtime cost for this, which is another win.&lt;/p&gt;&lt;p&gt;Tailwind also simplifies complex tasks like responsive styles and state-based styling with its &lt;a href=&quot;https://tailwindcss.com/docs/hover-focus-and-other-states&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;variants syntax&lt;/a&gt;. In other systems, writing what would otherwise be the same styles in different contexts can lead to a lot of extra code. With Tailwind, you can style states like &lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:focus&lt;/code&gt;, &lt;code&gt;:active&lt;/code&gt;, and &lt;code&gt;:disabled&lt;/code&gt;, as well as responsive states, and more with a simple prefix to your class.&lt;/p&gt;&lt;h3&gt;Customising app-specific components&lt;/h3&gt;&lt;p&gt;Applications and design systems typically move at very different paces. With a design system, everything is slower and more deliberate. You’re working on components that will be used in multiple applications by different teams, considering factors like API design, accessibility, and bundle size (just to name a few). On the other hand, application development usually moves much faster, focusing on validating ideas and testing with users to ensure functionality and user satisfaction.&lt;/p&gt;&lt;p&gt;By using Tailwind, we have a great API for styling components consistently with the design system since most of Tailwind’s classes are based on your design tokens. This gives application developers the freedom to use other libraries that provide the functionality they need without being blocked by the design system team’s pace. This is crucial in the early stages of rolling out the design system when there aren’t as many components available.&lt;/p&gt;&lt;p&gt;This flexibility lets teams validate their ideas and get user feedback much earlier in the process. Once these custom components have proven effective, they can be refined, standardised, and incorporated back into the design system. This ensures that the design system evolves with real-world use cases, making it more robust and versatile while still supporting rapid prototyping in the browser.&lt;/p&gt;&lt;h3&gt;Simplifying the technology stack&lt;/h3&gt;&lt;p&gt;The company we were consulting for maintained an ecosystem of multiple websites and apps, using a variety of front-end technologies. Since they were already moving towards Tailwind elsewhere, consolidation on this choice for the design system was an added benefit.&lt;/p&gt;&lt;p&gt;Using Tailwind for styling in the design system reduced the need for extensive documentation. For example, if a developer had previous experience using Tailwind on one of the marketing sites, they would find it easy to adapt to styling components in the design system. This familiarity ensures they can quickly become productive and feel more at home in a new codebase.&lt;/p&gt;&lt;h2&gt;Challenges of adopting Tailwind in a design system&lt;/h2&gt;&lt;h3&gt;Problem: Full class string must appear in markup&lt;/h3&gt;&lt;p&gt;With CSS-in-JS libraries like Emotion, the CSS is generated at runtime, allowing for dynamic styles. You can use JavaScript libraries to do things like tint or shade colours, create complex responsive grid systems, and more.&lt;/p&gt;&lt;p&gt;Tailwind’s &lt;a href=&quot;https://tailwindcss.com/blog/just-in-time-the-next-generation-of-tailwind-css&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;JIT compiler&lt;/a&gt; works differently. It scans all your template files for Tailwind classes to generate the required CSS, meaning the full class string needs to be present in the markup. Without it, your classes won’t produce the corresponding CSS.&lt;/p&gt;&lt;p&gt;To accommodate this limitation, you usually end up creating some pretty gnarly functions or lookup objects. In some cases, such as responsive props for layout components, there are just too many possible combinations for this to be a viable option.&lt;/p&gt;&lt;h3&gt;Solution: Use classes instead of props for styling components&lt;/h3&gt;&lt;p&gt;The solution is to do what we used to do — just use classes. This simplifies a lot of components since it offloads some of the work to the users. It doesn’t really degrade the developer experience either; it’s still fast and intuitive to style. If anything, it’s probably faster!&lt;/p&gt;&lt;h3&gt;Problem: Loss of type safety and autocomplete&lt;/h3&gt;&lt;p&gt;TypeScript provides valuable type safety and autocomplete, helping catch errors early and making refactoring much safer. With Tailwind, you lose some of these benefits. This can make it easier to overlook issues like typos in class names or classes that become invalid after making changes to your Tailwind config.&lt;/p&gt;&lt;h3&gt;Solution (sort of): IntelliSense plugin&lt;/h3&gt;&lt;p&gt;The &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;IntelliSense plugin&lt;/a&gt; for VS Code helps mitigate this. It provides autocomplete, syntax highlighting, and linting for Tailwind classes. This means you can catch typos, see what each class does, and even get helpful hover previews showing the exact CSS that will be generated. While it doesn’t offer full type safety, it makes working with Tailwind much more manageable.&lt;/p&gt;&lt;h3&gt;Problem: Overriding styles&lt;/h3&gt;&lt;p&gt;With CSS-in-JS solutions like Emotion, overriding styles is easy because you’re working with objects and key/value pairs. To change the style, you can simply change the value of the CSS property. With Tailwind, you’re working with strings instead of objects, so you can’t just change a value directly. You might try using a different class that targets the same property, but there’s no guarantee the last class you apply will win, since specificity is based on the order of the classes in the stylesheet (which we don’t really control) and not the markup. Even if we did control the order of the classes, the point of Tailwind is that utility classes are as reusable as possible. Fixing a specificity issue in one place might break it in another, as you’re likely using the same class in multiple places.&lt;/p&gt;&lt;h3&gt;Solution: Tailwind Merge&lt;/h3&gt;&lt;p&gt;To handle style overrides in Tailwind, we use &lt;a href=&quot;https://github.com/dcastil/tailwind-merge&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tailwind Merge&lt;/a&gt;. This tool understands every Tailwind class and the properties they target. It checks for conflicts among the classes you provide and ensures that the correct styles are applied by removing conflicting classes and keeping the last one. This way, you can confidently override styles without manually managing conflicts.&lt;/p&gt;&lt;h3&gt;Problem: One-off styles can’t be authored in the markup like your regular Tailwind classes&lt;/h3&gt;&lt;p&gt;Every project has a few “one-off” styling exceptions that need special treatment. In earlier versions of Tailwind, handling these exceptions meant writing regular CSS elsewhere in your codebase, so your styles weren’t necessarily co-located with your markup anymore. Creating custom styles wasn’t easy and often required stepping outside the framework.&lt;/p&gt;&lt;h3&gt;Solution: Arbitrary variants and arbitrary properties&lt;/h3&gt;&lt;p&gt;Tailwind introduced &lt;a href=&quot;https://tailwindcss.com/docs/adding-custom-styles#arbitrary-variants&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;arbitrary variants&lt;/a&gt; and &lt;a href=&quot;https://tailwindcss.com/docs/adding-custom-styles#arbitrary-properties&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;arbitrary properties&lt;/a&gt; to address this issue. These features allow you to write custom styles directly in your markup using special syntax. This keeps your styles within the component, maintains the utility-first approach, and provides a flexible way to handle those unique styling needs without breaking the flow.&lt;/p&gt;&lt;p&gt;Even though the syntax might look a bit ugly, its distinctiveness is actually a benefit. It makes these classes easy to find and remove or replace if needed. By co-locating styles with your markup, you ensure that these custom styles can’t “leak out” of the component, keeping everything tidy and manageable.&lt;/p&gt;&lt;h2&gt;Tailwind’s Evolution and Comparisons&lt;/h2&gt;&lt;p&gt;As Tailwind has evolved, its utility has expanded across more projects. This evolution is similar to how GraphQL changed data querying in development.&lt;/p&gt;&lt;h3&gt;Tailwind is like GraphQL for CSS&lt;/h3&gt;&lt;p&gt;The JIT compiler and arbitrary values added in Tailwind 3 have broadened its practical use. As my boss &lt;a href=&quot;https://twitter.com/jedwatson&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jed Watson&lt;/a&gt; aptly put it, “Tailwind is like GraphQL for CSS.”&lt;/p&gt;&lt;p&gt;In GraphQL, you have a schema, and in Tailwind, you have design tokens. GraphQL has a syntax to query for data, and in Tailwind, you query the JIT compiler for CSS by writing a class it recognises. Just like in GraphQL, where you never over-fetch data, in Tailwind, you only generate the CSS you need. This makes your styles more efficient and your development process more streamlined.&lt;/p&gt;&lt;h2&gt;Getting the most out of Tailwind in your design system&lt;/h2&gt;&lt;h3&gt;Create a preset&lt;/h3&gt;&lt;p&gt;Tailwind offers &lt;a href=&quot;https://tailwindcss.com/docs/presets&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;presets&lt;/a&gt;, which are pre-customised Tailwind configs. Using a preset simplifies setup and maintenance, as there’s less to change when updating core styles. By making the preset a function, you can pass in design tokens, meaning one preset can support multiple brands. This also cuts down on setup time and boilerplate code.&lt;/p&gt;&lt;p&gt;For example, here’s what the popular Tailwind-based component library &lt;a href=&quot;https://ui.shadcn.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;shadcn/ui&lt;/a&gt; generates when you initialise a new project:&lt;/p&gt;&lt;pre&gt;// tailwind.config.ts
import type { Config } from &apos;tailwindcss&apos;;

export default {
  darkMode: [&apos;class&apos;],
  content: [
    &apos;./src/**/*.{js,ts,jsx,tsx}&apos;,
    &apos;./node_modules/@acme-ds/core/dist/**/*.{js,ts,jsx,tsx}&apos;,
  ],
  prefix: &apos;&apos;,
  theme: {
    container: {
      center: true,
      padding: &apos;2rem&apos;,
      screens: {
        &apos;2xl&apos;: &apos;1400px&apos;,
      },
    },
    extend: {
      colors: {
        border: &apos;hsl(var(--border))&apos;,
        input: &apos;hsl(var(--input))&apos;,
        ring: &apos;hsl(var(--ring))&apos;,
        background: &apos;hsl(var(--background))&apos;,
        foreground: &apos;hsl(var(--foreground))&apos;,
        primary: {
          DEFAULT: &apos;hsl(var(--primary))&apos;,
          foreground: &apos;hsl(var(--primary-foreground))&apos;,
        },
        secondary: {
          DEFAULT: &apos;hsl(var(--secondary))&apos;,
          foreground: &apos;hsl(var(--secondary-foreground))&apos;,
        },
        destructive: {
          DEFAULT: &apos;hsl(var(--destructive))&apos;,
          foreground: &apos;hsl(var(--destructive-foreground))&apos;,
        },
        muted: {
          DEFAULT: &apos;hsl(var(--muted))&apos;,
          foreground: &apos;hsl(var(--muted-foreground))&apos;,
        },
        accent: {
          DEFAULT: &apos;hsl(var(--accent))&apos;,
          foreground: &apos;hsl(var(--accent-foreground))&apos;,
        },
        popover: {
          DEFAULT: &apos;hsl(var(--popover))&apos;,
          foreground: &apos;hsl(var(--popover-foreground))&apos;,
        },
        card: {
          DEFAULT: &apos;hsl(var(--card))&apos;,
          foreground: &apos;hsl(var(--card-foreground))&apos;,
        },
      },
      borderRadius: {
        lg: &apos;var(--radius)&apos;,
        md: &apos;calc(var(--radius) - 2px)&apos;,
        sm: &apos;calc(var(--radius) - 4px)&apos;,
      },
      keyframes: {
        &apos;accordion-down&apos;: {
          from: { height: &apos;0&apos; },
          to: { height: &apos;var(--radix-accordion-content-height)&apos; },
        },
        &apos;accordion-up&apos;: {
          from: { height: &apos;var(--radix-accordion-content-height)&apos; },
          to: { height: &apos;0&apos; },
        },
      },
      animation: {
        &apos;accordion-down&apos;: &apos;accordion-down 0.2s ease-out&apos;,
        &apos;accordion-up&apos;: &apos;accordion-up 0.2s ease-out&apos;,
      },
    },
  },
  plugins: [require(&apos;tailwindcss-animate&apos;)],
} satisfies Config;
&lt;/pre&gt;&lt;pre&gt;/* tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;

    --card: 0 0% 100%;
    --card-foreground: 222.2 84% 4.9%;

    --popover: 0 0% 100%;
    --popover-foreground: 222.2 84% 4.9%;

    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;

    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;

    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;

    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;

    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;

    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;

    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;

    --card: 222.2 84% 4.9%;
    --card-foreground: 210 40% 98%;

    --popover: 222.2 84% 4.9%;
    --popover-foreground: 210 40% 98%;

    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;

    --secondary: 217.2 32.6% 17.5%;
    --secondary-foreground: 210 40% 98%;

    --muted: 217.2 32.6% 17.5%;
    --muted-foreground: 215 20.2% 65.1%;

    --accent: 217.2 32.6% 17.5%;
    --accent-foreground: 210 40% 98%;

    --destructive: 0 62.8% 30.6%;
    --destructive-foreground: 210 40% 98%;

    --border: 217.2 32.6% 17.5%;
    --input: 217.2 32.6% 17.5%;
    --ring: 212.7 26.8% 83.9%;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}
&lt;/pre&gt;&lt;p&gt;Compared to just this with our preset:&lt;/p&gt;&lt;pre&gt;// tailwind.config.ts
import { tokens } from &apos;@acme-ds/brands/my-brand&apos;;
import { createPreset } from &apos;@acme-ds/core/tailwind&apos;;
import { type Config } from &apos;tailwindcss&apos;;

export default {
  content: [
    &apos;./node_modules/@acme-ds/core/src/**/*.{js,ts,jsx,tsx}&apos;,
    &apos;./src/**/*.{js,ts,jsx,tsx}&apos;,
  ],
  presets: [createPreset(tokens)],
} satisfies Config;
&lt;/pre&gt;&lt;pre&gt;/* tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
&lt;/pre&gt;&lt;h3&gt;Customising Tailwind’s default theme&lt;/h3&gt;&lt;p&gt;Tailwind’s &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/06972065defd552e4b4e8d52fcdbf5319e19ccd0/stubs/config.full.js&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;default theme&lt;/a&gt; is extensive but not overly opinionated. This is great for websites and small apps where getting started quickly is important, but for a design system, you’ll likely have strong opinions on things like colours and typography, so turning off many of Tailwind’s defaults is probably something you’ll want to do.&lt;/p&gt;&lt;p&gt;While Tailwind lets you disable built-in plugins using the &lt;code&gt;corePlugins&lt;/code&gt; key, it’s best to avoid this. We wanted developers already familiar with Tailwind to use our design system without having to learn a bunch of new conventions, so keeping the same prefixes was important. Also, turning off core plugins means losing features like arbitrary variants.&lt;/p&gt;&lt;p&gt;Instead, the best thing to do is to remove the defaults from the theme object. If you create your preset function, you can even add flags to turn features on or off. This is useful if you want to incrementally add the design system to an app that’s already using Tailwind.&lt;/p&gt;&lt;pre&gt;export function createPreset(
  tokens: Tokens,
  {
    shouldExcludeBaseStyles = false,
    shouldUseTailwindBorderRadius = false,
    shouldUseTailwindBoxShadows = false,
    shouldUseTailwindColors = false,
    shouldUseTailwindFontSize = false,
    shouldUseTailwindFontWeight = false,
  }: CreatePresetOptions = {}
): Config {
  return {
    content: [],
    theme: {
      ...defaultTheme,
      boxShadow: shouldUseTailwindBoxShadows ? defaultTheme.boxShadow : {},
      boxShadowColor: shouldUseTailwindBoxShadows ? defaultTheme.boxShadowColor : {},
      fontSize: shouldUseTailwindFontSize ? defaultTheme.fontSize : {},
      fontWeight: shouldUseTailwindFontWeight ? defaultTheme.fontWeight : {},
      ...getBorderRadiusThemeConfig(tokens, { shouldUseTailwindBorderRadius }),
      ...getColorThemeConfig({ shouldUseTailwindColors }),
    },
    plugins: [
      // ...
    ],
  };
}
&lt;/pre&gt;&lt;h3&gt;Making the most of custom plugins&lt;/h3&gt;&lt;p&gt;Tailwind plugins are a great way to add new styles. Don’t be afraid to reach for them when you need them. You even get IntelliSense for plugins you create, so the developer experience is still excellent.&lt;/p&gt;&lt;p&gt;Tailwind styles are split into &lt;code&gt;base&lt;/code&gt;, &lt;code&gt;component&lt;/code&gt;, and &lt;code&gt;utility&lt;/code&gt; layers. In Tailwind, the &lt;code&gt;base&lt;/code&gt; layer is for resets, global styles, and custom properties. The &lt;code&gt;component&lt;/code&gt; layer targets multiple CSS properties, and the &lt;code&gt;utility&lt;/code&gt; layer (with its higher specificity) overrides properties from the previous layers. Tailwind’s plugin function gives you a callback for adding styles to a layer, e.g. &lt;code&gt;addComponents&lt;/code&gt; for the &lt;code&gt;component&lt;/code&gt; layer.&lt;/p&gt;&lt;p&gt;Here are some examples of useful plugins we created:&lt;/p&gt;&lt;h4&gt;Typography plugin&lt;/h4&gt;&lt;p&gt;We created a typography plugin where we kept the familiar &lt;code&gt;text-&lt;/code&gt; prefix, turned off Tailwind’s defaults, and set up styles for &lt;code&gt;font-family&lt;/code&gt;, &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;font-weight&lt;/code&gt;, &lt;code&gt;line-height&lt;/code&gt;, and &lt;code&gt;letter-spacing&lt;/code&gt; together, for both regular text and headings. This gave us a robust set of styles and reduced the need for a &lt;code&gt;&amp;lt;Text&amp;gt;&lt;/code&gt; component.&lt;/p&gt;&lt;pre&gt;export function createTypographyPlugin(typography: GlobalTokens[&quot;typography&quot;]) {
  return plugin(({ addComponents, addUtilities }) =&amp;gt; {
    const headingUtilities = Object.fromEntries(
      Object.entries(typography.heading).map(([key, value]) =&amp;gt; [
        `.text-heading-${key}`,
        {
          fontFamily: [
            typography.fontFamily.heading,
            typography.fontFamily.fallback,
          ].join(&quot;, &quot;),
          fontSize: value.fontSize,
          fontWeight: value.fontWeight,
          lineHeight: value.lineHeight,
          letterSpacing: value.letterSpacing,
        },
      ])
    );

    const bodyUtilities = Object.fromEntries(
      Object.entries(typography.body).map(([key, value]) =&amp;gt; [
        `.text-body-${key}`,
        {
          fontFamily: [
            typography.fontFamily.body,
            typography.fontFamily.fallback,
          ].join(&quot;, &quot;),
          fontSize: value.fontSize,
          fontWeight: value.fontWeight,
          lineHeight: value.lineHeight,
          letterSpacing: value.letterSpacing,
        },
      ])
    );

    addComponents({
      ...headingUtilities,
      ...bodyUtilities,
    });

    addUtilities({
      &quot;.font-regular&quot;: {
        fontWeight: typography.fontWeight.regular,
      },
      &quot;.font-strong&quot;: {
        fontWeight: typography.fontWeight.strong,
      },
      &quot;.font-stronger&quot;: {
        fontWeight: typography.fontWeight.stronger,
      },
    });
  });
}
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use custom properties for font weights to style them differently between brands. For example, you might want bold headings in one brand and lighter headings in another.&lt;/p&gt;&lt;h4&gt;Colour plugin&lt;/h4&gt;&lt;p&gt;Next, we developed a colour plugin to manage colour schemes. By using custom properties for our colours, users don’t need to write any extra code to support switching between light and dark modes. We use both a &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query and &lt;code&gt;.light&lt;/code&gt;/&lt;code&gt;.dark&lt;/code&gt; classes so you can let users of your app manually set their preferred colour scheme or have it automatically switch based on system settings.&lt;/p&gt;&lt;pre&gt;export function createColorPlugin(
  colorSchemes: Tokens[&apos;colorSchemes&apos;],
  { shouldExcludeBaseStyles }: { shouldExcludeBaseStyles: boolean }
) {
  return plugin(({ addBase }) =&amp;gt; {
    if (!shouldExcludeBaseStyles) {
      const colors = getColors(colorSchemes);
      addBase({
        &apos;:root&apos;: {
          colorScheme: &apos;light dark&apos;,
          ...colors.light,
        },
        &apos;@media (prefers-color-scheme: dark)&apos;: {
          &apos;:root&apos;: {
            colorScheme: &apos;dark&apos;,
            ...colors.dark,
          },
        },
        &apos;.light&apos;: {
          colorScheme: &apos;light&apos;,
          ...colors.light,
        },
        &apos;.dark&apos;: {
          colorScheme: &apos;dark&apos;,
          ...colors.dark,
        },
      });
    }
  });
}
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Set a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;color-scheme&lt;/a&gt; along with dark/light mode classes and media queries so browser chrome elements like scrollbars and native selects match your design.&lt;/p&gt;&lt;h4&gt;Focus ring plugin&lt;/h4&gt;&lt;p&gt;We also created a focus ring plugin to ensure consistent focus styles across our components. This is a component class since it targets lots of CSS properties. It handles different states and improves accessibility by providing clear visual feedback for focusable elements.&lt;/p&gt;&lt;pre&gt;export const focusRingPlugin = plugin(({ addComponents, theme }) =&amp;gt; {
  addComponents({
    &apos;.focus-ring&apos;: {
      outline: &apos;none&apos;,
      &apos;--tw-ring-color&apos;: theme(&apos;borderColor.focus&apos;),
      &apos;--tw-ring-offset-color&apos;: theme(&apos;backgroundColor.neutral&apos;),
      &apos;--tw-ring-offset-width&apos;: theme(&apos;ringWidth.1&apos;),
      &apos;--tw-ring-offset-shadow&apos;:
        &apos;0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)&apos;,
      &apos;--tw-ring-shadow&apos;:
        &apos;0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)&apos;,
      &apos;box-shadow&apos;:
        &apos;var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 rgb(0 0 0 / 0))&apos;,
      &apos;@media (-ms-high-contrast: active), (forced-colors: active)&apos;: {
        &apos;outline-color&apos;: theme(&apos;outlineColor.transparent&apos;),
        &apos;outline-offset&apos;: theme(&apos;outlineOffset.2&apos;),
        &apos;outline-style&apos;: &apos;solid&apos;,
        &apos;outline-width&apos;: theme(&apos;outlineWidth.2&apos;),
      },
    },

    &apos;.no-focus-ring&apos;: {
      outline: &apos;revert&apos;,
      &apos;box-shadow&apos;: &apos;revert&apos;,
      &apos;@media (-ms-high-contrast: active), (forced-colors: active)&apos;: {
        &apos;outline-color&apos;: &apos;revert&apos;,
        &apos;outline-offset&apos;: &apos;revert&apos;,
        &apos;outline-style&apos;: &apos;revert&apos;,
        &apos;outline-width&apos;: &apos;revert&apos;,
      },
    },
  });
});
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Creating the &lt;code&gt;.focus-ring&lt;/code&gt; class with Tailwind’s plugin system means that it works for all valid Tailwind variants, e.g., using it with &lt;code&gt;has-[:focus]:&lt;/code&gt; for a radio card.&lt;/p&gt;&lt;h2&gt;Should you pick Tailwind for your design system?&lt;/h2&gt;&lt;p&gt;Like anything in web development, the answer is “it depends.” The decision to use Tailwind should be based on your project’s specific needs and your team’s preferences and capabilities.&lt;/p&gt;&lt;h3&gt;Reasons not to choose Tailwind&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Lower type safety is a deal-breaker ❌&lt;/li&gt;&lt;li&gt;Long strings of classes make you feel physically ill 🤮&lt;/li&gt;&lt;li&gt;You’re aiming for a design system that enforces strict uniformity 📏&lt;/li&gt;&lt;li&gt;The black magic that Tailwind Merge must be doing to know what every class does terrifies you 🧙&lt;/li&gt;&lt;li&gt;You prefer using props over juggling classes 🎛&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Reasons to choose Tailwind&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Great documentation and a vast amount of online resources 📚&lt;/li&gt;&lt;li&gt;Fastest developer experience for building custom UI ⚡&lt;/li&gt;&lt;li&gt;Variants make it easy to reuse tokens 🔄&lt;/li&gt;&lt;li&gt;Arbitrary variants provide an escape hatch for one-off styles that don’t leak outside of your components 🛡&lt;/li&gt;&lt;li&gt;No runtime cost 💸 (unless you’re using Tailwind Merge)&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Additional resources&lt;/h2&gt;&lt;p&gt;When we started building this design system, there weren’t many projects using Tailwind for this kind of work that we could reference. That’s not the case anymore. If you’re considering building a design system using Tailwind, I’d highly recommend checking out the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://tailwindui.com/templates/catalyst&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tailwind Catalyst&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nextui.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Next UI&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.tailwind-variants.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tailwind Variants&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ui.shadcn.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;shadcn/ui&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://react-spectrum.adobe.com/react-aria&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React Aria Components Tailwind Starter Kit&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/WestpacGEL/GEL-next&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Westpac GEL&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Overall, using Tailwind on this project was a great experience, and I think it’s a solid choice for almost any application. It’s also proving to be a dominant force in the CSS framework space, and I’m confident it will remain that way for some time to come. With version 4 just around the corner, it’s only going to get better!&lt;/p&gt;&lt;p&gt;That said, alternatives like Emotion remain strong contenders. There are also a ton of great new libraries popping up all the time. Some promising options worth exploring include &lt;a href=&quot;https://stylexjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;StyleX&lt;/a&gt;, &lt;a href=&quot;https://panda-css.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Panda CSS&lt;/a&gt;, &lt;a href=&quot;https://github.com/tokenami/tokenami&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tokenami&lt;/a&gt;, &lt;a href=&quot;https://css-hooks.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CSS-Hooks&lt;/a&gt;, and &lt;a href=&quot;https://github.com/mui/pigment-css&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pigment-css&lt;/a&gt;. Each of these libraries brings its own unique features and philosophies to the table, potentially addressing specific pain points or aligning better with your team’s preferences.&lt;/p&gt;&lt;p&gt;If you’re considering using Tailwind for your design system, I hope this article has given you some valuable insights and tips to help you make the most of it.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Storybook and Mock APIs: A Powerful Prototyping Combo</title><link>https://www.thinkmill.com.au/blog/storybook-and-mock-apis-a-powerful-prototyping-combo</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/storybook-and-mock-apis-a-powerful-prototyping-combo</guid><description>No back-end? No worries! In this tutorial we’ll be using Storybook and a Mock API to create a mocked prototype so we can get on with keeping our stakeholders excited.</description><pubDate>Thu, 02 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;You’re a Front-end Engineer tasked with implementing the UI for a killer new app feature. The design is solid, the data schema is ready to go, and everyone is excited to see the feature in action. There’s just one thing preventing you from fulfilling stakeholder dreams… the time it takes to develop the back-end of the API!&lt;/p&gt;&lt;p&gt;Sound familiar? There‘s a simple and effective way of getting around the API not being in place before UI development is complete… prototyping.&lt;/p&gt;&lt;p&gt;If you’re not familiar with the concept of prototyping in software development, it’s the process of creating an early model of a product or feature to test its design and functionality. Prototyping enables exploration and experimentation, highlights potential issues early, and is an incredible medium for communicating and presenting ideas to stakeholders.&lt;/p&gt;&lt;p&gt;In this tutorial we’ll be using &lt;a href=&quot;https://storybook.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Storybook&lt;/a&gt; and &lt;a href=&quot;https://mswjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mock Service Worker&lt;/a&gt; to create a prototype list of news articles for a &lt;a href=&quot;https://react.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React&lt;/a&gt; application that interfaces with a GraphQL API.&lt;/p&gt;&lt;p&gt;Storybook is a powerful tool for creating and testing UI components in isolation. It will help us focus on the building and iterating of individual components without having to worry about the larger context of the app.&lt;/p&gt;&lt;p&gt;Mock Service Worker is a library for mocking out a GraphQL API and testing app GraphQL queries and mutations in isolation. It is a powerful tool for enhancing prototypes, and will be especially useful in a scenario like this where we don’t have access to a deployed API.&lt;/p&gt;&lt;h2&gt;Getting setup&lt;/h2&gt;&lt;p&gt;Storybook needs to be installed in a project that is already setup with a framework, so before we dive into building our prototype we’ll want to spin up a new React project. We’ll be working from this &lt;a href=&quot;https://github.com/mantinedev/vite-min-template&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mantine Vite (Minimal) Template&lt;/a&gt; throughout this tutorial. So, grab a copy of the template and follow the steps below.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Want to dive straight into the complete code? &lt;a href=&quot;https://github.com/benderham/storybook-apollo-graphql-msw&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Here’s some I prepared earlier!&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;Installing Storybook&lt;/h3&gt;&lt;p&gt;First of all we’ll need to add Storybook to our project. Storybook provides a CLI allowing us to get setup with a single line command. From your project’s root directory run the following command in your terminal:&lt;/p&gt;&lt;pre&gt;yarn dlx storybook@latest init
&lt;/pre&gt;&lt;p&gt;If all goes well, you should see a setup wizard that provides you with a short tour of Storybook’s main concepts and features.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/hzlxeu3j0cnf/storybook-and-mock-apis-a-powerful-prototyping-combo-storybook-onboarding?width=848&quot; alt=&quot;The setup wizard will help you get started by introducing you to the main concepts and features of Storybook.&quot; width=&quot;848&quot; height=&quot;583&quot; /&gt;&lt;figcaption&gt;The setup wizard will help you get started by introducing you to the main concepts and features of Storybook.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;You’ll notice a couple of new folders in your project, &lt;code&gt;.storybook&lt;/code&gt; and &lt;code&gt;src/stories&lt;/code&gt;. &lt;code&gt;.storybook&lt;/code&gt; is where Storybook’s configuration files are saved. &lt;code&gt;src/stories&lt;/code&gt; contains some example component &lt;a href=&quot;https://storybook.js.org/docs/get-started/whats-a-story&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;stories&lt;/a&gt;, which you can leave for reference when creating stories of your own or if you wish, delete this folder entirely.&lt;/p&gt;&lt;h3&gt;Setup Mantine in Storybook&lt;/h3&gt;&lt;p&gt;As we’re using the &lt;a href=&quot;https://mantine.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mantine UI Component Library&lt;/a&gt; in our project, we’ll also need to &lt;a href=&quot;https://mantine.dev/guides/storybook/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;setup Mantine in Storybook&lt;/a&gt; before creating our first story.&lt;/p&gt;&lt;p&gt;Install the Storybook add-on:&lt;/p&gt;&lt;pre&gt;yarn add --dev storybook-dark-mode
&lt;/pre&gt;&lt;p&gt;Add &lt;code&gt;storybook-dark-mode&lt;/code&gt; add-on to &lt;code&gt;.storybook/main.ts&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;import type { StorybookConfig } from &apos;@storybook/react-vite&apos;;

import { join, dirname } from &apos;path&apos;;

/**
 * This function is used to resolve the absolute path of a package.
 * It is needed in projects that use Yarn PnP or are set up within a monorepo.
 */
function getAbsolutePath(value: string): any {
  return dirname(require.resolve(join(value, &apos;package.json&apos;)));
}

const config: StorybookConfig = {
  // ...config
  addons: [
    // ...addons
    getAbsolutePath(&apos;storybook-dark-mode&apos;),
  ],
};

export default config;
&lt;/pre&gt;&lt;p&gt;Save the &lt;code&gt;.storybook/preview.ts&lt;/code&gt; as &lt;code&gt;.storybook/preview.tsx&lt;/code&gt; and replace the content with the following:&lt;/p&gt;&lt;pre&gt;// Import styles of packages that you‘ve installed.
// All packages except `@mantine/hooks` require styles imports.
import &apos;@mantine/core/styles.css&apos;;

import React, { useEffect } from &apos;react&apos;;
import { addons } from &apos;@storybook/preview-api&apos;;
import { DARK_MODE_EVENT_NAME } from &apos;storybook-dark-mode&apos;;
import { MantineProvider, useMantineColorScheme } from &apos;@mantine/core&apos;;

const channel = addons.getChannel();

function ColorSchemeWrapper({ children }: { children: React.ReactNode }) {
  const { setColorScheme } = useMantineColorScheme();
  const handleColorScheme = (value: boolean) =&amp;gt;
    setColorScheme(value ? &apos;dark&apos; : &apos;light&apos;);

  useEffect(() =&amp;gt; {
    channel.on(DARK_MODE_EVENT_NAME, handleColorScheme);
    return () =&amp;gt; channel.off(DARK_MODE_EVENT_NAME, handleColorScheme);
  }, [channel]);

  return &amp;lt;&amp;gt;{children}&amp;lt;/&amp;gt;;
}

export const decorators = [
  (renderStory: any) =&amp;gt; (
    &amp;lt;ColorSchemeWrapper&amp;gt;{renderStory()}&amp;lt;/ColorSchemeWrapper&amp;gt;
  ),
  (renderStory: any) =&amp;gt; &amp;lt;MantineProvider&amp;gt;{renderStory()}&amp;lt;/MantineProvider&amp;gt;,
];
&lt;/pre&gt;&lt;h3&gt;Mock data&lt;/h3&gt;&lt;p&gt;As we don’t have an API to fetch a list of news articles from as yet, we’ll create a “hard-coded” set of mock articles to represent the data we expect to receive from the API. Create a new file called &lt;code&gt;src/mocks/articles.ts&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;export const MOCK_ARTICLE_LIST_DATA = [
  {
    id: 1,
    date: &apos;1st January 2024&apos;,
    slug: &apos;article-one&apos;,
    title: &quot;Golf’s Worst Kept Secrets: Why We Really Wear Funny Pants&quot;,
    imageUrl:
      &apos;https://images.unsplash.com/photo-1519682271141-57c25ad60410?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=540&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHwxMjAxNTQ0NXx8fHx8fHwxNzE0NjI4ODEy&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=720&apos;,
  },
  {
    id: 2,
    date: &apos;10th February 2024&apos;,
    slug: &apos;article-two&apos;,
    title: &apos;Golf Cart Grand Prix: When Tee Time Turns into a Race&apos;,
    imageUrl:
      &apos;https://images.unsplash.com/photo-1602991174407-a015b35a7b00?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=540&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHwxMjAxNTQ0NXx8fHx8fHwxNzE0NjI4ODUx&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=720&apos;,
  },
  {
    id: 3,
    date: &apos;12th March 2024&apos;,
    slug: &apos;article-three&apos;,
    title:
      &quot;Swing Like a Pro: How to Look Good Even When You’re Missing Every Shot&quot;,
    imageUrl:
      &apos;https://images.unsplash.com/photo-1684144064253-bb3b4c8fc700?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=540&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHwxMjAxNTQ0NXx8fHx8fHwxNzE0NjI4ODEy&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=720&apos;,
  },
  // ...articles
]
&lt;/pre&gt;&lt;h2&gt;Creating our component stories&lt;/h2&gt;&lt;p&gt;Now that we have Storybook up and running in our React project along with some mock data, we can get into prototyping our news article components. We’re going to need a couple of UI components for our list of news articles: An &lt;code&gt;ArticleCard&lt;/code&gt; to display a news articles overview, and an &lt;code&gt;ArticleList&lt;/code&gt; to render our news articles in a grid.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;src/components/ArticleCard.tsx&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;import { AspectRatio, Card, Image, Text } from &apos;@mantine/core&apos;;

type Props = {
  slug: string;
  date: string;
  title: string;
  imageUrl: string;
};

export const ArticleCard = ({ slug, date, title, imageUrl }: Props) =&amp;gt; {
  return (
    &amp;lt;Card p=&quot;md&quot; radius=&quot;md&quot; component=&quot;a&quot; href={`#/articles/${slug}`}&amp;gt;
      &amp;lt;AspectRatio ratio={720 / 540}&amp;gt;
        &amp;lt;Image src={imageUrl} /&amp;gt;
      &amp;lt;/AspectRatio&amp;gt;
      &amp;lt;Text c=&quot;dimmed&quot; size=&quot;xs&quot; tt=&quot;uppercase&quot; fw={700} mt=&quot;md&quot;&amp;gt;
        {date}
      &amp;lt;/Text&amp;gt;
      &amp;lt;Text fw=&quot;bold&quot; mt={5}&amp;gt;
        {title}
      &amp;lt;/Text&amp;gt;
    &amp;lt;/Card&amp;gt;
  );
};
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;src/components/ArticleList.tsx&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;import { Container, SimpleGrid } from &apos;@mantine/core&apos;;

import { ArticleCard } from &apos;./ArticleCard&apos;;

type Props = {
  articles: {
    id: number;
    slug: string;
    date: string;
    title: string;
    imageUrl: string;
  }[];
};

export const ArticleList = ({ articles }: Props) =&amp;gt; {
  return (
    &amp;lt;Container py=&quot;xl&quot;&amp;gt;
      &amp;lt;SimpleGrid cols={{ base: 1, sm: 2 }}&amp;gt;
        {articles.map((article) =&amp;gt; (
          &amp;lt;ArticleCard key={article.id} {...article} /&amp;gt;
        ))}
      &amp;lt;/SimpleGrid&amp;gt;
    &amp;lt;/Container&amp;gt;
  );
};
&lt;/pre&gt;&lt;p&gt;Alongside our &lt;code&gt;ArticleCard&lt;/code&gt; and &lt;code&gt;ArticleList&lt;/code&gt; components we now create a “story” file for each one like so:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;src/components/ArticleCard.stories.tsx&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;import type { Meta, StoryObj } from &apos;@storybook/react&apos;;

import { ArticleCard } from &apos;./ArticleCard&apos;;

const meta: Meta&amp;lt;typeof ArticleCard&amp;gt; = {
  component: ArticleCard,
};

export default meta;
type Story = StoryObj&amp;lt;typeof ArticleCard&amp;gt;;

export const Default: Story = {
  args: {
    date: &apos;1st January 2024&apos;,
    slug: &apos;article-one&apos;,
    title: &quot;Golf’s Worst Kept Secrets: Why We Really Wear Funny Pants&quot;,
    imageUrl:
      &apos;https://images.unsplash.com/photo-1519682271141-57c25ad60410?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=540&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHwxMjAxNTQ0NXx8fHx8fHwxNzE0NjI4ODEy&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=720&apos;,
  },
  render: (args) =&amp;gt; &amp;lt;ArticleCard {...args} /&amp;gt;,
};
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;src/components/ArticleList.stories.tsx&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;import type { Meta, StoryObj } from &apos;@storybook/react&apos;;

import { ArticleList } from &apos;./ArticleList&apos;;
import { MOCK_ARTICLE_LIST_DATA } from &apos;../mocks/articles&apos;;

const meta: Meta&amp;lt;typeof ArticleList&amp;gt; = {
  component: ArticleList,
};

export default meta;
type Story = StoryObj&amp;lt;typeof ArticleList&amp;gt;;

export const Default: Story = {
  args: {
    articles: MOCK_ARTICLE_LIST_DATA,
  },
  render: (args) =&amp;gt; &amp;lt;ArticleList {...args} /&amp;gt;,
};
&lt;/pre&gt;&lt;p&gt;Now when we start up our Storybook using the &lt;code&gt;yarn storybook&lt;/code&gt; command, we’ll be able to view and interact with our components.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/i2anzs1hp4i7/storybook-and-mock-apis-a-powerful-prototyping-combo-articlelist-storybook?width=848&quot; alt=&quot;ArticleList Storybook&quot; width=&quot;848&quot; height=&quot;479&quot; /&gt;&lt;figcaption&gt;ArticleList Storybook&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Mocking the GraphQL API&lt;/h2&gt;&lt;p&gt;Having our components available in Storybook provides an excellent way of testing, reviewing and iterating on them without having to integrate them into the application itself. We are however limited in our analysis of the components by the fact that they’re currently displaying hard-coded mock data. There’s no sense of what might happen if this data took a while to load, if the API was unable to return any data, or if the API returned an error. Let’s take a look at alleviating these shortfalls by setting up a Mock GraphQL API using Mock Service Worker and Apollo.&lt;/p&gt;&lt;h3&gt;Installing Apollo Client&lt;/h3&gt;&lt;p&gt;When interacting with a GraphQL API it can be beneficial to leverage the power of a state management library such as Apollo Client to help simplify the handling of remote and local data.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Install Apollo Client Dependencies&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;yarn add @apollo/client graphql
&lt;/pre&gt;&lt;p&gt;This will install the two dependencies required to use Apollo Client in our application: Apollo Client, which includes everything required to run itself; and GraphQL, which provides the logic required to parse GraphQL Queries and Mutations.&lt;/p&gt;&lt;h3&gt;Add a Mock Apollo Client Provider&lt;/h3&gt;&lt;p&gt;We’ll also need to setup an &lt;code&gt;ApolloClient&lt;/code&gt; instance that we can use to interact with the Mock API we’re going to be creating shortly.&lt;/p&gt;&lt;p&gt;Inside our “mocks” folder, create a new file called &lt;code&gt;src/mocks/MockApolloProvider.tsx&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;import { ApolloClient, InMemoryCache, ApolloProvider } from &apos;@apollo/client&apos;;

type MockApolloProviderProps = {
  children: React.ReactElement;
};

const client = new ApolloClient({
  uri: &apos;//fake.gql.server&apos;,
  cache: new InMemoryCache(),
  defaultOptions: {
    watchQuery: {
      fetchPolicy: &apos;no-cache&apos;,
      errorPolicy: &apos;all&apos;,
    },
    query: {
      fetchPolicy: &apos;no-cache&apos;,
      errorPolicy: &apos;all&apos;,
    },
  },
});

export const MockApolloProvider = ({ children }: MockApolloProviderProps) =&amp;gt; {
  return &amp;lt;ApolloProvider client={client}&amp;gt;{children}&amp;lt;/ApolloProvider&amp;gt;;
};
&lt;/pre&gt;&lt;h3&gt;Install Mock Service Worker&lt;/h3&gt;&lt;p&gt;Let’s go ahead and install Mock Service Worker into our project’s development dependencies:&lt;/p&gt;&lt;pre&gt;yarn add --dev msw
&lt;/pre&gt;&lt;p&gt;Mock Service Worker is designed to intercept any client-side requests our application makes, and mock out a response. To enable this behaviour in the browser, we’ll need to create and register a service worker in our application. The great thing about the Mock Service Worker library is that we don’t actually have to write any code to create this worker ourselves, the library provides a CLI that generates a worker file into our application’s &lt;code&gt;public&lt;/code&gt; directory, and registers it in our &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;yarn dlx msw init public/ --save
&lt;/pre&gt;&lt;h3&gt;Mocking an API Request&lt;/h3&gt;&lt;p&gt;Let’s say in our final application we’re expecting that a list of news articles will be populated by executing a GraphQL Query named &lt;code&gt;GetNewsArticles&lt;/code&gt;. To enable the simulation of this query request and possible responses in Storybook, we’ll need to write a mock request handler, and resolver for the &lt;code&gt;GetNewsArticles&lt;/code&gt; query.&lt;/p&gt;&lt;p&gt;Go ahead and create a new file &lt;code&gt;src/mocks/handlers.ts&lt;/code&gt; with the following code:&lt;/p&gt;&lt;pre&gt;import { graphql, HttpResponse } from &apos;msw&apos;
import { MOCK_ARTICLE_LIST_DATA } from &apos;./articles&apos;;

export const handlers = [
  // Intercepts &quot;GetNewsArticles&quot; graphql query.
  graphql.query(&apos;GetNewsArticles&apos;, () =&amp;gt; {
    return HttpResponse.json({
      data: {
        // Return MOCK_ARTICLE_LIST_DATA as the &quot;articles&quot; root-level property.
        articles: MOCK_ARTICLE_LIST_DATA,
      },
    });
  }),
];
&lt;/pre&gt;&lt;h3&gt;Add Mock Service Worker &amp;amp; Apollo Client to Storybook&lt;/h3&gt;&lt;p&gt;With our mocked request and response in place, we now need to configure Mock Service Worker to intercept any requests for &lt;code&gt;GetNewsArticles&lt;/code&gt; made in Storybook and resolve them using our mocked handler. Storybook has a convenient add-on for Mock Service Worker that handles the configuration and initialisation of the service worker for us.&lt;/p&gt;&lt;p&gt;Install the &lt;code&gt;storybook-msw-addon&lt;/code&gt; using the following command:&lt;/p&gt;&lt;pre&gt;yarn add --dev storybook-msw-addon
&lt;/pre&gt;&lt;p&gt;And register &lt;code&gt;storybook-msw-addon&lt;/code&gt; to &lt;code&gt;./storybook/main.ts&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;const config: StorybookConfig = {
  // ...config
  addons: [
    // ...addons
    getAbsolutePath(&apos;storybook-msw-addon&apos;),
  ],
};

export default config;
&lt;/pre&gt;&lt;p&gt;Then update our &lt;code&gt;./storybook/preview.tsx&lt;/code&gt; to initiate the Mock Service Worker and include the &lt;code&gt;mswLoader&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;// ...imports
import { initialize, mswLoader} from &apos;storybook-msw-addon&apos;;

// initialise MSW addon.
initialize({
  onUnhandledRequest: &apos;bypass&apos;,
});


// provide the MSWloader globally.
export const loaders = [
  //...loaders
  mswLoader
];
&lt;/pre&gt;&lt;p&gt;After running &lt;code&gt;yarn storybook&lt;/code&gt; and viewing the browser’s console you should see the message: &lt;code&gt;[MSW] Mocking enabled&lt;/code&gt;. This indicates that the service worker is in place and listening for any requests to intercept.&lt;/p&gt;&lt;p&gt;Finally we must now wrap our stories with the &lt;code&gt;MockApolloProvider&lt;/code&gt;, enabling our components to interact with the mock API. Add the following code to &lt;code&gt;./storybook/preview.tsx&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;// ...imports
import { MockApolloProvider } from &apos;../src/mocks/MockApolloProvider&apos;;

export const decorators = [
  // ...decorators
  (renderStory: any) =&amp;gt; &amp;lt;MockApolloProvider&amp;gt;{renderStory()}&amp;lt;/MockApolloProvider&amp;gt;
];
&lt;/pre&gt;&lt;h2&gt;Using the Mock GraphQL API&lt;/h2&gt;&lt;p&gt;With the setup behind us, all that’s left for us to do is configure the &lt;code&gt;ArticleList&lt;/code&gt; component to make an API request to fetch the articles.&lt;/p&gt;&lt;p&gt;In GraphQL we describe our expected response in a query declaration. Let’s add a declaration to our &lt;code&gt;src/components/ArticleList.tsx&lt;/code&gt; for the &lt;code&gt;GetNewsArticles&lt;/code&gt; query:&lt;/p&gt;&lt;pre&gt;import { gql } from &apos;@apollo/client&apos;

const GET_NEWS_ARTICLES = gql`
  query GetNewsArticles {
    articles {
      id
      date
      slug
      title
      imageUrl
    }
  }`;
&lt;/pre&gt;&lt;p&gt;Then we can use Apollo’s &lt;code&gt;useQuery&lt;/code&gt; hook to execute the API request in &lt;code&gt;src/components/ArticleList.tsx&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;// ...imports
import { gql, useQuery } from &apos;@apollo/client&apos;
import { Container, SimpleGrid } from &apos;@mantine/core&apos;;

import { ArticleCard } from &apos;./ArticleCard&apos;;

// ...GET_NEWS_ARTICLES

type Article = {
  id: number;
  slug: string;
  date: string;
  title: string;
  imageUrl: string;
};

export const ArticleList = () =&amp;gt; {
  const { data, loading, error } = useQuery(GET_NEWS_ARTICLES);
  if (loading) return &apos;Loading...&apos;;
  if (error) return `Error! ${error.message}`;

  return (
    &amp;lt;Container py=&quot;xl&quot;&amp;gt;
      &amp;lt;SimpleGrid cols={{ base: 1, sm: 2 }}&amp;gt;
        {data.articles.map((article: Article) =&amp;gt; (
          &amp;lt;ArticleCard key={article.id} {...article} /&amp;gt;
        ))}
      &amp;lt;/SimpleGrid&amp;gt;
    &amp;lt;/Container&amp;gt;
  );
};
&lt;/pre&gt;&lt;p&gt;In the above example code, the &lt;code&gt;useQuery&lt;/code&gt; hook is providing us with the returned data object, a loading boolean, and an error object. We’re then able to use these properties to conditionally render either a loading indicator, error notice, or the list of news articles.&lt;/p&gt;&lt;p&gt;We’ll also want to update our &lt;code&gt;src/components/ArticleList.stories.tsx&lt;/code&gt; to reflect the above changes:&lt;/p&gt;&lt;pre&gt;import type { Meta, StoryObj } from &apos;@storybook/react&apos;;

import { handlers } from &apos;../mocks/handlers&apos;;
import { ArticleList } from &apos;./ArticleList&apos;;

const meta: Meta&amp;lt;typeof ArticleList&amp;gt; = {
  component: ArticleList,
};

export default meta;
type Story = StoryObj&amp;lt;typeof ArticleList&amp;gt;;

export const Default: Story = {
  render: () =&amp;gt; &amp;lt;ArticleList /&amp;gt;,
};

Default.parameters = {
  msw: {
    handlers
  },
};
&lt;/pre&gt;&lt;p&gt;Note that we are now referencing the Mock Service Worker handlers. Fire up your Storybook and we should see something like this:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/storybook-and-mock-apis-a-powerful-prototyping-combo/articlelist-story.mov&quot;&gt;ArticleList component loading mock data in Storybook&lt;/a&gt;&lt;/p&gt;&lt;p&gt;And there we have it, a complete news article list that mimics the behaviour of a production ready component with no need for a deployed API to be in place. Of course we’ve only scratched the surface here of what’s possible when mocking out an API using Mock Service Worker. Why not go ahead and look at extending this example with &lt;a href=&quot;https://mswjs.io/docs/network-behavior/graphql&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;more advanced GraphQL operations&lt;/a&gt;?&lt;/p&gt;&lt;p&gt;If you’re looking to get more from your prototyping efforts, &lt;a href=&quot;https://www.thinkmill.com.au/services/prototyping&quot;&gt;we can help&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Building a Multi Brand Design System with Tailwind: Tips, Tricks and Tradeoffs</title><link>https://www.thinkmill.com.au/blog/talk-building-a-multi-brand-design-system-with-tailwind-tips-tricks-and-tradeoffs</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/talk-building-a-multi-brand-design-system-with-tailwind-tips-tricks-and-tradeoffs</guid><description>Lessons learned from using Tailwind CSS in a multi-brand design system project.</description><pubDate>Wed, 01 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This talk also comes in an &lt;a href=&quot;https://www.thinkmill.com.au/blog/building-a-multi-brand-design-system-with-tailwind-tips-tricks-and-tradeoffs&quot;&gt;article format&lt;/a&gt; where you’ll find links to resources and code examples you can try for yourself.&lt;/p&gt;&lt;/blockquote&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/m46rsyq6nfv7/luke-tailwind-talk?width=1200" length="538156" type="image/png"/></item><item><title>Unleashing Designers with Tailwind CSS</title><link>https://www.thinkmill.com.au/blog/unleashing-designers-with-tailwind-css</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/unleashing-designers-with-tailwind-css</guid><description>How TailwindCSS can help bridge the gap between design and development, and foster a more inclusive and efficient working environment.</description><pubDate>Fri, 12 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;In April 2024, I flew to Park City, Utah, to give a talk at &lt;a href=&quot;https://epicweb.dev/conf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Epic Web Conf&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I, &lt;em&gt;surprise surprise&lt;/em&gt;, did a talk on… Tailwind CSS.&lt;/p&gt;&lt;p&gt;This time around, I went for something a little different. I did not attempt to convince the audience that Tailwind is a good idea, and everyone should use it.&lt;/p&gt;&lt;p&gt;Instead, I explained how I personally think that Tailwind CSS is &lt;strong&gt;a design tool&lt;/strong&gt;, and the ultimate &quot;prototyping in the browser&quot; solution — making it a great collaboration tool between designers and developers within a team.&lt;/p&gt;&lt;p&gt;You can &lt;a href=&quot;https://www.epicweb.dev/talks/unleashing-designers-with-tailwind-css&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;watch the talk&lt;/a&gt; on the Epic Web platform.&lt;/p&gt;&lt;p&gt;Happy watching!&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rxjvt06m5ybb/cleanshot-2024-05-13-at-14-31-43-2x?width=1200" length="122503" type="image/png"/></item><item><title>Design System kickoff checklist</title><link>https://www.thinkmill.com.au/blog/design-system-kickoff-checklist</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/design-system-kickoff-checklist</guid><description>So, you want to build a design system? Here’s a list of questions to consider before you get started to avoid running into issues in the future.</description><pubDate>Fri, 12 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;We kicked off a new design system project this week. It provided a valuable opportunity to distil our thoughts and experiences around best practices for establishing a new project. We made ourselves a checklist of design system questions and tooling considerations which you may find helpful.&lt;/p&gt;&lt;h2&gt;General questions&lt;/h2&gt;&lt;p&gt;Below are some common questions to ask your project owner during kickoff. While some may seem obvious, it’s important to include them since the initial scramble can be chaotic and it’s easy to overlook things.&lt;/p&gt;&lt;h3&gt;What are the stakeholder expectations?&lt;/h3&gt;&lt;p&gt;As we’ve been &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;making design systems&lt;/a&gt; a long time, it’s easy to presume what is expected of a design system, however the desired outcomes are likely nuanced. To avoid costly assumptions it’s good to scrutinise delivery expectations to distil the project’s scope. Discuss and document, at regular intervals, so that all relevant parties are aligned from the outset.&lt;/p&gt;&lt;h3&gt;Who will use the design system?&lt;/h3&gt;&lt;p&gt;While this might seem like a silly question, it’s vital that we understand who we’re building for. Consider the various disciplines that will use the design system e.g. designers, developers, content editors, product owners etc. and acknowledge that their expectations and competency will differ. Assuming too much about consumers risks alienating those who aren’t as familiar with design systems.&lt;/p&gt;&lt;h3&gt;Where should we predominantly focus our time?&lt;/h3&gt;&lt;p&gt;It’s often the case that more than one product will benefit from the design system. However, there’s typically one project that will be the “test-flight”. Make sure that the initial effort focuses almost exclusively on the requirements of that project. There’s no point spending time on e.g. a &lt;code&gt;DatePicker&lt;/code&gt; when it won’t be necessary for 6 months.&lt;/p&gt;&lt;h3&gt;Is there a requirement for language support i.e &lt;a href=&quot;https://www.w3.org/International/i18n-drafts/nav/about#what&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;i18n&lt;/a&gt;?&lt;/h3&gt;&lt;p&gt;Introducing support for localisation into a design system that’s in-flight is far more difficult than preparing for it from the start.&lt;/p&gt;&lt;h3&gt;Is there a requirement for theme support? e.g. light/dark mode, multi-brand, etc.&lt;/h3&gt;&lt;p&gt;If there’s even a hint that theme support will be required in the future, build support for it from the start. Trying to introduce theming later in the project is difficult.&lt;/p&gt;&lt;h3&gt;What level of &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;accessibility&lt;/a&gt; conformance is acceptable?&lt;/h3&gt;&lt;p&gt;While we should strive for an inclusive web, the project’s needs and budget must be considered. Make sure stakeholders understand what is expected depending on their user requirements, and what is involved to meet those requirements.&lt;/p&gt;&lt;h3&gt;Is there an existing system that &lt;em&gt;must&lt;/em&gt; be extended?&lt;/h3&gt;&lt;p&gt;Starting fresh will almost always be quicker and easier than updating existing systems. Somewhat counterintuitive, and difficult to sell to the project owner; modifying components etc., that are already in production adds a great deal of overhead to the build.&lt;/p&gt;&lt;h3&gt;Is there an expectation that the design system will include a dedicated documentation website?&lt;/h3&gt;&lt;p&gt;Building a documentation website is deceptively non-trivial. Where possible, avoid this investment until it’s warranted, &lt;a href=&quot;https://storybook.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Storybook&lt;/a&gt; is typically enough to begin building and sharing progress.&lt;/p&gt;&lt;h3&gt;How much time should be invested building &lt;em&gt;the thing&lt;/em&gt; VS defining a roadmap for next steps?&lt;/h3&gt;&lt;p&gt;Ideally, depending on the length of the engagement, the implementation does most of the heavy lifting, laying foundational tooling and outlining best practices. Leaving breadcrumbs for other designers and developers to follow is more efficient than writing documents, but there’s definitely a balance to be struck.&lt;/p&gt;&lt;h3&gt;Who, at Client Company, will be responsible for the design system?&lt;/h3&gt;&lt;p&gt;This person should be a champion for the design system and promote adoption. Ideally they are competent in design and development, and capable of connecting the system with the teams and products that consume it. More people ≠ better outcomes: One person is capable of steering a ship, more than one increases the likelihood of indirection. We still want ambassadors to advocate for the design system, but a single person should be responsible for its direction.&lt;/p&gt;&lt;h2&gt;Design system assets&lt;/h2&gt;&lt;p&gt;What will be used for the assets below and where are the resources stored?&lt;/p&gt;&lt;h3&gt;Iconography&lt;/h3&gt;&lt;p&gt;Typically this will be a free and open-source library of SVG files. Though it could be a custom, in-house solution. Finding this out early will inform how you move forward with build steps etc., to keep design and development in sync.&lt;/p&gt;&lt;h3&gt;Typography&lt;/h3&gt;&lt;p&gt;Projects often require the use of one or more brand fonts. Make sure you see the license agreement: project owners sometimes assume that they have bought the font outright but in a lot of cases they only have a “desktop” license, not a web license. It’s our job to limit potential exposure for them.&lt;/p&gt;&lt;h3&gt;Illustrations&lt;/h3&gt;&lt;p&gt;These are often in-house designs, that compose SVG elements from open licensed libraries. Finding this out early will inform how you move forward with build steps etc., to keep design and development in sync.&lt;/p&gt;&lt;h3&gt;Imagery&lt;/h3&gt;&lt;p&gt;Ideally this can be avoided, better to inline SVGs where possible. When necessary, it’s important to make a plan for how images will be stored and served to the user.&lt;/p&gt;&lt;h3&gt;Glossary&lt;/h3&gt;&lt;p&gt;This one might seem out of place but it’s important to find or define as soon as possible. Every company uses their own jargon for miscellaneous terminology; when all parties are on the same page things move smoothly.&lt;/p&gt;&lt;h2&gt;Design system tooling&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The design system tooling space moves fast. This list is representative at time of writing. Your mileage may vary down the track.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;Baseline*&lt;/h3&gt;&lt;p&gt;Project requirements and constraints will vary wildly but there’s a handful of tools that can &lt;em&gt;hopefully*&lt;/em&gt; be assumed as a certainty.&lt;/p&gt;&lt;p&gt;Confirm the following with the project owner, and decide on versions where applicable:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://eslint.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ESLint&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.figma.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Figma&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nodejs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Node&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://prettier.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Prettier&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://react.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React&lt;/a&gt; / &lt;a href=&quot;https://legacy.reactjs.org/docs/react-dom.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React-DOM&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://storybook.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Storybook&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;TypeScript&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Project dependent&lt;/h3&gt;&lt;p&gt;While we all have preferences, many tools will be influenced by existing projects and the project owner’s familiarity + confidence.&lt;/p&gt;&lt;p&gt;The following list includes a handful of examples but is far from exhaustive. Choices here should be discussed with the owner to ensure that they fully understand the pros and cons of each decision with regard to their current and future requirements:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Styling&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://compiledcssinjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Compiled&lt;/a&gt;, developed by Atlassian, is a styling solution designed to streamline the creation of consistent and maintainable UI components. It offers a declarative syntax for defining styles, enabling developers to encapsulate design decisions and enhance code reusability. Compiled prioritises performance and scalability, making it suitable for building large-scale applications with complex styling requirements.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://emotion.sh/docs/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Emotion&lt;/a&gt; is popular in the React ecosystem, offering powerful features for styling components. It is a CSS-in-JS styling solution, allowing developers to write styles directly within their JavaScript code. Emotion emphasises flexibility, offering various utilities for managing styles, including theming, global styles, and dynamic styles based on component props.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://stylex-docusaurus.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;StyleX&lt;/a&gt; is a modern styling solution developed by Meta (formerly Facebook), optimised for building scalable and maintainable user interfaces. It introduces a new approach to styling components using JavaScript and CSS variables. With StyleX, developers can define styles programmatically and leverage composition to create reusable style components.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tailwind CSS&lt;/a&gt; is a utility-first CSS framework that provides a comprehensive set of pre-defined utility classes for building user interfaces. It emphasises simplicity and rapid development by enabling developers to apply styles directly to HTML elements. Tailwind’s utility-first approach facilitates quick prototyping and responsive design, offering a robust foundation for creating custom designs without the need for extensive CSS expertise.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://vanilla-extract.style/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vanilla Extract&lt;/a&gt; is a zero-runtime &quot;stylesheets in TypeScript&quot; solution that generates static CSS files at build time. It offers a type-safe approach to styling, enabling developers to write styles using familiar CSS syntax while leveraging the benefits of type checking. Vanilla Extract emphasizes performance and maintainability, producing optimized CSS output and enabling efficient caching and serving of stylesheets.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Framework&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nextjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Next.js&lt;/a&gt; is a mature web framework for building static and dynamic applications. Backed by Vercel.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://remix.run/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Remix&lt;/a&gt; is a full stack web framework that helps deliver a fast and resilient user experience. Backed by Shopify.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Package management&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://docs.npmjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;npm&lt;/a&gt; or Node Package Manager, is a widely used JavaScript package manager integrated with Node.js. It offers a vast repository of open-source packages for developers and simplifies dependency management and project updates through its command-line interface.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://pnpm.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pnpm&lt;/a&gt; stands out with its symlink-based approach to dependency management, reducing disk space usage and speeding up installation times. It shares dependencies across projects, promoting resource efficiency without sacrificing project isolation.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://yarnpkg.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Yarn&lt;/a&gt;, developed by Facebook, Google, and others, prioritises performance, security, and consistency. It features fast dependency management, deterministic builds, and offline package installation, optimising development workflows and enabling developers to focus on coding.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://design-tokens.github.io/community-group/format/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Design tokens&lt;/a&gt; — assuming tokens will be stored and managed in Figma, you may want a tool to translate token data from one format to another.&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://diez.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Diez&lt;/a&gt; (FOSS) is a design token framework focused on generating cross-platform design code using a single source of truth. It enables designers and developers to collaborate effectively by automatically synchronising design changes across multiple platforms, including iOS, Android, and web.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://amzn.github.io/style-dictionary/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Style Dictionary&lt;/a&gt; (FOSS), from Amazon, provides a flexible and customisable platform for managing design tokens and generating platform-specific output. It offers extensive configuration options and supports multiple platforms, making it suitable for large-scale design systems.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/salesforce-ux/theo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Theo&lt;/a&gt; (FOSS), from SalesForce, facilitates the conversion of design tokens into various formats, such as JSON, SCSS, and CSS. It streamlines the process of maintaining design consistency across different platforms and environments.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://specifyapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Specify&lt;/a&gt; (paid) is a design token engine designed to streamline the process of creating and managing design systems. It offers a user-friendly interface for defining and organising design tokens, allowing teams to efficiently generate platform-specific output for their projects.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.supernova.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Supernova&lt;/a&gt; (paid) is a design system tool focused on bridging the gap between design and development workflows. It offers features such as automated code generation, version control, and integration with popular design tools, enabling teams to efficiently translate design decisions into production-ready code.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://zeroheight.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ZeroHeight&lt;/a&gt; (paid) is a design system platform that centralises the documentation and distribution of design systems. It provides a collaborative space for designers and developers to create, organise, and share design tokens and components, ensuring consistency across projects.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Module bundler&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://parceljs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Parcel&lt;/a&gt; is a zero-config module bundler that simplifies the development process by automatically resolving dependencies and supporting various file formats out of the box. It offers fast and efficient builds with minimal configuration.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://rollupjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rollup&lt;/a&gt; is a module bundler known for its tree-shaking capabilities, which eliminate unused code to produce smaller bundles. It focuses on producing optimised output for libraries and packages, leveraging ES module syntax for efficient code splitting.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://vitejs.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vite&lt;/a&gt; is a modern build tool that leverages native ES module imports to deliver lightning-fast development experiences. It offers instant server start times and supports hot module replacement, making it ideal for building Vue.js and React applications.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://webpack.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Webpack&lt;/a&gt; is a powerful and highly configurable module bundler widely used in the JavaScript ecosystem. It offers extensive customisation options and supports a wide range of loaders and plugins for handling diverse project requirements, including code splitting, asset optimisation, and module bundling.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Component library&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://react-spectrum.adobe.com/react-aria/components.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React Aria Components&lt;/a&gt; is a robust and mature headless UI library from Adobe. Over 40 components with built-in behaviour, adaptive interactions, top-tier accessibility, and internationalisation out of the box.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.radix-ui.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Radix UI&lt;/a&gt; is an open source component library optimised for fast development, easy maintenance, and accessibility.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ariakit.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ariakit&lt;/a&gt; is an unstyled open source library with a focus on accessibility and composability.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Testing&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://jestjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jest&lt;/a&gt; is the obvious candidate for a test runner, due to its wide usage, support for a variety of testing strategies, and ease of setup.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://vitest.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vitest&lt;/a&gt; is modern Jest-compatible test runner built on top of Vite with out-of-the-box support for ESM, TypeScript, and JSX.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://testing-library.com/docs/react-testing-library/intro/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React Testing Library&lt;/a&gt; should be more than adequate for unit and integration tests. It encourages writing tests that focus on the behaviour of components from the user’s perspective, resulting in more robust and maintainable tests.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://playwright.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Playwright&lt;/a&gt; tests are powerful but can be resource-intensive. They provide end-to-end testing capabilities, making them ideal for ensuring the functionality and usability of user journeys. However, due to their cost in terms of execution time and resources, we suggest limiting their usage to the most critical user journeys.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/axe-core&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Axe&lt;/a&gt; tests can be included with end-to-end tests to ensure user journeys contain no violations. Unit tests should cover discrete a11y support; they lack important context to be valuable candidates for Axe testing.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;Got more questions about building your first design system? &lt;a href=&quot;https://www.thinkmill.com.au/contact&quot;&gt;We can help&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;/article&gt;</content:encoded></item><item><title>Thinkmill turns 10</title><link>https://www.thinkmill.com.au/blog/thinkmill-turns-10</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/thinkmill-turns-10</guid><description>In September 2013, our Co-Founders Jed Watson and Boris Bozic officially registered Thinkmill’s ABN. From there, we begun our journey as a software consultancy powered by a skilled team of design-engineers, open-source enthusiasts, and problem-solvers. Today, we’re still that, with sharper tools and the same goal of building great software. Thank you to our team, friends, and clients who’ve been a part of our first decade. We naturally had cookies and cake to celebrate. Here’s to the next!</description><pubDate>Thu, 07 Sep 2023 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/x2oqtvtrl8jn/tm-10th-birthday?width=1200" length="139878" type="image/jpeg"/></item><item><title>Designing at scale: an overview of Design Systems</title><link>https://www.thinkmill.com.au/blog/designing-at-scale-an-overview-of-design-systems</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/designing-at-scale-an-overview-of-design-systems</guid><description>Watch Design-engineer Nathan Simpson’s high-level overview of Design Systems, and how large organisations can achieve design at scale.</description><pubDate>Tue, 29 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;h2&gt;Transcript&lt;/h2&gt;&lt;p&gt;(00:22)&lt;/p&gt;&lt;p&gt;Awesome, thank you everyone for coming to my talk this evening. I’ll give a brief intro of myself first of all. My name’s Nathan, I’m a software developer at Thinkmill. I’m also an Orange local, I was born and raised here. I left in 2015 to study design at Western Sydney Uni and then I joined Thinkmill in 2018 as a junior designer and then moved into the development track from there. I’ve been working from home since March of 2020 and working from Orange since 2022 as well. We’re back to start a family and here’s that family: my wife Carrie, my four-month-old Sienna and my 15-month-old Winnie.&lt;/p&gt;&lt;p&gt;(01:07)&lt;/p&gt;&lt;p&gt;Thinkmill is a &lt;a href=&quot;https://www.thinkmill.com.au/services/product-design-engineering&quot;&gt;software development and design&lt;/a&gt; consultancy based in Sydney, we have 40 plus team members across Australia and New Zealand, founded in 2013. It’s actually our 10th anniversary next week. We’ve been recognised nationally for our &lt;a href=&quot;https://www.thinkmill.com.au/open-source&quot;&gt;open source&lt;/a&gt; contributions. We also run popular tech meetups like &lt;a href=&quot;https://react.sydney/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React Sydney&lt;/a&gt;, sponsor &lt;a href=&quot;https://sydjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SydJS&lt;/a&gt; as well. We’re a lot of speakers at local and international events, such as this one. Lovely to be here, thank you for having me.&lt;/p&gt;&lt;p&gt;(01:38)&lt;/p&gt;&lt;p&gt;I’m going to be speaking today about designing applications with scale. We’re going to talk about what I do for work, an overview of &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;design systems&lt;/a&gt;. So first, I want to solve the question: How do large organisations with multiple products — work done by several design and development teams — achieve consistency and a great user experience at scale? The answer is design systems.&lt;/p&gt;&lt;p&gt;A design system is a suite of design elements, guidelines, and standards that ensure visual and functional consistency in the creation of products or services. They’re used by product managers, designers, developers, writers, and managers to promote a unified and user-friendly experience across multiple touchpoints. At Thinkmill, we’ve worked on a bunch of design systems from &lt;a href=&quot;https://www.thinkmill.com.au/work/vocal&quot;&gt;startups&lt;/a&gt; to &lt;a href=&quot;https://www.thinkmill.com.au/work/atlassian&quot;&gt;Atlassian&lt;/a&gt;, to the Australian government. We’ve led, shaped, and contributed to over a dozen design systems in our 10 years.&lt;/p&gt;&lt;p&gt;(02:30)&lt;/p&gt;&lt;p&gt;I’m going to be explaining design systems today while discussing what I’ve been working for the last two years at the &lt;a href=&quot;https://www.thinkmill.com.au/work/daff&quot;&gt;Australian Department of Agriculture, Fisheries, and Forestry&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The export service is a digital transformation initiative designed to replace inefficient paper-based systems with digital apps to help Aussie producers sell to overseas markets.&lt;/p&gt;&lt;p&gt;Several teams were created to build various aspects of the export service, and it was imperative that measures were in place to ensure each piece of the experience was consistent and met accessibility targets.&lt;/p&gt;&lt;p&gt;These teams worked on various aspects of multiple different products designed for farmers and producers who are exporting goods overseas, whether it be food, grain, or animals. On-the-ground inspectors might visit the boats or the establishments to ensure that everything met standards and also to the internal staff who are processing the applications in the back end as well. To enable the development of all these products, we created &lt;a href=&quot;https://design-system.agriculture.gov.au/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AgDS or the Agriculture Design System&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;(03:38)&lt;/p&gt;&lt;p&gt;Our design system is comprised of a few elements: the design language, the principles, and the guidelines of what it looks like to build something for this organisation; a library of components, templates, and patterns; and a community of users and contributors to the design system. I’ll go through each of these points.&lt;/p&gt;&lt;p&gt;First one is the design language. You may have an idea about what I mean by design language. I’ll throw up this slide. This is a brand-new design language. You probably know who this is already. I don’t even have to point out the logo mark in the bottom right corner for you to know that that is definitely Optus. This is the visual feel of the organisation. Design language can also be seen in product design as well. I just want to throw this up there because I’m a massive car guy, but basically, a design language can contain elements that are consistent across multiple product lines, including industrial designs such as the double kidney grill in a BMW. And design language can be seen in software as well.&lt;/p&gt;&lt;p&gt;(04:36)&lt;/p&gt;&lt;p&gt;I’ll take a tangent off Agriculture for a moment. This is some work that we worked on with &lt;a href=&quot;https://www.thinkmill.com.au/work/reckon&quot;&gt;Reckon&lt;/a&gt;. Reckon, a financial software company, based in Sydney. They’ve been around for a long time. They’ve been around since the days where you’d procure your accounting software, and it would arrive to you on a disk, and you’d install that onto your computer, and that was the computer with your accounting software on it, and you had to keep that very, very safe. Since then, the cloud has come in, they have cloud solutions, but they brought in Thinkmill.&lt;/p&gt;&lt;p&gt;We came in to help them form the new generation of their product lines – a suite of apps built for the mobile businesses of today. This is the App Store screenshots for the invoicing app, Reckon Invoices. You create your invoices, send to customers, receive payments, etc. We also built a payroll app, Reckon Payroll. You can set up your employees, you can pay them, you can track their leave, it’s got all the government reporting built in. There’s also a web experience as well (web and mobile).&lt;/p&gt;&lt;p&gt;Now, if you forget about the vibrant colour backgrounds, these apps are built using the same components, the same toolkit – all the buttons and the icons, the badges, the typography –  they’re all from the same library. We also used the same library to build an employee experience. If an employer did a pay run, and paid your employees, the employee could open up their app, Reckon Mate, and they can see their payslips, they can do their expenses and see their leave entitlements, etc. We built the design language, a design system for Reckon, called &lt;a href=&quot;https://balance.reckon.com/introduction/welcome&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Balance&lt;/a&gt;, an accounting pun for you, to enable the building of the mobile applications as well as the web apps as well.&lt;/p&gt;&lt;p&gt;(06:40)&lt;/p&gt;&lt;p&gt;Now, the design language for the Export Service is very pared back. It’s simple and easy to understand. That’s by design. It’s built for a variety of experience levels with computers, as well as for a variety of usability needs. I’ll talk a bit more about that after.&lt;/p&gt;&lt;p&gt;(07:00)&lt;/p&gt;&lt;h3&gt;Part two – a design system includes a component library.&lt;/h3&gt;&lt;p&gt;The component library is kind of like the lego blocks to make up an application. You can assemble them in a design tool or in code. These are generic components for building things like forms and content pages. They are built in Figma, which is the tool of choice for UI designers, and also in &lt;a href=&quot;https://www.thinkmill.com.au/services/react&quot;&gt;React&lt;/a&gt;, which is a user interface library for code in JavaScript. It’s built by Facebook actually, which means it’s the best thing to come out of Facebook.&lt;/p&gt;&lt;p&gt;A couple of practical examples of what implementing the components looks like. The developer would import this into their file. This is a &lt;em&gt;button&lt;/em&gt; component. This button was designed with a few different variants. This is the primary button. It has an icon, it has a label. That’s probably the most simple version of a component we’ve got.&lt;/p&gt;&lt;p&gt;Here’s a slightly more complex version. Here’s a composition with a &lt;em&gt;form&lt;/em&gt; stack, which ensures consistent spacing between each of the different elements. We have a text input with a few different props on that. We have a button group with a primary and a secondary button.&lt;/p&gt;&lt;p&gt;Up from the micro layer of the components, we also have a suite of &lt;em&gt;templates&lt;/em&gt; as well. Here’s a multi-step form. If you’re registering for a business with the government, or if you’re processing an intent to export some livestock, you’ll probably see our multi-step form templates. We have a timeline of progress indicator on the left that shows you where you are in the process of building a form, and each of the pages are very simple. There’s only ever a couple of inputs on each page. And we have a suite of templates, some for forms, some for content pages.&lt;/p&gt;&lt;p&gt;We also are working on some for more dense dashboards for the internal staff who are reviewing the applications, etc. And then we have some design patterns as well.&lt;/p&gt;&lt;p&gt;So, what does it look like when a table is loading? If that was a gif, that’d be pulsing, but you get the idea. Or, what it looks like to filter data. So, we have a few patterns for table filtering as well.&lt;/p&gt;&lt;p&gt;(09:28)&lt;/p&gt;&lt;p&gt;Now, briefly on accessibility. &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;Accessibility&lt;/a&gt; is something that’s really at the heart for not only Thinkmill, but also with the Department of Agriculture. You know, it’s not like, if your customer at Optus has a bad experience, they could like to go elsewhere. But unless you want to move the country, you don’t really have a choice of provider with the government. Typically when you’re interacting with the government, it’s always for something important. It’s something that you have to do. So, we want to make sure that people, no matter what their needs are, are able to achieve what they need to.&lt;/p&gt;&lt;p&gt;For instance, we’ve provided experiences or we’ve tested for people who have partial or full vision impairment, people who are colourblind or deaf, have a physical or motor skill impairment, cognitively divergent, or just have a different experience level with computers as well.&lt;/p&gt;&lt;p&gt;When we design anything, whether it be a component or a template, we ensure that screen readers can describe it clearly. &lt;a href=&quot;https://www.visionaustralia.org/technology-products/resources/beginner-guides/introduction-to-screen-readers&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Screen readers&lt;/a&gt; are software that’s built into your computer. You can turn it on if you have vision difficulties, and it will audibly describe what it’s seeing as you tab around the screen. We ensure that screen reader support is right up there. We ensure that we build everything with valid HTML, and we use the right properties to describe the elements as well. So, every element has correct &lt;a href=&quot;https://webaim.org/articles/contrast/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;colour contrast&lt;/a&gt;, anyone with low vision can discern text and foreground elements from backgrounds. And also, &lt;a href=&quot;https://www.w3.org/WAI/wcag-curric/gid3-0.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;not using colour alone to convey meaning&lt;/a&gt;. If you imagine a red banner or a green banner to denote some sort of error state, we ensure that we use more than just colour because if you’re colourblind, you won’t be able to see the intent from that message.&lt;/p&gt;&lt;p&gt;One of the highlights of this project has been an accessibility audit. We engaged with a third-party consultant, &lt;a href=&quot;https://intopia.digital/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Intopia&lt;/a&gt;. They’re an accessibility expert to audit our component library and also to enable user testing. We were able to observe people who were totally vision impaired or people who couldn’t use their arms at all and were actually looking at a camera and using facial expressions to move a cursor around. It was pretty amazing to see. We had that audit and we’re very proud that they noted us as an exemplary example of an accessible component library, which was great. With their other clients, they actually use us as an example, because it’s open source, they use us as an example to say, this is how you do it right with building for accessibility.&lt;/p&gt;&lt;p&gt;(12:28)&lt;/p&gt;&lt;h3&gt;A design system includes a community of users.&lt;/h3&gt;&lt;p&gt;Design systems are effectively products of their own, but they’re built to serve other products. Like any product, you want to understand your users’ needs and foster relationships with them. So, it’s important to create a dialogue with your users and get feedback or encourage contributions to the system. A good design team regularly engages in discussion with consumers to build a picture of their needs so they can be building valuable enhancements to the design system, creating value that way.&lt;/p&gt;&lt;p&gt;The individual product teams in the organisation could be siloed from each other, so they have their own backlogs, plans, goals for sprints or for quarters. What a design system does is it centralises design practices, enables collaboration, and overall raises the standard of design for the whole department.&lt;/p&gt;&lt;p&gt;A design system enables collaboration in a few ways. We have internal chat channels, so if people are having design problems they can get in contact with a designer or developer in the design system team to get feedback on how to solve design problems. We also have fortnightly shareback sessions called “guilds”. There’s Design Guild and Tech Guild, and people can share back what they’ve been working on. We also have design system office hours as well. Every week, there’ll be a session, people can just drop in to ask their design questions and drop out as they need. That’s been really valuable to build that community and get people excited about the design system and using it and get feedback that way.&lt;/p&gt;&lt;p&gt;(14:15)&lt;/p&gt;&lt;p&gt;Like I said before, a key part of the community-building aspect of the design system is the fact that it’s all open source. All the code for the component library and documentation site is all open source, it’s all &lt;a href=&quot;https://github.com/agriculturegovau/agds-next&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;published on GitHub&lt;/a&gt;, it’s all public access. That allows people to contribute to the design system, and it actually also enables other government departments to share their codes. We’ve had other government departments get in contact to use the design system as well.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Q: Are you telling me that I could go and regenerate an exactness like the Department of Agriculture website, the look, the feel, the design language, using all AGDS?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;A: Yeah, in theory, depending on your skill level. But yes&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Q: Let’s assume I’m extremely skilled. Surely there are intellectual property issues. The government would not police its entire IP for someone to create an incredible looking phishing site that looks just like the genuine. Isn’t there an issue there?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;A: Short answer is no. I can try and answer that for you afterwards. Yeah, I’ve some [time for] questions at the end, but effectively, there’s no business domain logic in the design system, there’s nothing related to applications [with] important, sensitive information. There’s no data being stored in the design system. It’s purely the lego blocks used to assemble these websites. You can create a pretty credible-looking phishing scam without doing any of this as well.&lt;/p&gt;&lt;p&gt;(16:12)&lt;/p&gt;&lt;p&gt;With that, I might just show a quick demo of the documentation site. I’m just going to Google &quot;Agriculture Design System&quot; so you can play along at home. We got pretty good SEO as well, which we’re very proud of. Right, so this is the design system website. All of the internal teams use this.&lt;/p&gt;&lt;p&gt;As you’ve seen, it’s very accessible to get into it. A couple of notes that I’ll show you.&lt;/p&gt;&lt;p&gt;We have a schema of &lt;a href=&quot;https://design-system.agriculture.gov.au/foundations/tokens&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;design tokens&lt;/a&gt;. For instance, how we use colour is very well thought out in the system. We describe what the text colours are and how they’re used. The different types of background colours. We don’t have red or green or blue. We have success, error and info because that denotes the intent of the colours and how they’re used in the system. This is always very fun to play with, there is a dark mode as well, and people really get excited by pressing stuff like that, usually.&lt;/p&gt;&lt;p&gt;Right, what else have I got there? You’ve seen the &lt;a href=&quot;https://design-system.agriculture.gov.au/components&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;component library&lt;/a&gt;, so I’ll just show a page, if I can type, &quot;&lt;a href=&quot;https://design-system.agriculture.gov.au/components/page-alert&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;page alert&lt;/a&gt;&quot; as an example. Each of the components are thoroughly documented. We have do’s and don’ts on each of the components. We describe, for instance, the toned-alerts, when you should see something like this. Notice we have the different icons as well as talking about using colour alone before, so if you’re colourblind, you would see the difference between an error and a success state.&lt;/p&gt;&lt;p&gt;I won’t labor on this for too long but we do have, patterns for filtering, loading and empty states. There’s a gif I was promising here before. There are live examples as well, so they’re being rendered in this space and a developer could copy the code as well. Error states – we’ve thought about it and explained here, how to do those.&lt;/p&gt;&lt;p&gt;And there’s our suite of templates as well. So, yeah, feel free to take a look around it through that. You know how to Google it now. I’ll stop sharing. Now I’m back to my slides.&lt;/p&gt;&lt;p&gt;(19:40)&lt;/p&gt;&lt;p&gt;All right, quick word on who built the design system. The DS team, the A-Team, just not quite as jacked. A design system team requires a mix of unique skill sets. So, we have a product manager, we have interaction designers, front-end developers, and my personal favourite, design-engineers who kind of bridge both the design and the development space. We have a mix of full and part-time practitioners, so some people might be part-time on product teams as well. They fulfil a variety of responsibilities, like designing or building components, writing documentation, accessibility testing, providing design support, running the community engagement initiatives, surveying users for needs, or generally planning our sprints and running, the maintaining, the managing, a backlog of work.&lt;/p&gt;&lt;p&gt;(20:33)&lt;/p&gt;&lt;p&gt;All right, so I just wanted to end on some key success points. First of all, AGDS is already powering the &lt;a href=&quot;https://exports.agriculture.gov.au/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;export service portal&lt;/a&gt;. If you go to exports of agriculture, you actually see it being used in the real world. It’s also been used by other government partners around Australia as well, which is great. Home affairs, I believe, and the Northern Territory government also express interest as well.&lt;/p&gt;&lt;p&gt;We’ve also enabled collaboration across siloed teams, which we’re very proud of. A lot of teams who don’t have their own backlogs and planning and generally don’t talk to each other, the design system works to enable that collaboration.&lt;/p&gt;&lt;p&gt;We also enabled a departmental rebrand. So, interesting story, when the Labor government came into power, before the election, we were actually the Department of Agriculture, Water and the Environment; and then when Labor came in, we became Agriculture, Forestry, and Fisheries. And then there was Environment, they got split off. There was Department of Climate Change created as well. The teams that were already using the design system, that was just a software update, effectively, to ingest that new logo, and actually prompted other teams to start using the design system as well. So, that was good.&lt;/p&gt;&lt;p&gt;Like I said before, the biggest highlight for us was to have someone else, who is a real expert in the accessibility space, call us an exemplary accessible component library and they told all their friends about us.&lt;/p&gt;&lt;p&gt;All righty, that just about does it for me. Is there any other questions?&lt;/p&gt;&lt;h3&gt;Audience Q&amp;amp;A&lt;/h3&gt;&lt;h4&gt;Q: Is it just a component library or is it built on a UI framework? Specifically, do you have support for flexbox or CSS grid to automatically resize? Now, I work with component libraries before, it’s just a library and you still need to go and use Bootstrap.&lt;/h4&gt;&lt;p&gt;(22:42)&lt;/p&gt;&lt;p&gt;The primary touchpoint is the component library it’s built using React.js. It’s all CSS as well, so there’s flexbox and all those things that you mentioned as well, that’s all part of the system. But on top of that, there’s all the &lt;a href=&quot;https://design-system.agriculture.gov.au/foundations/design-principles&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;design principles&lt;/a&gt;, which I didn’t show in my demo, but we’ve got extensive documentation about what it looks like to build something with AGDS.&lt;/p&gt;&lt;h4&gt;Q: I assume you have a core library and then you fork it or branch it for other clients, but do you keep that up to date and push revisions to change the core library?&lt;/h4&gt;&lt;p&gt;(23:32)&lt;/p&gt;&lt;p&gt;A: Thinkmill doesn’t have a core library that all these other design systems are based off. They’re all independently built and ran. We actually make some open-source libraries that layers some of the foundation of them, but each of the libraries we’ve worked on, for the most part, independent of each other. We’ll share our ideas. A lot of the design systems we’ve worked on are open source. And the web development 10 years ago, it’s very different to what it is now. So, there’s always new things, and it would be more of a hindrance to have one central Thinkmill library that is always kept up to date. Frankly, we’re always fiddling with stuff, and we’re always wanting to try the new thing.&lt;/p&gt;&lt;h4&gt;Q: Are these components just for web or mobile too?&lt;/h4&gt;&lt;p&gt;(24:36)&lt;/p&gt;&lt;p&gt;A: In the case of Agriculture, there’s just web. For Reckon, which I spoke about before, there’s a core design language, of which we call Balance, and Balance has a React implementation as well as a React Native. Writing React Native is very similar to running regular React, but you run it through a different compiler, and it spits out an Android app and an iOS app.&lt;/p&gt;&lt;h4&gt;Q: Have you done any design for wearables like an Apple Watch?&lt;/h4&gt;&lt;p&gt;(25:11)&lt;/p&gt;&lt;p&gt;A: Not personally, but I think I’ve fiddled with a couple of apps just for fun before. Actually one of our guys, a couple weeks ago, was building a service that we use to basically build Lego characters inspired by each of the team members. And the website wasn’t very great. So he actually did a concept for building a new version where it was a 3D model in the browser. And it was like, what if I took that and put into the Apple Vision Pro headset and to Oculus, and he actually got a working prototype. We didn’t have the headset yet, but it was doing it in the simulator. Those libraries in the code were able to be shared between, web, iPhone, and VR systems.&lt;/p&gt;&lt;h4&gt;Q: You were talking about the design language there. Is it actually is a language that helps to create the design elements that gets compiled down into other frameworks?&lt;/h4&gt;&lt;p&gt;(26:20)&lt;/p&gt;&lt;p&gt;A: Essentially, yes. I didn’t labor on that part too much, but a really great thing about design systems is the shared handoff between designers and developers. What tends to happen without design systems is a designer will draw boxes and shapes, and developers have to translate that. As I showed a moment ago, the button with the variant primary and secondary, the same components effectively are built in Figma. A developer also calls that same thing a primary button as well. So, that makes the handoff a lot smoother.&lt;/p&gt;&lt;h4&gt;Q: With Reckon, you created a language called Balance. Do you actually make a specific language for each product?&lt;/h4&gt;&lt;p&gt;(27:12)&lt;/p&gt;&lt;p&gt;A: Not a coding language per se. The language is effectively what things are called. So, what do we call a badge or a button? It’s creating a shared vocabulary in all those elements between design and development. In terms of coding language, it’s all JavaScript, React and HTML.&lt;/p&gt;&lt;h4&gt;Q: Can component libraries be integrated into online reporting and business analytics systems such as Power BI?&lt;/h4&gt;&lt;p&gt;(28:30)&lt;/p&gt;&lt;p&gt;A: In theory, yes. That depends on the capabilities of things like Power BI. I have heard of people spending a bunch of time creating really nice Power BI dashboards. They’re not necessarily compatible with React, but they might be compatible with CSS and different styling frameworks as well.&lt;/p&gt;&lt;h4&gt;Q: You mentioned Figma at the start. Assume you know that Adobe is buying them out, and that seems to be moving to two trains of thought - either Adobe is going to ruin the software, or pump it full of AI and all their own stuff. Any thoughts, concerns?&lt;/h4&gt;&lt;p&gt;(29:42)&lt;/p&gt;&lt;p&gt;A: I guess when we originally read on the merger or the buyout, there were a few jokes like that being thrown around. I will say that recently, with the investment, they’ve developed a lot of really cool features like the design tokens. I don’t pay for the licenses, so I don’t think they’ve necessarily upped the price or anything like that yet or forced you to have a Creative Cloud subscription. I think if they did that, they would destroy their business and their customers, but so far, so good.&lt;/p&gt;&lt;h4&gt;Q: Just in terms of the AI technology [...] what’s Thinkmill going to do in the future?&lt;/h4&gt;&lt;p&gt;(31:18)&lt;/p&gt;&lt;p&gt;A: Thinkmill is really open-minded on AI. We have people that are experimenting with it, like what it could look like to integrate something like that into various products or systems, but nothing tangible at this point. We’re definitely remaining open-minded, and I’m sure we’ll have customers and clients who are interested in that space as well. That’s all I can say at this point.&lt;/p&gt;&lt;h4&gt;Q: Around generative AI, you’ve already made a lot of these very deliberate design decisions about these components and you could feed that into AI and say, go and build me a website and that would go and automate something [...]&lt;/h4&gt;&lt;p&gt;(32:10)&lt;/p&gt;&lt;p&gt;A: I guess I can say that the majority of problems that, as consultants, we’re there to solve most the time are more organisational or people problems than technology. Technology is certainly a factor of it, and there’s plenty of white label component libraries out there that people can adopt but rarely for large organisations. Those white label systems are great for startups or for getting something up and running, but for large organisations, typically, they need elements that are more opinionated, or templates that solve problems for their specific business needs. So we’re not too worried about our jobs at the moment.&lt;/p&gt;&lt;h4&gt;Q: The components you shared with us are largely static. Do you have any more interactive ones like sliders and date pickers?&lt;/h4&gt;&lt;p&gt;(33:15)&lt;/p&gt;&lt;p&gt;A: I can show you if you’d like. We have a date picker that when you click on the little icon, it has a little fly out. And there’s a calendar, accordions and modals as well. The accessibility challenges of modals in particular are really interesting and complex, and so we spend a bunch of time labouring over what an accessible implementation of a modal looks like.&lt;/p&gt;&lt;h4&gt;Q: The experience on mobile is completely different because you can’t click, right?&lt;/h4&gt;&lt;p&gt;(33:55)&lt;/p&gt;&lt;p&gt;A: There’s some components that, such as a tooltip, you hover over something, and then that a little fly out comes out. You can’t hover anything on a mobile device, so our design system team on top of building value and building components, you also need to have an idea of what not to do as well, and develop those design principles to inform direction.&lt;/p&gt;&lt;h4&gt;Q: What trade-offs you’ve seen with having a design system [like a government service] that enables you to move to high fidelity so quickly?&lt;/h4&gt;&lt;p&gt;(34:55)&lt;/p&gt;&lt;p&gt;A:There are certainly growing pains. I’ll answer the question I think you’re asking. Once you’ve got the critical mass of a design system and you’ve got templates that are tried and tested, then product teams are able to use those templates or contribute new ones. At the initial stage where you’re building a design system, you need to be informed by products and understand their needs to build something like that. If you’re building those things in parallel, there are trade-offs in terms of velocity to build and design something right, and making sure that it’s done in a systematic way rather than building a one-off prototype or a standalone product that doesn’t necessarily need a design system.&lt;/p&gt;&lt;h4&gt;Q: In contrast, the trade-offs that I’ve seen is that going to high fidelity really quickly means that the feedback that you get, whether it’s from users or other stakeholders in your organisation, ends up just kind of fiddling around the edges. Where the feedback that you’re after, whether this is meeting the needs as opposed to does this look right, and the trade-offs around going to high fidelity very quickly, whether it’s stakeholders or customers, they quickly go to, does this look right, yes, or I’ll just kind of adjust things on the page a little bit, as opposed to thinking about, does this actually meet my needs? It’s an interesting challenge that the benefits that you have of being able to move to high fidelity quickly versus stripping that back and making sure that the service is actually as effective as it needs to be. Probably less of a question and more of a comment.&lt;/h4&gt;&lt;p&gt;A: I guess our core clientele at the moment is large corporate entities that have a suite of existing products or existing systems as well. A lot of the time, you’re not just finding design and what looks right, but also, you know, we’ve got tech debt of a 30-year-old accounting software or something like that as well.&lt;/p&gt;&lt;h4&gt;Q: Is it the staff at the Department of AG using your design system and building their pages, or does Thinkmill also do that?&lt;/h4&gt;&lt;p&gt;(37:45)&lt;/p&gt;&lt;p&gt;A: In the case of AG, they are the staff of Agriculture, and Agriculture have a mix of contractors and full-time staff. But our work at Thinkmill is just on the design system front.&lt;/p&gt;&lt;h4&gt;Q: Have you had any external contributors to your open-source repos yet?&lt;/h4&gt;&lt;p&gt;(38:06)&lt;/p&gt;&lt;p&gt;A: We’ve had a lot of interest. The design language I was speaking about was actually very inspired by the &lt;a href=&quot;https://gold.designsystemau.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;former Australian government design system&lt;/a&gt; that was run by the DTA. That got defunded and turned to a community-driven model. It was also a 10-year-old tech stack so we wanted to take what that stood for – the design language, the thinking and the investment behind that – but re-implement it in modern React. We’ve had some really good feedback from people that used to work on that system as well.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/o52wjraqgjmq/designing-at-scale-overview-design-systems?width=1200" length="549400" type="image/png"/></item><item><title>Boris was a panellist at Design x Engineering Co-Lab</title><link>https://www.thinkmill.com.au/blog/boris-was-a-panellist-at-design-x-engineering-co-lab</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/boris-was-a-panellist-at-design-x-engineering-co-lab</guid><description>Our Co-CEO, Boris Bozic, was one of three expert panellists who spoke at the first Design x Engineering Co-Lab on Design Systems, in Sydney. We’re proud to have his serious design systems knowledge at the helm of our design-engineering practice. Thank you for the words of wisdom and experience shared by the other panellists Dominik Wilkowski and Maria Christley; and to Morgan Fletcher for MC-ing the event, and Atlassian for hosting us with an impressive pizza spread. We’ll be there for the next.</description><pubDate>Tue, 15 Aug 2023 00:00:00 GMT</pubDate></item><item><title>7 Things I Love About Keystatic — A Thread...</title><link>https://www.thinkmill.com.au/blog/7-things-i-love-about-keystatic</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/7-things-i-love-about-keystatic</guid><description>After using Keystatic full-time for about 5 months, I thought I’d share some of the things I really like about it. The talk was at the SydJS meetup, held on the 21st of June 2023 at the Atlassian HQ.</description><pubDate>Fri, 23 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;First of all, shoutout to the &lt;a href=&quot;https://sydjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SydJS Meetup&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/twalve&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Sharkie&lt;/a&gt; for hosting the event, and to &lt;a href=&quot;https://www.atlassian.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Atlassian&lt;/a&gt; for providing the awesome venue!&lt;/p&gt;&lt;p&gt;You can also read the &lt;a href=&quot;https://twitter.com/simonswiss/status/1671677583363297280&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter thread version of this post&lt;/a&gt;.&lt;/p&gt;&lt;hr /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/bd3kl9qhqfxe/7-things-i-love-about-keystatic-talk-cover?width=848&quot; alt=&quot;Title slide for the talk, with screenshots of a few sites built with Keystatic&quot; width=&quot;848&quot; height=&quot;508&quot; /&gt;&lt;p&gt;Over the past months, I’ve done a whole lot of exploration/prototyping with Keystatic. But I have also shipped real things.&lt;/p&gt;&lt;p&gt;Real production projects, with real clients. With real needs/shoulder tapping.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/i236typxp5q4/7-things-i-love-about-keystatic-sites?width=848&quot; alt=&quot;A few websites I built with Keystatic&quot; width=&quot;848&quot; height=&quot;476&quot; /&gt;&lt;figcaption&gt;A few websites I built with Keystatic&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;One of these clients is... my wife Heather. She can literally shoulder tap 24/7 😅&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/vfe0pobbqh0b/7-things-i-love-about-keystatic-wife-site?width=848&quot; alt=&quot;My wife’s website, Star Athletics&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;figcaption&gt;My wife’s website, Star Athletics&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;These months of exploration and shipping stuff give me a well-deserved certification star  ⭐️ to talk with &quot;authority&quot; about Keystatic, and what I really love about it.&lt;/p&gt;&lt;p&gt;Let’s dive in 🤗&lt;/p&gt;&lt;hr /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/mh7ihjvnb50r/7-things-i-love-about-keystatic-number-one?width=848&quot; alt=&quot;Slide for chapter 1 of the talk&quot; width=&quot;848&quot; height=&quot;475&quot; /&gt;&lt;h2&gt;#1 — Keystatic doesn’t really change how I would build websites for myself&lt;/h2&gt;&lt;p&gt;After showing off my puppies in slow motion on the big screen, I presented my first key point I love about Keystatic: the fact that Keystatic doesn’t really change how I would build a website for myself.&lt;/p&gt;&lt;p&gt;I used this Tailwind UI component as an example:&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/v3z7flmrdfz7/7-things-i-love-about-keystatic-tailwind-ui?width=848&quot; alt=&quot;Pricing plans UI&quot; width=&quot;848&quot; height=&quot;475&quot; /&gt;&lt;p&gt;The way I’d build this is exactly how the code is set up: a JavaScript array of objects to manage the &quot;data&quot; for each pricing plan:&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/zomcetquek0m/7-things-i-love-about-keystatic-js-array?width=848&quot; alt=&quot;JavaScript array of pricing tiers&quot; width=&quot;848&quot; height=&quot;935&quot; /&gt;&lt;p&gt;That’s great and easy to edit for developers. But what if my non-technical friend wants to edit?&lt;/p&gt;&lt;p&gt;You could argue that it’s &quot;not that hard&quot; to edit strings inside a JS object.&lt;/p&gt;&lt;p&gt;And you’re right.&lt;/p&gt;&lt;p&gt;But there’s a lot of &quot;baggage&quot; that comes with it for non-technical folks:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;open a JSX file in a code editor&lt;/li&gt;&lt;li&gt;push the code to GitHub&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We take those things for granted.&lt;/p&gt;&lt;p&gt;To help my friend out, I will re-create the pricing plan data structure in a Keystatic schema.&lt;/p&gt;&lt;p&gt;I create a &quot;pricing&quot; singleton in the Keystatic config, and set the schema to mirror the shape of the pricing tiers.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/p2irtegh2g7e/7-things-i-love-about-keystatic-schema-1?width=848&quot; alt=&quot;Keystatic config code snippet&quot; width=&quot;848&quot; height=&quot;1072&quot; /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/dtto6yjnwh57/7-things-i-love-about-keystatic-schema-2?width=848&quot; alt=&quot;Keystatic config code snippet&quot; width=&quot;848&quot; height=&quot;1505&quot; /&gt;&lt;p&gt;With that config in place, I can tell my friend to visit the &lt;code&gt;/keystatic&lt;/code&gt; route on the website. They’ll find a Pricing section where they can create some pricing tiers ✨&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/data-entry.mov&quot;&gt;Data entry in the Keystatic Admin UI&lt;/a&gt;&lt;/p&gt;&lt;p&gt;And here’s what Keystatic outputs:&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/watsu2k9ib6t/7-things-i-love-about-keystatic-keystatic-json-output?width=848&quot; alt=&quot;The generated JSON file&quot; width=&quot;848&quot; height=&quot;1508&quot; /&gt;&lt;figcaption&gt;The generated JSON file&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;A &lt;code&gt;pricing.json&lt;/code&gt; file inside &lt;code&gt;src/content&lt;/code&gt;, just like we defined in our config.&lt;/p&gt;&lt;p&gt;This is pretty neat!&lt;/p&gt;&lt;p&gt;But my friend still needs to manually enter the pricing tiers... which already exist in code.&lt;/p&gt;&lt;p&gt;What if instead we made the existing tiers the thing that Keystatic can edit?&lt;/p&gt;&lt;p&gt;Keystatic now writes (but also reads!) from that &quot;pricing.json&quot; file. Let’s move our tiers in that file!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/port JS to Keystatic JSON.mov&quot;&gt;Porting the hardcoded JavaScript tiers array into our Keystatic-managed JSON file&lt;/a&gt;&lt;/p&gt;&lt;p&gt;And just like that, we made our hardcoded tiers array editable with Keystatic!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/made-it-editable.mov&quot;&gt;Our original pricing tiers now in Keystatic!&lt;/a&gt;&lt;/p&gt;&lt;p&gt;All the data shows up in the Admin UI, and my friend can start editing the plans, instead of creating them again from scratch.&lt;/p&gt;&lt;p&gt;Neat 🎉&lt;/p&gt;&lt;p&gt;Ok, so my friend did some changes. Renamed the &quot;Basic&quot; plan to &quot;Keystatic&quot;, and added a feature item.&lt;/p&gt;&lt;p&gt;Before we can see this on the front end, we need to adjust our template code.&lt;/p&gt;&lt;p&gt;And it’s a &lt;strong&gt;very minimal&lt;/strong&gt; change!&lt;/p&gt;&lt;p&gt;All we need to do is replace the hardcoded tiers array with the new tiers from the &quot;pricing.json&quot; file.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/update our code.mov&quot;&gt;Minimal code change in our template&lt;/a&gt;&lt;/p&gt;&lt;p&gt;That’s it. Same variable name, so the rest of the code still works exactly the same way!&lt;/p&gt;&lt;p&gt;Now, if we visit our Pricing page once again, we can verify that the plans are indeed showing the changes made in Keystatic:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/ui-updated.mov&quot;&gt;Our UI is updated ✨&lt;/a&gt;&lt;/p&gt;&lt;p&gt;All we did was create a schema mirroring the data shape, and import the JSON output in our page. That’s it.&lt;/p&gt;&lt;p&gt;We’ve made life infinitely better for our non-technical friends.&lt;/p&gt;&lt;p&gt;But as developers, we can still work the same way, and edit the JSON data manually. We don’t have to use the Admin UI.&lt;/p&gt;&lt;p&gt;It’s there for those who need it.&lt;/p&gt;&lt;p&gt;And that’s pretty cool 😎&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ml7j6fj2d5gy/7-things-i-love-about-keystatic-corgi-brothers?width=848&quot; alt=&quot;Slide for chapter 2 of the talk&quot; width=&quot;848&quot; height=&quot;2762&quot; /&gt;&lt;h2&gt;#2 — The TypeScript self-documenting dev experience&lt;/h2&gt;&lt;p&gt;When I learned Keystatic (very early days), there was absolutely no documentation.&lt;/p&gt;&lt;p&gt;Besides asking questions to the core team — and I sure did 😅 — my primary way to learn: autocomplete TypeScript discovery.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rrccedhjfk5s/7-things-i-love-about-keystatic-typescript-def-experience?width=848&quot; alt=&quot;Autocomplete suggestions from Types&quot; width=&quot;848&quot; height=&quot;876&quot; /&gt;&lt;figcaption&gt;Autocomplete suggestions from Types&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Here’s a video recording of creating a new schema in the Keystatic config. It almost feels like between GitHub Copilot and TypeScript, all I need to do to write a schema is hit one key and then press &quot;tab tab tab&quot; to let AI build my schema 🤣&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/typescript-and-copilot.mov&quot;&gt;TypeScript and GitHub Copilot doing all the work for me&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Those Types also come handy when pulling content in the frontend!&lt;/p&gt;&lt;hr /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/pgyvzvta1dp6/7-things-i-love-about-keystatic-three-corgis?width=848&quot; alt=&quot;Slide for chapter 3 of the talk&quot; width=&quot;848&quot; height=&quot;1507&quot; /&gt;&lt;h2&gt;#3 — Keystatic works well with the tool I already use and love.&lt;/h2&gt;&lt;p&gt;So far, I built sites with Next.js and Astro. Keystatic’s flexibility makes it pair with those really nicely.&lt;/p&gt;&lt;p&gt;Let’s take the example of Astro. Astro has that built-in concept of &quot;Content Collections&quot;. Essentially a &quot;content&quot; directory in your project, that will let you create &quot;query-able&quot; data collections:&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/naj5jownwc5t/7-things-i-love-about-keystatic-content-collections?width=848&quot; alt=&quot;Astro’s content collections&quot; width=&quot;848&quot; height=&quot;1216&quot; /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ujw6w2vo88de/7-things-i-love-about-keystatic-astro-content-collections?width=848&quot; alt=&quot;Astro’s content collection folders&quot; width=&quot;848&quot; height=&quot;1819&quot; /&gt;&lt;p&gt;It allows you to query data from files in your file system using built-in helpers:&lt;/p&gt;&lt;pre&gt;---
import { getCollection } from &apos;astro:content&apos;
const blogPosts = await getCollection(&apos;blog&apos;)
---

&amp;lt;ul&amp;gt;
  {blogPosts.map(post =&amp;gt; &amp;lt;li&amp;gt;{post.title}&amp;lt;/li&amp;gt;)}
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;&lt;p&gt;We had already built the Thinkmill website with Astro and Content Collections &lt;em&gt;before&lt;/em&gt; thinking about bringing Keystatic into the picture. And so, we wanted to keep leveraging those content collections.&lt;/p&gt;&lt;p&gt;By setting the output &quot;path&quot; to &quot;src/content/blog&quot; in our Kesytatic config, we were able to do just that:&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/qremovxcaknd/7-things-i-love-about-keystatic-blog-schema?width=848&quot; alt=&quot;Keystatic blog post config&quot; width=&quot;848&quot; height=&quot;1226&quot; /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/og8jad2yxt2g/7-things-i-love-about-keystatic-blog-output?width=848&quot; alt=&quot;Keystatic blog post output&quot; width=&quot;848&quot; height=&quot;1511&quot; /&gt;&lt;p&gt;We did the same for authors, tags, etc:&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/f9b8sl3ejipc/7-things-i-love-about-keystatic-authors-and-tags?width=848&quot; alt=&quot;content collections folder structure&quot; width=&quot;848&quot; height=&quot;1503&quot; /&gt;&lt;p&gt;And It Just Worked™&lt;/p&gt;&lt;p&gt;Out of the box.&lt;/p&gt;&lt;p&gt;🤯&lt;/p&gt;&lt;p&gt;And that’s probably the coolest thing about Keystatic:&lt;/p&gt;&lt;p&gt;It can &lt;strong&gt;meet you where you are&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Whatever way you’re building stuff — it find ways to adapt, and be helpful without getting in the way.&lt;/p&gt;&lt;p&gt;Keystatic actually does have its own powerful Reader API to query the file system... But it’s not needed here!&lt;/p&gt;&lt;hr /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/t5ltk0ulm63o/7-things-i-love-about-keystatic-four-pups?width=848&quot; alt=&quot;Slide for chapter 4 of the talk&quot; width=&quot;848&quot; height=&quot;1514&quot; /&gt;&lt;h2&gt;#4 — Keystatic travels light, doesn’t bring much &quot;devOps baggage&quot;&lt;/h2&gt;&lt;p&gt;Traditionally, bringing content editing to a project means:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;bringing a database&lt;/li&gt;&lt;li&gt;needing to maintain said database&lt;/li&gt;&lt;li&gt;refactoring a bunch of stuff&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What about our Astro Content Collections? Most CMS won’t output their content inside the &lt;code&gt;src/content/blog&lt;/code&gt; directory like we want.&lt;/p&gt;&lt;p&gt;You’ll have an API or ORM to hit to get your data. Which is fine, but it may be a departure from your existing setup.&lt;/p&gt;&lt;p&gt;Thanks to Keystatic’s flexibility, in my experience, you often don’t need to add that extra &quot;baggage&quot;.&lt;/p&gt;&lt;hr /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/oy9c1xvx7p1m/7-things-i-love-about-keystatic-five-pups?width=848&quot; alt=&quot;Slide for chapter 5 of the talk&quot; width=&quot;848&quot; height=&quot;1517&quot; /&gt;&lt;h2&gt;#5 — With Keystatic, you include non-technical folks within your developer-first workflow&lt;/h2&gt;&lt;p&gt;You don’t invent/force a new system to allow content editing, and then force the dev team to use that new system.&lt;/p&gt;&lt;p&gt;You stay in your wheelhouse, and bring new folks along.&lt;/p&gt;&lt;p&gt;If you’re using MDX, you may assume that it’s fine for non-technical devs to edit MDX file.&lt;/p&gt;&lt;p&gt;It’s really not.&lt;/p&gt;&lt;p&gt;There’s a bunch of weirdness, module imports, bracket syntax, passing props etc.&lt;/p&gt;&lt;p&gt;Markdown could work with non-technical people, but MDX probably not.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/mnff6ng5cmyh/7-things-i-love-about-keystatic-markdown-mdx?width=848&quot; alt=&quot;Quote suggesting to edit MDX files for non-developers — probably not a good idea&quot; width=&quot;848&quot; height=&quot;1514&quot; /&gt;&lt;p&gt;So, if non-technical colleagues can’t do copy changes... instead they’ll do &quot;change requests&quot; tickets. In Notion, Jira, on your desk.&lt;/p&gt;&lt;p&gt;Post-It on your screen.&lt;/p&gt;&lt;p&gt;Weird feelings of &quot;nagging&quot; will creep in. Both ways.&lt;/p&gt;&lt;p&gt;Trust me, your non-technical colleagues don’t enjoy nagging you. They want autonomy!&lt;/p&gt;&lt;p&gt;Keystatic allows you to empower them to make these changes, without making sweeping changes to the way you work as a developer.&lt;/p&gt;&lt;p&gt;It makes your codebase more inclusive and accessible.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/oy6jab8n7h1t/7-things-i-love-about-keystatic-keystatic-a11y?width=848&quot; alt=&quot;Is this... a11y? 😮🫱🦋&quot; width=&quot;848&quot; height=&quot;937&quot; /&gt;&lt;figcaption&gt;Is this... a11y? 😮🫱🦋&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;What do I mean by &quot;bring non-technical folks in our dev-first workflow&quot;?&lt;/h3&gt;&lt;p&gt;Keystatic deeply integrates with GitHub. When a non-technical person edits content in Keystatic, they actually create a branch, make commits, open a PR, etc.&lt;/p&gt;&lt;p&gt;Deploy previews, CI pipeline, all of it ✨&lt;/p&gt;&lt;p&gt;Let’s take a fictive character for a non-technical person interacting with Keystatic.&lt;/p&gt;&lt;p&gt;We’ll call our character Sharkie.&lt;/p&gt;&lt;p&gt;Oh, hey &lt;a href=&quot;https://twitter.com/twalve&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@twalve&lt;/a&gt; 👋&lt;/p&gt;&lt;p&gt;Sharkie has a GitHub account with access to our repo, so they can &quot;log in&quot; and access the Admin UI.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/github-login.mp4&quot;&gt;GitHub login to access the Keystatic Admin UI&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Keystatic lets you teach lightweight concepts about dev workflows — in an environment that feels comfortable for folks like Sharkie ❤️&lt;/p&gt;&lt;p&gt;Concept like branches.&lt;/p&gt;&lt;p&gt;I explain that instead of changing the live site (main branch), they should create a new &quot;branch&quot; (preview).&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/new branch.mp4&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;p&gt;After creating a new branch, Sharkie can create a new author, and write a blog post:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/data-entry.mp4&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;p&gt;After creating the author and the post, the UI shows two little 1️⃣ badges.&lt;/p&gt;&lt;p&gt;We, as developers, can read through this: The &quot;demo-sharkie&quot; branch is &lt;em&gt;two commits&lt;/em&gt; ahead of main.&lt;/p&gt;&lt;p&gt;Sharkie doesn’t need to know this. The info here is:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Authors and Blog Popsts have both 1 new thing&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I explain to Sharkie that to suggest adding those changes to the live site, they should &quot;create a pull request&quot;.&lt;/p&gt;&lt;p&gt;This is how developers communicate intent to &quot;pull&quot; new code into a project.&lt;/p&gt;&lt;p&gt;Still a weird concept for non-devs, but at least wrapped in a non-intimidating UI ✨&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/pull request.mp4&quot;&gt;Sharkie opens a pull request without needing to care about GIT stuff&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The Pull Request video in the previous tweet is a great reminder that Keystatic is &quot;just a wrapper&quot; around GitHub workflows.&lt;/p&gt;&lt;p&gt;Without knowing it, Sharkie has created two Markdoc files with frontmatter metadata, and prepared those files into a PR.&lt;/p&gt;&lt;p&gt;Just as if it was done manually!&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/p96dk5wl20hd/7-things-i-love-about-keystatic-github-pr?width=848&quot; alt=&quot;Screenshot of GitHub pull request changes&quot; width=&quot;848&quot; height=&quot;994&quot; /&gt;&lt;p&gt;Who does the next step is up to you.&lt;/p&gt;&lt;p&gt;We can take over and handle the PR. But it’s a nice opportunity to teach a bit more about GitHub workflows to our friend Sharkie.&lt;/p&gt;&lt;p&gt;The reward? They can discover the world of deploy previews, GitHub Actions etc.&lt;/p&gt;&lt;p&gt;You can blow their mind with that 🤯&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/deploy preview build.mp4&quot;&gt;Triggering a deploy preview build&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Not only does this make Sharkie more fluent with dev jargon — and a better communicator with dev teams...&lt;/p&gt;&lt;p&gt;It also gives them insights on where to find the preview URL. And also answer their &quot;why are my changes not showing up immediately&quot; question.&lt;/p&gt;&lt;p&gt;They see the build process 👀&lt;/p&gt;&lt;p&gt;When the site has finished deploying, Sharkie can visit the deploy preview:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/branch-preview.mp4&quot;&gt;Branch preview on Vercel&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Knowing that this version of the site is not live, but they can see it and share it with the stakeholders, board of directors, their neighbour, or anyone...&lt;/p&gt;&lt;p&gt;... is fundamentally cool.&lt;/p&gt;&lt;p&gt;Look at Sharkie go...&lt;/p&gt;&lt;p&gt;Opening PRs and triggering Vercel deploys 👏👏👏&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Yes, Sharkie got a round of applause from the audience at that point of the talk. Well deserved!&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/g9xna9qodduq/7-things-i-love-about-keystatic-six-pups?width=848&quot; alt=&quot;Slide for chapter 6 of the talk&quot; width=&quot;848&quot; height=&quot;1519&quot; /&gt;&lt;h2&gt;#6 — Local mode is my JAM 🍯&lt;/h2&gt;&lt;p&gt;As cool as that GitHub integration is… my JAM in Keystatic is &lt;code&gt;local&lt;/code&gt; mode 👀&lt;/p&gt;&lt;p&gt;Think of it as a stripped-down, minimal, &quot;power user&quot; version of Keystatic.&lt;/p&gt;&lt;p&gt;No GitHub, no PRs.&lt;/p&gt;&lt;p&gt;Editing your file system directly.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/local-mode.mp4&quot;&gt;Switching Keystatic to `local` mode&lt;/a&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/wniws8f23cb9/7-things-i-love-about-keystatic-minimal-ui?width=848&quot; alt=&quot;Minimal UI without GitHub context&quot; width=&quot;848&quot; height=&quot;1502&quot; /&gt;&lt;figcaption&gt;Minimal UI without GitHub context&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Why do I love this?&lt;/p&gt;&lt;p&gt;The feedback loop (hot reload on changes) is much faster.&lt;/p&gt;&lt;p&gt;This is amazing for prototyping, and also teaching/demo-ing Keystatic 🔥&lt;/p&gt;&lt;p&gt;You’ll rarely catch me using Keystatic in another mode 😅&lt;/p&gt;&lt;p&gt;A great setup is to do local mode for dev, github mode for pro ⭐️&lt;/p&gt;&lt;p&gt;Here’s a demo of that immediate feedback loop I am talking about.&lt;/p&gt;&lt;p&gt;Edit content, and the UI is instantly updated:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/local-mode-admin-ui.mov&quot;&gt;Content editing DX with local mode&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Remember: this is a &lt;strong&gt;two-way street&lt;/strong&gt;: edit the code — and the UI is also immediately updated!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/local-mode-from-code.mov&quot;&gt;Instant update when editing the code as well!&lt;/a&gt;&lt;/p&gt;&lt;hr /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/g5buv9ehuquj/7-things-i-love-about-keystatic-7-pups?width=848&quot; alt=&quot;Slide for chapter 7 of the talk&quot; width=&quot;848&quot; height=&quot;1524&quot; /&gt;&lt;h2&gt;#7 — Flexibility of config settings for file location, format&lt;/h2&gt;&lt;p&gt;As we’ve seen, you can decide where you output any collection.&lt;/p&gt;&lt;p&gt;You can also choose where images go, independently of the collection settings.&lt;/p&gt;&lt;p&gt;Helps you fit framework-specific needs 👍&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/gvjg50celeey/7-things-i-love-about-keystatic-path?width=848&quot; alt=&quot;Path configuration&quot; width=&quot;848&quot; height=&quot;1735&quot; /&gt;&lt;figcaption&gt;Path configuration&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/vkb54a1fp5lt/7-things-i-love-about-keystatic-directory?width=848&quot; alt=&quot;Image directory configuration&quot; width=&quot;848&quot; height=&quot;1329&quot; /&gt;&lt;figcaption&gt;Image directory configuration&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;You can also pick between various format (YAML, JSON) for your data.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/dheaabcyzjx7/7-things-i-love-about-keystatic-format?width=848&quot; alt=&quot;Format configuration&quot; width=&quot;848&quot; height=&quot;1511&quot; /&gt;&lt;figcaption&gt;Format configuration&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;And choose if you want multiple files, or a single &quot;Frontmatter + content&quot; output, via the &quot;contentField&quot; option.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/tuar03dyqrae/7-things-i-love-about-keystatic-multi-files?width=848&quot; alt=&quot;Keystatic collection multi-file output&quot; width=&quot;848&quot; height=&quot;1664&quot; /&gt;&lt;figcaption&gt;Keystatic collection multi-file output&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rpv4wyb8hsqt/7-things-i-love-about-keystatic-contentfield?width=848&quot; alt=&quot;contentField configuration&quot; width=&quot;848&quot; height=&quot;1579&quot; /&gt;&lt;figcaption&gt;contentField configuration&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/s454k7t28hj0/7-things-i-love-about-keystatic-single-file?width=848&quot; alt=&quot;Single file output&quot; width=&quot;848&quot; height=&quot;1511&quot; /&gt;&lt;figcaption&gt;Single file output&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Again, this lets you fit a wide range of scenarios!&lt;/p&gt;&lt;h3&gt;Here’s a pretty advanced use-case, inside a monorepo&lt;/h3&gt;&lt;p&gt;This is a prototype for the Earth Design System, for one of the clients we work with Thinkmill.&lt;/p&gt;&lt;p&gt;We use Keystatic to power the documentation of Design Systems components.&lt;/p&gt;&lt;p&gt;We want to collocate the docs with each component:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/earth-demo-first-part.mov&quot;&gt;Earth Design System — Component documentation managed by Keystatic&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Noticed the Button components inside the WYSIWYG document field in the previous video?&lt;/p&gt;&lt;p&gt;Yeah, those are the real buttons — not some screenshots or representations of the Buttons.&lt;/p&gt;&lt;p&gt;You can use React components as custom blocks inside the document editor.&lt;/p&gt;&lt;p&gt;Anyway — at the end of that video we set the path to &lt;code&gt;packages/core/src/*/docs&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Let’s find why.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;*&lt;/code&gt; symbol is where the slug for each collection entry will go.&lt;/p&gt;&lt;p&gt;If you edit the Button docs page, it will go in &lt;code&gt;packages/core/src/button/docs&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Here’s our project tree:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/earth-demo-part-2.mov&quot;&gt;The monorepo project tree&lt;/a&gt;&lt;/p&gt;&lt;p&gt;See what happens?&lt;/p&gt;&lt;p&gt;From the Keystatic site, located in a very different place in the monorepo, we’re able to output the Keystatic-generated documentation for each component alongside the button source code, tests, stories...&lt;/p&gt;&lt;p&gt;How cool is that?!&lt;/p&gt;&lt;p&gt;Despite the fact that Keystatic and the UI components are in totally different folders, we retain this &quot;two-way editing&quot; experience.&lt;/p&gt;&lt;p&gt;Change code via code, or via the Admin UI. It hot reloads immediately either way.&lt;/p&gt;&lt;p&gt;🤯&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/earth-demo-part-3.mov&quot;&gt;Two-way editing within a monorepo&lt;/a&gt;&lt;/p&gt;&lt;hr /&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rvxq2jjg1qym/7-things-i-love-about-keystatic-eight-puppies?width=848&quot; alt=&quot;Single file output&quot; width=&quot;848&quot; height=&quot;1520&quot; /&gt;&lt;figcaption&gt;Single file output&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;#8 — The new Keystatic CLI 😻&lt;/h2&gt;&lt;p&gt;Yes — I know the title says &quot;7 Things&quot;.&lt;/p&gt;&lt;p&gt;But a new one came up recently, and it definitely belongs as the grand finale!&lt;/p&gt;&lt;p&gt;(That, plus I have 8 puppies — which makes this whole count increment decoration concept work great 😅)&lt;/p&gt;&lt;p&gt;Introducing... the new Keystatic CLI 🔥&lt;/p&gt;&lt;p&gt;It encapsulates all of the great little things I love about Keystatic:&lt;/p&gt;&lt;p&gt;lightweight, no &quot;baggage&quot;, get started in seconds. It underlines and promotes the &quot;two-way&quot; editing experience&lt;/p&gt;&lt;p&gt;It is sweet as hell!&lt;/p&gt;&lt;pre&gt;npm create @keystatic
&lt;/pre&gt;&lt;p&gt;This is definitely the recommended way to try Keystatic out.&lt;/p&gt;&lt;p&gt;You can try it right now!&lt;/p&gt;&lt;p&gt;It will spin up a brand new Next.js project with Keystatic setup in local mode.&lt;/p&gt;&lt;p&gt;We plan to support more frameworks (Astro, Remix, ...) or existing projects soon ✨&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/keystatic-cli-init.mp4&quot;&gt;New Keystatic project from the CLI&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The project it spins up is delightfully minimal:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Posts&lt;/code&gt; collection in Keystatic&lt;/li&gt;&lt;li&gt;homepage listing that one post&lt;/li&gt;&lt;li&gt;&quot;single post&quot; view to read the post&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The code is simple enough to immediately grasp how to read/render Keystatic data with the built-in Reader &amp;amp; Renderer APIs 🔥&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/zrq7jb0d548i/7-things-i-love-about-keystatic-reader-api?width=848&quot; alt=&quot;Reader API code snippet&quot; width=&quot;848&quot; height=&quot;1496&quot; /&gt;&lt;figcaption&gt;Reader API code snippet&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/m7269s8aw0bn/7-things-i-love-about-keystatic-renderer?width=848&quot; alt=&quot;Renderer API code snippet&quot; width=&quot;848&quot; height=&quot;1700&quot; /&gt;&lt;figcaption&gt;Renderer API code snippet&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As I mentioned, I like how the single post page invites you to think about this &quot;two-way&quot; editing.&lt;/p&gt;&lt;p&gt;Let’s try both — and see how fast the UI updates for us!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/keystatic-cli-ui.mp4&quot;&gt;Two-way editing in the Keystatic CLI new project&lt;/a&gt;&lt;/p&gt;&lt;hr /&gt;&lt;h2&gt;Aaaannnd... that’s it!&lt;/h2&gt;&lt;p&gt;I’ve got PLENTY more to say about Keystatic. So please, ask us anything, would love to hear your thoughts.&lt;/p&gt;&lt;p&gt;Check out &lt;a href=&quot;https://keystatic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Keystatic.com&lt;/a&gt;’s docs, try out the new CLI, and tell us what you think in the GitHub discussions.&lt;/p&gt;&lt;p&gt;Help us shape Keystatic ❤️&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/kl98ra6cr1g2/7-things-i-love-about-keystatic-github-discussions?width=848&quot; alt=&quot;GitHub Discussions are a great place to get involved!&quot; width=&quot;848&quot; height=&quot;1618&quot; /&gt;&lt;figcaption&gt;GitHub Discussions are a great place to get involved!&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;If you’ve made it to the end of this MEGA post...&lt;/p&gt;&lt;p&gt;... I’m impressed.&lt;/p&gt;&lt;p&gt;You’re a LEGEND.&lt;/p&gt;&lt;p&gt;To reward you, here’s a fairly cute &quot;Thank You&quot; slide I used last night to wrap up my talk 🤗&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/7-things-i-love-about-keystatic/puppy-wave-loop.mov&quot;&gt;The cutest closing slide you will ever see 🤗&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Seeya! ❤️&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Accessibility at Thinkmill</title><link>https://www.thinkmill.com.au/blog/accessibility-at-thinkmill</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/accessibility-at-thinkmill</guid><description>How we approach the multi-faceted world of accessible design, and how improvements made for one user group can benefit all of us.</description><pubDate>Fri, 26 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;This piece was written after a conversation with Matt Stow and Joss Mackison — two of Thinkmill’s most experienced engineers on the topic of accessibility.&lt;/p&gt;&lt;p&gt;At Thinkmill, we believe accessibility touches every facet of the design of web and mobile interfaces. By making websites and applications accessible, we can ensure all users are able to access the information and services they deserve. This includes, but is not limited to: people with colour blindness and/or vision impairments, cognitive and/or neurological impairments, and impaired motor function.&lt;/p&gt;&lt;p&gt;Building accessible products is a shared responsibility between our stakeholders, designers and developers. Because a good user experience benefits all users, its functionality and usability should take priority over how an interface looks. However, it can sometimes be tough to bring this perspective to clients. Still, we aim to champion accessibility within project teams, helping others to become comfortable using the tools and techniques needed to build accessible interfaces while also adopting an inclusive mindset. Check out our &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;Accessibility&lt;/a&gt; page to learn how we help teams with this journey or continue reading for a quick rundown of some accessibility considerations.&lt;/p&gt;&lt;h2&gt;Designing for assistive technologies&lt;/h2&gt;&lt;p&gt;To design accessible experiences, we must be aware of the various input methods and assistive technologies available to our end users, such as mouth wands, eye-tracking software, screen readers, alternative keyboards, on-screen magnifiers and voice recognition. Where necessary, we’ll add (often invisible) affordances for users of assistive technologies as we know it will benefit all users, regardless of whether they have a permanent disability, a situational impairment or no restriction at all.&lt;/p&gt;&lt;p&gt;Many accessibility considerations relate to meeting users’ basic expectations, for example, using simple wording, ordering the interface in a logical way, highlighting where a user’s current focus is, using colour in a discernible manner, announcing additional context and sizing elements appropriately. We also strive to create lean applications since network accessibility and reliability can negatively affect accessibility and usability.&lt;/p&gt;&lt;h2&gt;Style guides &amp;amp; design systems&lt;/h2&gt;&lt;p&gt;Style guides are an excellent tool for upholding accessible designs and documenting patterns. A style guide can be abstracted into something functional, like a component library or design system that future projects can use to speed up development time and ensure consistent, accessible functionality throughout interfaces. Many accessibility considerations can be built into a component library and/or design system, including visual considerations (such as sufficient text contrast and indication of focus on interactive elements), screen reader considerations (such as the use of roles, states and landmarks), and physical considerations (such as ensuring an interface is keyboard navigable and sufficient hit area for click/press targets).&lt;/p&gt;&lt;h2&gt;Web Content Accessibility Guidelines (WCAG)&lt;/h2&gt;&lt;p&gt;At Thinkmill, we go &lt;em&gt;beyond&lt;/em&gt; the WCAG 2.0 AA guidelines as a simple compliance checklist. We aim to understand how real-world users interact with our interfaces by utilising a range of accessibility tools and manual testing throughout development. Our designers and developers integrate plugins into their environments (like Figma and Storybook) and use browser extensions such as axe DevTools and WebAIM’s Wave Web Accessibility Evaluation Tool. However, automated tools generally find high-level accessibility issues only, so cross-platform, cross-browser manual testing is vital. We regularly test with screen readers such as JAWS, NVDA, VoiceOver, and TalkBack and work with accessibility consultants such as &lt;a href=&quot;https://intopia.digital/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Intopia&lt;/a&gt; to ensure our interfaces meet our users’ needs.&lt;/p&gt;&lt;h2&gt;Designing for different screen sizes&lt;/h2&gt;&lt;p&gt;As a baseline, we ensure our products and services work across every screen size, from the smallest mobile to the largest desktop. We then optimise the experience based on the devices and screen sizes from which users will most likely access the application. We also group breakpoints into categories to ensure the experience is fluid and coherent between them, a feature important for users who move between devices and those who change their browser/device zoom level from the default.&lt;/p&gt;&lt;h2&gt;Designing for native&lt;/h2&gt;&lt;p&gt;Building accessible native mobile applications allows us to consider additional interactions to enhance the experience that aren’t readily available for web experiences. For example, we can take advantage of a swipe on the screen or the device’s physical volume controls to change a &lt;em&gt;star rating&lt;/em&gt; component where appropriate. Of course, screen readers are heavily used by vision impaired mobile users, so we ensure they’re able to understand and navigate between all elements and screens.&lt;/p&gt;&lt;h2&gt;Talk to us about accessibility&lt;/h2&gt;&lt;p&gt;This post speaks to just a few areas to consider when designing an accessible product. By bringing empathy and care to product design and development, we can all benefit from better digital experiences. If you’d like to learn more about the importance of accessibility, &lt;a href=&quot;https://www.thinkmill.com.au/contact&quot;&gt;reach out to Thinkmill&lt;/a&gt; today.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>What we learned applying the Thinkmill Method to a complex accounting app</title><link>https://www.thinkmill.com.au/blog/what-we-learned-applying-the-thinkmill-method-to-a-complex-accounting-app</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/what-we-learned-applying-the-thinkmill-method-to-a-complex-accounting-app</guid><description>We recently wrapped up delivery of a feature for a client’s complex accounting app. The project presented a valuable opportunity to battle-test our thinking around how we build products. We implemented a range of activities and techniques from the Thinkmill Method, and came away with valuable insights related to where it shines, what’s needed in order for it to do so, and how to improve it going forward. Here‘s what we learned along the way.</description><pubDate>Thu, 25 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;h2&gt;About the Thinkmill Method&lt;/h2&gt;&lt;p&gt;If you haven’t heard of the Thinkmill Method, it’s a combination of principles, mindsets, and approaches to teamwork that we use to build what we call “good and compelling software”. We’ve spent over 10 years helping clients develop apps and platforms, and we’ve distilled our learnings and techniques into a practice that we can pull from and add to as we evolve. &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;Learn more about the Thinkmill Method&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Project &amp;amp; team setup&lt;/h2&gt;&lt;p&gt;The project was a four-month initiative to deliver a self-service feature that let accountants import legacy ledgers from their personal computers into our client’s new cloud-based application. We set out with the intention to collaborate around a functional prototype at the earliest opportunity and iterate on it over time; improving fidelity and functionality until it became complete. This approach represented a departure from some more established ways of working that relied on designing high-fidelity mockups in tools like Figma, and the sequential handing over of work from design to development.&lt;/p&gt;&lt;p&gt;Initially we set up the &lt;strong&gt;core team&lt;/strong&gt; following the Thinkmill Method, with a Product owner, UX lead, Tech lead and Design-engineer all co-piloting the project with equal responsibility. However, due to shifting project priorities, we (the UX lead and Design-engineer) ended up becoming more responsible for guiding the project and making design decisions. Though this team structure wasn’t ideal, we were able to bring in the full team during key collaborative stages, allowing us to ensure all the questions and concerns were (mostly) covered.&lt;/p&gt;&lt;p&gt;This core squad was supported by a &lt;strong&gt;delivery squad&lt;/strong&gt; consisting of additional designers and engineers who helped complete the work and bring it to production.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/knm06h7a6rum/what-we-learned-applying-the-thinkmill-method-to-a-complex-accounting-app-ledger-project-team-setup-diagram?width=848&quot; alt=&quot;The team setup according to the Thinkmill Method vs what worked best for our team.&quot; width=&quot;848&quot; height=&quot;325&quot; /&gt;&lt;figcaption&gt;The team setup according to the Thinkmill Method vs what worked best for our team.&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Our findings&lt;/h2&gt;&lt;p&gt;Our journey to build a ledger feature through application of the Thinkmill Method gave us the  following key insights:&lt;/p&gt;&lt;h3&gt;Collaborating around the design in code helped us move fast and improved project visibility for stakeholders&lt;/h3&gt;&lt;p&gt;We strive to keep design &lt;a href=&quot;https://www.thinkmill.com.au/method#bias-towards-code&quot;&gt;as close to the finished thing&lt;/a&gt; as early, and as much as possible. Our team was comfortable with this as a standing goal, and throughout the project we were able to do just that. This mostly looked like prioritising design effort towards working in the browser, instead of static tools like Figma. By working this way:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We &lt;strong&gt;reduced confusion related to what the source of truth was&lt;/strong&gt;. (Hint…it was mostly the code).&lt;/li&gt;&lt;li&gt;Instead of spending time drawing high fidelityUI in Figma, we focused the conversation around the functional prototype. This meant &lt;strong&gt;more time improving upon the real thing so we could see results faster&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Our &lt;strong&gt;non-technical stakeholders had access to what was being built&lt;/strong&gt;. Work-in-progress was not a black box, but something they could interact with, and generate informed opinions about along the way.&lt;/li&gt;&lt;li&gt;We &lt;strong&gt;let the code do more heavy lifting&lt;/strong&gt;. We leaned into the Design System instead of creating throwaway components in Figma; pulling data from a mock API instead of spending time manually populating Figma assets with realistic data.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Cross-disciplinary collaboration can’t be assumed as a given, but your choice of tools can be transformational&lt;/h3&gt;&lt;p&gt;Our team set out with the mutual agreement to work in an open and cross-disciplinary manner, but living up to our plan ended up being more difficult than we’d anticipated for. In the early phase of the project we found it hard (as consultants) to make an impact on what appeared to be a cultural preference for our client-colleagues to stay in their respective roles (preferring more siloed ways of working).&lt;/p&gt;&lt;p&gt;After finding that mutual intent wasn’t enough to get us where we wanted to be, we managed to find a breakthrough by co-designing the feature‘s user journey from beginning to end in a collaborative white-boarding tool.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/what-we-learned-applying-the-thinkmill-method-to-a-complex-accounting-app/ledgerUserJourneyCodesign.mov&quot;&gt;Timelapse of the core team co-designing the user flow from a user experience, technical and business perspective.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;As the process evolved we found that our team had a range of differing mental models around the &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; of the user journey. By documenting it as a cross-functional team, and working from an empty canvas, we were able to fast-track our &lt;a href=&quot;https://www.thinkmill.com.au/blog/shared-understanding&quot;&gt;shared understandings&lt;/a&gt;, check our assumptions, and bring one another into a deeper and more trusting engagement with the project. We came out of the journey-mapping exercise in a much better place, and working across roles and silos got progressively easier as time went on.&lt;/p&gt;&lt;h3&gt;Even for an existing system, a schema-first approach can bring shared clarity&lt;/h3&gt;&lt;p&gt;Even though we were working within a well established architecture, we still wanted to gain a deep understanding of the existing systems, processes, and user experiences that informed it. A &lt;a href=&quot;https://www.thinkmill.com.au/method#schema-led&quot;&gt;schema-first&lt;/a&gt; approach helped us unravel the complexities of the current environment, and let the team connect to the user’s domain more comprehensively.&lt;/p&gt;&lt;p&gt;By employing a schema-first approach to this project we were able to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Build and iterate quickly on the frontend&lt;/strong&gt;. Having a schema made it possible to mock data and test the frontend with no dependency on a server-side API being ready. In simulating the behaviour and responses of a real server-side API, our mock API let the team interact with the data to understand how it would flow through the application, so they could make improvements along the way.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Speak the same language&lt;/strong&gt;. By defining a schema we were able to establish a common vocabulary and reduce the likelihood of confusion arising as a result of team members using terms from their own disciplines to describe the same thing.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Surface&lt;/strong&gt; &lt;strong&gt;data dependencies.&lt;/strong&gt; Through the process of defining a schema, we were able to identify data dependencies and interactions that may have otherwise been obscured until later in the project. Determining how different entities rely on each other, and how changes to one entity may affect another, meant that we were making informed design decisions as a team.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Visualise the data structure.&lt;/strong&gt; By presenting an object schema in a visual design tool, we were able to show team members and stakeholders how different entities within the system related to one another. This helped us see the system as a whole, rather than just individual components.&lt;/li&gt;&lt;/ul&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/xytveu467wzs/what-we-learned-applying-the-thinkmill-method-to-a-complex-accounting-app-ledger-object-map?width=848&quot; alt=&quot;Object model diagram of the ledger product core entities and how they relate to one and another.&quot; width=&quot;848&quot; height=&quot;350&quot; /&gt;&lt;figcaption&gt;Object model diagram of the ledger product core entities and how they relate to one and another.&lt;/figcaption&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Produce documentation and reference artefacts.&lt;/strong&gt; The process of defining and visualising the schema produced artefacts that also served as documentation materials for the import feature. These artefacts provided a comprehensive overview of the schema which team members could use to gain insights into the feature‘s structure and behaviour – even when working on different parts of the products.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The benefits of our schema-first approach were key to “winning-over” some team members who were accustomed to a code-first mindset, and gave them the courage and inspiration to opt for a better way of working in the future.&lt;/p&gt;&lt;h2&gt;C&lt;strong&gt;onclusions&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;The Thinkmill Method is a useful way to work, and has the potential to create an environment where teams become more productive and empowered over time.&lt;/p&gt;&lt;p&gt;Every project brings its own unique challenges and team dynamics. Understanding &lt;em&gt;how&lt;/em&gt; the method can and should adapt to meet those unique needs is just as valuable as possessing knowledge of the method‘s fundamentals.&lt;/p&gt;&lt;p&gt;Cultural habits can be hard to change within a short span of time, but moving towards more synchronous, trust-based, cross-functional ways of working always provides some degree of benefit.&lt;/p&gt;&lt;p&gt;The task of creating optimal conditions for fully empowered cross-functional teams is never ending! But that‘s a big part of what makes our work so fascinating.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Visualising a schema-led approach using FigJam</title><link>https://www.thinkmill.com.au/blog/visualising-a-schema-led-approach-using-figjam</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/visualising-a-schema-led-approach-using-figjam</guid><description>Thinkmill uses a schema-led approach to design, which involves building visual representations of data structures to help teams understand the underlying relationships and dependencies. Recently, we used the Schema Nodes FigJam Widget to visualise the schema and relationships.</description><pubDate>Wed, 24 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;At Thinkmill, we believe in taking a &lt;a href=&quot;https://www.thinkmill.com.au/method#schema-led&quot;&gt;schema-led&lt;/a&gt; approach to design. This approach involves building visual representations of data structures to help teams understand the underlying relationships and dependencies.&lt;/p&gt;&lt;p&gt;Recently, we applied this approach while working on a piece of accounting software. Our goal was to simplify and streamline the process of disbursing funds from a trust account to third-party creditors.&lt;/p&gt;&lt;p&gt;To achieve this, we needed the entire team to understand the underlying data structure and relationships. We decided to use fellow Thinkmiller &lt;a href=&quot;https://twitter.com/jordangeizer&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@jordangeizer&lt;/a&gt;’s Schema Nodes FigJam Widget to visualise the GraphQL schema and establish a common vocabulary between team members.&lt;/p&gt;&lt;p&gt;Using the widget, we listed each entity and built out each data object with its respective type. We then highlighted any dependencies or relationships between them.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/u4j7b2oxh8zz/visualising-a-schema-led-approach-using-figjam-graphql-schema-figjam?width=848&quot; alt=&quot;GraphQL Schema presented using Schema Nodes FigJam Widget&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;figcaption&gt;GraphQL Schema presented using Schema Nodes FigJam Widget&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;We also used the widget to identify any queries or mutations required, as well as their resulting data object.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/qym4vopvm797/visualising-a-schema-led-approach-using-figjam-graphql-operations-figjam?width=848&quot; alt=&quot;GraphQL Operations presented using Schema Nodes FigJam Widget&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;figcaption&gt;GraphQL Operations presented using Schema Nodes FigJam Widget&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;A visual representation of the GraphQL schema, in a shareable format such as FigJam, is much more accessible to all team members than coding it up or scribbling it on a whiteboard.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/zetdmh4bhtv8/visualising-a-schema-led-approach-using-figjam-trying-to-explain-schemas-before-schema-nodes?width=848&quot; alt=&quot;Version of the Pepe Sylvia meme with text &amp;quot;Trying to explain schemas before schema nodes&amp;quot;&quot; width=&quot;848&quot; height=&quot;642&quot; /&gt;&lt;p&gt;This approach helped us establish a common vocabulary and &lt;a href=&quot;https://www.thinkmill.com.au/blog/shared-understanding&quot;&gt;shared understanding&lt;/a&gt; of the data structure among the team. By visualising the schema and relationships, we were able to simplify and streamline the process of disbursing funds from a trust account to third-party creditors.&lt;/p&gt;&lt;p&gt;If you’re interested in trying out a schema-led approach in your team, check out &lt;a href=&quot;https://twitter.com/jordangeizer&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@jordangeizer&lt;/a&gt;’s Schema Nodes FigJam Widget &lt;a href=&quot;https://figma.com/community/widget/1237411278055495507&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>How we think about research at Thinkmill</title><link>https://www.thinkmill.com.au/blog/how-we-think-about-research-at-thinkmill</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/how-we-think-about-research-at-thinkmill</guid><description>The true goal of conducting user research is to test the assumptions that have been made, and bring confidence and clarity to a project and its vision. However, organisations often perceive research as a long, drawn-out process that won’t bring value for months. At Thinkmill, we focus on designing user research that can be acted on immediately and will make a substantial difference to your team today.</description><pubDate>Tue, 23 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;h2&gt;Research at Thinkmill is &lt;em&gt;actionable&lt;/em&gt;&lt;/h2&gt;&lt;p&gt;The product development process is rarely a linear affair. With new problems and solutions being discovered each day, user research needs to be flexible and pragmatic to aid the creative process, rather than hinder it. That’s why at Thinkmill, we focus on research that delivers actionable insights – outcomes that can be directly and immediately translated into the project and across the team.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/risa8rwreu0b/how-we-think-about-research-at-thinkmill-actionable-insights?width=848&quot; alt=&quot;We focus on uncovering concrete improvements that will move the project forward today.&quot; width=&quot;848&quot; height=&quot;292&quot; /&gt;&lt;figcaption&gt;We focus on uncovering concrete improvements that will move the project forward today.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;To ensure the research outcomes are actionable, there are few techniques we employ. We always use the current project context and timeline to develop a realistic research plan to ensure timely feedback. Because all our researchers are also practitioners, they understand the type of insights required to progress the project. Additionally, we involve engineers and leadership in the research sessions and playbacks so they can hear what real-world users have to say, deepening their empathy and understanding of the users’ needs, drivers and pain points. This first-hand experience also helps to ensure the research findings are taken onboard and have a meaningful impact within the organisation.&lt;/p&gt;&lt;h2&gt;A typical research cycle&lt;/h2&gt;&lt;p&gt;Though we will design each research plan to fit the context of the project, our typical research cycle normally includes the following steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Understanding the ask&lt;/strong&gt; - Through discussion and conversation with project stakeholders, we uncover the questions we’re looking to answer through the research effort, as well as the level of clarity we need to get to.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Designing the research&lt;/strong&gt; - Our research specialists decide on the best research methodologies and approach to answer the defined questions. The methodologies we specialise in can be found on our &lt;a href=&quot;https://www.thinkmill.com.au/services/user-research&quot;&gt;User Research page&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Defining participants&lt;/strong&gt; - In collaboration with project stakeholders and subject-matter experts, we identify the types of participants who will best help us uncover the insights we seek, as well as whether an incentive will be provided. The metrics for defining the participants are based on the context of the research project and can include things like level of expertise, company size, familiarity with the product or service, etc. We then define a plan to recruit them.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Recruitment and set up&lt;/strong&gt; - We create the parameters to recruit the desired participants and organise the recruitment through either third-party user research services, or the existing user base of the product or service. We also create any artefacts needed for the sessions based on the selected research methodologies and designed session structure.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Facilitate the sessions&lt;/strong&gt; - Now it’s time to conduct the sessions with the recruited participants. We take detailed notes, and where permitted and appropriate, record the sessions. We encourage key stakeholders to attend sessions as a learning exercise and to increase the impact of the findings within the team.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Analyse to establish actionable insights&lt;/strong&gt; - Our research specialists then synthesise the recordings and notes to uncover patterns and insights from the sessions. The findings come with actionable insights that teams can apply to the project immediately.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Document and playback&lt;/strong&gt; - We also prepare the insights into a report that is presented back to the key stakeholders and the broader team. This document acts as a stand-alone report of the research findings which the team can refer to in the future.&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;Growing user research maturity&lt;/h2&gt;&lt;p&gt;At Thinkmill we have experience working with organisations that are taking their first steps into research, as well as ones that are looking to build a permanent research capability within their team.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;First steps into user research&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;When working with an organisation that is new to user research, our goal is to help build an understanding of the value of research within the team. Research can help to achieve the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Ensure the product or service meets users’ needs&lt;/li&gt;&lt;li&gt;De-risk projects by validating with real users&lt;/li&gt;&lt;li&gt;Provide guidance for product direction&lt;/li&gt;&lt;li&gt;Give confidence to project teams&lt;/li&gt;&lt;li&gt;Create alignment through research-backed data&lt;/li&gt;&lt;li&gt;Reduce prevalence of personal opinions and biases in product discussions&lt;/li&gt;&lt;li&gt;Establish a process that will enable future rounds of research&lt;/li&gt;&lt;li&gt;Present findings in a standalone report that can be referred to again and again&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;strong&gt;Building a culture of UX research&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Once the initial value of user research has been accepted by the team, we can begin to embed it into the product development cycle, which means:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Small and iterative research cycles that guide product decisions&lt;/li&gt;&lt;li&gt;A culture of using research to answer product questions rather than relying on opinion&lt;/li&gt;&lt;li&gt;An understanding that a week of research upfront will save time and cost later in the project&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Talk to us about user research&lt;/h2&gt;&lt;p&gt;If the principles and methodology outlined here sounds like something that could benefit your project, have a &lt;a href=&quot;https://www.thinkmill.com.au/contact&quot;&gt;chat with us&lt;/a&gt; today. We believe that user research can bring valuable insights to any stage of a project and are excited to work with teams who are ready to reach out to their user base and uncover how to improve their products.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Jordan Geizer has made another Figma plugin that’s near and dear to our heart.</title><link>https://www.thinkmill.com.au/blog/jordan-geizer-schema-nodes-plugin</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/jordan-geizer-schema-nodes-plugin</guid><description>Schema Nodes makes it easy to create a visual representation of your product’s GraphQL schema. Handy for practicing schema-led design &amp; engineering.</description><pubDate>Thu, 11 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;&lt;a href=&quot;https://www.figma.com/community/widget/1237411278055495507&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Checkout Jordan‘s Schema Nodes plugin here&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>We travelled to Chennai to present a progress update of the work we’re doing for the team at Anywhere Works</title><link>https://www.thinkmill.com.au/blog/chennai-trip-design-system-may-2023</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/chennai-trip-design-system-may-2023</guid><description>A group of Thinkmillers returned to Chennai to present a progress update of the work we’re doing for the team at Anywhere Works, and share our thoughts on effective cross–functional product design &amp; development.</description><pubDate>Mon, 01 May 2023 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/j2rtnvj4xtu7/jordan-geizer-chennai?width=1200" length="196400" type="image/jpeg"/></item><item><title>Meet Keystatic</title><link>https://www.thinkmill.com.au/blog/meet-keystatic</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/meet-keystatic</guid><description>Thinkmill founder Jed Watson introduced React Sydney to Keystatic our new file-based content management system that makes Markdown, JSON and YAML content in your codebase editable by humans.</description><pubDate>Thu, 13 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;For more information on Keystatic, &lt;a href=&quot;https://keystatic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;checkout the website&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>​Accessibility in Design Systems</title><link>https://www.thinkmill.com.au/blog/accessibility-in-design-systems</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/accessibility-in-design-systems</guid><description>Jordan Overbye spoke at React Sydney about our collaboration with the Australian Government’s Department of Agriculture, Fisheries &amp; Forestry to implement AgDS – a modern and accessible React Design System. AgDS achieved AA-level compliance against WCAG 2.1 and is noted as an exemplary implementation of an accessible React-based design system by independent accessibility consultants, Intopia.</description><pubDate>Thu, 13 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Read our &lt;a href=&quot;https://www.thinkmill.com.au/work/daff&quot;&gt;case study&lt;/a&gt; to learn more about our our collaboration with the Australian Government’s Department of Agriculture, Fisheries &amp;amp; Forestry.&lt;/p&gt;&lt;h2&gt;Transcript&lt;/h2&gt;&lt;p&gt;(00:05)&lt;/p&gt;&lt;p&gt;Thank you. As Jed said, I’m from Queensland on the Sunshine Coast. This is actually my first React Sydney event ever. Thank you all for having me. Very exciting to be here tonight. I’m going to be talking about &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;Accessibility&lt;/a&gt; and &lt;a href=&quot;https://www.thinkmill.com.au/services/design-systems&quot;&gt;Design Systems&lt;/a&gt;, so let’s get into it. I’m Jordan, and I work at a company called Thinkmill. For the last year, I’ve been working with the Federal Australian Government’s Department of Agriculture, Fisheries, and Forestry on a &lt;a href=&quot;https://design-system.agriculture.gov.au/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;new open-source design system&lt;/a&gt;. As it is open source, all the code is available to &lt;a href=&quot;https://github.com/steelthreads/agds-next&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;view on GitHub&lt;/a&gt;, and we publish everything into &lt;a href=&quot;https://design-system.agriculture.gov.au/foundations/technical-overview#npm-packages&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;npm&lt;/a&gt; as well. So, I’ve put a little link there if anyone’s interested in checking that out.&lt;/p&gt;&lt;p&gt;At the end of last year, we undertook a big accessibility and usability audit which was facilitated by a company called &lt;a href=&quot;https://intopia.digital/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Intopia&lt;/a&gt;. Tonight, we’ll talk about what the Agriculture Design System (AGDS) is, followed by a summary of the accessibility and usability audit last year. And finally, we’ll look at how our team approaches accessibility and usability at a design system level.&lt;/p&gt;&lt;p&gt;(01:15)&lt;/p&gt;&lt;p&gt;Let’s get into it by looking at what AGDS is. To give you some visual context, this is our &lt;a href=&quot;https://design-system.agriculture.gov.au/storybook/index.html?path=/story/testing-kitchen-sink--application-layout&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Storybook kitchen sink example&lt;/a&gt;, essentially a dump of all our components in the system in no particular order. We’re using Storybook here because it’s a great tool for switching between our light and dark palettes. AGDS is also a themeable system. In this video right now, I’m switching between what we call the “Agriculture” theme (which is what we use in the department), and to what is known as the “Gold” theme (we’ll take a look at gold in just a minute).&lt;/p&gt;&lt;p&gt;As you can see one part of AGDS is a &lt;a href=&quot;https://www.thinkmill.com.au/services/react&quot;&gt;React&lt;/a&gt; component Library, but at a higher level it’s really a shared language between the design, development, and content practitioners at the department. As well as the components that you just saw we do have templates for common page compositions and patterns that exist in the department and for the designers we have a Figma library where all the components and tokens live.&lt;/p&gt;&lt;p&gt;(02:24)&lt;/p&gt;&lt;p&gt;In the video I demonstrated switching between the Agriculture theme and the Gold theme. A lot of you probably don’t know what Gold is so we’re going to look into that right now. &lt;a href=&quot;https://gold.designsystemau.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Gold is an open-source design system for building government services and products&lt;/a&gt;. It’s designed to replace the former Australian government design system which was being funded by the DTA but unfortunately decommissioned in 2021.&lt;/p&gt;&lt;p&gt;Gold and AuDS have been widely used across many government products before: the Department of Health, the Department of Veterans Affairs, as well as the New South Wales government at some point have used Gold or AuDS before.&lt;/p&gt;&lt;p&gt;It’s no secret that Gold has been the inspiration for AGDS. We’ve really tried to take the aesthetic and design principles but extend that to meet the needs of the department. The main reason for that is because Gold did a really good job in terms of &lt;a href=&quot;https://www.thinkmill.com.au/services/ux-ui-design&quot;&gt;usability&lt;/a&gt; and accessibility, so a lot of research went into the development of Gold and we wanted to leverage as much of that as possible. But gold was built a long time ago and things move very quickly in Tech, so I like to think of AGDS as a modern interpretation of Gold. We’ve built everything from the ground up using React and TypeScript and all the styling is done with &lt;a href=&quot;https://github.com/emotion-js/emotion&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Emotion&lt;/a&gt; which is another Thinkmill package for CSS-inJS, and we have a bunch of really cool shiny toys for our &lt;a href=&quot;https://github.com/Thinkmill/monorepo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;monorepo setup&lt;/a&gt; and documentation site.&lt;/p&gt;&lt;p&gt;(04:05)&lt;/p&gt;&lt;p&gt;At a really high level this is the architecture of the design system. We have the components which are things that you typically think of when you think of a design system – things like a &lt;code&gt;button&lt;/code&gt;, or &lt;code&gt;accordion&lt;/code&gt;, or a &lt;code&gt;select&lt;/code&gt; component. But really they are a composition of our primitive components: things like &lt;code&gt;box&lt;/code&gt; and &lt;code&gt;columns&lt;/code&gt; and &lt;code&gt;text&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;We expose these components to our consumers to also build components that we don’t have in the system. The cool thing with primitive components are they’re really flexible and they have style props which allow us to interact with our design tokens, so the values for a colour, typography, border, etc., we have a nice way of of referencing those values in React in a way that’s both &lt;a href=&quot;https://en.wikipedia.org/wiki/Type_safety&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;type-safe&lt;/a&gt; and works in a responsive way as well.&lt;/p&gt;&lt;p&gt;(04:52)&lt;/p&gt;&lt;p&gt;As an example of that here’s our global &lt;code&gt;alert&lt;/code&gt; component that typically sits at the top of the page, you can see it’s got a background colour with an icon and some typography. The code for this component is pretty simple, it’s just a composition of flex-box, an icon, and a couple of buttons in there as well. This syntax might look weird at first for people who aren’t familiar with this, but it is a really powerful and flexible way for handling styling in our design system. As I mentioned before it’s really good to be able to make sure everything’s mapped back to our tokens correctly.&lt;/p&gt;&lt;p&gt;(05:37)&lt;/p&gt;&lt;p&gt;Now, let’s get into the Accessibility audit. As mentioned, it was facilitated by a third-party company called Intopia. Neither the department nor Thinkmill has any affiliation with them whatsoever, and they did a fantastic job. The audit was split up into three phases. We did an audit of the components in isolation, followed by an audit of our page templates and documentation site, which is built using the design system components. Finally, we had some usability sessions. These sessions were with the users who had specific accessibility requirements: things like [requiring] a screen reader (we had some low vision users who needed screen magnification software), and neurodivergent users as well. Having &lt;a href=&quot;https://storybook.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Storybook&lt;/a&gt; for this phase was amazing because it does such a good job at being able to provide a playground for testing components in isolation. In this video, I’m just stepping through our text input component, seeing all the different variations that it can be in, and the auditors had a nice little playground down the bottom for changing all the props and performing their checks.&lt;/p&gt;&lt;p&gt;(06:46)&lt;/p&gt;&lt;p&gt;Phase one. What we were looking for here is making sure the components respond nicely to both screen-reader and keyboard input, making sure the components respond nicely when placed in Windows high contrast mode. When you zoom up all the way to 400%, make sure everything responds nicely. Of course, making sure that colors have sufficient contrast between background and foreground elements, and just making sure the HTML is valid. There were some cases where we were rendering a &lt;code&gt;div&lt;/code&gt; instead of a &lt;code&gt;span&lt;/code&gt; which yielded some HTML validation errors that can cause a lot of issues for screen readers. I’m pretty proud to say our team got an 80% pass for AA compliance before we did any remediation work.&lt;/p&gt;&lt;p&gt;A couple of issues that came out of this phase were things like a button loading state. What we were doing is we have a loading prop on the button, and when that’s true, we just overlay the text with some loading dots. But what we actually had to do was make sure there was our &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Aria-live region&lt;/a&gt; inside the button, and when the loading prop is true, we insert the text that we want to be announced to the screen reader, because simply just toggling the buttons on and off with the ternary operator wasn’t enough for some screen readers.&lt;/p&gt;&lt;p&gt;(08:11)&lt;/p&gt;&lt;p&gt;Things like &lt;a href=&quot;https://www.smashingmagazine.com/2022/06/guide-windows-high-contrast-mode/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Windows high contrast mode&lt;/a&gt; – our date range picker, the selected date range, you can’t see in Windows high contrast mode because we were relying on background colour alone. So what we had to do was make sure we put a font weight to indicate that as well as an outline on the selected dates. Here’s an example of content not reflowing when you zoom all the way to 400%. So we forgot to add some scrolling to our main nav component on the left here, so you couldn’t see the items that were cut off by your screen.&lt;/p&gt;&lt;p&gt;Then phase two, once we made all the fixes in that were caught there, we moved on to phase two where we audited the templates and documentation site.&lt;/p&gt;&lt;p&gt;(08:48)&lt;/p&gt;&lt;p&gt;Here’s &lt;a href=&quot;https://design-system.agriculture.gov.au/templates/sign-in&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;an example&lt;/a&gt; of one of the &lt;a href=&quot;https://design-system.agriculture.gov.au/templates&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;templates&lt;/a&gt; we offer. It’s just a simple sign-in form. Although it is a really simple layout, it does a good job of demonstrating how different components can be composed together on a page. We have some skip links at the top of the page followed by main nav, followed by main content area, and a footer. Then we have a form with client-side validation. We have a button with a loading state to mimic an API call, and then we have an API response returning an error. We’re just testing here that our components fit nicely when placed together on a page and that we’re managing focus correctly as well. We did a pretty good job in that phase as well. We got an 86% pass for &lt;a href=&quot;https://www.w3.org/WAI/WCAG2AA-Conformance&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AA compliance [WCAG 2.1]&lt;/a&gt;. A lot of the issues that came out of this were on our documentation. We have a live code editor for some snippets, and there’s a couple of issues there with keyboard input.&lt;/p&gt;&lt;p&gt;(09:50)&lt;/p&gt;&lt;p&gt;Once that was done, we moved on to the usability sessions. What we were doing, we were observing users with specific accessibility requirements: screen readers, etc. What we  asked them to do was register a pet for a fake government service we created. I’m just going to let this video play and talk a little bit about what’s going on. This is a form flow for registering a pet for a fake government service. The reason why we built this instead of using a product that exists in the department already, was that in order to use some of these products, you need quite specific domain knowledge in agriculture, which is really hard to recruit for. So what we decided to do was take the UI and UX patterns that exist in the department, which is taking these huge forms, hundreds of form fields long, and they get split up into multiple tasks, which then gets further split up into multiple sections. The user has stepped through that in a flow. We tried to mimic that as best as we could using something that a regular user could understand. Here, we’re just registering a fake pet.&lt;/p&gt;&lt;p&gt;(11:02)&lt;/p&gt;&lt;p&gt;The findings that came out of this were really fascinating. The screen reader users, they provided overall positive feedback. They were able to navigate around the page using shortcuts and interact with our components using keyboard inputs as expected. But the visual users were pretty confused by some elements on the page. They were confused by some icons looking like radio buttons or unclear with some copy that was on the page as well. So we knew we had to do some work for our visual users. Before we made any changes, here’s what one page in that form looked like, and here it is after. I’ll just quickly show before and after again. Some things to note, we added some colour and made the progress indicator (which is what you see on the left) to make it more look like a timeline of step test rather than just these individual tasks that can be completed in any order. Added some subheading above the progress indicator as well to make it more clear what step you’re on. We consulted with the content team to make our labels in that component as clear as possible. And finally, down the bottom here, this is our summary list component. We made sure that the key and the value were as close together as possible because a lot of users were confused. They weren’t sure that the key and the value were related. They were like, &lt;em&gt;&quot;Well, this one thing’s all the way over here, and this is over here.&quot;&lt;/em&gt; This was really interesting to me because it showed that accessibility does not mean usability. You can spend a lot of time making everything as accessible as possible, but as soon as you get it in front of your users, they might not understand what that means. It’s really important that usability sessions happen early.&lt;/p&gt;&lt;p&gt;(13:07)&lt;/p&gt;&lt;p&gt;At the end of this three-phase approach, &lt;a href=&quot;https://intopia.digital/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Intopia&lt;/a&gt; gave the AGDS team a certificate of compliance, and they said, &lt;em&gt;&quot;Hey, you guys did a great job.&quot;&lt;/em&gt; Because it’s open source, we can now show any issues that come up in the future to potential future clients as well, saying, &lt;em&gt;&quot;Here’s how you can implement an accessible React-based design system in the future.&quot;&lt;/em&gt; So that was great, but that was no accident. There was a lot of hard work that went into making that possible. I wanted to share with you how we tackle that as a team.&lt;/p&gt;&lt;p&gt;(13:27)&lt;/p&gt;&lt;p&gt;Let’s get into it. I think the main reason for this is that we make accessibility a core pillar of AGDS. It’s really fundamental to everything that we do. Whenever a design decision is made or a code change is made, we’re asking ourselves, &lt;em&gt;&quot;Is this the most accessible thing we can do, or is there an alternative solution?&quot;&lt;/em&gt; It does take a lot of time, but &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;it is really fundamental to what we do, and we have that printed on our website&lt;/a&gt;, so it’s really easy to rationalise that. It is quite easy to justify this time spent in doing that because, yeah, it is a government service, and we are legally obliged to make it as accessible as possible. But I think for private companies who don’t have that same requirement, you still have to find a reason why accessibility is important to you and write that somewhere because if you don’t, it can very easily fall through the cracks.&lt;/p&gt;&lt;p&gt;(14:35)&lt;/p&gt;&lt;p&gt;Another thing we do is &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;our way of working&lt;/a&gt;. We have a philosophy that &lt;a href=&quot;https://www.thinkmill.com.au/method#design-is-a-team-sport&quot;&gt;design is everyone’s job&lt;/a&gt;, and by design, I don’t really mean jumping into Figma and changing border radii of components and things like that. I mean &lt;em&gt;design as in the act of problem solving&lt;/em&gt;.  Whenever a problem is brought to us, we jump on a call together and discuss what needs to be done (both engineers and designers). This gives everyone an opportunity to bring their areas of expertise in and just catch things early rather than a traditional software setup where you have designers in one room and developers in another room, and the developers just do whatever the designers say and slap some ARIA tags on the end and try to make it accessible. We try to do everything in a collaborative manner.&lt;/p&gt;&lt;p&gt;(15:32)&lt;/p&gt;&lt;p&gt;The other thing that we do is we try to &lt;a href=&quot;https://www.thinkmill.com.au/method#bias-towards-code&quot;&gt;get things into code as quickly as possible&lt;/a&gt;. While Figma is an awesome tool for coming up with ideas, and testing ideas quickly, it is just a picture at the end of the day (it’s not what the end user is going to see). Being able to get ideas into code quickly, testing with a screen reader, testing with a keyboard, seeing how it behaves in a real browser is amazing for us. That’s not just unique to our team; it’s just the standard way of working at Thinkmill. Boris and Jed have done a great job of publishing that on the Thinkmill website. If anyone is interested, there’s a link available.&lt;/p&gt;&lt;p&gt;(16:19)&lt;/p&gt;&lt;p&gt;Here’s a tool we use for prototyping in code. It’s called &lt;a href=&quot;https://github.com/seek-oss/playroom&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Playroom&lt;/a&gt;, it’s a website you can visit where you get a code editor at the bottom with all your components loaded in, providing autocomplete and snippets you can use. The top section shows a live demo of your code, and you can preview it in various screen sizes that you can configure. You can share that [demo] with your team as well - the URL syncs up with the code, making it easy to share around the team.&lt;/p&gt;&lt;p&gt;This an awesome tool for us to use internally (to be able to just jump into this write some code share it with the designers) but usually we have some designers or developers come to us and say &lt;em&gt;“Hey like what’s the most accessible way to to do this or or do that”&lt;/em&gt; and it’s awesome just to jump on a call with them show them playroom do it together and then they can fix the problem in their code base. This has been an amazing tool for us.&lt;/p&gt;&lt;p&gt;(17:24)&lt;/p&gt;&lt;p&gt;Another thing we do is make sure to manually test in as many different browsers and assistive technologies as possible. I know as Engineers we love to automate as much as possible but unfortunately there are just so many issues that aren’t captured in automated testing. Gov.uk published an article saying that only something like 30% of accessibility issues can be captured with automated tests so there’s just no way around not doing manual testing. We try and do a bunch of different manual testing with every code change or design change.&lt;/p&gt;&lt;p&gt;I just wanted to give a shout out to this website called &lt;a href=&quot;https://assistivlabs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Assistiv Labs&lt;/a&gt;. It’s like &lt;a href=&quot;https://www.browserstack.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Browser Stack&lt;/a&gt; but for screen readers so you can remotely log on test a website in &lt;a href=&quot;https://www.nvaccess.org/about-nvda/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NVDA,&lt;/a&gt; or &lt;a href=&quot;https://www.freedomscientific.com/products/software/jaws/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jaws,&lt;/a&gt; or voiceover, Windows high contrast as well, so that’s that’s a really great tool for testing.&lt;/p&gt;&lt;p&gt;(18:30)&lt;/p&gt;&lt;p&gt;And finally, having multiple respected accessibility references. Whenever you want to build something new it’s it’s good to reference someone has someone’s done it before. I always love to reference the &lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Working Aria Initiative (WAI-ARIA) authoring practice guides&lt;/a&gt;. They have really clear guidance on how to implement modern components such as dialogue, sliders, carousels, or anything like that. But it is quite verbose, and the code is very outdated at times, so seeing how people tackle those issues in a more modern application like WK or Reach UI, React ARIA, Radix UI s is really useful as well.&lt;/p&gt;&lt;p&gt;To wrap up um I think &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;accessibility&lt;/a&gt; needs to be made a priority not just a nice to have. Check out the &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;Thinkmill Method&lt;/a&gt; because this method of working has a direct impact on the accessibility of an application. And finally, do not solely rely on automated testing – there’s there’s just no way around it yet.&lt;/p&gt;&lt;p&gt;Thank you so much. Any questions or otherwise I’ll leave it at that&lt;/p&gt;&lt;p&gt;[Applause].&lt;/p&gt;&lt;h3&gt;Audience Q&amp;amp;A&lt;/h3&gt;&lt;h4&gt;Q: What are the most suitable methods for testing different screen resolutions across devices?&lt;/h4&gt;&lt;p&gt;(19:53)&lt;/p&gt;&lt;p&gt;A: A bunch of different devices. A phone, iPad, a laptop, a smaller tablet as well. I try to do a bunch of Windows [testing] as well as Apple. I don’t think there’s like a silver bullet or anything just yet.&lt;/p&gt;&lt;h4&gt;&lt;strong&gt;Q: Is the primary motivation of making AGDS open source for others outside government to use?&lt;/strong&gt;&lt;/h4&gt;&lt;p&gt;(20:22)&lt;/p&gt;&lt;p&gt;A: Mainly for other government departments to use as well that are not in the Department of Agriculture. The government are trying to work more in the open, but a lot of a lot of people don’t. Mainly to allow other government departments to pick it up in the future (hopefully as a Gold replacement one day potentially).&lt;/p&gt;&lt;h4&gt;&lt;strong&gt;Q: What kind of tools can you use for testing Accessibility?&lt;/strong&gt;&lt;/h4&gt;&lt;p&gt;(21:10)&lt;/p&gt;&lt;p&gt;Good question. Engaging with a third-party accessibility company would be good because they can perform audits and give you that certificate of compliance. But if you’re just doing it on your own, you’re kind of just saying yeah like trust me in a way. You can use the WCAG guidelines as your Baseline, they they do have pretty clear guidance like saying this type of component needs to respond to these keyboard events, in order to do this pattern you need these elements, you can follow that but it is quite verbose.&lt;/p&gt;&lt;h4&gt;&lt;strong&gt;What inspiration from React Aria, Radix UI, Radix Primitives, Reach UI were used to save development time?&lt;/strong&gt;&lt;/h4&gt;&lt;p&gt;(21:53)&lt;/p&gt;&lt;p&gt;We decided to try not to use any external libraries at first because we we were trying to be a Gold replacement at first (which had almost zero dependencies), but as the design system grew we needed a combobox component, so in that case we use uh Downshift. There’s like some things where we would save a lot of time by using like Radix or Reach UI but we didn’t want to be locked in to to their way of thinking. I think Reach UI is great but they just like got deprecated and don’t support React 18 which is like... oh man like if you’re relying on them what do you do?&lt;/p&gt;&lt;h4&gt;&lt;strong&gt;Q: From a managing multiple design systems perspective with Gold and AGDS now. Do you think more departments will branch out and invest in their own design system?&lt;/strong&gt;&lt;/h4&gt;&lt;p&gt;(22:50)&lt;/p&gt;&lt;p&gt;I don’t think we’ve we’ve reached that bridge yet, but hopefully other departments can can take this as a starting point and extend it and change it to meet the needs of their department, because every department has quite specialised requirements. It is possible now for another department to pick it up and and theme it, but we haven’t seen that just yet.&lt;/p&gt;&lt;h4&gt;&lt;strong&gt;Q: Why did you use Emotion, and did you evaluate anything else? What are your thoughts on CSS-in-JS?&lt;/strong&gt;&lt;/h4&gt;&lt;p&gt;(24:05)&lt;/p&gt;&lt;p&gt;Emotion is great for design systems right now. It doesn’t really require tooling so getting it into existing products that have ways of styling - we can incrementally adopt components without having to change too much. I know a lot of Thinkmill people are exploring Vanilla Extract and some other cool styling libraries but we’ve gone with the classic Emotion for now.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/xfkvknnxtl9w/accessibility-in-design-systems-cover-image?width=1200" length="553476" type="image/png"/></item><item><title>Integrating Keystatic with Astro’s Content Collections</title><link>https://www.thinkmill.com.au/blog/integrating-keystatic-with-astro-s-content-collections</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/integrating-keystatic-with-astro-s-content-collections</guid><description>Simon has a new video walking you through the process of implementing Keystatic to power parts of the site you’re on right now! Built with Astro, the Thinkmill website site leverages the power of two new features recently added to the framework: Markdoc, and Content Collections.</description><pubDate>Sun, 02 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;For more information on Keystatic, &lt;a href=&quot;https://keystatic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;checkout the website&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>How a well thought out design system is turbocharging Keystatic development</title><link>https://www.thinkmill.com.au/blog/how-a-well-thought-out-design-system-is-turbocharging-keystatic-development</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/how-a-well-thought-out-design-system-is-turbocharging-keystatic-development</guid><description>The Labs team are making great strides with their new codebase-friendly CMS: Keystatic. Here’s an example of how a good design system can turbocharge front-end development.</description><pubDate>Fri, 31 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;&lt;a href=&quot;https://keystatic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Learn more about Keystatic here&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Route Map - a lightweight schema for defining a UX blueprint</title><link>https://www.thinkmill.com.au/blog/route-maps</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/route-maps</guid><description>A Route Map is essentially a lightweight URL schema for an app or experience. This is how we use it.</description><pubDate>Mon, 20 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Let’s talk process and methods.&lt;/p&gt;&lt;p&gt;We recently published our basic methodology for making good and compelling software, and alongside the principles, we have an extensive playbook/toolbox for tackling different kinds of problems when making software.&lt;/p&gt;&lt;p&gt;We’ll be publishing these as we go, but for today, here’s a quick look at one of these tools – a &lt;strong&gt;Route Map.&lt;/strong&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A Route Map is essentially a lightweight URL schema for an app or experience. We think of them like a UX blueprint.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;An example to illustrate: say we sell a suite or products to professional service companies who themselves have clients – eg. law firms or similar.  Consider the administration needs of this setup:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Account administration&lt;/strong&gt; – management of the relationship between software provider and the prof. services company.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Org administration&lt;/strong&gt; – management of organisation matters by eg staff members&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Client experience&lt;/strong&gt; – the app used by clients of the prof. services company.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;First, we need to describe this &lt;em&gt;conceptually&lt;/em&gt; – that is, what are the jobs that fall under each of these three top level concerns.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ut9tp8e8qmtl/route-maps-01?width=848&quot; alt=&quot;How might we draw the product lines? Account Administration: admin-centric management of account level concerns. Org Administration: Staff-centric management of Org-level concerns. Makes org admin easy. Client experience: Client-user-centric. Simple, polished. Makes the client org look good.&quot; width=&quot;848&quot; height=&quot;560&quot; /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/pad23ci9bi0e/route-maps-02?width=848&quot; alt=&quot;Mind map diagram of Administration with branches of Account Admin, Customer Org Admin, Client Experience, Emails.&quot; width=&quot;848&quot; height=&quot;539&quot; /&gt;&lt;p&gt;That’s useful ☝️  Now we can start to categorise related jobs and start inferring an IA and UX. But this conceptual view lacks opinions about UX, UI or technical design concerns.&lt;/p&gt;&lt;p&gt;So we can articulate this same thing in a &lt;strong&gt;route map&lt;/strong&gt; to start to express some of these opinions – not to lock them in, but to facilitate the conversation and help make trade-offs between the UX / engineering / business perspective which are in a (hopefully healthy) tension.&lt;/p&gt;&lt;p&gt;A route map – a light-weight URL schema – is a bit special because it’s accessible enough to all practices in a cross functional team. To create it, we use a small set of basic concepts...&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/heq4qq23gl98/route-maps-03?width=848&quot; alt=&quot;Route map legend displaying icons for Page/Scene/Route, Overlay, User action, Display of information, External link&quot; width=&quot;848&quot; height=&quot;419&quot; /&gt;&lt;p&gt;With that, we build up a picture that looks like this:&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ba1bd11cf6ui/route-maps-04?width=848&quot; alt=&quot;This is an example of a Route Map ☝️&quot; width=&quot;848&quot; height=&quot;481&quot; /&gt;&lt;figcaption&gt;This is an example of a Route Map ☝️&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Now we are beginning to express initial opinions about the UX and IA of this system.&lt;/p&gt;&lt;p&gt;We’re also starting to think about UI functionality by addressing CRUD patterns. Eg. we think a user should view a list of contacts on the Contacts page. And perform bulk actions on it in the same page. But should edit the details of the contact on a separate page.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/gt9352czd24a/route-maps-05?width=848&quot; alt=&quot;Subset of the route map diagram showing individual items and their associated concept type (Overlay, User Action, etc)&quot; width=&quot;848&quot; height=&quot;347&quot; /&gt;&lt;p&gt;The cross functional team can shape these opinions and iterate on it together, bringing in their respective perspectives.&lt;/p&gt;&lt;p&gt;Like most artefacts, a route map is a point-in-time artefact. It takes forward the &lt;a href=&quot;https://www.thinkmill.com.au/blog/shared-understanding&quot;&gt;shared understanding&lt;/a&gt; of a cross-functional team that is designing a new product or experience.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Related: &lt;a href=&quot;https://twitter.com/GiselleStidston&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@GiselleStidston&lt;/a&gt; wrote a piece on this topic titled &lt;a href=&quot;https://www.thinkmill.com.au/blog/all-about-artefacts&quot;&gt;All about artefacts&lt;/a&gt;. Worth a read.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A fleshed out route map becomes a UX blueprint for:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;interface prototyping&lt;/li&gt;&lt;li&gt;app scaffolding and draft APIs&lt;/li&gt;&lt;li&gt;product and business stakeholders, who can more easily picture the product and refine the ambition and scope&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Finally – why not just draw a typical IA diagram? You can if that’s your jam. Our view is that defining an IA is a process best informed by synthesising multiple takes on a problem – conceptual, route-based, flows, etc. But that’s another thread.&lt;/p&gt;&lt;p&gt;That’s all on Route Maps. Thanks for reading. We’re publishing our ways of working and how we build shared understanding in our &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;Method page&lt;/a&gt; ✌️&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Celebrating a decade of partnership with P.JOHNSON</title><link>https://www.thinkmill.com.au/blog/celebrating-a-decade-of-partnership-with-p-johnson</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/celebrating-a-decade-of-partnership-with-p-johnson</guid><description>This month, we celebrated P.Johnson being a Thinkmill client for 10+ years — and a true partner. Not only did our co-founder’s wedding see the groom and his groomsmen don P.Johnson suits, but we’re proud to have grown together with them over this decade of learning and progress. Here’s to the next decade of success (and weddings).</description><pubDate>Wed, 15 Mar 2023 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/tu8nq405odf7/p-johnson?width=1200" length="345660" type="image/jpeg"/></item><item><title>Project walkthrough: Agriculture Design System</title><link>https://www.thinkmill.com.au/blog/project-walkthrough-agriculture-design-system</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/project-walkthrough-agriculture-design-system</guid><description>Watch Jordan Overbye and Nathan Simpson outline the origins of our recent collaboration with the Australian Government’s Department of Agriculture, Fisheries &amp; Forestry to implement a modern and accessible React Design System.</description><pubDate>Wed, 08 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;&lt;a href=&quot;https://www.thinkmill.com.au/work/daff&quot;&gt;Learn more about our collaboration with the Department of Agriculture, Fisheries &amp;amp; Forestry&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ox5pkqjnrq8j/agds-project-walkthrough?width=1200" length="537923" type="image/png"/></item><item><title>The $64,000 question to create more accessible and inclusive experiences</title><link>https://www.thinkmill.com.au/blog/64k-question-more-accessible-inclusive-experiences</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/64k-question-more-accessible-inclusive-experiences</guid><description>Empathising with your blind users to create better experiences for all.</description><pubDate>Fri, 03 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Catering to the needs of &lt;strong&gt;all&lt;/strong&gt; your users can be difficult. However, as you design and build software, asking one straightforward question focused on blind screen reader users will help improve the experiences you create for all.&lt;/p&gt;&lt;p&gt;As you continue to ask this question, “accessibility” should become an opportunity for creating great experiences rather than a burden. You’ll learn which patterns to use or avoid, and reduce the anxiety felt should you need to “make things accessible” after the fact.&lt;/p&gt;&lt;p&gt;Okay, so what’s the question? Unashamedly, it’s surprisingly simple: &lt;strong&gt;If I couldn’t see the screen, how would I…?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Then, for every element and interaction in your interface, you’d expand on the question with one or more of the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;If I couldn’t see the screen, how would I &lt;strong&gt;know what this is?&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;If I couldn’t see the screen, how would I &lt;strong&gt;know what this does?&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;If I couldn’t see the screen, how would I &lt;strong&gt;know what just happened?&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In my experience, we ensure we design and build the right, most usable features by regularly asking these questions.&lt;/p&gt;&lt;p&gt;However, if you can’t answer a question easily or at all, something is amiss with your solution.&lt;/p&gt;&lt;p&gt;If you can’t answer a question in the design phase, you may need to re-think the user experience. If you can’t answer them during development, solutions may come from correctly using semantic HTML, applying necessary ARIA attributes, or managing a user’s focus.&lt;/p&gt;&lt;p&gt;If you still don’t have answers after making changes, your feature may be unfit for purpose and could unnecessarily exclude some users.&lt;/p&gt;&lt;p&gt;On the plus side, needing to redesign or rebuild the feature will often result in a better experience, which is the ultimate goal for our users.&lt;/p&gt;&lt;h2&gt;Practising asking the questions&lt;/h2&gt;&lt;p&gt;Let’s look at a standard pattern of navigation and wayfinding, and see how we would answer these questions.&lt;/p&gt;&lt;p&gt;Imagine this screenshot of browsing XYZ Corp’s website.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/yi02ba5bs73t/64k-question-more-accessible-inclusive-experiences-xyz-corp?width=848&quot; alt=&quot;Mockup of a website Best Sellers page for XYZ corporation showing a list of product results.&quot; width=&quot;848&quot; height=&quot;653&quot; /&gt;&lt;figcaption&gt;Mockup of a website Best Sellers page for XYZ corporation showing a list of product results.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Sighted users can see that they are currently on the Best Sellers page, and it has four additional navigation items. By clicking one of the other links, they would know they have navigated to a new page.&lt;/p&gt;&lt;p&gt;But as a blind screen reader user, how would they be informed of this? Let’s examine the code and explain how it answers the essential questions.&lt;/p&gt;&lt;pre&gt;&amp;lt;title&amp;gt;Best Sellers - XYZ Corp&amp;lt;/title&amp;gt;

…

&amp;lt;header&amp;gt;
  &amp;lt;a href=&quot;/&quot;&amp;gt;
    &amp;lt;img src=&quot;/images/logo.svg&quot; alt=&quot;XYZ Corp&quot; /&amp;gt;
  &amp;lt;/a&amp;gt;
  &amp;lt;nav aria-labelledby=&quot;main-navigation&quot;&amp;gt;
    &amp;lt;h2 id=&quot;main-navigation&quot;&amp;gt;Main navigation&amp;lt;/h2&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href=&quot;/&quot;&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a aria-current=&quot;page&quot; href=&quot;/best-sellers&quot;&amp;gt;Best Sellers&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href=&quot;/new-arrivals&quot;&amp;gt;New Arrivals&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href=&quot;/about-us&quot;&amp;gt;About Us&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href=&quot;/contact-us&quot;&amp;gt;Contact Us&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/nav&amp;gt;
&amp;lt;/header&amp;gt;

…

&amp;lt;main aria-labelledby=&quot;main-heading&quot; tabindex=&quot;-1&quot;&amp;gt;
  &amp;lt;h1 id=&quot;main-heading&quot;&amp;gt;Best Sellers&amp;lt;/h1&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/pre&gt;&lt;h3&gt;How does this implementation answer the $64,000 question?&lt;/h3&gt;&lt;p&gt;We have a &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; element that is essential to providing a concise means of wayfinding. Not only can screen reader users choose to hear the title at any time, but average users also benefit by seeing this in their browser’s tabs UI. Notice how the site/company’s name comes last because it’s less important information as you browse the site. The critical part is the unique page name, which you want to see/hear first.&lt;/p&gt;&lt;p&gt;We also have a matching navigation link, which includes the &lt;code&gt;aria-current=&quot;page&quot;&lt;/code&gt; attribute. This announces that this link is the current page within the complete set of links, again reinforcing that we’re on Best Sellers.&lt;/p&gt;&lt;p&gt;Our URLs are also indicative of the page’s name. While not all devices and browsers expose them by default, a relevant, meaningful URL is yet another great indicator of where you are.&lt;/p&gt;&lt;p&gt;And finally, we have a single &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; with matching content that’s associated with the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element using the &lt;code&gt;aria-labelledby&lt;/code&gt; attribute. So regardless of whether a screen reader user navigated to the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; directly or via landmarks to the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, they would know that this page is about Best Sellers.&lt;/p&gt;&lt;p&gt;We have now answered the question: If I couldn’t see the screen, would I know what this (page) is?&lt;/p&gt;&lt;h3&gt;What about the actual navigation?&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; element describes that the links within it are for navigation. The &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; elements will announce that the navigation is a list with a specific number of options, and of course, the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; lets users know that these are links to another page.&lt;/p&gt;&lt;p&gt;Similar to the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element earlier, we add a &lt;a href=&quot;https://www.a11yproject.com/posts/how-to-hide-content/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;visually hidden&lt;/a&gt; heading and associate that to the &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; with an &lt;code&gt;aria-labelledby&lt;/code&gt; attribute. This lets screen reader users quickly find the main navigation via headings or landmarks.&lt;/p&gt;&lt;p&gt;Here we’ve answered the first and second questions. If you couldn’t see the screen and were using a screen reader, you’d know exactly what the links are and what they do.&lt;/p&gt;&lt;p&gt;That leaves one final question for the navigation: If I couldn’t see the screen, how would I know what just happened?&lt;/p&gt;&lt;p&gt;If you’re building a traditional server or statically-rendered page, using &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements will automatically solve this for you. The browser will inform the screen reader to announce a navigation event, put the user’s focus at the root of the next page, and often start reading.&lt;/p&gt;&lt;p&gt;However, you may have additional work to do if the site/app’s navigation requires or is enhanced with client-side JavaScript.&lt;/p&gt;&lt;p&gt;At a minimum, you should add a visually hidden element that announces the route change by hooking into your route change’s success event, similar to:&lt;/p&gt;&lt;pre&gt;&amp;lt;div role=&quot;alert&quot;&amp;gt;
  {routeHasChanged ? `Navigated to ${pageTitle} - XYZ Corp` : &apos;&apos;}
&amp;lt;/div&amp;gt;
&lt;/pre&gt;&lt;p&gt;Because of the &lt;code&gt;role=&quot;alert&quot;&lt;/code&gt; attribute, a screen reader would immediately broadcast “Navigated to Best Sellers - XYZ Corp”, thus satisfying the question of knowing what just happened.&lt;/p&gt;&lt;p&gt;As with a traditional site, you should move the user’s focus to the first element on the page so they fully understand that a change has occurred, and can start navigating.&lt;/p&gt;&lt;p&gt;If you’re using Next.js, it has a similar feature out of the box, albeit not quite up to my standards for two reasons:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;It doesn’t include the “Navigated to” prefix, thus making it slightly less contextually useful for users; and&lt;/li&gt;&lt;li&gt;The focus isn’t always set, so a screen reader user may sometimes be left on a phantom element, thus making their Previous and Next commands not behave as expected.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Alternatively, you could improve the experience further by focusing on the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element when navigating forward, so users are instantly taken to the new page’s content. When navigating back, automatically focusing on the element that caused the original route change would allow users to continue from where they left off.&lt;/p&gt;&lt;p&gt;Unfortunately, while I have previously implemented this behaviour in a custom Vue.js application, I haven’t found a similar drop-in solution for common React-based frameworks.&lt;/p&gt;&lt;p&gt;Hopefully, you can appreciate how much thinking goes into making a ubiquitous, seemingly simple pattern accessible to screen reader users and improving the UX for all users.&lt;/p&gt;&lt;h2&gt;Questioning another pattern&lt;/h2&gt;&lt;p&gt;With this awareness, imagine you continue developing XYZ Corp’s online store. You’re asked to implement a set of two icon buttons that change the search results to display as either a grid or list view, with grid selected by default. This pattern should be obvious for sighted users, but what other things should you consider?&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/x479hatiwo29/64k-question-more-accessible-inclusive-experiences-xyz-corp-2?width=848&quot; alt=&quot;Mockup of a website Best Sellers page for XYZ corporation with list/grid toggle buttons to alter the way Best Seller results are displayed.&quot; width=&quot;848&quot; height=&quot;1100&quot; /&gt;&lt;figcaption&gt;Mockup of a website Best Sellers page for XYZ corporation with list/grid toggle buttons to alter the way Best Seller results are displayed.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;You, of course, need to ask the $64,000 question.&lt;/p&gt;&lt;p&gt;In this context, you may take the questions and reframe them as: If I couldn’t see the screen:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;How would I &lt;strong&gt;know what the buttons do?&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;How would I &lt;strong&gt;know when one is pressed?&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;How would I &lt;strong&gt;know that the results view has changed?&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Happily, this pattern requires minimal effort:&lt;/p&gt;&lt;pre&gt;&amp;lt;button
  aria-controls=&quot;results-view&quot;
  aria-label=&quot;Display as grid view&quot;
  aria-pressed=&quot;true&quot;
  onClick=&quot;handleToggleResultsView&quot;
&amp;gt;
  &amp;lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; role=&quot;img&quot;&amp;gt;…&amp;lt;/svg&amp;gt;
&amp;lt;/button&amp;gt;

&amp;lt;button
  aria-controls=&quot;results-view&quot;
  aria-label=&quot;Display as list view&quot;
  aria-pressed=&quot;false&quot;
  onClick=&quot;handleToggleResultsView&quot;
&amp;gt;
  &amp;lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; role=&quot;img&quot;&amp;gt;…&amp;lt;/svg&amp;gt;
&amp;lt;/button&amp;gt;

&amp;lt;div id=&quot;”results-view”&quot;&amp;gt;… list of results…&amp;lt;/div&amp;gt;
&lt;/pre&gt;&lt;p&gt;By providing &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; elements whose &lt;code&gt;aria-pressed&lt;/code&gt; attributes change appropriately in &lt;code&gt;handleToggleResultsView&lt;/code&gt;, we create accessible toggle buttons that automatically indicate which is pressed. Of course, sighted users need to be made aware which is pressed by changing its appearance, and not just its colour.&lt;/p&gt;&lt;p&gt;The buttons use &lt;code&gt;aria-label&lt;/code&gt; as a descriptive label for screen readers, and various attributes on the SVG to ensure they’re not accidentally reachable by screen readers and keyboard users of various devices and browsers.&lt;/p&gt;&lt;p&gt;The buttons also have an &lt;code&gt;aria-controls&lt;/code&gt; attribute which points to the &lt;code&gt;id&lt;/code&gt; of the element containing the results. While this attribute isn’t supported in all screen readers, it’s still worthwhile creating this explicit relationship between elements.&lt;/p&gt;&lt;p&gt;We’ve successfully answered the $64,000 question! A user will know what the buttons are, what they do and what happens when you press them.&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Hopefully, I’ve demonstrated that by thinking about software in terms of how its elements and actions are conveyed, and not just by how it looks, you can create accessible and usable experiences for a much wider range of your user base.&lt;/p&gt;&lt;p&gt;Luckily, you don’t have to work out accessible solutions for many standard patterns, as there are well-tested and documented approaches in the &lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ARIA Authoring Practices Guide&lt;/a&gt; and &lt;a href=&quot;https://inclusive-components.design/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;elsewhere&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;However, sometimes the things you work on don’t have an agreed-upon, accessible solution. In these cases, ask yourself one question: &lt;strong&gt;If I couldn’t see the screen, how would I…?&lt;/strong&gt;&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Serverless website &amp; booking system with Keystone, Planetscale, Vercel, &amp; Next.js</title><link>https://www.thinkmill.com.au/blog/serverless-website-and-booking-system-with-keystone-planetscale-vercel-and-next-js</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/serverless-website-and-booking-system-with-keystone-planetscale-vercel-and-next-js</guid><description>Josh Calder has open-sourced a new website he built for his wife’s performing arts business. It lets students view a timetable, check fees, find available lesson times, and enrol through a portal. It combines Planetscale and Vercel to make for a fully “serverless” Keystone stack.</description><pubDate>Tue, 28 Feb 2023 00:00:00 GMT</pubDate></item><item><title>Shared understanding: why it’s important and how to fast-track it in your project</title><link>https://www.thinkmill.com.au/blog/shared-understanding</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/shared-understanding</guid><description>Tips and tricks to get your team’s collective intelligence buzzing in product development.</description><pubDate>Thu, 23 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;When it comes to building digital products and experiences, the amount of time it takes teams to find shared understanding can have a big impact on delivering outcomes.&lt;/p&gt;&lt;p&gt;This article explores how creating an environment where psychological safety and active learning are supported can help teams accelerate early alignment; leading to better outcomes, happier teams, and improved time-to-market. Included are tips and tricks to help you accelerate alignment within your own unique team and delivery context.&lt;/p&gt;&lt;h2&gt;Shared understanding in a nutshell&lt;/h2&gt;&lt;p&gt;You may have encountered the term shared understanding by other labels such as “unified mental model”, “&lt;a href=&quot;https://www.merriam-webster.com/dictionary/hive%20mind&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;hive mind&lt;/a&gt;”, or “&lt;a href=&quot;https://en.wikipedia.org/wiki/Collective_intelligence&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;collective intelligence&lt;/a&gt;”. From the perspective of shipping software, we see it as a product team having shared sense of:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;What’s to be built.&lt;/li&gt;&lt;li&gt;How it relates to the business domain and users it’s in service to.&lt;/li&gt;&lt;li&gt;How and where the needs and constraints of the business, users, and the underlying tech are in conflict with one another (tradeoffs!).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Here’s a simplistic illustration of a how a team with no shared understandings might start out before a project kickoff:&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/yy7uvuab361d/gaps?width=848&quot; alt=&quot;This is an image&quot; width=&quot;848&quot; height=&quot;413&quot; /&gt;&lt;figcaption&gt;This is an image&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Here the needs and concerns of practitioners who are in service to Business, UX, and Tech exist apart from one another. This leaves room for &lt;a href=&quot;https://www.thinkmill.com.au/method#mind-the-gap&quot;&gt;gaps&lt;/a&gt; (in understanding, and execution) to manifest, which can negatively impact outcomes, team health, and perhaps even exacerbate feelings of isolation that team members might already be feeling.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/znim6d74l8w7/understanding?width=848&quot; alt=&quot;A representation of team specialisations operating from a position of shared understanding. Business, UX and Tech circles overlap with one another.&quot; width=&quot;848&quot; height=&quot;413&quot; /&gt;&lt;p&gt;Here’s those same three concerns operating when a critical mass of shared understanding has been reached. There are less gaps, and practitioners in any one of the three domains have greater overlap with the needs and constraints of that which exists outside their zone.&lt;/p&gt;&lt;h2&gt;Why finding shared understanding early is important&lt;/h2&gt;&lt;p&gt;If you’ve worked on dynamic cross-functional projects you might be thinking that shared understandings evolve throughout the entire project. In our experience, you’re 100% right! Understandings do need updating and re-qualification throughout. However, just because they change doesn’t mean that you can’t be more effective by striving for a “critical mass” of alignment early. Here’s a handful of reasons why:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You’re more likely to identify and close the big (and expensive to fix) gaps sooner.&lt;/li&gt;&lt;li&gt;Less gaps often increase the possibility of achieving better outcomes, and reducing time-to-market.&lt;/li&gt;&lt;li&gt;You’re more likely to develop actionable empathy for the needs and constraints of your cross-functional teammates.&lt;/li&gt;&lt;li&gt;Being more tuned-in to your colleagues means you’re less reliant on specification documents as everybody is &lt;a href=&quot;https://www.thinkmill.com.au/method#stay-synced&quot;&gt;in sync&lt;/a&gt;, and on the same page.&lt;/li&gt;&lt;li&gt;You’re more likely to enjoy the making process, which can increase employee retention and build a better organisational culture.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We’ve found that in order to fast-track the path to a unified mental image, a few things need to be in place:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Organisational and cultural support for engaging in active learning in the early stages of the project.&lt;/li&gt;&lt;li&gt;The ability for teams to self-select tools and processes that are inclusive and match the unique composition of their team.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Impediments to shared understanding, and how to overcome them&lt;/h2&gt;&lt;p&gt;Here’s a list of obstacles you may encounter on your quest, and some suggestions for how to sidestep them:&lt;/p&gt;&lt;h3&gt;Tricky terms and domain logic&lt;/h3&gt;&lt;p&gt;Getting aligned can be tough if you don’t know exactly what it is that you’re talking about! Setting aside access to the right users and stakeholders for a moment – collaboratively building a dictionary of terms and core concepts can go a long way to improving alignment in the first weeks of your project.&lt;/p&gt;&lt;h3&gt;A perceived need to demonstrate measurable progress early&lt;/h3&gt;&lt;p&gt;Tech teams often experience pressure to appear as if they are being productive in easy-to-measure ways on day one. This pressure often manifests as rushing into code or wireframing. It can be a real dampener for reaching alignment, and often ends up manifesting as increased learning debt across the life of the project, inferior outcomes, and poor team health.&lt;/p&gt;&lt;p&gt;Talk to your teammates and superiors about &lt;a href=&quot;https://www.loom.com/share/5d075f4f38ae4288ac6cc75930a6aae2?t=237&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the value of starting slow to go fast&lt;/a&gt;, avoid learning debt by making the practice of &lt;a href=&quot;https://twitter.com/grimalkina/status/1493321929331068929?s=20&amp;amp;t=qY6hrrz0OSgS1D9mNZBIkA&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;active learning&lt;/a&gt; visible early, and nudge your collaborators in the direction of making your alignment-generating efforts both valued and measurable. The cost of dealing with last-minute gaps can get a lot more expensive that the effort you put in here!&lt;/p&gt;&lt;h3&gt;Existing predispositions towards siloed and linear ways of working&lt;/h3&gt;&lt;p&gt;If you’re in a team dynamic where tasks are handed over the fence to you (waterfall model, we’re looking at you), it can be hard to find a holistic sense of what it is y’all are working on. Gaps arise, the blame game sets in, you may have experienced how the rest of that story goes...&lt;/p&gt;&lt;p&gt;Talk to one another, and talk often. Check out our thoughts on the &lt;a href=&quot;https://www.thinkmill.com.au/method#stay-synced&quot;&gt;value of working synchronously&lt;/a&gt;, and be sure to dive in to Shamsi Brinn’s excellent &lt;a href=&quot;https://nohandoff.org/manifesto/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;No-Handoff Manifesto&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;A preference for sharing polished work over work-in-progress&lt;/h3&gt;&lt;p&gt;You may practice your craft in an environment where formal, polished presentations are highly sought after. These certainly have their place. But if you’re not careful, overemphasising on bulletproof presentations can slow your journey to common ground.&lt;/p&gt;&lt;p&gt;Can you skip the prep time required to make polished thing, and instead opt for sharing your unvarnished work and thoughts in a quick huddle? It might just bring you closer to a solution and build more trust while you’re at it.&lt;/p&gt;&lt;h3&gt;Confusing documentation with understanding&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;Our instinctive reaction to the knowledge gap is to seek more information. We confuse understanding with information.&lt;/p&gt;&lt;p&gt;Steven Bangay, &lt;a href=&quot;https://www.stephenbungay.com/ExecutingStrategy&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Executing Strategy&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Documentation can play an important role in how shared understanding is created within a team. However, there can be be subtle, yet meaningful differences between (a) documentation in service to &lt;em&gt;advancing understandings&lt;/em&gt;, and (b) documentation in service to &lt;em&gt;specification&lt;/em&gt;. The former can be very helpful for improving alignment (and these kinds of artifacts &lt;a href=&quot;https://www.thinkmill.com.au/blog/all-about-artefacts#artefact-accuracy-diminishes-over-time&quot;&gt;often lose relevance and value as shared understanding increases&lt;/a&gt;, while the latter is often better suited to situations where things are less likely to change.&lt;/p&gt;&lt;p&gt;If you’re reaching for a documentation effort – ask yourself which of the two outcomes you’re in service to. When your cause is in service to advancing understandings – use tools that are accessible to all disciplines in your team to find flow faster.&lt;/p&gt;&lt;h3&gt;A lack of psychological safety&lt;/h3&gt;&lt;p&gt;Many of us have struggled with imposter syndrome in some way, shape, or form. One could argue that it’s both a symptom of, and contributor to, all the preceding items in this list!&lt;/p&gt;&lt;p&gt;Creating a psychologically safe environment can take time and genuine effort. It’s hard to offer a prescription for achieving this within the bounds of this article; but if you’re able to make progress on some of the other obstacles throughout your next project – re-check your vibes at the end. You may just find that your team feels safer and more excited about what’s next. That’s a good sign that trust and safety are on the improve.&lt;/p&gt;&lt;h2&gt;How to gauge the health of your team’s shared understanding&lt;/h2&gt;&lt;p&gt;Every project brings its own unique variables to bear on what “enough shared understanding” might look and feel like. While there’s no one-size-fits-all solution to lean on, this doesn’t mean you can’t find your own way to confidence and consensus. Here’s some questions you might like to ask yourselves in order to evaluate the state of your team’s hive mind:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;As a &lt;strong&gt;team&lt;/strong&gt;:&lt;ul&gt;&lt;li&gt;Are we confident that the most opaque parts of this project are sufficiently transparent from the perspective of all participants?&lt;/li&gt;&lt;li&gt;Have we generated enough trust and safety to openly explore our thinking and &lt;a href=&quot;https://www.thinkmill.com.au/method#work-in-open&quot;&gt;work in the open&lt;/a&gt; from here on out?&lt;/li&gt;&lt;li&gt;Will the mindshare we have empower us to &lt;a href=&quot;https://www.thinkmill.com.au/method#stay-synced&quot;&gt;work in a highly synchronous manner&lt;/a&gt; going forward?&lt;/li&gt;&lt;li&gt;Are we aware of, and agreed upon, the key tradeoffs that are stake within the stated ambitions of the project?&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;As an &lt;strong&gt;individual&lt;/strong&gt;:&lt;ul&gt;&lt;li&gt;Do I know enough about the cross-functional needs and constraints of my colleagues such that my work will help (and not hinder) them in their goals?&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you’re interested to learn more about how we apply these principles at Thinkmill, checkout &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;The Thinkmill Method&lt;/a&gt;. If you’re running a project and feel like you’d benefit from seeing this way of working applied in a more hands-on, context specific manner, we can &lt;a href=&quot;https://www.thinkmill.com.au/services/augmentation&quot;&gt;augment&lt;/a&gt; your delivery team and help improve your delivery culture while we’re there. &lt;a href=&quot;https://www.thinkmill.com.au/contact&quot;&gt;Get in touch&lt;/a&gt; to learn more.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;strong&gt;Credits and recommended reading&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The following resources have made a valuable contribution to the way we reason about effective collaboration within cross-functional software environments:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Shamsi Brinn’s &lt;a href=&quot;https://nohandoff.org/manifesto/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;No-handoff manifesto&lt;/a&gt; offers a way out of waterfall inspired our way of working.&lt;/li&gt;&lt;li&gt;Dr Cat Hicks’ &lt;a href=&quot;https://twitter.com/grimalkina/status/1493321929331068929?s=20&amp;amp;t=qY6hrrz0OSgS1D9mNZBIkA&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;research&lt;/a&gt; into the value active learning and psychological safety in developer teams is excellent reading.&lt;/li&gt;&lt;li&gt;John Cutler’s “&lt;a href=&quot;https://www.loom.com/share/5d075f4f38ae4288ac6cc75930a6aae2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Go slower to go fast&lt;/a&gt;” advocates for spending meaningful time on finding shared understanding in order to mitigate the many pitfalls of misaligned teams. His &lt;a href=&quot;https://cutlefish.substack.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;product-management substack&lt;/a&gt; is top-shelf.&lt;/li&gt;&lt;li&gt;Strategy boffin Steven Bungay has summarised his key thoughts on &lt;a href=&quot;https://www.stephenbungay.com/ExecutingStrategy&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;executing strategy&lt;/a&gt; which overlap with many of the pillars of cross-functional product development.&lt;/li&gt;&lt;/ul&gt;&lt;/article&gt;</content:encoded></item><item><title>Keystatic is now in early access</title><link>https://www.thinkmill.com.au/blog/keystatic-is-now-in-early-access</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/keystatic-is-now-in-early-access</guid><description>Our latest R&amp;D project, Keystatic, is ‘officially’ in early access. Like most of the innovation borne of Thinkmill Labs, Keystatic is in its fledgling stage. There are some rough edges, and we’d love your feedback to keep the wheels of innovation turning. </description><pubDate>Thu, 23 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;For more information on Keystatic, &lt;a href=&quot;https://keystatic.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;checkout the website&lt;/a&gt;, and &lt;a href=&quot;https://github.com/thinkmill/keystatic&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;give us a star on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/jbj9t15ja1yn/keystatic-news?width=1200" length="24357" type="image/png"/></item><item><title>Orienteering: A framing for software consultancy </title><link>https://www.thinkmill.com.au/blog/orienteering-consulting</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/orienteering-consulting</guid><description>Orienteering aims to better align a consultant’s mindset with the real-world experience and evolve their way of thinking in a more sustainable way. </description><pubDate>Wed, 22 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;Whether you are just starting out on your consulting journey or have been away from the game for a while, the unpredictable nature of being a consultant can be a bit of a shock. We learn that product development is straightforward, that if we follow the steps we will deliver an amazing product - but this is simply not how things work in the real-world.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Orienteering&lt;/strong&gt; aims to better align a consultant’s mindset with the real-world experience and evolve their way of thinking in a more sustainable way.&lt;/p&gt;&lt;h2&gt;Why orienteering?&lt;/h2&gt;&lt;p&gt;Orienteering is the process of navigating through unfamiliar terrain which is a useful way to think about consulting projects. Orienteers know the value of planning and are prepared to meet obstacles along the journey. Most of all, orienteers are adventurous, practical and inherently curious and this is the mindset that will both preserve the orienteer’s well-being and lead to a successful outcome. So grab a map and compass and get ready for an adventure!&lt;/p&gt;&lt;hr /&gt;&lt;h2&gt;The basics&lt;/h2&gt;&lt;p&gt;Before the orienteer starts the adventure, it is important to know a bit about what is expected of them and the environment they are heading into. The &lt;strong&gt;basics&lt;/strong&gt; cover the three main areas:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Role&lt;/li&gt;&lt;li&gt;Terrain&lt;/li&gt;&lt;li&gt;Experience&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;👤 Role&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;role&lt;/strong&gt; is the set of responsibilities that the orienteer is expected to fulfil. Often this can manifest as dual-responsibilities, with the &lt;em&gt;internal role&lt;/em&gt; playing in concert with the &lt;em&gt;external role&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Your &lt;em&gt;internal role&lt;/em&gt; are the expectations of you from your consultancy’s perspective. This includes what your consultancy has been engaged to work on, your consultancy’s vision and overarching objectives, as well as evolving your own experience as an orienteer. Your &lt;em&gt;internal role&lt;/em&gt; tends to stay constant throughout the adventure and can be used as a north star if your responsibilities become fuzzy.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;At Thinkmill, consultants can play a different role from one project to another depending on the type of engagement that was agreed upon with the client. The engagement could be:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Augment&lt;/strong&gt; - Become part of the client’s team and take direction from them.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Augment &amp;amp; Lead&lt;/strong&gt; - Become a leader in the client’s team and take on responsibility for an initiative or area of work.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Outsource&lt;/strong&gt; - Become part of a Thinkmill team working autonomously on the initiative.&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;p&gt;Your &lt;em&gt;external role&lt;/em&gt; are the expectations of you from the client’s perspective. This includes what the client believes they have engaged you on and the varying levels of understanding from your team members on the client side. Your &lt;em&gt;external role&lt;/em&gt; tends to change as the adventure progresses and the orienteer should be ready to adapt as required.&lt;/p&gt;&lt;p&gt;Understanding your role will help to calibrate what is expected of you so you can perform accordingly, reducing friction between the team and your consultancy and have a better chance of a successful adventure.&lt;/p&gt;&lt;h3&gt;⛰️ Terrain&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;terrain&lt;/strong&gt; describes the company environment the orienteer is dropped into. This means the way the company works and what they value, including accepted level of communication, inclination to collaboration and proficiency with the product development process. This is often referred to as &lt;em&gt;company culture&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;terrain&lt;/strong&gt; makes it easier or harder to reach your destination and depending on the environment, will require you to draw on a different mix of skills and tools. Harder &lt;strong&gt;terrains&lt;/strong&gt; have a bigger impact on the wellbeing of orienteers, so newer orienteers should be paired with someone more experienced as a guide. The complexity of the company’s subject-matter adds another dimension to the terrain and orienteers should be prepared to spend ample time getting across complex subjects.&lt;/p&gt;&lt;p&gt;Each &lt;strong&gt;terrain&lt;/strong&gt; falls into one of the following grades:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;🟩 &lt;strong&gt;Grade 1&lt;/strong&gt; – A friendly, proficient and collaborative team with a high-level of communication and context sharing. Clearly defined goals, scope and timeline.&lt;/li&gt;&lt;li&gt;🟦 &lt;strong&gt;Grade 2&lt;/strong&gt; – A relatively collaborative and proficient team with adequate communication and context sharing. Goals, scope and timeline are defined but may be prone to changes.&lt;/li&gt;&lt;li&gt;🟥 &lt;strong&gt;Grade 3&lt;/strong&gt; – Some difficult members on the team that make communication and context sharing hard. General lack of confidence around the defined goals, scope and timeline.&lt;/li&gt;&lt;li&gt;⬛ &lt;strong&gt;Grade 4&lt;/strong&gt; – A potentially unskilled team that does not want to cooperate and has very little communication or context sharing. Poorly defined goals, scope or timeline.&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;At Thinkmill, we have experienced all grades of terrain. In practice, a &lt;strong&gt;Grade 1&lt;/strong&gt; terrain may be the result of a well-timed engagement with a client who is ready to do great work us. The team has been prepped for collaboration and the expectations are clear.&lt;/p&gt;&lt;p&gt;A &lt;strong&gt;Grade 4&lt;/strong&gt; terrain may be the result of a client who has initiated the engagement at the wrong time and didn’t get buy-in internally. This could lead to stakeholders who feel threatened by the consultant and are unwilling to cooperate, creating roadblocks either actively or due to inaction.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;⭐ Experience&lt;/h3&gt;&lt;p&gt;The more adventures the orienteer completes, the more &lt;strong&gt;experience&lt;/strong&gt; they gain with tackling different terrains and applying their skills. &lt;strong&gt;Experience&lt;/strong&gt; allows an orienteer to more quickly predict or identify obstacles and expertly apply the appropriate tools to guide the team successfully. Therefore &lt;strong&gt;it’s important for orienteers to view each adventure as an opportunity to learn, experiment and develop their craft&lt;/strong&gt;. This is the secret to sustainable consulting and embracing this mindset will serve the orienteer time and time again.&lt;/p&gt;&lt;hr /&gt;&lt;h2&gt;Stages of the adventure&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ok0royalx6sd/orienteering-consulting-orienteering-1?width=848&quot; alt=&quot;Author’s depiction of the stages of the adventure.&quot; width=&quot;848&quot; height=&quot;582&quot; /&gt;&lt;figcaption&gt;Author’s depiction of the stages of the adventure.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Once the orienteer is across the basics, they can start their adventure! The adventure is broken up into three distinct stages:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The drop-in&lt;/li&gt;&lt;li&gt;The journey&lt;/li&gt;&lt;li&gt;The destination&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;🪂 The drop-in&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;drop-in&lt;/strong&gt; is the point where the orienteer starts their adventure, noting that the rest of the team may have already begun. The orienteer can expect to spend a period of time getting across the context, subject-matter and processes that the internal team members may already understand.&lt;/p&gt;&lt;p&gt;If you find yourself in a &lt;strong&gt;Grade 3&lt;/strong&gt; or &lt;strong&gt;Grade 4&lt;/strong&gt; terrain, the &lt;strong&gt;drop-in&lt;/strong&gt; can feel a bit like groping around in the dark. This is to be expected and let’s just say it’s hard to stick the landing at night. Experience with &lt;em&gt;getting your bearings&lt;/em&gt; will help in these situations.&lt;/p&gt;&lt;h4&gt;Getting your bearings&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Getting your bearings&lt;/strong&gt; is the process of figuring out which direction to take the project first. This means determining:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Who is involved&lt;/li&gt;&lt;li&gt;Where work takes place&lt;/li&gt;&lt;li&gt;What work has already been done&lt;/li&gt;&lt;li&gt;What are the expectations&lt;/li&gt;&lt;li&gt;What is the desired destination&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This process separates &lt;em&gt;the drop-in&lt;/em&gt; stage from &lt;em&gt;the journey&lt;/em&gt; stage and orienteers should ensure the above questions have been answered before they take their first steps. You want to be sure your chosen route isn’t taking you towards a cliff!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;At Thinkmill, we also call this step the &lt;em&gt;reverse brief&lt;/em&gt;. Once the project has kicked off, we often find that the work initially scoped is a little different to the work required. This may be because the person involved in defining the engagement doesn’t have enough context to properly explain the brief, or it may be because we’ve seen this situation before and know how we can be most effective.&lt;/p&gt;&lt;p&gt;Either way, playing our answers to the above questions back to the client is a great way to ensure everyone is on the same page and set the project up for success.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;🏃‍♀️ The journey&lt;/h3&gt;&lt;p&gt;Once the orienteer is confident in which direction to head, they can take their first step. The journey represents the work the orienteer does to progress the project, applying their expertise as required and working with the team to move towards the destination.&lt;/p&gt;&lt;h4&gt;Expect the unexpected&lt;/h4&gt;&lt;p&gt;One thing every orienteer should expect to encounter is obstacles. Even if the terrain seems familiar, the conditions are always different and things don’t go according to plan. Bringing a sense of adventure and curiosity will go a long way in staying resilient in the face of the unexpected.&lt;/p&gt;&lt;h4&gt;Share the load&lt;/h4&gt;&lt;p&gt;Though each team member usually has their own responsibilities, seeking feedback from others can ensure that blind spots are covered and obstacles are identified ahead of time. The most robust ideas always come through brainstorming with others.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;At Thinkmill, we love to collaborate and test the strength of our ideas in a process we call &lt;a href=&quot;https://www.thinkmill.com.au/method/playbook/sparring&quot;&gt;Sparring&lt;/a&gt;. It can be a bit daunting to share evolving concepts with team mates, but this process always results in a better outcome overall. Additionally, being able to give and receive feedback gracefully is a valuable skill to practise.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;Valley of Despair&lt;/h3&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/i8c1g7150c6l/orienteering-consulting-orienteering-2?width=848&quot; alt=&quot;Author’s depiction of the Valley of Despair.&quot; width=&quot;848&quot; height=&quot;559&quot; /&gt;&lt;figcaption&gt;Author’s depiction of the Valley of Despair.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;There’s an upcoming deadline and the designer is producing concept after concept but the team cannot seem to agree on how a feature is supposed to work - everyone’s patience is wearing thin. You’re beginning to wonder whether the whole project was a waste of time and maybe this was all for nothing. You’ve reached the &lt;em&gt;Valley of Despair&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Sometimes a project can lose momentum due to changing priorities or requirements, and the team may find that morale and team synergy is running low. The &lt;em&gt;Valley of Despair&lt;/em&gt; can be a tough place to recover from. Even for experienced orienteers, one of the most effective ways to navigate out is to talk through the situation with someone senior outside the immediate team. Sometimes someone with a bit of distance can provide a better vantage point and a new perspective.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;At Thinkmill, we have walked the end to end product development journey enough times to know that it’s often the strength of your team that gets you through the &lt;em&gt;Valley of Despair&lt;/em&gt; in one piece. This is why building rapport and trust early-on is fundamental (/method#fundamentals) to how we work.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;🏁 The destination&lt;/h2&gt;&lt;p&gt;The completion of an adventure is something to celebrate as the orienteer will have gained experience and practiced their skills. To preserve learning, it is encouraged that the orienteer documents any interesting and useful thoughts to feed back to the rest of the team.&lt;/p&gt;&lt;p&gt;It is also important to remember that not all adventures will end at the destination that was initially set out for and it is never the orienteer’s fault if things do not go as expected. The orienteer can only do the best with what they have and the learnings gathered along the way are valuable in themselves. Even a short time spent looking back on the journey will surface things to be proud of, so use this time to reflect and synthesise before starting on the next adventure!&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/gxurc9843ruv/orienteering-consulting-orienteering-3?width=848&quot; alt=&quot;Directional signpost stating “Next adventure this way”&quot; width=&quot;848&quot; height=&quot;352&quot; /&gt;&lt;/article&gt;</content:encoded></item><item><title>All about Artefacts</title><link>https://www.thinkmill.com.au/blog/all-about-artefacts</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/all-about-artefacts</guid><description>Artefacts are a guaranteed by-product of software development and managing them effectively is important. This article aims to give deeper insight into what we mean when we say artefacts and highlight some key shortcomings as well as the strategies we’ve found to overcome them.</description><pubDate>Wed, 22 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;During the product development process, supporting documents and visuals are created as we take an idea from conception to delivery – we call these artefacts. Artefacts serve as the physical and digital touch points whose combined information conceive the final product. They are where problems are uncovered and resolved, where solutions are created and tested, and where knowledge is recorded for future use. Artefacts are as much of the product development process as the product itself.&lt;/p&gt;&lt;p&gt;As we move to a more remote way of working, we reduce face-to-face time and begin to rely more heavily on asynchronous methods of communication such as artefacts. Understanding their strengths and limitations will ensure your team uses artefacts effectively and hopefully also help them to avoid the common frustrations.&lt;/p&gt;&lt;h2&gt;What is an artefact?&lt;/h2&gt;&lt;p&gt;Artefact is a broad term used to describe any supporting material that aids the process of building a product or service. They vary hugely in form and function, but all exist on a spectrum of symbolic to practical &lt;a href=&quot;https://www.thinkmill.com.au/#footnote-1&quot;&gt;&lt;strong&gt;*&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Symbolic&lt;/strong&gt; artefacts are used to represent an aspect of the product or service, as a way of visualising what the final experience could be like.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Practical&lt;/strong&gt; artefacts are more concerned with the logistical side of the project. There are many artefacts that sit within these bounds that serve to bridge the gap between domains.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;By understanding where an artefact sits on this scale, it can give an indication on who will be likely to engage with it. For example, a designer would be happy looking at a symbolic artefact such as a user flow diagram as a way of understanding the intended journey through the product, whilst a senior operations person would be more interested in a practical artefact such as a roadmap or timeline, as they are responsible for delivery. Thinking about who your audience is will help you to be more effective when using artefacts to collaborate with others.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/dr01bbixjuga/all-about-artefacts-artefacts-1?width=848&quot; alt=&quot;Examples of artefacts sitting on the scale of symbolic to practical.&quot; width=&quot;848&quot; height=&quot;368&quot; /&gt;&lt;figcaption&gt;Examples of artefacts sitting on the scale of symbolic to practical.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Additionally, artefacts can be divided into two main categories; &lt;strong&gt;personal artefacts&lt;/strong&gt; and &lt;strong&gt;shared artefacts&lt;/strong&gt;.&lt;/p&gt;&lt;h3&gt;Personal artefacts&lt;/h3&gt;&lt;p&gt;As creators, we are in a constant cycle of materialising our thoughts – this means getting ideas out of our heads and onto paper. Artefacts provide our thinking with a medium, allowing us to mull over and iterate on an idea, pushing and pulling on it until it becomes something with enough form to share with others.&lt;/p&gt;&lt;h3&gt;Shared artefacts&lt;/h3&gt;&lt;p&gt;We also create artefacts to clarify and explain concepts to others, covering everything from project timelines to the technical feasibility of a feature. They serve as communication tools between peers to present our point of view and the trade-offs we are grappling with. Most importantly, they contribute to the &lt;a href=&quot;https://www.thinkmill.com.au/blog/shared-understanding&quot;&gt;shared understanding&lt;/a&gt; of the team, which grows as more shared artefacts are created.&lt;/p&gt;&lt;h2&gt;The lifecycle of a shared artefact&lt;/h2&gt;&lt;p&gt;Throughout the lifetime of a project, many artefacts are created and shared as part of the product development process. A wireframe is utilised to get consensus on how a user would move through a product, or a component is published on Storybook to get feedback on an animation.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/u4apm4nj5itu/all-about-artefacts-artefacts-2?width=848&quot; alt=&quot;Artefacts are created throughout the lifetime of a project.&quot; width=&quot;848&quot; height=&quot;372&quot; /&gt;&lt;figcaption&gt;Artefacts are created throughout the lifetime of a project.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Together these artefacts contribute to the team’s overall understanding of the final product, until it reaches its peak at the end of a project. Naturally, this means that artefacts created later will have been created with a greater level of understanding than earlier ones.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/peqcb51tccq7/all-about-artefacts-artefacts-3?width=848&quot; alt=&quot;Understanding increases over the lifetime of a project.&quot; width=&quot;848&quot; height=&quot;559&quot; /&gt;&lt;figcaption&gt;Understanding increases over the lifetime of a project.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/s4pyi42xmgey/all-about-artefacts-artefacts-4?width=848&quot; alt=&quot;Later artefacts are created with a greater understanding than earlier ones.&quot; width=&quot;848&quot; height=&quot;559&quot; /&gt;&lt;figcaption&gt;Later artefacts are created with a greater understanding than earlier ones.&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Artefact accuracy diminishes over time&lt;/h2&gt;&lt;p&gt;Though an artefact may have represented an accurate view of things when it was created, as the project and the team’s understanding progresses, the accuracy of that artefact diminishes over time until it eventually becomes stale. For some artefacts like project plans, it’s practical to keep them updated, but for others they may have served their purpose as a communication tool and can be left to retire. These retired artefacts still serve as a reference point to an earlier stage of the project.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/x6b87m2ce5b5/all-about-artefacts-artefacts-5?width=848&quot; alt=&quot;The accuracy of artefacts diminishes over time.&quot; width=&quot;848&quot; height=&quot;682&quot; /&gt;&lt;figcaption&gt;The accuracy of artefacts diminishes over time.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The above graph reveals a critical limitation of artefacts which is often overlooked. &lt;strong&gt;Artefacts can only contain the level of understanding that the creator/s possess at that point in time.&lt;/strong&gt; This is completely OK but is something to be aware of as sometimes too much weight can be placed on an artefact that has been created at a time when the team had less understanding.&lt;/p&gt;&lt;p&gt;Let’s look at an example. A head of product asks a developer to create a timeline to show how long each stage of the project will take to complete. Based on the current requirements, the developer maps out a 6 month timeline. However, as the project progresses, a new requirement is added and it becomes clear that one of the chosen technologies can’t support this additional feature. The developer tries to pivot to a new technology but the 6 month deadline arrives and the project is not complete. The head of product gets frustrated that the timeline was incorrect.&lt;/p&gt;&lt;p&gt;Let’s take another example. A company has an active design system that is used across their products. A designer uses it to create UI specifications for a new feature, but there’s no available development resource and the UI specifications are put on hold for a few months. Once a developer is ready to pick up the designs, the design system has evolved and some components used in the UI specification are now out of date. The developer gets frustrated that the designs are incorrect.&lt;/p&gt;&lt;p&gt;In both examples there is no wrongdoing by anyone involved. &lt;strong&gt;The frustration stems from the belief that artefacts remain as accurate as on the day they were created&lt;/strong&gt;. However because the accuracy of artefacts diminishes over time, these and similar scenarios are bound to occur if the team isn’t aware of this limitation. An artefact created today will be different to a artefact created tomorrow, simply because we gain more information as time goes on.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;At Thinkmill, we’ve been documenting our way of working that combat a lot of the issues seen in the above examples. Learn more about how we approach about product development at &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;The Thinkmill Method&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;So what should be done about it? (Alt: “How to guard against reliance on stale artefacts)&lt;/h2&gt;&lt;p&gt;Artefacts can sometimes be a seemingly strong foothold in the midst of the unknown nature of product development. However, to avoid frustration we should be wary of putting too much weight on artefacts which are best consumed in the context of evolving understanding. Here are a few strategies to try.&lt;/p&gt;&lt;h3&gt;1. Refer to the codebase more&lt;/h3&gt;&lt;p&gt;Unlike artefacts, the codebase of the product is being actively worked on throughout its life. Once a product or service starts to be realised in code, shifting the reference point here is a great way to ensure you’re always looking at what’s most up-to-date. This is just one of the many reasons to &lt;a href=&quot;https://www.thinkmill.com.au/method#bias-towards-code&quot;&gt;start building things in code as soon as possible&lt;/a&gt;, as it gives the team a great early reference point to see how the product is progressing.&lt;/p&gt;&lt;p&gt;However, code is often not accessible by everyone on the team. Tools like Storybook and TestFlight, can reduce the barrier to entry for non-technical people, and give them a way to interact with the codebase.&lt;/p&gt;&lt;h3&gt;2. Increase the synchrony of your team&lt;/h3&gt;&lt;p&gt;From our experience, the less a team &lt;a href=&quot;https://www.thinkmill.com.au/method#stay-synced&quot;&gt;stays in-sync&lt;/a&gt;, the more artefacts are relied on as a form of communication. Because of this, increasing real-time collaboration and focussing attention on a few key &lt;em&gt;shared artefacts&lt;/em&gt;, will help to prevent the creation of too many artefacts that fall out of relevance and become stale.&lt;/p&gt;&lt;p&gt;Additionally, by having representation from different domains engaged and contributing throughout the development of the product, the &lt;a href=&quot;https://www.thinkmill.com.au/method#check-your-blindspots&quot;&gt;blind spots&lt;/a&gt; in artefacts can be addressed early making them more resistant to diminishing accuracy.&lt;/p&gt;&lt;h3&gt;3. Add context to artefacts&lt;/h3&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/mw4r3uy1vo4l/all-about-artefacts-artefacts-6?width=848&quot; alt=&quot;An example of statuses used to communicate the state of a Figma page.&quot; width=&quot;848&quot; height=&quot;469&quot; /&gt;&lt;figcaption&gt;An example of statuses used to communicate the state of a Figma page.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Creating a system that communicates the lifecycle of an artefact is not just useful for collaboration, it’s also useful to warn others when an artefact may have gone stale. This is important because stale artefacts can have unintended consequences if picked up by someone who doesn’t have the current context. By communicating the condition of artefacts with something like a status or creation date, other team members will have an indication on the accuracy level of the information they are looking at.&lt;/p&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;p&gt;In this article we’ve explored what an artefact is as well as how understanding its purpose can maximise engagement from an audience. We’ve also looked at the lifecycle of an artefact and how its accuracy naturally diminishes over time unless its maintained. We’ve discussed the key shortcoming of artefacts which is that they can only contain the level of understanding that the creator(s) possess at that point in time and worked through some scenarios where this limitation can result in frustration from team members. Finally, we’ve looked at some strategies to reduce the reliance on artefacts as well as ideas for communicating the diminishing accuracy and reduce the likelihood of these frustrations occurring.&lt;/p&gt;&lt;p&gt;Hopefully this article has provided a renewed respect for the role that artefacts play and how they can be utilised in projects today and in the future.&lt;/p&gt;&lt;hr /&gt;&lt;h4&gt;Notes:&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;*&lt;/strong&gt;  The idea for the symbolic to practical artefact categorisation came from the article &lt;a href=&quot;https://gineersnow.com/engineering/the-importance-of-an-artifact-in-the-software-development-process&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Importance Of An Artifact In The Software Development Process&lt;/a&gt; by GN. It was remixed and turned into a scale for this article.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Building layouts with placeholder components</title><link>https://www.thinkmill.com.au/blog/placeholder-components-design-system-layouts</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/placeholder-components-design-system-layouts</guid><description>An approach to rapidly building and evolving layouts as a project progresses and becomes more defined.</description><pubDate>Tue, 21 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;We’re in the early stages of building a complex application for a client. While we figure out how the underlying schema will work, we thought it would be a good idea to build the layout with placeholder components in place:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/placeholder-components-design-system-layouts/responsive-layout-placeholder-ui.mov&quot;&gt;A responsive app layout using placeholder components&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The placeholders give us a nice baseline to build from, and will provide obvious visual indication of progress to stakeholders as the real components replace them. We included image dimensions to help us make sure we use correct aspect ratios, and indicate to devs at a glance what sizes we’ll be needing.&lt;/p&gt;&lt;h2&gt;Image component&lt;/h2&gt;&lt;p&gt;The Image component simply takes &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; props and returns a placeholder image which automatically adds the dimensions. We used images instead of a styled HTML element to better understand how the layout might shift as they load.&lt;/p&gt;&lt;pre&gt;function PlaceholderImage({ height, width, ...consumerProps }) {
  return (
    &amp;lt;Image
      {...consumerProps}
      alt=&quot;&quot;
      src={`https://placehold.co/${width}x${height}.png`}
    /&amp;gt;
  )
}
&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/stowball/27dd326e481e5d24e8ff174f53374363#file-placeholder-image-js&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;View this snippet as a Gist&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Text component&lt;/h2&gt;&lt;p&gt;We have a Text component that takes a number of characters and visually hides Lorem Ipsum up to that length and sets a repeating grey/white background to simulate text. Because it uses Plain Text under the hood, it responds and wraps sensibly.&lt;/p&gt;&lt;pre&gt;const lipsum =
  &apos;Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias quo voluptas fugit quidem nemo autem eos reprehenderit voluptatum nam. Error odio repellendus quisquam magni rerum hic facere laboriosam, aspernatur fugiat.&apos;

function PlaceholderText({ characters, ...consumerProps }) {
  const string =
    characters &amp;lt;= lipsum.length
      ? lipsum.slice(0, characters)
      : lipsum
          .repeat(Math.ceil(characters / lipsum.length))
          .slice(0, characters)

  return (
    &amp;lt;span
      {...consumerProps}
      style={{
        backgroundImage:
          &apos;repeating-linear-gradient(to bottom, #fff 0px, #fff 2px, #bbb 2px, #bbb 10px, #fff 10px, #fff 18px)&apos;,
        display: &apos;inline-block&apos;,
        fontSize: 12,
        lineHeight: 1,
      }}
    &amp;gt;
      &amp;lt;span style={{ visibility: &apos;hidden&apos; }}&amp;gt;{string}&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;
  )
}
&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/stowball/27dd326e481e5d24e8ff174f53374363#file-placeholder-text-js&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;View this snippet as a Gist&lt;/a&gt;&lt;/p&gt;&lt;p&gt;All in all, it’s been a handy way to create momentum on layout without adding a lot of technical debt.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Keystatic is now on Twitter/X</title><link>https://www.thinkmill.com.au/blog/keystatic-is-now-on-twitter-x</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/keystatic-is-now-on-twitter-x</guid><description>Follow along to stay connected to the latest news.</description><pubDate>Mon, 20 Feb 2023 00:00:00 GMT</pubDate></item><item><title>Livestream: landing page with Keystatic, Tailwind, and Next.js</title><link>https://www.thinkmill.com.au/blog/livestream-landing-page-with-keystatic-tailwind-and-next-js</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/livestream-landing-page-with-keystatic-tailwind-and-next-js</guid><description>We’re working on a few Keystatic starter templates for devs interested in early access. Watch Simon livestream the build of our first example – a marketing website landing page.</description><pubDate>Sun, 19 Feb 2023 00:00:00 GMT</pubDate></item><item><title>Our new database-free CMS experiment is up on GitHub</title><link>https://www.thinkmill.com.au/blog/our-new-database-free-cms-experiment-is-up-on-git-hub</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/our-new-database-free-cms-experiment-is-up-on-git-hub</guid><description>While it’s still early days for this project, you can now take “Keystatic” for a test drive. </description><pubDate>Sat, 11 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;For more information on Keystatic, &lt;a href=&quot;https://github.com/thinkmill/keystatic&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;checkout the GitHub repo&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>The Thinkmill Learning Loop</title><link>https://www.thinkmill.com.au/blog/the-learning-loop</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/the-learning-loop</guid><description>How the work we do in open source and client services drives our craft to new levels.</description><pubDate>Fri, 10 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Many people who don’t know much about Thinkmill perceive us as a classic agency with all the classic agency problems like short term projects, churn, and superficial work.&lt;/p&gt;&lt;p&gt;While our commercial model is one of a classic agency (we sell time for money), how we create value, is not. We take a longer term view of developing knowledge, which is enabled through our work with &lt;a href=&quot;https://www.thinkmill.com.au/work&quot;&gt;clients&lt;/a&gt;. This knowledge &lt;em&gt;is&lt;/em&gt; the product of Thinkmill, one that we work hard to improve over time.&lt;/p&gt;&lt;p&gt;We get to practice and innovate over and over again, reinforcing a &lt;em&gt;learning loop&lt;/em&gt; that means over time, we collectively get better at a certain set of things: &lt;a href=&quot;https://www.thinkmill.com.au/product-design-engineering&quot;&gt;product design&lt;/a&gt; and &lt;a href=&quot;https://www.thinkmill.com.au/design-systems&quot;&gt;design systems&lt;/a&gt; to name a few.&lt;/p&gt;&lt;p&gt;We get to practice doing the thing in a wide variety of environments, under many different constraints – technology, people, culture, budgets, ambition, industry, etc.&lt;/p&gt;&lt;p&gt;With each iteration of the loop (client engagement) we accumulate new experience, and refine what we know. We get opportunities to innovate and try out new approaches and methods. Sometimes we get long stretches, sometimes we get short bursts. This is the innovation wheel that causes our product knowledge to get better over time.&lt;/p&gt;&lt;p&gt;This is also why all of Thinkmill is a team, and why we invest a lot in breaking down silos and spreading knowledge. We all contribute to the turning of the innovation wheel.&lt;/p&gt;&lt;p&gt;If we get too caught up in any individual project, we may lose sight of the value we gain from the engagement. But if we keep the bigger picture in view, and remember that each experience is additive, it’s easier to remember that the challenges of any engagement are transient, and always present an opportunity to improve our craft.&lt;/p&gt;&lt;p&gt;Getting better at our craft over time is the goal. It’s the whole game. This perspective is crucial for growing at Thinkmill and having a good time with amazing people (both clients, and colleagues) throughout.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Our Philosophy of Working</title><link>https://www.thinkmill.com.au/blog/thinkmills-philosophy-of-working</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/thinkmills-philosophy-of-working</guid><description>The mindsets, principles, and enablers that shape how we build great products.</description><pubDate>Wed, 08 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Over the last decade, Thinkmill has organically evolved a set of mindsets that inform how we build products. Together, these mindsets form a Philosophy of Working that permeates our daily practice and shapes the tools we make in &lt;a href=&quot;https://www.thinkmill.com.au/open-source&quot;&gt;open source&lt;/a&gt;. This article unpacks our mindsets to give you a better sense of how we bring value to the work of product, and offer a case for adopting them in your own team.&lt;/p&gt;&lt;h2&gt;Thinkmill mindsets&lt;/h2&gt;&lt;h3&gt;Engineering-led&lt;/h3&gt;&lt;p&gt;Originally coined by our Design Director Lauren Argenta, engineering-led emerged as a product-building mindset stemming from the fact that most Thinkmill practitioners are engineers.&lt;/p&gt;&lt;p&gt;In practical terms, engineering-led means that we tend to think about how the schema, database, and system architecture are going to be developed as a way of informing and shaping (sometimes constraining) the products that we then end up designing. Engineering-led has been a useful way for us to differentiate our way of working from a design-led one, which you may find in other organisations.&lt;/p&gt;&lt;p&gt;For a deeper dive into the engineering-led mindset, watch Lauren talk about how we design in an engineering-led way at &lt;a href=&quot;https://reactconfau.com/talks/engineering-led-design?from=talks&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ReactConf AU&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Embedded media — view this post on thinkmill.com.au.&lt;/em&gt;&lt;/p&gt;&lt;h3&gt;Code is the source of truth&lt;/h3&gt;&lt;p&gt;This mindset is especially important to the work we do in design. It conveys the idea that in a world of abstractions, converging on code as the primary source of truth — wherever possible — is the least problematic way to build products.&lt;/p&gt;&lt;p&gt;Practitioners who don’t subscribe to this approach might be of the opinion that the artefacts of a design tool like Figma represent the source of truth. In our view they make for a brittle source of truth because they’re a static picture of the dynamic thing that will exist as code running on the internet.&lt;/p&gt;&lt;p&gt;We’ve developed the mindset that as technical designers, all the stuff we’re creating and expressing will ultimately live as code in a repo. So we should work with this reality and limit the abstractions by understanding, referencing, subscribing to, and contributing to – the code. As much and as soon as possible.&lt;/p&gt;&lt;p&gt;As an example, our goal is not to maintain perfect synchrony between Figma components, and the components in the code repository or Storybook; but rather to be comfortable using and improving those components in their native context.&lt;/p&gt;&lt;h3&gt;Schema-first&lt;/h3&gt;&lt;p&gt;We often talk about schema-first as an extension of the engineering-led mindset. In some ways it’s an expression of how we learned to build software. We found that by describing the data model or a schema for an application first, it serves as middleware to align a product’s back-end infrastructure, API, and front-end needs.&lt;/p&gt;&lt;p&gt;If the practitioners involved in delivering those aspects can converge and collaborate on the schema – it ends up being a useful way of aligning those different practices. Such that you can then go away and work relatively independently and come back to the schema when the need to update shared understandings arises.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://keystonejs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Keystone&lt;/a&gt; is an open source framework we maintain that’s predicated on this schema-first way of working. With Keystone you literally write schema and it gives you back a database, GraphQL API, and Admin UI.&lt;/p&gt;&lt;p&gt;As opposed to starting with the interface or elsewhere, we strive to start with schema-related questions as much as possible. These questions look something like:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;How do we describe the world that we’re building this product in?&lt;/li&gt;&lt;li&gt;What are the core concepts that underpin this world?&lt;/li&gt;&lt;li&gt;What’s the underlying data model that could capably represent those concepts?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Answering these questions in the form of a schema gives us a really good idea of how to design interfaces and flows that are inherently aware of the data that underpins the user experience. The result is fewer gaps when we’re delivering the thing we designed.&lt;/p&gt;&lt;h3&gt;Cross-functional collaboration&lt;/h3&gt;&lt;p&gt;We’ve found that it’s really hard (if not impossible) to build good software and products if we’re not intentionally working in a deeply collaborative and cross-functional way. Getting people from different practices collaborating effectively is something we think a lot about. Two extensions of this cross-functional mindset are &lt;strong&gt;Design Engineering&lt;/strong&gt; and &lt;strong&gt;Code ♥ Design&lt;/strong&gt;.&lt;/p&gt;&lt;h4&gt;Design Engineering&lt;/h4&gt;&lt;p&gt;Invision coined this term in their excellent &lt;a href=&quot;https://www.designbetter.co/design-engineering-handbook&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Design Engineering Handbook&lt;/a&gt;. We subscribe to this mindset, and have embedded it as a central concept in the Thinkmill Method. Design Engineering to us is not about filling the gaps between the disciplines of design and engineering, but rather acknowledging and optimising their overlap.&lt;/p&gt;&lt;p&gt;As designers, the more we care about accessibility and API design, the better. As engineers, the more we care about interface quality, usability principles and heuristics, the better. The more we overlap, the more we reduce the likelihood of gaps both &lt;em&gt;technical&lt;/em&gt; and &lt;em&gt;experiential&lt;/em&gt;. Where &lt;em&gt;technical&lt;/em&gt; gaps are what we might miss on the path to delivery, and &lt;em&gt;experiential&lt;/em&gt; gaps arise from a designer creating without due consideration of something like performance, or conversely, an engineer building something that’s really performant without considering how useable that performant thing is.&lt;/p&gt;&lt;h4&gt;Code ♥ Design&lt;/h4&gt;&lt;p&gt;Code ♥ Design is another shorthand for our cross-functional way of working across design and engineering that we’re known for. In 2018 we partnered on a &lt;a href=&quot;https://codeheartdesign.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mini-conf&lt;/a&gt; to bring greater awareness to cross functional collaboration, so there’s a literal physical expression of our mindset out there.&lt;/p&gt;&lt;h2&gt;Our Philosophy of Working&lt;/h2&gt;&lt;p&gt;This cloud of mindsets comes together to form our Philosophy of Working. Most of these mindsets have been around for a while. They’re robust, proven, and hang together in such a way that we, as a group of people, can channel and embody them to build great things. Feel free to adopt or remix them to meet your own team needs.&lt;/p&gt;&lt;p&gt;If you’re interested in learning more about how we embody our philosophy in the work of building products, watch my talk from Dovetail’s Delighter’s conference:&lt;/p&gt;&lt;p&gt;&lt;em&gt;Embedded media — view this post on thinkmill.com.au.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;To explore how we can help you build great products and delivery culture within your organisation – &lt;a href=&quot;https://www.thinkmill.com.au/contact&quot;&gt;get in touch&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Design, content, &amp; code in parallel: Thinkmill’s new website</title><link>https://www.thinkmill.com.au/blog/design-content-and-code-in-parallel-thinkmill-s-new-website</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/design-content-and-code-in-parallel-thinkmill-s-new-website</guid><description>Simon made a playback of an internal team presentation we did this week. It looks at how we built the new Thinkmill website in a hyper-iterative way; with content, design, and code evolving in parallel streams. Our journey to launch presented an interesting set of challenges and learning opportunities.</description><pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate></item><item><title>How we built our website newsfeed</title><link>https://www.thinkmill.com.au/blog/how-we-built-our-website-newsfeed</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/how-we-built-our-website-newsfeed</guid><description>Here’s Tom Whitaker with an inside look at how we built the Thinkmill website news feed to surface material from a wide range of content types including articles, snippets, events, case studies, and more.</description><pubDate>Fri, 13 Jan 2023 00:00:00 GMT</pubDate></item><item><title>Dinesh has released a series of blog templates for Astro</title><link>https://www.thinkmill.com.au/blog/dinesh-has-released-a-series-of-blog-templates-for-astro</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/dinesh-has-released-a-series-of-blog-templates-for-astro</guid><description>Checkout these these beautiful, performant, and accessible open source blog templates for Astro made by our very own Dinesh Pandiyan.</description><pubDate>Thu, 22 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;You can clone the templates from Dinesh’s &lt;a href=&quot;https://github.com/flexdinesh/blogster&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github Repo&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Using CSS logical properties for internationalisation</title><link>https://www.thinkmill.com.au/blog/using-css-logical-properties-for-internationalisation</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/using-css-logical-properties-for-internationalisation</guid><description>We discuss the juiciest bits of an internal Slack conversation on design systems and logical properties. Served up with a dose of casual chat, two of our design systems experts – Joss Mackison and Luke Bennett – unpack best practices for the use of CSS logical properties for better internationalisation in design systems.</description><pubDate>Wed, 21 Dec 2022 00:00:00 GMT</pubDate></item><item><title>Building a share text selection to Twitter feature in React</title><link>https://www.thinkmill.com.au/blog/share-text-selection-to-twitter</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/share-text-selection-to-twitter</guid><description>Medium has a feature that lets readers share selected article text straight to Twitter — without having to copy, paste and edit. It’s a cool way to support sharing without drawing the user too far away from their reading experience. But, that same nifty sharing feature isn’t available for all the great content on the internet.</description><pubDate>Tue, 13 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/mft9lw2x7vc7/share-text-selection-to-twitter-share-to-twitter?width=848&quot; alt=&quot;What our end result will look like when selecting text&quot; width=&quot;848&quot; height=&quot;404&quot; /&gt;&lt;figcaption&gt;What our end result will look like when selecting text&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The likely result: social sharing takes a hit (or, it takes forever). So, what if there was a solution to take the great content you’re engaging with, and share it to a social platform really quickly — and really easily?&lt;/p&gt;&lt;p&gt;We recently created a version of this solution on a Thinkmill project, finding a way to improve the experience for users who prefer keyboard-based navigation when sharing to Twitter. This article will show you how to build your own using PopperJS and the native Selection API with improved accessibility for keyboard-based users thanks to your web browser’s Caret Browsing capability.&lt;/p&gt;&lt;h2&gt;Getting started&lt;/h2&gt;&lt;p&gt;You can try this out now, by selecting this paragraph and, with any luck, being presented with a popover interface that builds the tweet, and takes you to Twitter:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/share-text-selection-to-twitter/shareToTwitter.mov&quot;&gt;Share text selection to Twitter&lt;/a&gt;&lt;/p&gt;&lt;p&gt;To build this feature we’ll need to accomplish the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Show the user a popover interface when they’ve selected some text&lt;/li&gt;&lt;li&gt;Get the page URL for the tweet&lt;/li&gt;&lt;li&gt;Extract the selected text using the native selection API&lt;/li&gt;&lt;li&gt;Build the tweet to send through to Twitter’s URL API&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;We’ll use &lt;a href=&quot;https://popper.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PopperJS&lt;/a&gt; to display the sharing interface at the location where the user selected the text. We’ll also tie this up into a reusable hook in order to access it across many parts of a project. To do this we’ll break the feature into 2 parts:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A &lt;em&gt;hook&lt;/em&gt; called &lt;code&gt;useShareToSocial&lt;/code&gt; that will do all the heavy lifting of building the tweet and sharing it to Twitter.&lt;/li&gt;&lt;li&gt;A &lt;em&gt;wrapper component&lt;/em&gt; which we’ll use to handle the PopperJS reference for our popover.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Capturing selected text with the Selection API&lt;/h2&gt;&lt;p&gt;First things first, let’s get text highlight selection in place. To do this we need to reach for the HTML spec’s &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Selection API&lt;/a&gt;. As well as getting the selected text we want to also get the coordinates to pass through to PopperJS.&lt;/p&gt;&lt;pre&gt;const selectionChange = React.useCallback(() =&amp;gt; {
  const selection = window.getSelection();
  if (selection) {
    const selectedText = selection.toString();
    if (selection.isCollapsed || !selectedText.length) {
      setSelectedText(&apos;&apos;);
    }
    setSelectedText(selectedText);
  }
  const virtualReference: NullableVirtualElement = {
    getBoundingClientRect: () =&amp;gt; {
      return window
        .getSelection()
        ?.getRangeAt(0)
        .getBoundingClientRect() as DOMRect | null;
    }
  };
  setReferenceElement(virtualReference);
}, [setSelectedText]);
&lt;/pre&gt;&lt;p&gt;In the above we are also resetting our text selection when we have de-selected text on the screen.&lt;/p&gt;&lt;h2&gt;Extracting the page URL with window.location.href&lt;/h2&gt;&lt;p&gt;While we’re at it, let’s grab the URL of the page we’re on to include it in the tweet. To do that we can call &lt;code&gt;window.location.href&lt;/code&gt;. Then we pass that to the &lt;code&gt;encodeURIComponent()&lt;/code&gt; method, which converts it to UTF-8 encoding to put in our URL bar.&lt;/p&gt;&lt;pre&gt;const pageUrl = encodeURIComponent(window.location.href)
&lt;/pre&gt;&lt;h2&gt;Supporting keyboard-based selection with Caret Browsing&lt;/h2&gt;&lt;p&gt;We can improve the accessibility of this feature by taking advantage of a browser setting called &lt;a href=&quot;https://www.guidingtech.com/caret-browsing-chrome-firefox-edge-how-to-use/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Caret Browsing&lt;/a&gt;. Available in Chrome, Firefox and Edge through the F7 keyboard shortcut - Caret Browsing lets users navigate the page and select text with directional keys like they are in a MS Word or Google document. We will also trigger the function to fire on text selection change.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/pbek24wy1gh0/share-text-selection-to-twitter-caret-browsing-setting?width=848&quot; alt=&quot;Caret browsing setting in Chrome&quot; width=&quot;848&quot; height=&quot;74&quot; /&gt;&lt;figcaption&gt;Caret browsing setting in Chrome&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/share-text-selection-to-twitter/shareViaCaret.mov&quot;&gt;Using Caret Browsing to select text with keyboard and share to Twitter&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Positioning the popover&lt;/h2&gt;&lt;p&gt;PopperJS provides us with a &lt;code&gt;virtualReference&lt;/code&gt; of the selected text which we can use to position the popover. We’re going to use the coordinates Popper provides to set our reference element by passing it up to the parent to be set as the &lt;code&gt;ref&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;return () =&amp;gt; ({
  width: 0,
  height: 0,
  top: y,
  right: x,
  bottom: y,
  left: x,
})
&lt;/pre&gt;&lt;h2&gt;Limiting tweets to 280 characters&lt;/h2&gt;&lt;p&gt;Now that we have the main pieces, we can put them together to build that tweet!&lt;/p&gt;&lt;p&gt;We need to ensure that the selected text fits within Twitter’s tweet limit of 280 characters. By including the truncated text in the popover’s preview, we’ll avoid any last-minute surprises that the user might encounter when they go to share their tweet.&lt;/p&gt;&lt;pre&gt;const buildTweet = ({
  pageUrl,
  tweetContent,
}: {
  tweetContent: string | undefined
  pageUrl: string
}) =&amp;gt; {
  if (tweetContent !== undefined) {
    const characterTotal = tweetContent.length + pageUrl.length
    if (characterTotal + 19 &amp;lt; 280) {
      return encodeURIComponent(`&quot;${tweetContent}&quot; – `)
    }
    const amountToTrim = 280 - (pageUrl.length + 19)
    return encodeURIComponent(`&quot;${tweetContent.slice(0, amountToTrim)}…&quot; – `)
  }
  return &apos;&apos;
}

export const buildTargetLink = ({ quote }: ShareQuoteProps) =&amp;gt; {
  const pageUrl = encodeURIComponent(window.location.href)
  const twitterLink = (tweetContent: string) =&amp;gt;
    `https://twitter.com/intent/tweet?text=${tweetContent}&amp;amp;url=${pageUrl}&amp;amp;via=thethinkmill`
  const tweet = buildTweet({
    pageUrl,
    tweetContent: quote,
  })
  return twitterLink(tweet)
}
&lt;/pre&gt;&lt;p&gt;Let’s go through some of the numbers in the snippet above:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We add 19 to the &lt;code&gt;characterTotal&lt;/code&gt; to reflect the number of characters we wrap the text selection with &lt;code&gt;&quot;&quot; – via @thethinkmill&lt;/code&gt;. If we have too many characters with the wrapping text included, we move to the alternate option in the function.&lt;/li&gt;&lt;li&gt;Where we have the 19 added to the page URL but we cut the string and truncate. We then run it through the &lt;code&gt;encodeURIComponent()&lt;/code&gt; to make it appropriate to pass to the URL.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Sharing the tweet with Twitter’s URL API&lt;/h2&gt;&lt;p&gt;Now that we have our page URL and our built tweet at the right length we can use Twitter’s &lt;em&gt;&lt;strong&gt;intent/tweet&lt;/strong&gt;&lt;/em&gt; URL to fire off the tweet. The function we have above builds this URL in this way:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;https://twitter.com/intent/tweet&lt;/code&gt; – our main starting point&lt;/li&gt;&lt;li&gt;&lt;code&gt;text=${tweetContent}&lt;/code&gt; – the built quote at the correct length&lt;/li&gt;&lt;li&gt;&lt;code&gt;&amp;amp;url=${pageUrl}&lt;/code&gt; – the page URL we are on taken from window.location&lt;/li&gt;&lt;li&gt;&lt;code&gt;&amp;amp;via=thethinkmill&lt;/code&gt; – adding the intended account to be tagged when someone shares from an article&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Bringing it all together&lt;/h2&gt;&lt;p&gt;Now that we have done all the heavy lifting of selecting the text and building our tweet. All that’s left is to wrap it all up and be ready to ship this feature. To do this we will make a component to wrap our content and trigger the share function. Below is the whole component and then we will go into it more in-depth.&lt;/p&gt;&lt;pre&gt;export const ShareableText = ({
  wrapperId,
  children,
}: {
  wrapperId: string
  children: React.ReactElement
}) =&amp;gt; {
  const { textElementRef, textElementSelectionChangeHandler, popupElement } =
    useShareableText({ wrapperId })
  const childrenWithProps = React.Children.map(children, (child) =&amp;gt;
    React.cloneElement(child, {
      id: wrapperId,
      ref: textElementRef,
      onMouseUp: textElementSelectionChangeHandler,
    })
  )
  return (
    &amp;lt;React.Fragment&amp;gt;
      {childrenWithProps}
      {popupElement}
    &amp;lt;/React.Fragment&amp;gt;
  )
}
&lt;/pre&gt;&lt;p&gt;We are pulling in the pieces we need out of the useShareableText hook. We grab the selection handler function and the relevant parts used by PopperJS.&lt;/p&gt;&lt;pre&gt;const { textElementRef, textElementSelectionChangeHandler, popupElement } =
  useShareableText({ wrapperId })
&lt;/pre&gt;&lt;p&gt;Then we use the &lt;em&gt;&lt;strong&gt;cloneElement&lt;/strong&gt;&lt;/em&gt; method from React and attach the required props needed.&lt;/p&gt;&lt;pre&gt;const childrenWithProps = React.Children.map(children, (child) =&amp;gt;
  React.cloneElement(child, {
    id: wrapperId,
    ref: textElementRef,
    onMouseUp: textElementSelectionChangeHandler,
  })
)
&lt;/pre&gt;&lt;p&gt;Now we can return a fragment with the props for the wrapper and the PopperJS element.&lt;/p&gt;&lt;pre&gt;return(
  {childrenWithProps}
  {popupElement}
);
&lt;/pre&gt;&lt;p&gt;The result is a convenient wrapper around our content, which is the final piece of the share-to-Twitter puzzle. That gets placed around our content and we’re done.&lt;/p&gt;&lt;pre&gt;return &amp;lt;ShareableText wrapperId=&quot;shareable-wrapper&quot;&amp;gt;{content}&amp;lt;/ShareableText&amp;gt;
&lt;/pre&gt;&lt;h2&gt;What we’ve learned&lt;/h2&gt;&lt;p&gt;We used the selection API to get selected text from the page. We took advantage of PopperJS’ &lt;code&gt;virtualReference&lt;/code&gt; function to know where on the page our selected text was located. And finally, we built a neat function that creates the tweet with some wrapping text and fits it within Twitter‘s 280 character limit. The end result is a seamless sharing experience you can use across mouse, touch, and keyboard inputs to let readers share their favourite excerpt of an article without having to copy, paste and edit.&lt;/p&gt;&lt;h2&gt;Working example&lt;/h2&gt;&lt;p&gt;You can &lt;a href=&quot;https://codesandbox.io/s/shareastweet-40n67j?file=/src/useShareToTwitter.tsx&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;check out the CodeSandbox here&lt;/a&gt;, and the &lt;a href=&quot;https://40n67j.csb.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;working example page here&lt;/a&gt;. If you have more questions about the implementation you can find &lt;a href=&quot;https://twitter.com/tomWritesCode&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;me on Twitter&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Further reading&lt;/h2&gt;&lt;p&gt;To learn more about the tools and APIs used in this tutorial check out the links below.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;selection API&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://popper.js.org/docs/v2/virtual-elements/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Popper VirtualReference&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;getBoundingRectClient&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.guidingtech.com/caret-browsing-chrome-firefox-edge-how-to-use/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;What is Caret Browsing?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/article&gt;</content:encoded></item><item><title>GraphQL for designers</title><link>https://www.thinkmill.com.au/blog/graph-ql-for-designers</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/graph-ql-for-designers</guid><description>Why do developers love GraphQL and how can designers best work with them?</description><pubDate>Mon, 14 Nov 2022 00:00:00 GMT</pubDate></item><item><title>Dinesh has released an app for sharing social notes</title><link>https://www.thinkmill.com.au/blog/dinesh-has-released-an-app-for-sharing-social-notes</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/dinesh-has-released-an-app-for-sharing-social-notes</guid><description>Dinesh made a handy tool to let you embed text into Open Graph images for social sharing purposes. Pocketnotes lets you quickly create and share notes to platforms like Twitter/Slack/Discord and the text will show up in the link preview.</description><pubDate>Wed, 02 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Try it out at &lt;a href=&quot;https://pocketnotes.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://pocketnotes.app/&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Talking remote-first work at the Tech Council of Australia</title><link>https://www.thinkmill.com.au/blog/talking-remote-first-work-at-the-tech-council-of-australia</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/talking-remote-first-work-at-the-tech-council-of-australia</guid><description>Jed was invited by The Tech Council of Australia to talk at their showcase on the future of work. As a panel member, Jed shared Thinkmill’s experience of working in a Covid-era, how our business has been successfully navigating a remote-first approach since 2019, and what the future of our HQ may be. As a member of the TCA, we’re able to give a voice to software R&amp;D and tech, from the trenches of community and innovation.
</description><pubDate>Tue, 01 Nov 2022 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/cy1f11kc4e9n/tech-council-australia?width=1200" length="226448" type="image/png"/></item><item><title>Luke Bennett special guests on The NavBar pod</title><link>https://www.thinkmill.com.au/blog/luke-bennett-special-guests-on-the-nav-bar-pod</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/luke-bennett-special-guests-on-the-nav-bar-pod</guid><description>Luke Bennett joined Simon &amp; John from The NavBar to review the exciting new announcements at Next Conf 2022, and the release of Next.js 13.</description><pubDate>Thu, 27 Oct 2022 00:00:00 GMT</pubDate></item><item><title>Delightful products, the Antifragile way</title><link>https://www.thinkmill.com.au/blog/delightful-products-the-antifragile-way</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/delightful-products-the-antifragile-way</guid><description>Boris recently spoke at Dovetail’s Delighters Conference about our method for building great products within cross-functional teams.</description><pubDate>Thu, 27 Oct 2022 00:00:00 GMT</pubDate></item><item><title>Jed &amp; Alex traveled to India for a week-long code/design audit and training workshop with a new client. </title><link>https://www.thinkmill.com.au/blog/jed-and-alex-traveled-to-india-for-a-week-long-code-design-audit-and-training-workshop-with-a-new-client</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/jed-and-alex-traveled-to-india-for-a-week-long-code-design-audit-and-training-workshop-with-a-new-client</guid><description>We’re helping AnywhereWorks find better ways of working and levelling-up their codebase and design system to support future growth.</description><pubDate>Tue, 25 Oct 2022 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ss0pcpz651y6/jed-alex-chennai?width=1200" length="204511" type="image/jpeg"/></item><item><title>Schema-driven Development</title><link>https://www.thinkmill.com.au/blog/schema-driven-development</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/schema-driven-development</guid><description>Dinesh recently gave a lightning talk at Sydney’s Web Directions Meetup on schema-driven development. Here’s a quick recap in case you missed it.</description><pubDate>Sun, 09 Oct 2022 00:00:00 GMT</pubDate></item><item><title>A day out at DDD Perth 2022</title><link>https://www.thinkmill.com.au/blog/day-out-ddd-perth-2022</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/day-out-ddd-perth-2022</guid><description>Highlights from Perth’s annual inclusive conference for the tech community.</description><pubDate>Tue, 20 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;&lt;a href=&quot;https://dddperth.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DDD Perth&lt;/a&gt; is an inclusive conference for the Perth tech community, packed with incredible speakers, and only costs $60! This year it was held at the Perth Convention and Exhibition Centre on Saturday 10th September. I woke up terribly early for a Saturday morning and headed into town to soak up some knowledge.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/hurrdty4rudh/day-out-ddd-perth-2022-ddd-perth?width=848&quot; alt=&quot;DDD Perth Neon Light Sign on stage at Perth Convention and Exhibition Centre&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;figcaption&gt;DDD Perth Neon Light Sign on stage at Perth Convention and Exhibition Centre&lt;/figcaption&gt;&lt;/figure&gt;&lt;hr /&gt;&lt;p&gt;Learning from previous years, I got in early to have my Keep Cup filled with coffee. I’m glad I did, as the queue grew rapidly behind me. Coffee in hand (huge shoutout to the coffee cart sponsors &lt;a href=&quot;https://www.moqdigital.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MOQ Digital&lt;/a&gt;, &lt;a href=&quot;https://www.planittesting.com/au/home&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PlanIt&lt;/a&gt; and &lt;a href=&quot;https://www.bunnings.com.au/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bunnings&lt;/a&gt;), I made my way around the sponsor stands to collect my prize draw stamps. One of the great things about DDD is being able to chat to representatives from some awesome tech organisations (and of course pick up some swag), and ask them about their services and/or products. After learning a bit about &lt;a href=&quot;https://www.twilio.com/docs/studio&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twilio Studio&lt;/a&gt;, using &lt;a href=&quot;https://auth0.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Auth0&lt;/a&gt; with Next.js and raiding &lt;a href=&quot;https://github.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;’s sticker collection, it was time to head into the main auditorium for the opening keynote.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://medium.com/u/966323d46a22?source=post_page-----23e0cd2066eb--------------------------------&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Christina Aldan&lt;/a&gt; delivered an opening keynote centred around understanding how our memories are stored and processed, and how to train our brain to store and process the memories more efficiently. This practical soft-skills talk helped me to understand some of the ways in which my own brain prefers to encode, process and retrieve information, as well as acknowledge how practicing mindfulness can boost my brain’s efficiency.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: The brain isn’t always ready to roll, providing up front information, reference materials and facilitating async collaboration goes a long way in helping us to process information efficiently.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;For the first breakout session of the day I headed to Phil Wild’s talk on Doodling and Drawing which for me was a perfect way to start the day as I’ve always been interested in using drawings to show my ideas, but I’ve never been any good at drawing. Phil took us through a series of practical tips and exercises in using the Bikablo drawing technique to communicate ideas and concepts. He demonstrated that simply using a few primitive shapes, it’s possible to create almost any visual to support an idea or concept.&lt;/p&gt;&lt;p&gt;After a quick coffee refill (again, huge shoutout to the coffee cart sponsors!), I listened to &lt;a href=&quot;https://twitter.com/krustywan&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kristy Haines&lt;/a&gt; clear up any confusion about what design is. The great thing for me about Kristy’s talk was its timing. Personally I’ve worked in close collaboration with a design team for most of my career, so sharing ideas and feedback throughout a project feels quite natural to me by now. Recently I’ve been working with engineering teams that haven’t had as much experience working with designers and Kristy dropped some great explanations that I can use in helping them understand the benefits of working as a cross-functional team.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: Design is never complete. Design and engineering collaboration continues throughout the lifecycle of a product, innovation is the responsibility of the entire team, not only the designers.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;I’m pretty sure we’ve all experienced that feeling that we’re not good enough, or don’t deserve to be in the position we are. I know I certainly have and apparently so have 58% of tech workers. Former psychologist turned software engineer, &lt;a href=&quot;https://www.linkedin.com/in/cristimiddag?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAAA53I28BnnRiB9hTwrsJI5duh6z3n6m61mQ&amp;amp;lipi=urn%3Ali%3Apage%3Ad_flagship3_pulse_read%3BeHhztSi6QN6d5bJtjUEV9g%3D%3D&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cristi Middag&lt;/a&gt; detailed the journey of her career change and how she navigated the feeling of being an imposter. Her talk also included some tools and techniques that can be used to help ban out imposter feelings individually including Cognitive Restructuring and Dysfunctional Thought Records, as well as insights for team leaders to help reduce the effect of Imposter Syndrome on individual and team performance.&lt;/p&gt;&lt;p&gt;As a recent convert to full time remote work I was super keen to hear &lt;a href=&quot;https://medium.com/u/2fb0d0f9abc0?source=post_page-----23e0cd2066eb--------------------------------&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ming Johanson&lt;/a&gt;’s talk on productivity when working remotely. Throughout the talk were a number of tips and tricks for employers, as well as some great testimonials from Ming’s own team members, however the key takeaway here was clear as day. Trust your team, if you can’t trust your team, you have the wrong team!&lt;/p&gt;&lt;p&gt;After a delicious conference lunch and a cheeky pint across the road with some fellow devs, I headed back inside the Convention Centre for the last couple of talks. One of the things no one tells you as a junior developer is that as you become more senior, you write less code and have to explain your solutions to everybody in the organisation. Fortunately there are folks out there like &lt;a href=&quot;https://twitter.com/DakarRally&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Trent Jones&lt;/a&gt; who are willing to share their techniques in how best to explain yourself. In a short session, Trent unpacked an example architecture diagram for a fictional cannery, breaking it down into a simple yet detailed chart that anyone from engineers to product owners and stakeholders could understand. Combining the learnings from this talk with some of the drawing tips from Phil’s earlier talk could make for some excellent artefacts in my daily work.&lt;/p&gt;&lt;p&gt;That lunchtime pint was a mistake, feeling sleepy I headed back to the coffee cart for round 3, then into the main auditorium for the locknote. Chris Ferrie’s presentation was an energetic and entertaining explanation of the complex concept of Schrodinger’s cat and quantum physics, which in addition to the caffeine hit, perked me right up.&lt;/p&gt;&lt;p&gt;All that was left now was the prize draw, in which I didn’t win one of the amazing prizes on offer, a couple of beers and some pizza at the after-party. Once again I was blown away by the incredible experience of attending DDD Perth. For $60 the conference is on par or better than conferences that charge a lot more than that. For that you get loads of international and local speakers, way too much swag, coffee, lunch, snacks and drinks. No wonder it sells out so quickly. Thank you to everyone involved in making DDD happen, I’ll see you next year.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>We’re really proud to see so much adoption of our OSS tools</title><link>https://www.thinkmill.com.au/blog/we-re-really-proud-to-see-so-much-adoption-of-our-oss-tools</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/we-re-really-proud-to-see-so-much-adoption-of-our-oss-tools</guid><description>Our ability to do great work is inseparable from the communities we’re a part of. Seeing our open-source projects used by so many people remains a constant source of inspiration.</description><pubDate>Thu, 15 Sep 2022 00:00:00 GMT</pubDate></item><item><title>Single source of truth development</title><link>https://www.thinkmill.com.au/blog/single-source-of-truth-development</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/single-source-of-truth-development</guid><description>Jed demonstrates an end-to-end project using Prisma, Keystone, GraphQL, TypeScript and Next.js where everything is derived from a single source of truth for the schema and types.</description><pubDate>Thu, 15 Sep 2022 00:00:00 GMT</pubDate></item><item><title>How to establish multi-disciplinary teams to effectively design and build products</title><link>https://www.thinkmill.com.au/blog/how-to-establish-multi-disciplinary-teams-to-effectively-design-and-build-products</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/how-to-establish-multi-disciplinary-teams-to-effectively-design-and-build-products</guid><description>Lauren recently co-presented with Paul Merrell from Meld Studios at UX Australia 2022 about how Thinkmill &amp; Meld established a multi-disciplinary team to effectively design and build a product.</description><pubDate>Fri, 09 Sep 2022 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/nht1vajplcnp/how-to-establish-multidisciplinary-teams-ux-australia?width=1200" length="528579" type="image/png"/></item><item><title>New TypeScript utilities released as OSS</title><link>https://www.thinkmill.com.au/blog/new-type-script-utilities-released-as-oss</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/new-type-script-utilities-released-as-oss</guid><description>Design-engineer Joss Mackison has released a new open source collection of utilities that smooth out the rough edges of working with TypeScript.</description><pubDate>Thu, 08 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/Thinkmill/emery&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Checkout Emery on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Jordan’s Automatic Style Guides plugin now supports hsla()</title><link>https://www.thinkmill.com.au/blog/jordan-s-automatic-style-guides-plugin-now-supports-hsla</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/jordan-s-automatic-style-guides-plugin-now-supports-hsla</guid><description>Jordan Geizer has released an update to his awesome Automatic Style Guides plugin. Now with hsla() support.</description><pubDate>Thu, 08 Sep 2022 00:00:00 GMT</pubDate></item><item><title>The State of Headless CMS</title><link>https://www.thinkmill.com.au/blog/the-state-of-headless-cms</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/the-state-of-headless-cms</guid><description>Jed joined a panel of experts to discusses the current state of Headless CMS and what lies over the horizon.</description><pubDate>Wed, 27 Jul 2022 00:00:00 GMT</pubDate></item><item><title>Simon Vrachliotis has launched a Podcast series</title><link>https://www.thinkmill.com.au/blog/simon-vrachliotis-has-launched-a-podcast-series</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/simon-vrachliotis-has-launched-a-podcast-series</guid><description>Simon Vrachliotis and his friend John Myers from Supabase have launched the NavBar – a podcast dedicated to web development and content creation. Check out the teaser.</description><pubDate>Tue, 19 Jul 2022 00:00:00 GMT</pubDate></item><item><title>Introducing KeystoneJS, the CMS &amp; API Platform for Prisma</title><link>https://www.thinkmill.com.au/blog/introducing-keystone-js-the-cms-and-api-platform-for-prisma</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/introducing-keystone-js-the-cms-and-api-platform-for-prisma</guid><description>Jed’s workshop on how to build a polling system into a blog with Keystone, NextJs, GraphQL &amp; Prisma.</description><pubDate>Sat, 02 Jul 2022 00:00:00 GMT</pubDate></item><item><title>Keystone 6 is now in General Availability!</title><link>https://www.thinkmill.com.au/blog/keystone-6-is-now-in-general-availability</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/keystone-6-is-now-in-general-availability</guid><description>Today’s Keystone is faster and more flexible than it’s ever been, and is ready for you to build amazing things with.</description><pubDate>Tue, 21 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;For more information visit &lt;a href=&quot;https://keystonejs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://keystonejs.com/&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/vd68e40eewy5/keystone-6-ga?width=1200" length="111057" type="image/png"/></item><item><title>Keystone, GraphQL &amp; Prisma</title><link>https://www.thinkmill.com.au/blog/keystone-graph-ql-and-prisma</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/keystone-graph-ql-and-prisma</guid><description>Jed discusses the new Keystone 6 at Prisma’s Korea Meetup </description><pubDate>Sat, 09 Oct 2021 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/hrw2a6vwplng/keystone-prisma-graphql-next-gen-cms?width=1200" length="555349" type="image/png"/></item><item><title>We’ve joined the Tech Council of Australia</title><link>https://www.thinkmill.com.au/blog/we-ve-joined-the-tech-council-of-australia</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/we-ve-joined-the-tech-council-of-australia</guid><description>We’ve joined the Tech Council of Australia to help reach 1m tech jobs by 2025</description><pubDate>Tue, 05 Oct 2021 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/jszacynhidgh/tech-council-australia-2?width=1200" length="47621" type="image/jpeg"/></item><item><title>Next-gen CMS &amp; GraphQL API with Keystone &amp; Prisma</title><link>https://www.thinkmill.com.au/blog/next-gen-cms-and-graph-ql-api-with-keystone-and-prisma</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/next-gen-cms-and-graph-ql-api-with-keystone-and-prisma</guid><description>Jed shared our freshly minted Keystone 6 with the world at Prisma Day conference in July 2021. His talk is a great way learn how Keystones combination of features and flexibility set it apart from other backend frameworks and Content Management Systems</description><pubDate>Fri, 16 Jul 2021 00:00:00 GMT</pubDate></item><item><title>ReactConf AU, one year later</title><link>https://www.thinkmill.com.au/blog/react-conf-au-one-year-later</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/react-conf-au-one-year-later</guid><description>We put on Australia’s first ever React Conf in February 2020. You won’t believe what happened next!</description><pubDate>Fri, 26 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/n4j2ocnogbn7/illustrations-for-reactconf-au-2020-react-conf-1?width=848&quot; alt=&quot;Dinesh Pandiyan &amp;amp; Dominik Wilkowski having fun at ReactConf AU in front of a ReactConf AU banner poster&quot; width=&quot;848&quot; height=&quot;848&quot; /&gt;&lt;figcaption&gt;Dinesh Pandiyan &amp;amp; Dominik Wilkowski having fun at ReactConf AU in front of a ReactConf AU banner poster&lt;/figcaption&gt;&lt;/figure&gt;&lt;hr /&gt;&lt;p&gt;There’s nothing like an anniversary to remind us how fast time moves, and just how much things change. And that’s just in a normal year!&lt;/p&gt;&lt;p&gt;Following a tough start to the year with bushfires raging across Australia, we had no real idea what 2020 had in store when we hosted Australia’s first ever React Conference on the 27th &amp;amp; 28th of February.&lt;/p&gt;&lt;p&gt;Overseas speakers flew in, old friends reacquainted and new connections were made. Close to 600 local and international developers gathered at Sydney Harbour to connect with, listen to, and learn from members of the React community.&lt;/p&gt;&lt;p&gt;Looking back, it was truly amazing that all this happened just two weeks before we all went remote and had to stop seeing each other in person for the rest of the year.&lt;/p&gt;&lt;p&gt;We realise this made us twice as lucky — not only did we manage to wrangle such an awesome event, but we threw one of the last parties Australia’s React Community could enjoy for a while!&lt;/p&gt;&lt;p&gt;Since then, we’ve been reflecting on the 2020 rollercoaster, plans that were made, and what we have achieved since.&lt;/p&gt;&lt;h2&gt;Plans Made&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;As the event was closing, 93% of our attendees said they’d come to the next ReactConf AU, and our keynote speaker Max Stoiber ranked it as one of the top four conferences he’s ever been to globally 😍&lt;/li&gt;&lt;li&gt;While 2021 may not be the year, we do look forward to hosting another ReactConf AU again when the time is right. And in the meantime we’re all exploring new ways of connecting with each other from home 🏡&lt;/li&gt;&lt;li&gt;Our second keynote speaker Diana Mounter gave a talk on how Themeability is the path to Dark Mode, with a wealth of insight into how GitHub are building themeing foundations and applying colour theory to deliver their most-requested UI update ever&lt;/li&gt;&lt;li&gt;Our co-founder Jed closed the conference with a talk on The Value of Open Source, and surprised the audience with a voucher taped under their seat, to donate to a project that matters to them on Open Collective 💵&lt;/li&gt;&lt;li&gt;And of course, just a few weeks after the conference we were all making plans on how to keep our heads down and survive the new reality we found ourselves in&lt;/li&gt;&lt;/ul&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/izm9nxm8l4qe/react-conf-au-one-year-later-react-conf-2?width=848&quot; alt=&quot;Jed Watson presenting on stage at ReactConf AU with a screen next to him displaying &apos;Satisfaction ☺️&apos;&quot; width=&quot;848&quot; height=&quot;565&quot; /&gt;&lt;figcaption&gt;Jed Watson presenting on stage at ReactConf AU with a screen next to him displaying &apos;Satisfaction ☺️&apos;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Goals Kicked&lt;/h2&gt;&lt;p&gt;It didn’t feel like the rest of the year was going to plan at all, so it’s been great to reflect on how things actually played out:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Despite everything, we’re proud to say that with the support and flexibility of our team and partners, Thinkmill not only survived 2020 but continued to grow (even if only slightly compared to past years) 🙏🏻&lt;/li&gt;&lt;li&gt;After the conference, several hundred developers donated to open source projects on Open Collective for the first (and not last) time! It was brilliant watching the donations show up in unexpected places as our community paid them forward ⭐️&lt;/li&gt;&lt;li&gt;More broadly, we saw groups and fiscal hosts coming together on Open Collective’s platform to help each other through the economic fallout from COVID-19, including their UPLIFT! event which raised $105K to support the open source ecosystem 📈&lt;/li&gt;&lt;li&gt;We applied our Budget for Open Source initiative internally, renewing our commitment to maintaining projects like &lt;a href=&quot;http://www.react-select.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;react-select&lt;/a&gt; and funding a dedicated team (for the first time!) to focus on developing &lt;a href=&quot;http://www.keystonejs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;KeystoneJS&lt;/a&gt; — our open source Headless CMS, empowering front-end developers to build powerful, easy to manage, content-driven websites 🌏&lt;/li&gt;&lt;li&gt;AirBnB got a special shout for their $100k open source funding initiative, and this year have increased that by 50%. It’s absolutely worth reading about &lt;a href=&quot;https://twitter.com/AirbnbEng/status/1364650154716880897?s=20&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the impact this has has on open source projects&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;And delivering on Diana’s promise, GitHub finally launched their Dark Mode to great acclaim across the internet 👏🏻&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;In Case You Missed It&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Last year’s event was packed with incredible talks, well worth a watch (or re-watch). Browse them all right here: &lt;a href=&quot;https://reactconfau.com/talks&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;reactconfau.com/talks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Possibly our most artistic developer, Tom Walker prepared unique, inspired artworks for each of the talks, and followed up with a whole write-up about the process. &lt;a href=&quot;https://medium.com/the-thinkmill/illustrations-for-reactconf-au-2020-85978a49eae5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;See and read about them in our blog&lt;/a&gt;!&lt;/li&gt;&lt;li&gt;We’re glad to see more companies prioritising open source funding and sustainability, but there is still a long way to go. Big things start small, so remember to advocate for the value you get from open source and help your company dedicate some budget to sustaining it.&lt;/li&gt;&lt;li&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/tnv7ydpvseph/react-conf-au-one-year-later-react-conf-3?width=848&quot; alt=&quot;Overhead image of a happy crowd at ReactConf Au looking at the camera&quot; width=&quot;848&quot; height=&quot;565&quot; /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Thanks again to everyone who was part of the event — including the organising team (especially our indomitable duo Debbie and Kim), the support crew who made each day run smoothly, the speakers both local and international who put so much effort into preparing great talks, our sponsors who made the whole thing possible, and of course everyone who came to the event. We couldn’t have done it without any of you.&lt;/p&gt;&lt;p&gt;And for everyone who watched the conference, either online or in person, we’re glad you enjoyed it, and look forward to seeing you again one day!&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Jordan Geizer has released an automated style guide plugin for Figma</title><link>https://www.thinkmill.com.au/blog/jordan-geizer-has-released-an-automated-style-guide-plugin-for-figma</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/jordan-geizer-has-released-an-automated-style-guide-plugin-for-figma</guid><description>Jordan Geizer has released an Automatic Style Guide plugin for Figma that generates (and keeps up to date) formal style guide documentation based on your local Figma styles. Now you can prepare a clean and functional style guide for clients &amp; developers with a click of a button.</description><pubDate>Mon, 22 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;&lt;a href=&quot;https://www.figma.com/community/plugin/838622388628773312/automatic-style-guides&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Try the Automatic Style Guides plugin&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>All the ReactConf AU talks are available online</title><link>https://www.thinkmill.com.au/blog/all-the-react-conf-au-talks-are-available-online</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/all-the-react-conf-au-talks-are-available-online</guid><description>All the recordings from ReactConf AU are available on YouTube. Here’s the full playlist.</description><pubDate>Sun, 01 Nov 2020 00:00:00 GMT</pubDate></item><item><title>The &quot;state&quot; of UI</title><link>https://www.thinkmill.com.au/blog/the-state-of-ui</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/the-state-of-ui</guid><description>This will come as a surprise to many of you, but user interfaces on the web, have different types of state. It might be that &quot;State&quot;, much like &quot;Design Systems&quot;, is a word that has lost all meaning for you, but regardless, I think you will agree the UI state designers talk about is different from the application state developers refer to. Let’s figure out how to have a discussion about these similar sounding but very different concepts for the sake of our sanity and better design system components.</description><pubDate>Tue, 09 Jun 2020 00:00:00 GMT</pubDate></item><item><title>Illustrations for ReactConf AU 2020</title><link>https://www.thinkmill.com.au/blog/illustrations-for-reactconf-au-2020</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/illustrations-for-reactconf-au-2020</guid><description>A unique illustration for each presentation</description><pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/e971jqzx81w8/illustrations-for-reactconf-au-2020-react-conf-illustrations-key-image?width=848&quot; alt=&quot;ReactConf AU main illustration&quot; width=&quot;848&quot; height=&quot;622&quot; /&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/q4hasrq38jzy/illustrations-for-reactconf-au-2020-search-and-find?width=848&quot; alt=&quot;Search and find illustration&quot; width=&quot;848&quot; height=&quot;451&quot; /&gt;&lt;h2&gt;About&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://reactconfau.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;ReactConf AU 2020&lt;/strong&gt;&lt;/a&gt; was recently held in Sydney, Australia. Presented by Thinkmill — ReactConf AU is a tech conference featuring domestic and international speakers. I helped organise the conference, provided gelato for attendees, and created the illustrations above.&lt;/p&gt;&lt;p&gt;The goal was to illustrate each of the 23 presentations at the conference and then combine them into one mega illustration.&lt;/p&gt;&lt;p&gt;Illustrations were based on talk titles and descriptions, and it’s worth mentioning that these presentations were very technical and abstract at times, so interpretation varied wildly.&lt;/p&gt;&lt;p&gt;It was completed a few days before the conference, printed onto stretch fabric, and displayed at the conference! Each illustration was also shown on stage immediately before each presentation, including the time lapses videos found below.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/n4j2ocnogbn7/illustrations-for-reactconf-au-2020-react-conf-1?width=848&quot; alt=&quot;Dinesh Pandiyan and Dominik Wilkowski at ReactConf AU, 27th February 2020&quot; width=&quot;848&quot; height=&quot;848&quot; /&gt;&lt;figcaption&gt;Dinesh Pandiyan and Dominik Wilkowski at ReactConf AU, 27th February 2020&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Thanks&lt;/h2&gt;&lt;p&gt;The illustrations would not have been possible without the help of &lt;a href=&quot;https://medium.com/u/9739268ecdcb?source=post_page-----85978a49eae5--------------------------------&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Dominik Wilkowski&lt;/a&gt; — who brainstormed ideas with me for hours throughout the illustration process and provided lots of great feedback after each illustration was completed.&lt;/p&gt;&lt;p&gt;Daniel Cousens and Alex Walker were also a great help, especially with perspective — one of the hardest aspects to get right and something I’ll keep improving on.&lt;/p&gt;&lt;h2&gt;Illustrations&lt;/h2&gt;&lt;p&gt;Here’s a one-by-one overview of all 23 illustrations, which talk they were paired with, and the story I was telling in each one.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/yfp0j2lhjrhx/illustrations-for-reactconf-au-2020-themeability-illustration?width=848&quot; alt=&quot;Themeability is the path to dark mode illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;1. Themeability is the path to dark mode&lt;/h2&gt;&lt;p&gt;Diana Mounter&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;When the design systems team at GitHub implemented their colour system in 2017 they were working with Sass, trying to wrangle thousands of disconnected colour values into… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/themeability-is-the-path-to-dark-mode?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Engineers take the existing light theme on the GitHub website and produce a dark theme from it. Processing power is focused on getting dark mode code output.&lt;/p&gt;&lt;p&gt;Printers print the code necessary to be input with hashes depicting hex colour codes. It’s a lot to keep track of, so why are they printing this on paper anyway?&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=mBZBwL_aseA&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ottfztzv2kp1/illustrations-for-reactconf-au-2020-bbc-news-illustration?width=848&quot; alt=&quot;(Proto)typing innovation in BBC News illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;2. (Proto)typing innovation in BBC News&lt;/h2&gt;&lt;p&gt;James Dooley&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;BBC News Labs explore, prototype and incubate new ways to tell stories, reach audiences and create cutting edge journalism. James will demonstrate how his team… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/prototyping-innovation-in-bbc-news?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A famous presenter with a British accent interviews a bear with the help of a real-time transcribing computer (with support for translation too), beaming the results real time to back to the broadcaster.&lt;/p&gt;&lt;p&gt;Due to its remote location, equipment — including a satellite — has to be brought in to beam the signal back. The bears are mischievous; they have taken off with the BBC sign.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: BBC documentaries (TV)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=CrBQLXhOwY0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/it8mf6rda4s2/illustrations-for-reactconf-au-2020-modern-forms-illustration?width=848&quot; alt=&quot;Modern forms in React illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;3. Modern Forms in React&lt;/h2&gt;&lt;p&gt;Erik Rasmussen&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If the new React Context API and Suspense killed Redux (they didn’t), surely Hooks kill the need for a form library, right? Well, not exactly. Managing form state is more than just holding… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/modern-forms-in-react?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Developers are using fishing rods and hooks to collect form fields to use in their forms.&lt;/p&gt;&lt;p&gt;They’re looking for buttons, drop downs, toggles, radio buttons, check boxes, range sliders, and password fields.&lt;/p&gt;&lt;p&gt;It’s a lucrative business; the checked radio button is rare and sells for many credits.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XJrRZjqhZtM&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/lzfw31v7418l/illustrations-for-reactconf-au-2020-react-dnd-illustration?width=848&quot; alt=&quot;react-beautiful-dnd: Behind the magic illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;4. react-beautiful-dnd: Behind the magic&lt;/h2&gt;&lt;p&gt;Alex Reardon&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;react-beautiful-dnd&lt;/code&gt; (&lt;code&gt;rbd&lt;/code&gt;) is a successful open source project that aims to provide performant, beautiful and accessible drag and drop. In this talk, Alex will break… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/react-beautiful-dnd-behind-the-magic?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;An architectural design reminiscent of drag and drop lists, each item being an occupied space.&lt;/p&gt;&lt;p&gt;Each block can move up, down, left, and right — even to an entirely different building by going straight down and detaching.&lt;/p&gt;&lt;p&gt;Occupants include: VR chamber, an industrial house, gelato factory, tropics, magician, gelato store, and a home with lots of plants — which is currently moving to another space.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Drag and drop lists (web)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=nlcujSpyKd4&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ixseh9okgwct/illustrations-for-reactconf-au-2020-pair-programming-illustration?width=848&quot; alt=&quot;10x your teamwork through pair programming illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;5. 10 x your teamwork through pair programming&lt;/h2&gt;&lt;p&gt;Michael Milewski &amp;amp; Selena Small&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Selena and Michael take you on a roller coaster journey of getting into and getting the most out of pair programming. Live on stage trying to deliver a feature whilst coding in React… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/10-x-your-teamwork-through-pair-programming?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A roller coaster just for pair programming! Two seats, one keyboard, and one screen in each carriage. Riders often reach peak productivity at the top of the coaster.&lt;/p&gt;&lt;p&gt;It has got some very tight turns, though. It’s no wonder that guy feels ill after riding.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Roller Coaster Tycoon (game)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ax4yqYCl6jA&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/j7c6rzfh8m3h/illustrations-for-reactconf-au-2020-faster-page-load-illustration?width=848&quot; alt=&quot;The first two seconds: Faster page loads with React illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;6. The first two seconds: Faster page loads with React&lt;/h2&gt;&lt;p&gt;Josh Duck&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We’re often told it’s impossible to create React pages that load quickly. Are React developers really doomed to a life of embarrassing Lighthouse scores? Working with the ABC… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/the-first-two-seconds-faster-page-loads-with-react?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Website performance equipment is perched atop Lighthouse Beach. Today. the ABC News website is being tested. Once the tests are run, the lighthouse projects a score from 0 to 100, which indicates how well the website performs.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Lighthouse&lt;/a&gt; analysis tool (web)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=2NECbj0fuBU&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/fy5qlj0slvpb/illustrations-for-reactconf-au-2020-engineering-lead-design-illustration?width=848&quot; alt=&quot;Engineering led design illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;7. Engineering led design&lt;/h2&gt;&lt;p&gt;Lauren Argenta&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Designers and engineers: we have different experiences, different skills, different mindsets, and yet bridging these differences effectively, and making the most of this inherent diversity is the secret sauce in making good software… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/engineering-led-design?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Engineers guide designers up a mountain, sharing their experience and skills. It’s rough terrain, cold, and snowing, but everyone is doing their best to work together!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=70h9Nz6osSs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/o77xch2c6zng/illustrations-for-reactconf-au-2020-async-react-illustration?width=848&quot; alt=&quot;Take a load off with React illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;8. Take a load off with React&lt;/h2&gt;&lt;p&gt;Jake Moxey&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;When designing asynchronous operations in a React application (such as data fetching), it’s easy to overlook the confusing and inconsistent user experience it can result… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/take-a-load-off-with-react?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A React artifact has been discovered inside a cave — but it’s being crushed by the severe load above it! Things don’t look good; it’s starting to show signs of stress — but a crew of engineers are working together to rectify the problem.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=JMfthWPgeBg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/v36xiiv51k84/illustrations-for-reactconf-au-2020-senior-to-lead-illustration?width=848&quot; alt=&quot;Senior to leader: Taking the next step illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;9. Senior to leader: Taking the next step&lt;/h2&gt;&lt;p&gt;Cathy Lill&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The path to senior, cross disciplinary, technical leadership can be difficult to navigate as a front-end specialist. Will my skills be valued? Will I be considered… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/senior-to-leader-taking-the-next-step?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;When seniors become leaders, they must take a journey to prove themselves. It’s tough. There are 3 levels:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Defeat a barrage of difficult and challenging bugs&lt;/li&gt;&lt;li&gt;Fight your way through the wooden barricade, defended by small bears&lt;/li&gt;&lt;li&gt;Prove that you have the experience necessary for the job.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Super Mario (game)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=JxvUQ3KYpqg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ib5sykifp3e5/illustrations-for-reactconf-au-2020-extensibility-illustration?width=848&quot; alt=&quot;Targeted extensibility: Lessons learned from building Atlaskit illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;10. Targeted extensibility: Lessons learned from building Atlaskit&lt;/h2&gt;&lt;p&gt;Charles Lee&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Often when building a design system we face tension between consistent and resilient API’s that encapsulate valuable and reusable logic, and sufficient flexibility to ensure that… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/targeted-extensibility-lessons-learned-from-building-atlaskit?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The React Swiss Army Knife Ship or the RSAK Ship can adapt to many different situations for extensibility! Screw laser, drone launcher, reamer satellite, and large knives are at hand to help any situation.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Swiss Army Knives&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Y4but0Vskjg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/lmyer62c4qn2/illustrations-for-reactconf-au-2020-xss-attack-illustration?width=848&quot; alt=&quot;What is an XSS attack — and why should you care illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;11. What is an XSS attack — and why should you care?&lt;/h2&gt;&lt;p&gt;Carmen Chung&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;XSS (Cross-Site Scripting) attacks are often referred to as the “most common vulnerability online”. With victims like eBay, McAfee, and Yahoo, these attacks involve injecting… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/what-is-an-xss-attack-and-why-should-you-care?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Dangerous drones are staging an XSS attack from multiple angles on a React ship. They’re exposing holes in the shield!&lt;/p&gt;&lt;p&gt;Engineers are closing the vulnerabilities as fast as possible using shield repair plasma guns.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Team Fortress 2 (game), Faster Than Light (game)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=GLigXqVRUwA&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/t6ayi7uds893/illustrations-for-reactconf-au-2020-form-handler-illustration?width=848&quot; alt=&quot;Creating a custom form handler with hooks illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;12. Creating a custom form handler with hooks&lt;/h2&gt;&lt;p&gt;Hannah Thompson&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;What do you do when your new application is just a bunch of forms? Well, in the past you’d probably use a library, but now you can lean on react hooks and create a custom form… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/creating-a-custom-form-handler-with-hooks?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This multi-load crane helps engineers implement validation into their forms, with each block configured for a different purpose.&lt;/p&gt;&lt;p&gt;Assassins are sabotaging the validation blocks by slashing the cables holding it all up, but security are throwing out hooks to catch them in the act.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=hSoH32HtW2c&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/h5r9vpffsad7/illustrations-for-reactconf-au-2020-reactronica-illustration?width=848&quot; alt=&quot;Reactronica: Music as a Function of State illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;13. Reactronica: Music as a Function of State&lt;/h2&gt;&lt;p&gt;Kaho Cheung&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;React treats UI as a function of state. What if React’s declarative programming model could be applied to music as well? Reactronica aims to treat music as a function of state…&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It’s Reactronica! The best musical act in the React universe. Ukulele, piano, giant horn, and percussion combine to produce some sweet beats for its fans.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ziOl0ePX8MU&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/r3qmv6hjxe6m/illustrations-for-reactconf-au-2020-connect-vs-hooks-illustration?width=848&quot; alt=&quot;Redux Connect vs Redux Hooks: A play illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;14. Redux Connect vs Redux Hooks: A play&lt;/h2&gt;&lt;p&gt;Terence Jeong&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Redux Connect vs Redux Hooks is an enchanting tale about going from the Redux Higher Order Component to the new Redux Hooks. Our hero, a Class Component is connected to… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/redux-connect-vs-redux-hooks-a-play?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Watch out! Redux Hooks has arrived in town, taking on the Redux Connect sheriff.&lt;/p&gt;&lt;p&gt;Redux Hooks swarms its hook-like tentacles. Onlookers heckle nearby. It’s going to be a bloody fight.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Western Movies (genre)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=HfRZv1XfcQw&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/n5nb5kbqmm3j/illustrations-for-reactconf-au-2020-design-practices-illustration?width=848&quot; alt=&quot;Rethinking Design Practices illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;15. Rethinking Design Practices&lt;/h2&gt;&lt;p&gt;Mark Dalgleish&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;In a world of components, how should our design processes change? How should our tooling change? How should we, as front-end developers, better enable this change? In this talk… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/rethinking-design-practices?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The small bears are enforcing status quo design practices on the engineers: get dumped out, learn the current state of design, and get sucked back in.&lt;/p&gt;&lt;p&gt;A small group of resistance fighters are trying to stop the drone-like people. “Wake up” they scream. “We can do better!”.&lt;/p&gt;&lt;p&gt;Peeling back the guide rail, they direct them into a waiting escape ship.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Lemmings (game)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tKSZc6BEQmo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/h65fkv4hfgpf/illustrations-for-reactconf-au-2020-wordpress-illustration?width=848&quot; alt=&quot;5 things you should know about WordPress illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;16. 5 things you should know about WordPress… wait, what?&lt;/h2&gt;&lt;p&gt;Isabel Brison&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Did you know a large chunk of WordPress is now built in React? WordPress is a 16 year old open source project with hundreds of contributors, most of whom came from a PHP, HTML… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/5-things-you-should-know-about-wordpress-wait-what?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;After years of planning, WordPress has been split open to consume React itself!&lt;/p&gt;&lt;p&gt;It’s a messy operation — fires break out, radioactive material is exposed, electric surges and smoke is pouring out. It won’t be easy, but it’s for the best, taking the platform forward.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=vDS2nUjsOss&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/xcsn7bykk3if/illustrations-for-reactconf-au-2020-model-based-testing-illustration?width=848&quot; alt=&quot;Write fewer tests! Model-based testing in React illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;17. Write fewer tests! Model-based testing in React&lt;/h2&gt;&lt;p&gt;David Khourshid&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;What if you could automatically generate hundreds of integration and end-to-end tests, instead of manually coding them? Discover how model-based testing greatly improves… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/write-fewer-tests-model-based-testing-in-react?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The Jest ship travels around and produces hundreds of tests on command to help engineers.&lt;/p&gt;&lt;p&gt;Many tests in a fraction of a second — with no manual work required. Just hit a button and they’re swarming, ready to expose the most hidden edge-cases.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=naVQJ2hALhE&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/np64n7exeqmj/illustrations-for-reactconf-au-2020-scale-illustration?width=848&quot; alt=&quot;The tragedy of scale illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;18. The tragedy of scale&lt;/h2&gt;&lt;p&gt;Hannes Obweger &amp;amp; Nadia Makarevich&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;What do farmers 200 years ago and today’s software developers have in common? Why use the word “tragedy” to describe it when it’s not tragic at all? Join us for this tale… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/the-tragedy-of-scale?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It’s 200 years ago and rapid growth has produced too many bugs - they’re infiltrating farms and crops the world over!&lt;/p&gt;&lt;p&gt;The only way to contain them is to catch and secure them, preventing reproduction. Latching onto your body is one of their favourite things to do.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LekWopgBFmw&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/p2s5itz27qdz/illustrations-for-reactconf-au-2020-monorepos-illustration?width=848&quot; alt=&quot;Building with Monorepos illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;19. Building with Monorepos&lt;/h2&gt;&lt;p&gt;Emma Hamilton&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Monorepos are everywhere in the React community from design systems and apps to open source libraries. Being able to work on multiple packages together is powerful, but… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/building-with-monorepos?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;After a long journey through the jungle searching for a React artifact, our adventurer arrives at a mysterious door. What lies behind it?&lt;/p&gt;&lt;p&gt;In front of them is a puzzle. They’re going to have to move each piece to gain access to what lies within.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Jurassic Park&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=I5_UnsQ1_s0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/jg3q1lftjmtj/illustrations-for-reactconf-au-2020-senior-developer-illustration?width=848&quot; alt=&quot;How to teach an old dev new tech: Learning React as a senior developer illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;20. How to teach an old dev new tech: Learning React as a senior developer&lt;/h2&gt;&lt;p&gt;Trent Willis&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Have you ever learned a new technology? Were you uncomfortable? Intimidated? That was Trent’s recent experience when he had to learn a new framework… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/how-to-teach-an-old-dev-new-tech-learning-react-as-a-senior-developer?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A React entity intimidates and stares down on the battle-worn engineer. Its intense glare can see all the way into the engineer’s soul.&lt;/p&gt;&lt;p&gt;This is the final battle step to conquer React.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Eye of Sauron from LOTR (movie)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LDrZ4MMxiW0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/liia2brzdjig/illustrations-for-reactconf-au-2020-graphql-illustration?width=848&quot; alt=&quot;A GraphQL Survival Kit illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;21. A GraphQL Survival Kit&lt;/h2&gt;&lt;p&gt;Petra Gulicher&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;GraphQL lets us model our business domain as a graph, but what does that mean? What are the tools we need to design a model that works for our business, products and users both… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/a-graphql-react-survival-kit-how-to-avoid-the-pitfalls-and-level-up-your-teams-creativity?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;After venturing from base camp in the jungle, an engineer comes across a very protective elder bear guarding their offspring.&lt;/p&gt;&lt;p&gt;Rushing back to their camp, the bridge collapses under the stress. Using knowledge and equipment from the GraphQL Survival Guide, they throw a rope device to avoid sudden death below from raging waters and poisonous snakes.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Inspirations&lt;/strong&gt;: Indiana Jones (movie)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=6LxEKNeMYhg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/zq5rvdx2p73w/illustrations-for-reactconf-au-2020-powerful-rest-illustration?width=848&quot; alt=&quot;Powerful REST in a GraphQL world illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;22. Powerful REST in a GraphQL world&lt;/h2&gt;&lt;p&gt;Tejas Kumar&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;As a startup that integrates with larger, often older enterprises, we have to integrate with a number of RESTful services (against our will). This technical talk will outline how we are… &amp;lt;a class=&quot;external-article&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://reactconfau.com/talks/powerful-rest-in-a-graphql-world?from=talks&quot;&amp;gt;Read more&amp;lt;span class=&quot;sr-only&quot;&amp;gt; about this talk&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;On top of an aging REST API facility, a large GraphQL satellite has been built to act as a reliable and easy-to-use single end-point.&lt;/p&gt;&lt;p&gt;In the depths below, maintenance staff battle creatures in the dark, fending off fast growing vines and repairing equipment that is constantly malfunctioning.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=y8fohGBR_Tc&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/oje2qnfjkr1m/illustrations-for-reactconf-au-2020-bonus-drawing-illustration?width=848&quot; alt=&quot;Bonus Drawing illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h3&gt;Bonus Drawing&lt;/h3&gt;&lt;p&gt;&lt;em&gt;For a presentation that had to be cancelled at short notice.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Three civilizations from around the universe meet to declare peace and work with one another.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=xuSo7RFmcKw&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Watch time-lapse&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/h149n1qhu1fu/illustrations-for-reactconf-au-2020-thinkmill-spaceship-illustration?width=848&quot; alt=&quot;Thinkmill Spaceship illustration&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;h2&gt;Thinkmill Spaceship&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://reactconfau.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ReactConf AU&lt;/a&gt; was presented by &lt;a href=&quot;https://www.thinkmill.com.au/&quot;&gt;Thinkmill&lt;/a&gt;. Take a look at my &lt;a href=&quot;https://link.medium.com/T6Elnlz1D5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Thinkmill In Space 🛸&lt;/a&gt; for a complete explanation of the back story for this illustration.&lt;/p&gt;&lt;h2&gt;Process&lt;/h2&gt;&lt;p&gt;Illustrations were created on an iPad Pro with an Apple Pencil using &lt;a href=&quot;https://www.adobe.com/products/draw.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Adobe Draw&lt;/a&gt;. It took about 120 hours to complete.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/hjmleucalx3t/illustrations-for-reactconf-au-2020-drawing-process?width=848&quot; alt=&quot;Image of ipad mid illustration&quot; width=&quot;848&quot; height=&quot;636&quot; /&gt;&lt;p&gt;My two favourite creations are: &lt;strong&gt;Rethinking Design Practices (Lemmings inspiration)&lt;/strong&gt; and &lt;strong&gt;How to teach an old dev new tech (Eye of Sauron inspiration)&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Thanks for reading!&lt;/p&gt;&lt;p&gt;Thanks to Dominik Wilkowski&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>The Value of Open Source</title><link>https://www.thinkmill.com.au/blog/the-value-of-open-source</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/the-value-of-open-source</guid><description>Jed wraps up ReactConf AU by discussing the sustainability of open source, the value OSS creates for us all individually and as a collective, and then proposes some small steps we can all take to ensure the future of our vibrant community.</description><pubDate>Wed, 04 Mar 2020 00:00:00 GMT</pubDate></item><item><title>Engineering-led design</title><link>https://www.thinkmill.com.au/blog/engineering-led-design</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/engineering-led-design</guid><description>Lauren Argenta describes Thinkmill’s Engineering-led Design mindset, and the 5 behaviours that a cross-disciplinary team can adopt to make work life (and the software we make) better.</description><pubDate>Wed, 04 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;h2&gt;Transcript&lt;/h2&gt;&lt;p&gt;(00:02)&lt;/p&gt;&lt;p&gt;My name is Lauren, and I am the design director at Thinkmill. I think you’ve heard about them, a software company. Before we go any further, I’ve got to disclose to you, I’m actually in a new job at the minute. I started five and a half months ago when we welcomed a small little humanoid into the house who doesn’t pay rent, and last night decided to wake up and be like, “hiya!”, every two hours. So, yeah, feeling great today. I’m having a new problem now where I literally can’t remember words I’m trying to say. So, when that happens invariably in the next 20 minutes, heckle me, please. I actually appreciate it. Yesterday when I was trying to practice for this, I kept getting the wrong word in place of another – differential and deferential, and difference, they all got mixed up. In your brains, just go, I think she meant this. And I would really appreciate it. If you are parents and have looked after small children and know how to make them sleep, please come talk to me afterwards because I can’t figure it out. So yeah, in this new role... failing at it. But when I come back to work, not too bad at it.&lt;/p&gt;&lt;p&gt;(01:14)&lt;/p&gt;&lt;p&gt;As I said, I’m a designer. &lt;a href=&quot;https://www.thinkmill.com.au/services/product-design-engineering&quot;&gt;Product designer&lt;/a&gt;. I specialise in user experience and &lt;a href=&quot;https://www.thinkmill.com.au/services/user-research&quot;&gt;user research&lt;/a&gt; and generally for functional products. That’s my happy space. Give me a form and I’m your gal. I get overly excited about sorting content into simple flows and making sense of it. Or really nailing an information architecture. Or even, you know, you can see me having a little dance if I’ve reduced the certain amount of clicks it takes to enable a user to achieve an action.&lt;/p&gt;&lt;p&gt;But I’m just a designer. And I’ve been just a designer since I graduated in 2008. And was recently, up until very recently, working in design-led environments. What I mean is design-first. We start with design before we start talking to any engineers. And then in 2017, I had this bright idea to join Thinkmill. And Thinkmill was a pure engineering-led company. At the time we were mostly software engineers. There were few of those engineers who’ve come from design, and then shunned it and gone, yeah, no. And rock star into the engineering space (generally in the React space as well, just saying). There was a lot of design that was happening every day, but it was kind of happening in pockets. And it is fair to say that the design practice was somewhat basic, somewhat scrappy, and somewhat meek.&lt;/p&gt;&lt;p&gt;Basic in that, yeah, it was &lt;a href=&quot;https://www.thinkmill.com.au/services/ux-ui-design&quot;&gt;UX/UI&lt;/a&gt;. But there was only a little bit of research (not in a rigorous way) going on, and it was heavily informed on intuition. Which isn’t always a bad thing. Scrappy in that there wasn’t a shared, cohesive view of what design at Thinkmill was. And meek in that it just wasn’t confident that it played a crucial role in making good software.&lt;/p&gt;&lt;p&gt;(03:19)&lt;/p&gt;&lt;p&gt;Now, I was invited in to change that. To develop a more mature shared and confident design practice in that engineering-led environment. So, that kind of became my brief. Introduce a proper design practice in an engineering-led environment. So, not scary at all. And not terrifying.&lt;/p&gt;&lt;p&gt;A quick note before I go any further. These things [Components, &lt;a href=&quot;https://www.thinkmill.com.au/services/graphql&quot;&gt;GraphQL&lt;/a&gt;, &lt;a href=&quot;https://www.thinkmill.com.au/services/react&quot;&gt;React&lt;/a&gt;], they are absolutely lovely. And I think they’re absolutely amazing, but I’m not going to talk about any of them whatsoever. Because everyone else who will be up here at this conference will do so much more knowledgeably and eloquently than I could. It would be funny to see me try to do it, admittedly but I’m not going to. I think they’re amazing tools to work efficiently at scale. But I’m going to talk about engineering-led design.&lt;/p&gt;&lt;p&gt;(04:09)&lt;/p&gt;&lt;p&gt;Engineering-led design as we see it &lt;strong&gt;is a mindset&lt;/strong&gt;. It is a mindset embedded into shared behaviours. Now, these behaviours are what we’ve established and identified along the journey since I joined Thinkmill in 2017. They are &lt;a href=&quot;https://www.thinkmill.com.au/method&quot;&gt;becoming the vehicle&lt;/a&gt; in helping us to introduce a proper design practice into a company full of engineers.&lt;/p&gt;&lt;p&gt;These are particularly useful to use when you are in the context of solution design. In an engineering-led environment which many of you I imagine are, or when you have designers on your team. And I am hoping there are a few designers here. But if not, it’s when you come into contact with them.&lt;/p&gt;&lt;p&gt;Please bear in mind the other thing. I am a designer talking to a room full of engineers, I’m guessing. And these are basically going to be practical things that you can do as an engineer. But especially as an engineering leader. But they are just the starting point. And this mindset does run a whole lot more deeper. And if you want to talk about it in more specific detail later on, we can do so. These behaviors are a starting point for you to get going with.&lt;/p&gt;&lt;p&gt;(05:37)&lt;/p&gt;&lt;p&gt;So, let’s dive into them. There are five that we’re going to cover:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Context together.&lt;/li&gt;&lt;li&gt;Create a dictionary&lt;/li&gt;&lt;li&gt;Teach as you work&lt;/li&gt;&lt;li&gt;Play it back to validate&lt;/li&gt;&lt;li&gt;Break down language barriers&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I’m gonna kind of unpack them as we go.&lt;/p&gt;&lt;p&gt;(05:53)&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;Context together&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Shared context isn’t just valuable. It’s really, really valuable. When adopting an engineering-led design mindset, we gather context together. In the early stages of a project, when we’re still discovering the requirements, the constraints, the opportunities, all the unknowns, the known unknowns, etc., getting this shared context as a team is really hard.&lt;/p&gt;&lt;p&gt;It takes time, it takes focus, and it takes a lot of brain power and attention. But so much depends on having shared context for making good decisions. Technical decisions, product decisions, business decisions, strategic decisions, timing decisions, release decisions, all the decisions.&lt;/p&gt;&lt;p&gt;If we can ensure that we have shared context as a multidisciplinary team, we can be confident that when we’re making decisions, that are design, or technical decisions we’re using the same inputs and parameters. So, what does it mean to context together in practice? Well, context together means everyone, and I mean everyone, attends all context-gathering meetings early on – regardless of if the focus is design or engineering.&lt;/p&gt;&lt;p&gt;This is vital because what it results in is on one hand, you’ll have designers like me who immediately become informed about technical constraints. And about what hard problems are going to be as we go into them. And for me, I get a bunch of engineers who become very acutely aware of user needs. And what they’re going to have to have down the track.&lt;/p&gt;&lt;p&gt;So, that means that those engineers are in a much stronger position to be able to spot relevant technical overlaps much, much, much sooner. Context together means engineers are in the room when we do research. This is key. We need engineers to see what designers are seeing.&lt;/p&gt;&lt;p&gt;A quick interlude here. This is some &lt;a href=&quot;https://www.thinkmill.com.au/services/user-research&quot;&gt;research&lt;/a&gt; that we ran. And the majority of the team that attended that research are engineers. They saw what we saw. There was zero translation needed after the fact. And the last thing is that context together means educating your designer about the hard problems in software, in real time.&lt;/p&gt;&lt;p&gt;Designing with dates? Help a designer understand why time zones are hard and why you can’t just use server time. Because I know you guys know that. But we probably don’t. Contexting together prevents gaps, prevents rabbit holes, and tangents in the early stages of a design project.&lt;/p&gt;&lt;p&gt;(08:54)&lt;/p&gt;&lt;h3&gt;Create a dictionary&lt;/h3&gt;&lt;p&gt;I hope that’s self-explanatory. Naming is hard. Using precise language is very important. But in design and technical practices, the same terms such as users often have different meanings but come up quite often. And I think it’s fair to say that we’re both quite guilty of using jargon from time to time. And look, creating a dictionary, you might be sitting there going &lt;em&gt;“You know what? That sounds like busy work”&lt;/em&gt; and I can absolutely assure it is not. It is really simple. Yes, it takes some time to maintain. But the payback is absolutely huge. So, what this means in practice:&lt;/p&gt;&lt;p&gt;We create a shared document using a low friction tool that everyone can use. By low friction, what I mean is that if you guys have some designers on your team and they’re not comfortable on markdown or GitHub, please use that. Use something they are comfortable in so that they can be part that have club. We define project-specific terms. Like data structure, interface elements, etc. This is because using accurate language is very important. What this dictionary gives you (even a rudimentary dictionary) is it makes using that accurate language much easier, and makes sure that the team can adopt it much, much faster.&lt;/p&gt;&lt;p&gt;This next one is really important. Always be a diva when defining your freakin’ acronyms. We all have them. I got them, you got them, everybody got them. And sometimes, you know, it’s a little embarrassing to put your hand up, I’m sorry, what does HTTP mean? But it is important for us to be able to have those conversations. And it’s also important for us to realise that, *“oh, your acronym is the same as mine, but they mean completely different things”,*because that’s expensive down the track. Such is why we need to REST all the time &lt;em&gt;(yes, I did ask that question once; don’t laugh; it’s not nice; safe space)&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;And lastly, define practice-specific jargon and note reserved words. So, in user research, for example, there are these three words: &lt;strong&gt;Observation&lt;/strong&gt;, &lt;strong&gt;finding&lt;/strong&gt;, and &lt;strong&gt;insight&lt;/strong&gt;. They mean completely different things. An observation is a similar thing, a finding is a group of observations, and if you take the time to figure out what that means, it might turn into an insight. That’s pretty loaded. These are also everyday words. If we don’t reserve those meanings how will they know what we mean with an observation versus an insight. The weighting is very different.&lt;/p&gt;&lt;p&gt;Equally, &lt;a href=&quot;https://www.thinkmill.com.au/services/accessibility&quot;&gt;accessibility&lt;/a&gt;, it’s got a lot of meaning behind it from both a technical and design point of view. We need to make sure we reserve words like that so that everyone is on the same page at all times.&lt;/p&gt;&lt;p&gt;This is an example of a dictionary that we did for a project. You can see some definitions around the schema and acronyms, etc. And we just used Notion for this tool. The important thing here is that we created and maintain this across practices, both in design and engineering. And we pretty much used this every day. We use this every day from the beginning of the schema design through to the design system build.&lt;/p&gt;&lt;p&gt;And this wasn’t just a nice to have. It was a foundational item. Now I want to note that this kind of basic dictionary evolves into a much more sophisticated source of truth that will be housed in many places such as the design system, schema, and now the documentation. But at the start, it’s just a page with definitions. But don’t underestimate that page. Because it is a crucial bridge between design and engineering.&lt;/p&gt;&lt;p&gt;(12:50)&lt;/p&gt;&lt;h3&gt;Teach as you work&lt;/h3&gt;&lt;p&gt;What this refers to is the acknowledgment that as designers and engineers, we have deep knowledge and expertise and that we cannot become experts of each other’s domain.&lt;/p&gt;&lt;p&gt;I’m not going to become a &lt;a href=&quot;https://www.thinkmill.com.au/services/react&quot;&gt;React engineer&lt;/a&gt;. And you probably don’t want to become what I do. And that’s okay. That’s cool. But what we both need to do is learn from each other.&lt;/p&gt;&lt;p&gt;Because you know what? When we do, that’s when the magic really happens. In practical terms, what this means is injecting short, structured lessons into your day-to-day work. In Thinkmill we tend to call them like little asides. They’re usually 30-minute sessions, delivered by an expert on the team, whoever is most knowledgeable, into a topic that unlocks something so that we can actually make sure that we’ve moved forward from not being able to understand to all going, ah, the pennies drop.&lt;/p&gt;&lt;p&gt;The trick here, though, is to frame them not as a distraction, but to make them a key part and a first-class citizen of every day work. This is just an example of some work that we did on &lt;a href=&quot;https://www.thinkmill.com.au/work/enlitic&quot;&gt;a medical annotation platform&lt;/a&gt;. For those who don’t know what a medical annotation platform is (and that was once myself) that is when you go for a scan and they take the scan, a radiographer has to go through it and mark it up later. Now, there’s a lot of UI work based around processing radiological images in this. And if you know anything about radiology, you know that involves basically finding something in a 3D space A.K.A. spatial localization.&lt;/p&gt;&lt;p&gt;So, this is a bit of a whiteboard session with myself and the lead architect on the project called Tim. He’s basically breaking down the concept of spatial localization, so that we together could potentially construct the rudimentary form of the UI. From a design point of view, what this unlocked was an underlying foundation that I could use as constraints as an inspiration to fly forward though. It was time well spent.&lt;/p&gt;&lt;p&gt;This is another example, Tim the architect explaining the basics of graph theory. I didn’t need to know the whole shebang, but I needed to know some of it to move forward.&lt;/p&gt;&lt;p&gt;(15:40)&lt;/p&gt;&lt;h3&gt;Play it back to validate&lt;/h3&gt;&lt;p&gt;No matter how much we context together, and create our shared language through this diligent use of a dictionary, or be open to teaching each other in all of these little asides; we will always carry a lot of assumptions.&lt;/p&gt;&lt;p&gt;Sometimes these are explicit and documented. And that’s fabulous. But sometimes they are implicit and hidden, which is less fabulous. Playing it back is a way to reveal your thought, rationale, your logic, your steps. The parts that got you to that point. And doing so with your team regularly is a great way to uncover the hidden assumptions. Those &lt;em&gt;unknown unknowns&lt;/em&gt; which help you to avoid very expensive mistakes.&lt;/p&gt;&lt;p&gt;In practice, what this looks like is: I’ve just designed a UX workflow. Awesome. Before I lock it in, I play back the thinking to the engineers or the architect on the team, so that we can validate my thinking and assumptions against things like the data model. What this might show is, ah, the data model doesn’t actually have a provision for this at this moment, we can’t account for that. Which may mean that I need to change my design. Or it might mean that we need to change the data model. But either way, we spotted it early.&lt;/p&gt;&lt;p&gt;The second thing to think about is begin a sentence with, &lt;em&gt;“let me play that back to you”&lt;/em&gt;. Actually have a go at playing back what you understand back to them. So if an engineer makes a change to the data model - how might this impact the UX or the UI and play it back and check that we’ve both got it. And the other thingto do is keeping this front of mind. Do I need to go and talk to the engineer? Do I need to talk to the designer? Will this impact? If you get that gut niggling feeling, do it. It might take 10 to 15 minutes, but you might find that you have a big saving after it.&lt;/p&gt;&lt;p&gt;(17:33)&lt;/p&gt;&lt;h3&gt;Break down language barriers&lt;/h3&gt;&lt;p&gt;Now, between our two practices, there’s a language barrier problem. I know we all know this. And in an engineering-led environment, when it’s actually engineers who are the majority, and designers that are the minority, you, the engineer, need to be the translator. That’s on you. You need to be in a heightened state of awareness. Taking extra care with empathy to rephrase or reframe in a way that your designers can come to the table and actually get on board. Because if the designers aren’t understanding, it’s not because they’re dumb. It’s because it just hasn’t been explained well enough for them to be able to make sense of it.&lt;/p&gt;&lt;p&gt;So, here’s a few sentences you can try to get over this hurdle.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;“Did that make sense?”&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;“Can you play that back to me?”&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;“Can I explain it in another way?”&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You’ll be really surprised how powerful doing this can actually be.&lt;/p&gt;&lt;p&gt;And pro tip. Most designers, they actually think visually first. Stick men are fine. If you can draw this, it will &lt;a href=&quot;https://www.thinkmill.com.au/blog/all-about-artefacts&quot;&gt;actually help to unlock it a lot quicker&lt;/a&gt; so don’t hesitate to pick up that pen and go to that whiteboard. Let me just give you an example. And this is a real-world example. Take the concept of one to many [relationship]. Imagine we’re in a conversation where Content is &lt;code&gt;1&lt;/code&gt;, and Author is &lt;code&gt;N&lt;/code&gt;. That all makes sense to you. That doesn’t make sense to me. News flash, I went to art school. I understand a lot about &lt;a href=&quot;https://www.thinkmill.com.au/services/product-design-engineering&quot;&gt;products&lt;/a&gt;, a lot about people, and how to get people and products to work together. But I didn’t study relational databases, and I’m probably not going to either. So, we’re in a bit of a gulf here. Sorry. You, the engineer, [need to] unpack this for me. Ah, a Post has a single Author, an Author can have many Posts - amazing, I got this. Now I can contribute. When the Author wants to transfer the Authorship to somebody else, what happens? Is there a provision for that? What if the Author wants to edit that later? How do they do that? Is that sorted? How are we going to grouped these, under the author or something else? Can I have categories?&lt;/p&gt;&lt;p&gt;(20:01)&lt;/p&gt;&lt;p&gt;And suddenly we’re back to beautiful greatness, heading towards the product of our dreams.&lt;/p&gt;&lt;p&gt;So, engineering-led design. These are the five very early stage kind of behaviors that you can really champion. So why bother?&lt;/p&gt;&lt;p&gt;Well, as engineers, what you’ll get out of this is you’ll receive &lt;strong&gt;designs that are much more in line with engineering thinking&lt;/strong&gt;. They’ll be more viable for the schema that you have actually undertaken. As a multidisciplinary team, you’re going to be able to &lt;strong&gt;discuss constraints and negotiate tradeoffs much more effectively and efficiently than ever before&lt;/strong&gt;. You will be able to &lt;strong&gt;make better decisions in a faster context&lt;/strong&gt;. And you’re going to &lt;strong&gt;uncover things that you didn’t know were there&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;(21:06)&lt;/p&gt;&lt;p&gt;You’re going to get a lot more wins, a lot more bang for your buck. And ultimately, I promise, you will have a better time doing it. I’m no longer just a designer. I’m an engineering-led designer these days. And when we adopt an engineering-led mindset, design can thrive within an engineering-led environment. And as engineers, you’ve got the power and are crucial to making this change. Give these behaviors a go. You’d be amazed at how much it’s going to improve your products. And when you do, come and tell me about it because I will be dying to know. Thanks so much.&lt;/p&gt;&lt;/article&gt;</content:encoded><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/esg2d942x04j/engineering-led-design?width=1200" length="545767" type="image/png"/></item><item><title>Building with Monorepos</title><link>https://www.thinkmill.com.au/blog/building-with-monorepos</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/building-with-monorepos</guid><description>Monorepos are everywhere in the React community from design systems and apps to open source libraries. Being able to work on multiple packages together is powerful, but it’s not without its problems. Emma Hamilton shows you how to solve these problems by exploring the evolution of Emotion’s monorepo.</description><pubDate>Wed, 04 Mar 2020 00:00:00 GMT</pubDate></item><item><title>Targeted extensibility: Lessons learned from building Atlaskit</title><link>https://www.thinkmill.com.au/blog/targeted-extensibility-lessons-learned-from-building-atlaskit</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/targeted-extensibility-lessons-learned-from-building-atlaskit</guid><description>Often when building a design system we face tension between consistent and resilient API’s that encapsulate valuable and reusable logic, and sufficient flexibility to ensure that our components can be customised to fit bespoke product use cases. Towing this line is difficult, and becomes more difficult as a design system scales. Charles uncovers some of the hard-learned lessons towing this line in React-Select, and Atlaskit – Atlassian’s Design System.</description><pubDate>Tue, 04 Feb 2020 00:00:00 GMT</pubDate></item><item><title>Keystone 6 featured on the SyntaxFM podcast</title><link>https://www.thinkmill.com.au/blog/keystone-6-featured-on-the-syntax-fm-podcast</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/keystone-6-featured-on-the-syntax-fm-podcast</guid><description>Wes Bos talks about Keystone. Best practices, things to avoid, and why you should check it out.</description><pubDate>Mon, 30 Dec 2019 00:00:00 GMT</pubDate></item><item><title>How to safely inject HTML in React using an iframe</title><link>https://www.thinkmill.com.au/blog/how-to-safely-inject-html-react-iframe</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/how-to-safely-inject-html-react-iframe</guid><description>If you need to inject HTML into your React app and you don’t want to use `dangerouslySetInnerHTML`, this article is for you.</description><pubDate>Fri, 13 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/vuv1a1uww5yg/how-to-safely-inject-html-react-iframe-inject-html-react-iframe?width=848&quot; alt=&quot;Poster: React logo on left. Plus symbol in center. Icon symbolising secure browsing on right.&quot; width=&quot;848&quot; height=&quot;485&quot; /&gt;&lt;hr /&gt;&lt;h2&gt;Why (in most cases) you shouldn’t use dangerouslySetInnerHTML&lt;/h2&gt;&lt;p&gt;As the react documentation says, &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; is React’s replacement for using &lt;code&gt;innerHTML&lt;/code&gt; in the browser DOM. In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a &lt;a href=&quot;https://en.wikipedia.org/wiki/Cross-site_scripting&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cross-site scripting (XSS)&lt;/a&gt; attack.&lt;/p&gt;&lt;p&gt;In fewer words, &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; isn’t safe.&lt;/p&gt;&lt;h2&gt;Why?&lt;/h2&gt;&lt;p&gt;let’s say that you have a database with a bunch of article in HTML format that you want to display in your React app. Using dangerouslySetInnerHTML that would be quite easy to achieve:&lt;/p&gt;&lt;pre&gt;function MyArticle({ articleId }) {
  return &amp;lt;div dangerouslySetInnerHTML={getArticleFromDB(articleId)} /&amp;gt;
}
&lt;/pre&gt;&lt;p&gt;The problem with this is that the HTML you are injecting to your app could be malformed, it could have CSS/JS that breaks your app, or even worse, JS that steals data from your app.&lt;/p&gt;&lt;h2&gt;How to make an iframe secure?&lt;/h2&gt;&lt;p&gt;In order to make the iframe really safe, you need to add extra restrictions to the content inside of it. To do that, you should use the sandbox attribute.&lt;/p&gt;&lt;pre&gt;&amp;lt;iframe id={IFRAME_ID} name={IFRAME_ID} sandbox=&quot;allow-same-origin&quot; /&amp;gt;
&lt;/pre&gt;&lt;p&gt;You should avoid using both allow-scripts and allow-same-origin, as that lets the embedded document remove the sandbox attribute — making it no more secure than not using the sandbox attribute at all.&lt;/p&gt;&lt;h2&gt;How to inject and update the content in an iframe?&lt;/h2&gt;&lt;p&gt;Here you have two options:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;by using the iframe attribute &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/srcdoc&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;srcdoc&lt;/a&gt; (which is not supported by IE and Edge)&lt;/li&gt;&lt;li&gt;by using the iframe.document.open()/write()/close()&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Using srcdoc&lt;/h3&gt;&lt;p&gt;the srcdoc property allows you to specify the content of the iframe, so using it is quite simple:&lt;/p&gt;&lt;pre&gt;&amp;lt;iframe
  id={IFRAME_ID}
  name={IFRAME_ID}
  sandbox=&quot;allow-same-origin&quot;
  srcDoc={iframeContent}
/&amp;gt;
&lt;/pre&gt;&lt;p&gt;Unfortunately srcdoc isn’t supported in IE and Edge. There is a &lt;a href=&quot;https://github.com/jugglinmike/srcdoc-polyfill&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;polyfill&lt;/a&gt; but it isn’t useful in this case because it requires allow-scripts (and remember that’s not safe)&lt;/p&gt;&lt;h3&gt;Using iframe.document.open()/write()/close()&lt;/h3&gt;&lt;p&gt;This method is not straightforward as using srcdoc, but it works the good thing is that works in all browsers.&lt;/p&gt;&lt;pre&gt;const IFRAME_ID = &apos;my-iframe&apos;
const IFRAME_CONTAINER_ID = &apos;my-iframe-container&apos;

export const ArticleBody = (props: Props) =&amp;gt; {
  useEffect(() =&amp;gt; {
    const setIframeContent = (body) =&amp;gt; {
      const currentIframe = document.getElementById(IFRAME_ID)
      const iframeContainer = document.getElementById(IFRAME_CONTAINER_ID)
      if (iframeContainer) {
        const newIframe = frames[IFRAME_ID]
        if (newIframe !== null) {
          const iframeDocument = newIframe.document
          iframeDocument.open()
          iframeDocument.write(`${body}`)
          iframeDocument.close()
        }
      }
    }

    setIframeContent(props.body)
  }, [props.body])
}
&lt;/pre&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;I hope you’ve found this article useful! &lt;a href=&quot;https://twitter.com/intent/tweet?text=Hey%20@morklash%20I%20read%20your%20post%20about%20how%20to%20safely%20inject%20HTML%20in%20React%20using%20an%20iframe&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Start a conversation&lt;/a&gt; with the author in &lt;a href=&quot;https://twitter.com/morklash&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter&lt;/a&gt; on what you think about this article.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Thinkmill in Space</title><link>https://www.thinkmill.com.au/blog/tm-in-space</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/tm-in-space</guid><description>The year is 2225. Thinkmill has taken to space. Currently orbiting the moon, sending explorers to discover the universe.</description><pubDate>Thu, 21 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/wi9si0qsj4q7/tm-in-space-tm-in-space-key-image?width=848&quot; alt=&quot;Thinkmill in space illustration&quot; width=&quot;848&quot; height=&quot;848&quot; /&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/qdwo8ygrw6ch/tm-in-space-tm-borg-cube?width=848&quot; alt=&quot;Borg Cube&quot; width=&quot;848&quot; height=&quot;599&quot; /&gt;&lt;figcaption&gt;Borg Cube&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Ever since I saw this particular variation of the &lt;a href=&quot;https://www.thinkmill.com.au/&quot;&gt;Thinkmill&lt;/a&gt; logo, a cube-shaped ship travelling through space came to mind.&lt;/p&gt;&lt;p&gt;I’ve always wanted to base a drawing on &lt;a href=&quot;https://www.thinkmill.com.au/&quot;&gt;Thinkmill&lt;/a&gt;, and this logo was the key! The spaceship idea manifested inside my head.&lt;/p&gt;&lt;p&gt;Months passed, gathering ideas in my head for what else could be drawn beside the spaceship. What came to mind were the tools we use day-to-day.&lt;/p&gt;&lt;p&gt;The Thinkmill spaceship dominates the middle of the picture. A fleet of support ships nearby.&lt;/p&gt;&lt;p&gt;What follows is a breakdown of everything in the picture, piece by piece!&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Thinkmill&lt;/li&gt;&lt;li&gt;Gelato Refill&lt;/li&gt;&lt;li&gt;Node.js&lt;/li&gt;&lt;li&gt;GraphQL&lt;/li&gt;&lt;li&gt;React&lt;/li&gt;&lt;li&gt;Keystone&lt;/li&gt;&lt;li&gt;Design&lt;/li&gt;&lt;li&gt;Explorer&lt;/li&gt;&lt;li&gt;Gelato Servery&lt;/li&gt;&lt;li&gt;Satellite&lt;/li&gt;&lt;li&gt;Moon Base&lt;/li&gt;&lt;li&gt;Other&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;1. Thinkmill&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/hc6u28zc65w4/tm-in-space-tm-cube?width=848&quot; alt=&quot;The largest ship of the fleet&quot; width=&quot;848&quot; height=&quot;969&quot; /&gt;&lt;figcaption&gt;The largest ship of the fleet&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Front Top Deck&lt;/strong&gt; — From here commands are given to the crew and the rest of the armada by the captain and senior members. A wide variety of informational displays show ship status, planets in range, map coordinates and so on.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Back Top Deck&lt;/strong&gt; — Engine columns, one of which is currently experiencing issues. Stasis tubes positioned behind, where crew members float in a deep sleep, receiving nutrients via a variety of tubes.&lt;/p&gt;&lt;p&gt;A gelato dispenser sits serving coconut and dark chocolate &lt;a href=&quot;https://tomwalkergelato.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;gelato&lt;/a&gt; — a staple of the crew.&lt;/p&gt;&lt;p&gt;A door leads into the gelato manufacturing room, taking in ingredients from tubes connected to the roof from a supply ship.&lt;/p&gt;&lt;p&gt;The ship departure date from Earth is above the sliding back door, currently displaying 24th June 2019. The date the drawing was completed.&lt;/p&gt;&lt;p&gt;Current versions of &lt;a href=&quot;https://www.keystonejs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Keystone&lt;/a&gt;, &lt;a href=&quot;https://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GraphQL&lt;/a&gt; and &lt;a href=&quot;https://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React&lt;/a&gt; display on information panels on the back wall as well as other stats.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Bottom Deck&lt;/strong&gt; — The lower deck of the ship, used for transport and storage — a one-man scout ship is departing, a Node harvesting ship lies dormant.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Roof&lt;/strong&gt; — A variety of antennas and radio communication equipment. A hatch for repair drones. Intake points for milk, cream and sugar lead to the gelato manufacturing plant.&lt;/p&gt;&lt;p&gt;An array of solar panels power the ship and store energy in large battery arrays.&lt;/p&gt;&lt;h2&gt;2. Gelato Refill&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/myghal52vda2/tm-in-space-gelato-refill?width=848&quot; alt=&quot;Crew members have nightmares about the tentacle-like tubes&quot; width=&quot;848&quot; height=&quot;459&quot; /&gt;&lt;figcaption&gt;Crew members have nightmares about the tentacle-like tubes&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The &lt;a href=&quot;https://www.instagram.com/tomwalkergelato/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Thomas Walker Gelato&lt;/a&gt; refill ship supplies the ingredients for gelato manufacturing on the main ship.&lt;/p&gt;&lt;p&gt;While refilling, the ship hovers close by, extending its three floating tentacles and pumping milk, cream and sugar into the main ship.&lt;/p&gt;&lt;p&gt;The ship can move both in space and a planet’s atmosphere, allowing it to refill with supplies.&lt;/p&gt;&lt;p&gt;&lt;em&gt;This ship is also featured in my other drawings and is a recurring feature.&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;3. Node.js&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/cvqwvmnwhtbz/tm-in-space-node-js-illustration?width=848&quot; alt=&quot;The crew have started observing strange side effects from handling pieces of Node&quot; width=&quot;848&quot; height=&quot;848&quot; /&gt;&lt;figcaption&gt;The crew have started observing strange side effects from handling pieces of Node&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Throughout the galaxy are crater-filled &lt;a href=&quot;https://nodejs.org/en/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Node&lt;/a&gt; asteroids. This is the main source of fuel aside from React.&lt;/p&gt;&lt;p&gt;Harvesters dispatched from the main ship have a crew of miners inside. Their job: dislodging chunks of Node material from the craters using specialised jackhammers.&lt;/p&gt;&lt;p&gt;While the crew are outside, suits and helmets protect the crew from the harshness of space, providing oxygen and life support systems.&lt;/p&gt;&lt;p&gt;Once the Node material is collected, it’s taken back to the harvester and finally hauled back to the main ship for processing.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/t36ktyxhqkwr/tm-in-space-node-logo-shape?width=848&quot; alt=&quot;The Node asteroids are the shape and pattern of the Node.js logo&quot; width=&quot;848&quot; height=&quot;289&quot; /&gt;&lt;figcaption&gt;The Node asteroids are the shape and pattern of the Node.js logo&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;4. GraphQL&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/wm2lcv0jnhgb/tm-in-space-graphql-illustration?width=848&quot; alt=&quot;Space is limited, the crew often don’t last more than a few days&quot; width=&quot;848&quot; height=&quot;618&quot; /&gt;&lt;figcaption&gt;Space is limited, the crew often don’t last more than a few days&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Another hexagon-shaped ship called the &lt;a href=&quot;https://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GraphQL&lt;/a&gt; is a ship computer system used to define, process and output data based on parameters.&lt;/p&gt;&lt;p&gt;Linked to the main ship and used for most of the calculation and data storage work to help traverse space.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ynp7llwyjprt/tm-in-space-graphql-logo?width=848&quot; alt=&quot;The GraphQL ship resembles the shape of the GraphQL logo&quot; width=&quot;848&quot; height=&quot;198&quot; /&gt;&lt;figcaption&gt;The GraphQL ship resembles the shape of the GraphQL logo&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;5. React&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/we4qv2h01261/tm-in-space-react-illustration?width=848&quot; alt=&quot;Crew members enjoy swimming through the React material — the material eventually floats back into its original position&quot; width=&quot;848&quot; height=&quot;587&quot; /&gt;&lt;figcaption&gt;Crew members enjoy swimming through the React material — the material eventually floats back into its original position&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React&lt;/a&gt;, like Node.js, is a critical part of the equation of ship refuelling.&lt;/p&gt;&lt;p&gt;Floating in space often shaped like an atom symbol, these resource particles are most abundant and plentiful in the middle of the ring.&lt;/p&gt;&lt;p&gt;Pockets of them are also found orbiting planets in a ring shape.&lt;/p&gt;&lt;p&gt;A vacuum ship nearby extends a tube, sucking all the React particles into the ship’s storage and then back to the main ship for processing.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/xfgupikd5h0r/tm-in-space-react-logo?width=848&quot; alt=&quot;The atom shape resembles the shape of the React logo&quot; width=&quot;848&quot; height=&quot;212&quot; /&gt;&lt;figcaption&gt;The atom shape resembles the shape of the React logo&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;6. Keystone&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/i8u0tp2y36tn/tm-in-space-keystone-illustration?width=848&quot; alt=&quot;Here we see a ship disappearing into the portal for destination [ ‘ | + * &amp;amp;&amp;amp; / : !! == ‘ ; — ] ? ! { . ‘ “ — who knows what they’ll find!&quot; width=&quot;848&quot; height=&quot;774&quot; /&gt;&lt;figcaption&gt;Here we see a ship disappearing into the portal for destination [ ‘ | + * &amp;amp;&amp;amp; / : !! == ‘ ; — ] ? ! { . ‘ “ — who knows what they’ll find!&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Discovered on a recent trip light-years away: the &lt;a href=&quot;https://v5.keystonejs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Keystone&lt;/a&gt; object.&lt;/p&gt;&lt;p&gt;An ancient artefact of unknown origin and time, it is a critical tool that allows Thinkmill to explore the universe.&lt;/p&gt;&lt;p&gt;A circular gate-like device marked with punctuation stones, that allow the crew to form a set of coordinates.&lt;/p&gt;&lt;p&gt;Once a sequence is locked and the destination exists, a portal opens to travel at faster-than-light speeds to unknown lands.&lt;/p&gt;&lt;p&gt;More and more punctuation symbols are being found as time goes on, increasing the many different places the crew can travel to.&lt;/p&gt;&lt;p&gt;A major downside to the device is that it is only big enough to fit transport ships. So it is only used scouting purposes.&lt;/p&gt;&lt;p&gt;When the entire armada needs to travel, it can often take months to reach the destination.&lt;/p&gt;&lt;p&gt;The Keystone name comes from the four keystone objects with the letter K lodged into them.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;keystone | ˈkiːstəʊn | noun&lt;/p&gt;&lt;p&gt;a central stone at the summit of an arch, locking the whole together.&lt;/p&gt;&lt;p&gt;• the central principle or part of a policy, system, etc., on which all else depends: cooperation remains the keystone of the government’s security policy.&lt;/p&gt;&lt;/blockquote&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/emak10lqizhf/tm-in-space-keystone-logo?width=848&quot; alt=&quot;The four keystones on the circular object resemble the K in the Keystone logo&quot; width=&quot;848&quot; height=&quot;228&quot; /&gt;&lt;figcaption&gt;The four keystones on the circular object resemble the K in the Keystone logo&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;7. Design&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/dv0865g8b649/tm-in-space-design-illustration?width=848&quot; alt=&quot;Pushing around these anchors points is hard work, they keep floating away&quot; width=&quot;848&quot; height=&quot;497&quot; /&gt;&lt;figcaption&gt;Pushing around these anchors points is hard work, they keep floating away&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Thinkmill’s design efforts are lead by the crew working together, using a series of anchor points and paths in space.&lt;/p&gt;&lt;p&gt;Here we can see the crew creating a large Thinkmill logo.&lt;/p&gt;&lt;p&gt;Once anchor points are set, the object gets analysed, minified, and used on various ships akin to a 3D printer.&lt;/p&gt;&lt;h2&gt;8. Explorer&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/wxsgb8w52fzw/tm-in-space-explorer-illustration?width=848&quot; alt=&quot;A stark piece of design paving the way for the future of the Thinkmill fleet&quot; width=&quot;848&quot; height=&quot;1115&quot; /&gt;&lt;figcaption&gt;A stark piece of design paving the way for the future of the Thinkmill fleet&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Made possible by Keystone’s technological findings, Node.js and React fuel deposits, and the design teams expertise, the Explorer is a brand new class of ship, designed to explore the universe at much faster speeds than before.&lt;/p&gt;&lt;p&gt;The Explorer is T-shaped as homage to the Thinkmill name.&lt;/p&gt;&lt;p&gt;Command resides at the top of the ship behind a giant class facade, traditional telescopes and paper maps have been digitised for navigation.&lt;/p&gt;&lt;h2&gt;9. Gelato Servery&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/nijniqh1ikee/tm-in-space-gelato-servery-illustration?width=848&quot; alt=&quot;Eating 3 cups or more induces a euphoric effect&quot; width=&quot;848&quot; height=&quot;384&quot; /&gt;&lt;figcaption&gt;Eating 3 cups or more induces a euphoric effect&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Besides serving gelato on the main ship, Thinkmill also has a dedicated &lt;a href=&quot;https://tomwalkergelato.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;gelato&lt;/a&gt; servery ship.&lt;/p&gt;&lt;p&gt;Usually manned by Thomas Walker himself, the zero gravity gelato cups float inside the gelato cup-shaped ship.&lt;/p&gt;&lt;p&gt;Crew members who are out and about stop at the servery whenever they feel like taking a break during the working day.&lt;/p&gt;&lt;h2&gt;10. Satellite&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/l3ownobo7ijr/tm-in-space-satellite-illustration?width=848&quot; alt=&quot;10,000 down / 10,000 up&quot; width=&quot;848&quot; height=&quot;565&quot; /&gt;&lt;figcaption&gt;10,000 down / 10,000 up&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Long-range communications are the job for the satellite ship, square in shape with two solar panel arrays and a powerful satellite dish.&lt;/p&gt;&lt;p&gt;Along with an array of monitoring equipment — this ship provides a lifeline back home when light-years away in space.&lt;/p&gt;&lt;h2&gt;11. Moon Base&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/of6bgzzufhqk/tm-in-space-moonbase-illustration?width=848&quot; alt=&quot;Reminiscing of Total Recall&quot; width=&quot;848&quot; height=&quot;1160&quot; /&gt;&lt;figcaption&gt;Reminiscing of Total Recall&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Still under construction, the Thinkmill Moon Base has become a launchpad to other planets in the system and an important supply hub.&lt;/p&gt;&lt;p&gt;Mining machines have excavated two craters thus far: one an access hatch, and another an exhaust system.&lt;/p&gt;&lt;h2&gt;12. Other&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/gpcsgi6hfkia/tm-in-space-space-traffic-illustration?width=848&quot; alt=&quot;Watch out for those moving traffic lights in 3D space&quot; width=&quot;848&quot; height=&quot;1150&quot; /&gt;&lt;figcaption&gt;Watch out for those moving traffic lights in 3D space&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;An array of other objects have their place around the Thinkmill fleet. Nearby Saturn provides an alternative source of React material. Transport ships cruise around with mobile traffic light systems. Comets are a regular sight, and another moon resides in the distance.&lt;/p&gt;&lt;p&gt;That about wraps it up for the drawing. I had a lot of fun creating it. It’s my biggest drawing yet with another one in the works.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/gpcsgi6hfkia/tm-in-space-space-traffic-illustration?width=848&quot; alt=&quot;Print of all the illustrations in the office&quot; width=&quot;848&quot; height=&quot;1150&quot; /&gt;&lt;/article&gt;</content:encoded></item><item><title>Progressive Rendering — The Key to Faster Web</title><link>https://www.thinkmill.com.au/blog/progressive-rendering</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/progressive-rendering</guid><description>Progressive Rendering is the technique of sequentially rendering portions of a webpage in the server and streaming it to the client in parts without waiting for the whole page to rendered.</description><pubDate>Thu, 21 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/g814jtiggytf/progressive-rendering-progressive-rendering-key-image?width=848&quot; alt=&quot;Progressive rendering&quot; width=&quot;848&quot; height=&quot;411&quot; /&gt;&lt;p&gt;&lt;em&gt;Note: The content of this post may be relevant to all types of servers but written within the context of a node.js server. The perf metrics are assumptive and may differ based on network bandwidth and latency.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;To understand progressive rendering we must first understand how client side rendering and server side rendering work.&lt;/p&gt;&lt;h3&gt;Client Side Rendering&lt;/h3&gt;&lt;p&gt;Client side rendering is the technique in which the server sends a simple HTML without any content in the &lt;code&gt;body&lt;/code&gt; and script tags in the &lt;code&gt;head&lt;/code&gt;. This is what &lt;a href=&quot;https://github.com/facebook/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;create-react-app&lt;/a&gt; builds output.&lt;/p&gt;&lt;p&gt;Typical page load behaviour in CSR —&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Browser requests the server for HTML&lt;/li&gt;&lt;li&gt;Server sends HTML with script tags in &lt;code&gt;head&lt;/code&gt; and no content in &lt;code&gt;body&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Browser parses the HTML and makes http requests to load the scripts&lt;/li&gt;&lt;li&gt;Once the scripts are loaded, the browser parses them and makes API requests and loads all the content asynchronously&lt;/li&gt;&lt;/ol&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/dbjn2ftj6omu/progressive-rendering-csr-1?width=848&quot; alt=&quot;Client side rendering&quot; width=&quot;848&quot; height=&quot;621&quot; /&gt;&lt;figcaption&gt;Client side rendering&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Since the all the &lt;strong&gt;content starts loading only after loading the initial JavaScript&lt;/strong&gt;, it takes a &lt;strong&gt;longer time&lt;/strong&gt; to show any &lt;strong&gt;content&lt;/strong&gt; on the page. If the user is on a &lt;strong&gt;slow network&lt;/strong&gt;, the &lt;strong&gt;content is blocked&lt;/strong&gt; for an even &lt;strong&gt;longer time&lt;/strong&gt; due to &lt;strong&gt;lower bandwidth&lt;/strong&gt; and &lt;strong&gt;higher latency&lt;/strong&gt;.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rhwjks20m6pq/progressive-rendering-csr-2?width=848&quot; alt=&quot;Client side rendering FP metric&quot; width=&quot;848&quot; height=&quot;629&quot; /&gt;&lt;figcaption&gt;Client side rendering FP metric&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Once the initial JavaScript is loaded, the &lt;strong&gt;content can be loaded asynchronously&lt;/strong&gt;. We can load the &lt;strong&gt;critical content first&lt;/strong&gt; and then load &lt;strong&gt;non-critical content later&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Server serves static HTML and initial load JavaScript bundles can be &lt;strong&gt;cached&lt;/strong&gt; in the browsers which may benefit frequent users.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Initial load JavaScript &lt;strong&gt;blocks content load&lt;/strong&gt;. The larger your bundle size is, the longer it will take to show content on the page.&lt;/li&gt;&lt;li&gt;API requests to load content have to travel all over the world to fetch data as they are &lt;strong&gt;not co-located&lt;/strong&gt; with the browser location. This may take even longer in slower networks due to high latency and low bandwidth.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SEO&lt;/strong&gt; scores take a huge hit since search engines don’t execute JavaScript (arguably) and only an empty page is indexed. This matters when you want your page to be public and searchable.&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Server Side Rendering&lt;/h3&gt;&lt;p&gt;Server side rendering is the technique in which the whole HTML is rendered on the server and sent to client. This is what &lt;a href=&quot;https://nextjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Next.js&lt;/a&gt; or &lt;a href=&quot;https://www.gatsbyjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Gatsby&lt;/a&gt; (build time server render) does.&lt;/p&gt;&lt;p&gt;Typical page load behaviour in SSR —&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Browser requests the server for HTML.&lt;/li&gt;&lt;li&gt;Server makes API requests (usually co-located) and renders the content in the server.&lt;/li&gt;&lt;li&gt;Once the page is ready, the server sends it to the browser.&lt;/li&gt;&lt;li&gt;The browser loads and parses the HTML and paints the content on the screen without waiting for the JavaScript bundle(s) to load.&lt;/li&gt;&lt;li&gt;Once the JavaScript bundle(s) are loaded, the browser hydrates interactivity to DOM elements, which is usually attaching event handlers and other interactive behaviours.&lt;/li&gt;&lt;/ol&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/qr5l09hddw9b/progressive-rendering-ssr-1?width=848&quot; alt=&quot;Server side rendering&quot; width=&quot;848&quot; height=&quot;629&quot; /&gt;&lt;figcaption&gt;Server side rendering&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Since the APIs are usually co-located with the server, the content is loaded super fast (faster than CSR) and the HTML is sent to the browser. Initial JavaScript load doesn’t block content load as the HTML sent by the server already has the content.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/zus4slj5414s/progressive-rendering-ssr-2?width=848&quot; alt=&quot;Server side rendering FP metric&quot; width=&quot;848&quot; height=&quot;641&quot; /&gt;&lt;figcaption&gt;Server side rendering FP metric&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Since the server serves &lt;strong&gt;HTML with content&lt;/strong&gt;, neither the browser nor search engines have to depend on JavaScript for the content.&lt;/li&gt;&lt;li&gt;Content is &lt;strong&gt;not&lt;/strong&gt; blocked by the time taken to load JavaScript bundle(s).&lt;/li&gt;&lt;li&gt;Page loads a lot &lt;strong&gt;faster than CSR&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The entire HTML has to be rendered on the server before sending it to the client. This means, even the non-critical content has to rendered on the server to before sending the response HTML to the client.&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Progressive Rendering&lt;/h3&gt;&lt;p&gt;Progressive Rendering (aka &lt;strong&gt;Progressive Server Side Rendering&lt;/strong&gt;) is a technique in which once you &lt;strong&gt;render the critical content&lt;/strong&gt; on the server, you start &lt;strong&gt;streaming it to the client without waiting for non-critical content&lt;/strong&gt;. You then stream the non-critical content later once it’s rendered on the server. The &lt;strong&gt;browser&lt;/strong&gt; starts to &lt;strong&gt;progressively render&lt;/strong&gt; (paint) the HTML on the page as soon as a chunk for critical content is received. Non-critical content is then later rendered (paint) on the page when the browser receives it from the server.&lt;/p&gt;&lt;p&gt;Typical page load behaviour in PSSR —&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Browser requests the server for HTML.&lt;/li&gt;&lt;li&gt;Server makes API requests (usually co-located) and &lt;strong&gt;renders the critical content first in the server and streams it to the browser&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;The browser receives the &lt;strong&gt;chunk&lt;/strong&gt; of HTML and renders (paints) it on the screen.&lt;/li&gt;&lt;li&gt;The server renders non-critical content &lt;strong&gt;after&lt;/strong&gt; rendering critical content and &lt;strong&gt;streams&lt;/strong&gt; it to the client.&lt;/li&gt;&lt;li&gt;The browser receives and renders (paints) the non-critical content later.&lt;/li&gt;&lt;li&gt;Once the entire page is loaded, the browser hydrates interactivity to DOM elements, which is usually attaching event handlers and other interactive behaviours.&lt;/li&gt;&lt;/ol&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/bs0khornhalm/progressive-rendering-pssr-1?width=848&quot; alt=&quot;Progressive rendering&quot; width=&quot;848&quot; height=&quot;628&quot; /&gt;&lt;figcaption&gt;Progressive rendering&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Progressive rendering &lt;strong&gt;bridges the benefits of both CSR and SSR&lt;/strong&gt;. Content is rendered quickly since the APIs are co-located in the server and at the same time, &lt;strong&gt;critical content can be rendered quickly without having to wait for non-critical content&lt;/strong&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;With Progressive Rendering, you can make your site load faster asynchronously &lt;strong&gt;without relying on JavaScript to load content&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/bkpfrpmvgpcu/progressive-rendering-pssr-2?width=848&quot; alt=&quot;Progressive rendering FP metric&quot; width=&quot;848&quot; height=&quot;631&quot; /&gt;&lt;figcaption&gt;Progressive rendering FP metric&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The whole idea of progressive rendering depends on the concept of &lt;strong&gt;streaming HTML from the server to client&lt;/strong&gt;. You can read more about it &lt;a href=&quot;https://www.freecodecamp.org/news/node-js-streams-everything-you-need-to-know-c9141306be93&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You often get magnitudes of performance boost if you do PSSR right. Here’s an example of the same website rendered with &lt;strong&gt;SSR vs PSSR&lt;/strong&gt;. &lt;em&gt;(Assuming the site is loaded over a low bandwidth network with high latency)&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/progressive-rendering/rendering-gif.mov&quot;&gt;SSR vs PSSR&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;How to progressively render a page?&lt;/h2&gt;&lt;p&gt;Node.js has this Readable API which will let you pipe a readable stream. An example code to stream HTML might look like this&lt;/p&gt;&lt;pre&gt;router.get(&apos;/stream&apos;, async function (req, res, next) {
  res.status(200)
  res.type(&apos;text/html; charset=utf-8&apos;)

  // myRendered renders the HTML content
  const DOCTYPE = await myRendered.DOCTYPE()
  const openHTML = await myRendered.openHTML()
  const head = await myRendered.head()
  const openBody = await myRendered.openBody()

  res.write(`${DOCTYPE}${openHTML}`)
  res.write(head)
  res.write(openBody)

  const pageChunks = [
    myRendered.header,
    myRendered.criticalContentOpen,
    myRendered.nonCriticalContentOpen,
    myRendered.nonCriticalContentClose,
    myRendered.criticalContentClose,
    myRendered.closeBody,
    myRendered.closeHTML,
  ]

  const pageStream = new Readable({
    async read(size) {
      if (!pageChunks.length) {
        pageStream.push(null)
      } else {
        const chunkToRender = pageChunks.shift()
        const renderedChunk = await chunkToRender()
        pageStream.push(renderedChunk)
      }
    },
  })

  // stream content as soon as they are rendered
  pageStream.pipe(res)
})
&lt;/pre&gt;&lt;p&gt;The example for this website built with &lt;a href=&quot;https://expressjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Express&lt;/a&gt; and vanilla JS is here —&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/flexdinesh/progressive-rendering&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/flexdinesh/progressive-rendering&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The idea is you &lt;strong&gt;predictably slice portions of your HTML content and stream it to the browser as soon as it is rendered&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Render on server but stream critical content to client without waiting for non-critical content.&lt;/li&gt;&lt;li&gt;Content is not blocked by the time taken to load JavaScript bundle(s).&lt;/li&gt;&lt;li&gt;Page loads a lot faster than both CSR and SSR.&lt;/li&gt;&lt;li&gt;Build fruitful user experiences even for users on slower networks.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The browser will hydrate the DOM and attach event listeners only after the whole page is loaded. Even though content shows up quickly, interactivity will be enabled only after non-critical content is loaded. (&lt;em&gt;But showing up content a lot quicker is still a win&lt;/em&gt;).&lt;/li&gt;&lt;li&gt;There is no established framework for progressive rendering and is highly dependent on the web application and its limitations.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Tips for effective progressive rendering&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Load critical CSS in the head&lt;/li&gt;&lt;li&gt;Using CSS grid for layouts removes the dependency on the order of how the content is streamed&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That’s all folks.&lt;/p&gt;&lt;p&gt;Drop a comment below or &lt;a href=&quot;https://twitter.com/intent/tweet/?text=Hey%20@flexdinesh%20I%20read%20your%20post%20about%20Progressive%20Rendering%20&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;start a conversation&lt;/a&gt; with the author in &lt;a href=&quot;https://twitter.com/flexdinesh&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter&lt;/a&gt; on what you think about this article.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Introducing Preconstruct</title><link>https://www.thinkmill.com.au/blog/introducing-preconstruct</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/introducing-preconstruct</guid><description>Preconstruct is a build tool for JavaScript packages with first-class support of monorepos and strong opinions to get you back to work faster.</description><pubDate>Wed, 06 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;A year ago, I was experiencing lots of problems maintaining Emotion’s build system so I set out to make a build system to solve the consistency and reliability problems in a repo with many packages and this eventually turned into &lt;a href=&quot;https://preconstruct.tools/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Preconstruct&lt;/a&gt;. It’s working really well on projects like &lt;a href=&quot;https://emotion.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Emotion&lt;/a&gt; and &lt;a href=&quot;https://react-select.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;React Select&lt;/a&gt; so I’m super excited to finally share it!&lt;/p&gt;&lt;h2&gt;What Does Preconstruct Do?&lt;/h2&gt;&lt;p&gt;More than just bundling, environment-specific builds, and ES Module + CommonJS targets, Preconstruct also takes the pain out of setting up your &lt;code&gt;package.json&lt;/code&gt;, and just Works when importing your unbuilt files from within both single-package repos and &lt;a href=&quot;https://en.wikipedia.org/wiki/Monorepo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;monorepos&lt;/a&gt;. It might be a bit more opinionated than other tools so it might not work for every possible use case but we think that supporting fewer use cases in a more complete way is better than supporting every possible use case in non-ideal ways.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Supporting fewer use cases in a more complete way is better than supporting every possible use case in non-ideal ways.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Preconstruct’s model&lt;/h2&gt;&lt;p&gt;Preconstruct has a concept of a project which contains one or more packages with one or more entrypoints which lets it work on small and large projects. For some projects, you might only have a single package which only has one entrypoint. Some projects might have many packages with lots of entrypoints.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/q90bt7g1blbj/introducing-preconstruct-preconstruct-project?width=848&quot; alt=&quot;Example of Preconstruct projects&quot; width=&quot;848&quot; height=&quot;271&quot; /&gt;&lt;figcaption&gt;Example of Preconstruct projects&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Source Module Resolution&lt;/h2&gt;&lt;p&gt;Because Preconstruct is designed with monorepos in mind, it handles the problem of “&lt;em&gt;I want to test/use a package but when I import it I get the dist file so I have to rebuild it everytime&lt;/em&gt;“. With the &lt;code&gt;preconstruct dev&lt;/code&gt;. command, Preconstruct will create files that redirect back to your source code so you can import those. When running in Node, Preconstruct will even use a require hook to compile your code with Babel automatically.&lt;/p&gt;&lt;h2&gt;Multiple Entrypoints&lt;/h2&gt;&lt;p&gt;Packages can have multiple entrypoints(e.g. &lt;em&gt;react-dom&lt;/em&gt; and &lt;em&gt;react-dom/server&lt;/em&gt;). &lt;strong&gt;Entrypoints are first-class citizens&lt;/strong&gt; in Preconstruct so they have all the same multiple module formats as a package with a single entrypoint has.&lt;/p&gt;&lt;h2&gt;Strict Validation and Auto Fixing&lt;/h2&gt;&lt;p&gt;Preconstruct will validate as much as it can to make sure your package works. This includes making sure the values of your &lt;em&gt;main&lt;/em&gt; and &lt;em&gt;module&lt;/em&gt; fields are correct and fixing them if they’re not which is especially helpful when managing a large number of packages. It’ll also make sure that your dist files will be included when your package is publishedOn so you’ll never publish a package but forget to add the dist &lt;em&gt;files&lt;/em&gt; to your package’s files field again.&lt;/p&gt;&lt;h2&gt;Getting Started&lt;/h2&gt;&lt;p&gt;Assuming you already have a source file at &lt;em&gt;src/index.js&lt;/em&gt; (or &lt;em&gt;src/index.ts&lt;/em&gt;) or you’re using Yarn Workspaces and have packages with &lt;em&gt;src/index.js&lt;/em&gt; (or &lt;em&gt;src/index.ts&lt;/em&gt;), you can setup Preconstruct like this.&lt;/p&gt;&lt;pre&gt;yarn add --dev @preconstruct/cli
yarn preconstruct init
&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;If you’re in a monorepo, you should also run &lt;code&gt;yarn preconstruct dev&lt;/code&gt; and add it to a postinstall script(&lt;code&gt;&quot;postinstall&quot;: &quot;preconstruct dev&quot;&lt;/code&gt;) that runs &lt;code&gt;preconstruct dev&lt;/code&gt; so that you can import your code without having to rebuild your project every time in changes.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Publishing packages&lt;/h2&gt;&lt;p&gt;Before you publish packages to npm, run &lt;code&gt;yarn preconstruct build&lt;/code&gt;. Preconstruct will use your Babel config and build flat bundles so make sure to &lt;a href=&quot;https://preconstruct.tools/guides/configuring-babel&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;configure Babel&lt;/a&gt; with the transforms you want.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We strongly recommend making a single script in your package.json that runs both build and publish, to stop broken publishes, such as &lt;code&gt;&quot;release&quot;: &quot;preconstruct build &amp;amp;&amp;amp; yarn publish:packages&quot;&lt;/code&gt;. If you’re in a single-package repo, you could instead run &lt;code&gt;preconstruct build&lt;/code&gt; in a &lt;code&gt;prepare&lt;/code&gt; or &lt;code&gt;prepublishOnly&lt;/code&gt; script.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Try going through the docs to learn more:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://preconstruct.tools/tutorials&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tutorials that offer a step-by-step process for how to use Preconstruct&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://preconstruct.tools/guides&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Guides that explain how to solve a common problem&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://preconstruct.tools/commands&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Commands API docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://preconstruct.tools/configuration&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Config API docs&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;p&gt;Preconstruct is built with a ton of amazing tools like &lt;a href=&quot;https://rollupjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rollup&lt;/a&gt; and &lt;a href=&quot;https://github.com/preconstruct/preconstruct/blob/c5e08d12362a2cc52f3445f42cbe7a911f576304/packages/preconstruct/package.json#L16-L49&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;so many others&lt;/a&gt;!! Preconstruct also wouldn’t have been possible without the support of &lt;a href=&quot;https://www.thinkmill.com.au/&quot;&gt;Thinkmill&lt;/a&gt;. 💝🎁&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Building React Select</title><link>https://www.thinkmill.com.au/blog/building-react-select</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/building-react-select</guid><description>Jed talks about how he accidentally built the most popular React component, experienced death by a hundred props, got buried under hundreds of PRs, invented a component extension API, learned that styles are also a function of state, influenced some major design systems and finally got some sleep.</description><pubDate>Thu, 31 Oct 2019 00:00:00 GMT</pubDate><enclosure url="https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/vc49l4krldf1/building-react-select-cover?width=1200" length="505266" type="image/png"/></item><item><title>React Dev Tools - Debug like a ninja</title><link>https://www.thinkmill.com.au/blog/react-dev-tools</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/react-dev-tools</guid><description>Hands down console.log() was, is and will always be the greatest debugging tool of all time.</description><pubDate>Sat, 14 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/rq9w4qhs7f07/react-dev-tools-react-dev-tools-key-image?width=848&quot; alt=&quot;React dev tools&quot; width=&quot;848&quot; height=&quot;477&quot; /&gt;&lt;p&gt;React team launched a &lt;strong&gt;new version&lt;/strong&gt; of the &lt;strong&gt;React Dev Tools&lt;/strong&gt; (v4) a few weeks ago and it’s &lt;strong&gt;fantabulous&lt;/strong&gt;. It gives you &lt;strong&gt;debugging superpowers&lt;/strong&gt; to navigate through your tree, trace data flow, spot weak links and optimise for perf.&lt;/p&gt;&lt;p&gt;There are &lt;strong&gt;two separate tabs (Components, Profiler)&lt;/strong&gt; now as opposed to the old version which had only one React tab.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/tl8fprbd6li1/react-dev-tools-react-dev-tools-tab?width=848&quot; alt=&quot;React dev tools tab&quot; width=&quot;848&quot; /&gt;&lt;figcaption&gt;React dev tools tab&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;New Dev Tools has great many improvements over the old version. To highlight a few —&lt;/p&gt;&lt;ul&gt;&lt;li&gt;🏎 Faster&lt;/li&gt;&lt;li&gt;🎣 Hooks support&lt;/li&gt;&lt;li&gt;🧐 Advanced tree filter&lt;/li&gt;&lt;li&gt;🤠 Better search UX&lt;/li&gt;&lt;li&gt;👮‍♀️ Badges for HOCs&lt;/li&gt;&lt;li&gt;🎯 Remembers selection after page reload&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Top 10 Features&lt;/h3&gt;&lt;p&gt;Dev Tools can do a lot of things. We’re are not going to dive into the nitty gritty details of each and every feature. Instead we’ll take a look at the &lt;strong&gt;top 10 helpful features&lt;/strong&gt; that help us save time and write better React code.&lt;/p&gt;&lt;h4&gt;Components Tab&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Live edit state/props&lt;/li&gt;&lt;li&gt;Search through tree&lt;/li&gt;&lt;li&gt;Advanced tree filter&lt;/li&gt;&lt;li&gt;Rendered by&lt;/li&gt;&lt;li&gt;Owners tree&lt;/li&gt;&lt;li&gt;Quick Navigation&lt;/li&gt;&lt;li&gt;Utils&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;Profiler Tab&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Profile initial mount&lt;/li&gt;&lt;li&gt;Why did this render?&lt;/li&gt;&lt;li&gt;Rendered at&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;1. Live Edit State/Props&lt;/h2&gt;&lt;p&gt;You can live edit the state and props of a component and instantly see the results in the page.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/dev-tools-edit-state-demo-1.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;2. Search Through Tree&lt;/h2&gt;&lt;p&gt;The search bar comes handy when you want to quickly find and jump to a component in the tree. Now that the &lt;strong&gt;Dev Tools can remember stuff&lt;/strong&gt; the &lt;strong&gt;selected node is saved between reloads&lt;/strong&gt; and you don’t have to repeat the action every time you reload the page.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/dom-tree.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;3. Component Filter&lt;/h2&gt;&lt;p&gt;Filter is a powerful feature to reduce visual noise and focus on what matters at the moment in a huge tree. You can filter with a variety of options like context, HOCs, host(DOM) nodes, component name, etc.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/component-filter.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;4. Rendered By&lt;/h2&gt;&lt;p&gt;Rendered By is a list of &lt;strong&gt;owner/parent components&lt;/strong&gt; that rendered the selected component. You can quickly jump to a parent component by selecting an entry from the list.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/rendered-by.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;5. Owners Tree&lt;/h2&gt;&lt;p&gt;Owners Tree gives you an &lt;strong&gt;eagle eye view&lt;/strong&gt; of a particular component and its sub-tree and hides the rest of the component tree.&lt;/p&gt;&lt;p&gt;To enter into owners tree view, you have to &lt;strong&gt;double click&lt;/strong&gt; a component in the tree.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/owners-tree.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;6. Quick Navigation&lt;/h2&gt;&lt;p&gt;Switching between Elements tab (browser dev tools) and Components tab (react dev tools) is pretty easy and the corresponding node is auto selected in these tabs.&lt;/p&gt;&lt;p&gt;To find the corresponding DOM node in the Elements tab, &lt;strong&gt;select the component and click the small eye icon&lt;/strong&gt; on the right pane. It will take you to the Elements tab with the corresponding DOM node selected.&lt;/p&gt;&lt;p&gt;To find the corresponding React node from Elements tab, &lt;strong&gt;just select a node in Elements tab and click the Components tab&lt;/strong&gt;. React Dev Tools will automatically select the corresponding component.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/quick-navigation.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;7. Utils&lt;/h2&gt;&lt;p&gt;Components tab offers three small handy utils.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Suspend component&lt;/li&gt;&lt;li&gt;Inspect matching DOM element&lt;/li&gt;&lt;li&gt;Log component data to console&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Suspend Component&lt;/h3&gt;&lt;p&gt;You can easily switch between the waiting(loading) and resolved states of &lt;strong&gt;Suspense&lt;/strong&gt; components.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/suspend-component.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Inspect matching DOM element&lt;/h3&gt;&lt;p&gt;We already saw this in &lt;strong&gt;Quick Navigation&lt;/strong&gt;. You can select a component and click the &lt;strong&gt;eye icon&lt;/strong&gt; on the right pane to jump to its corresponding node in Elements tab to inspect it.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/inspect-matching-dom-elements.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Log component data to console&lt;/h3&gt;&lt;p&gt;This is a handy utility that logs the entire component meta data to the console. The log gives details like props, hooks, corresponding DOM nodes and the component file location in the file system.&lt;/p&gt;&lt;p&gt;To log component data to console, select the component and &lt;strong&gt;click the small bug icon in the right pane&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/log-component-data-to-console.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Intro to Profiler&lt;/h2&gt;&lt;p&gt;The next three features are going to be about the Profiler. Before diving into them, we’ll take a look at how profiler works and the key metrics that are used during profiling.&lt;/p&gt;&lt;p&gt;Every profiling session has two important colour graphs —&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Commit graph&lt;/li&gt;&lt;li&gt;Component graph&lt;/li&gt;&lt;/ol&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/joanvznu2rgt/react-dev-tools-dev-tools-profiler?width=848&quot; alt=&quot;Dev tools profiler breakdown&quot; width=&quot;848&quot; height=&quot;225&quot; /&gt;&lt;figcaption&gt;Dev tools profiler breakdown&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;Commit Graph&lt;/h3&gt;&lt;p&gt;Commit graph gives you a list of commits (whole tree) during the session. Each commit denotes a user activity or side-effect that triggered a render in the tree .You will see it coloured from orange to green with orange denoting the costliest (took most time) commit and green denoting the cheapest (took least time) commit.&lt;/p&gt;&lt;h3&gt;Component Graph&lt;/h3&gt;&lt;p&gt;Component graph gives you info on which component rendered &lt;strong&gt;during a single commit&lt;/strong&gt;. You can select each commit from the commit graph to see its component graph. It is also colour coded with cost information —&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Dark Grey&lt;/strong&gt; — Component did &lt;strong&gt;not&lt;/strong&gt; render but a &lt;strong&gt;part of its sub-tree&lt;/strong&gt; rendered&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Transparent Grey&lt;/strong&gt; — &lt;strong&gt;Neither&lt;/strong&gt; the component nor its subtree rendered&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Coloured&lt;/strong&gt; — &lt;strong&gt;Both&lt;/strong&gt; the component and its entire subtree rendered&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;8. Profile Initial Mount&lt;/h2&gt;&lt;p&gt;Initial mount and first render is usually a perf heavy op in most React applications. In older versions of Dev Tools, it was impossible to profile and audit the initial mount phase. But the latest version provides an option to profile and audit the initial mount.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/profile-initial-mount.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;9. Why Did This Render?&lt;/h2&gt;&lt;p&gt;This is the most powerful feature of the Dev Tools yet and it tells you why a component rendered during a profiling session. A component might have rendered for one or many of these reasons —&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Props changed&lt;/li&gt;&lt;li&gt;State changed&lt;/li&gt;&lt;li&gt;Hooks changed&lt;/li&gt;&lt;li&gt;Parent component rendered&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You can profile an activity and see why a component rendered during the profiling session to &lt;strong&gt;spot weak links&lt;/strong&gt; and prevent unnecessary renders in the tree.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/why-did-this-render.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: You have check this setting — “Record why each component rendered while profiling” to see this info in the right pane of the Profiler Tab.&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;10. Rendered At&lt;/h2&gt;&lt;p&gt;Rendered At info on the right pane gives you two time metrics —&lt;/p&gt;&lt;ol&gt;&lt;li&gt;When exactly the component rendered during the profiling session&lt;/li&gt;&lt;li&gt;How long it took for the component to render during the profiling session&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Eg. &lt;strong&gt;2.2s for 2.3ms&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Here, &lt;strong&gt;2.2s&lt;/strong&gt; means the component rendered at &lt;strong&gt;2.2s(2200ms)&lt;/strong&gt; from the start of the profiling session which is &lt;strong&gt;0s&lt;/strong&gt;. And &lt;strong&gt;2.3ms&lt;/strong&gt; is the amount of time it took for the component to render.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/react-dev-tools/rendered-at.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;p&gt;That’s all folks.&lt;/p&gt;&lt;p&gt;If you’re interested in exploring all the features, the React team has built a site just for that — &lt;a href=&quot;https://react-devtools-tutorial.now.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Interactive Dev Tools&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Play around with Dev Tools and drop a comment below or &lt;a href=&quot;https://twitter.com/intent/tweet/?text=Hey%20@flexdinesh%20I%20read%20your%20post%20about%20@reactjs%20Dev%20Tools%20&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;start a conversation&lt;/a&gt; with the author in &lt;a href=&quot;https://twitter.com/intent/tweet/?text=Hey%20@flexdinesh%20I%20read%20your%20post%20about%20@reactjs%20Dev%20Tools%20&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter&lt;/a&gt; on what you think.&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Introducing Keystone 5</title><link>https://www.thinkmill.com.au/blog/introducing-keystone-5</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/introducing-keystone-5</guid><description>Keystone 5 is a scalable platform that allows you to create a highly customised CMS and API in minutes.</description><pubDate>Thu, 15 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;It’s not just a new version of Keystone, but a complete re-imagining of KeystoneJS for the future.&lt;/p&gt;&lt;p&gt;Keystone 5 builds on the lessons learned over the last 5 years of the Keystone’s history and focuses on what we believe are the most powerful features for modern web and mobile applications.&lt;/p&gt;&lt;p&gt;This means less focus on template-driven websites and more focus on a flexible architecture, a powerful GraphQL API with deep access control features, an extensible Admin UI and plugins for rich field types, file and database adapters, and session management.&lt;/p&gt;&lt;p&gt;We believe it’s the ideal back-end for React, Vue or Angular applications, Gatsby and Next.js websites, Static sites, Mobile applications and more.&lt;/p&gt;&lt;h2&gt;Open source&lt;/h2&gt;&lt;p&gt;Keystone is proudly open-source and allows individual and commercial reuse and modification. The code is &lt;a href=&quot;https://github.com/keystonejs/keystone-5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;freely available on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Zero assumptions&lt;/h2&gt;&lt;p&gt;Keystone is not opinionated about your front-end architecture. It works great with React, Vue, Angular, or anything else you like to use.&lt;/p&gt;&lt;p&gt;Keystone also tries to make zero assumptions about your data structure allowing you to build any type of application. It achieves flexibility through small, highly composable parts that allow you to build a foundation for any application.&lt;/p&gt;&lt;p&gt;Don’t need ‘Users’ or ‘Posts’? Not a problem! Keystone’s extensible architecture means you choose the features you need. Pick an ideal starting point, rather than working hard to adapt an opinionated framework.&lt;/p&gt;&lt;h2&gt;Fully featured&lt;/h2&gt;&lt;p&gt;Zero assumptions doesn’t mean zero features. Keystone comes with dozens of features out of the box including Lists, Fields, Access Control, Authentication, and Apps. You can add and configure each these, as well as extend, modify or build your own.&lt;/p&gt;&lt;h3&gt;Lists&lt;/h3&gt;&lt;p&gt;Lists define a set of Fields that make up a collection in Keystone. A List might represent a content type, such as a “Page” or “Post”, but they can be any collection of data. Creating a List is how you define the data structure of your application.&lt;/p&gt;&lt;h3&gt;Fields&lt;/h3&gt;&lt;p&gt;Fields represent a data type. They are used in Keystone for everything from rendering components in the Admin UI, to configuring Access Controls, to generating a GraphQL API. Keystone has dozens of built-in Field types and as always, you can create your own.&lt;/p&gt;&lt;h3&gt;Access Control&lt;/h3&gt;&lt;p&gt;Keystone has powerful Access controls that can be configured on Lists and Fields to define the specific actions authenticated and anonymous users can take within the Admin UI or GraphQL Apps.&lt;/p&gt;&lt;h3&gt;Authentication&lt;/h3&gt;&lt;p&gt;Authentication refers to a user identifying themselves and “logging in” to an application. Keystone provides multiple authentication strategies including a basic password or third-party authentication services, such as Twitter and Google.&lt;/p&gt;&lt;p&gt;Once again, authentication strategies are configurable and extensible allowing you to create your own login methods.&lt;/p&gt;&lt;h3&gt;Apps&lt;/h3&gt;&lt;p&gt;Keystone uses Apps to compose high-level features into a larger application.&lt;/p&gt;&lt;p&gt;A Keystone &lt;code&gt;index.js&lt;/code&gt; file exports two things, a Keystone instance, and Apps:&lt;/p&gt;&lt;pre&gt;module.exports = {
  keystone,
  apps: [
    new GraphQLApp(),
    new StaticApp({ path: &apos;/&apos;, src: &apos;public&apos; }),
    new AdminUIApp({ enableDefaultRoute: true }),
  ],
}
&lt;/pre&gt;&lt;p&gt;From a technical perspective Apps are simply bundled &lt;code&gt;express&lt;/code&gt; middleware that accept configuration options. Essential Keystone Apps include the GraphQL App, Admin UI App, and Static App.&lt;/p&gt;&lt;h2&gt;GraphQL App&lt;/h2&gt;&lt;p&gt;The GraphQL App is the keystone App of KeystoneJS. It provides a powerful GraphQL API with CRUD operations and powerful filtering options for all your lists.&lt;/p&gt;&lt;h3&gt;Admin UI App&lt;/h3&gt;&lt;p&gt;The Admin App is a fully functional administration UI to help you manage your data.&lt;/p&gt;&lt;h3&gt;Static App&lt;/h3&gt;&lt;p&gt;The Static App allows you to serve content from a folder using an &lt;a href=&quot;https://expressjs.com/en/starter/static-files.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;express static server&lt;/a&gt;. This can be quick and easy way to deploy a simple front-end with a Keystone application.&lt;/p&gt;&lt;p&gt;The Static App, along with other Keystone Apps, can decoupled from the main application and deployed separately if required.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Apps like the Static App provides an alternative to Keystone 4’s views and templates engine.&lt;/p&gt;&lt;h3&gt;Headless&lt;/h3&gt;&lt;p&gt;Keystone is headless, meaning it requires no front-end application. Keystone 5’s primary focus is on the API and Admin UI. It is non-prescriptive about your front-end application. Because of this Keystone 5 works with all major front-end frameworks.&lt;/p&gt;&lt;h3&gt;Self host&lt;/h3&gt;&lt;p&gt;Own your own data and host Keystone anywhere. Have complete freedom of choice when hosting your data, admin application, API and front-end applications.&lt;/p&gt;&lt;h3&gt;BYO Database&lt;/h3&gt;&lt;p&gt;Keystone allows you to choose different storage methods. Adapters for MongoDB and Postgres are available now, with additional adapters in development.&lt;/p&gt;&lt;p&gt;Because of its extensible architecture, Keystone is not even explicitly tied to a database. You could create adapters for file-system storage such as SQLite or even JSON and cloud storage options. We hope to see many more adapter options in the future.&lt;/p&gt;&lt;p&gt;Field level adapters allow you to fetch or store data for specific fields separately from your main storage location. Examples include uploading images to Cloudinary or fetching information via &lt;code&gt;o-embed&lt;/code&gt; links.&lt;/p&gt;&lt;h2&gt;Getting Started&lt;/h2&gt;&lt;p&gt;Keystone has a high level of configurability when you need it, but most customisations are opt-in and Keystone provides sensible defaults to help you get started quickly.&lt;/p&gt;&lt;p&gt;Create your first Keystone 5 project in seconds:&lt;/p&gt;&lt;pre&gt;npm init keystone-app my-app
cd my-app
npm run dev
&lt;/pre&gt;&lt;p&gt;This will configure a simple Todo application. Then visit &lt;code&gt;http://localhost:3000&lt;/code&gt; to see it in action. 🎉&lt;/p&gt;&lt;p&gt;See the complete &lt;a href=&quot;https://v5.keystonejs.com/quick-start/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Getting Started Guide&lt;/a&gt; for more details.&lt;/p&gt;&lt;h2&gt;🙏 Thank you&lt;/h2&gt;&lt;p&gt;A huge thanks to our Core Contributors, who have been working hard on tackling all the challenges that come with building such a powerful CMS like Keystone 5:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Tim Leslie (&lt;a href=&quot;https://twitter.com/timl&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@timl&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Jess Telford (&lt;a href=&quot;https://twitter.com/jesstelford&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@jesstelford&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Emma Hamilton (&lt;a href=&quot;https://twitter.com/emmahamiltown&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@emmahamiltown&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Joss Mackison (&lt;a href=&quot;https://twitter.com/jossmackison&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@jossmackison&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Jed Watson (&lt;a href=&quot;https://twitter.com/jedwatson&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@jedwatson&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;And &lt;a href=&quot;https://github.com/keystonejs/keystone-5/blob/master/CONTRIBUTING.md#contributors&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;all those who have supported the development&lt;/a&gt;; Nathan Simpson, Mike Hazell, John Molomby, Dominik Wilkowski, Ben Connolly, Jared Crowe, Luke Batchelor, Ticiana de Andrade, Gautam Singh, and Ajay Narain Mathur.&lt;/p&gt;&lt;p&gt;And of course, a massive thank you to the Keystone Community; without you, none of this would be possible ❤️&lt;/p&gt;&lt;h3&gt;What about Keystone 4?&lt;/h3&gt;&lt;p&gt;Keystone 4 is alive and well! Going forward, we will hand the npm packages over to our community leaders who can continue to help it grow, merge bug fixes, and add new features. As the Keystone 5 alpha nears stable release, we will communicate more details.&lt;/p&gt;&lt;p&gt;Thanks to Emma Hamilton, Jess Telford, and Joss Mackison&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>How I prototyped a social network with Keystone</title><link>https://www.thinkmill.com.au/blog/how-i-prototyped-a-social-network-with-keystone</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/how-i-prototyped-a-social-network-with-keystone</guid><description>Nathan Simpson shares how he built &quot;The Garage&quot; – a social network for car lovers, using Keystone 5 and React Native.</description><pubDate>Sun, 23 Jun 2019 00:00:00 GMT</pubDate></item><item><title>On design systems legibility</title><link>https://www.thinkmill.com.au/blog/on-design-systems-legibility</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/on-design-systems-legibility</guid><description>A design system that nobody can understand is a design system nobody can use. This is why good documentation is so key to having a well-used and well-loved design system. Noviny walks you through two of Atlassian’s documentation sites for the company-wide design system &amp; component library Atlaskit, showing how the content is built and written, and the practical steps you can take immediately to tackle the hard problems around large documentation projects.</description><pubDate>Mon, 14 Jan 2019 00:00:00 GMT</pubDate></item><item><title>Rebuilding React-Select</title><link>https://www.thinkmill.com.au/blog/rebuilding-react-select</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/rebuilding-react-select</guid><description>Jed discusses the upcoming React Select 2 and his experiences working in open source.</description><pubDate>Fri, 16 Mar 2018 00:00:00 GMT</pubDate></item><item><title>Creating Apps with JavaScript</title><link>https://www.thinkmill.com.au/blog/creating-apps-with-java-script</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/creating-apps-with-java-script</guid><description>Tom and Boris discuss and demonstrate how (and why) to prototype UI animations and interactions before development, and how to build a native-quality app for SydJS using web technologies.</description><pubDate>Wed, 18 Nov 2015 00:00:00 GMT</pubDate></item><item><title>Content Management &amp; Node.js</title><link>https://www.thinkmill.com.au/blog/content-management-and-node-js</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/content-management-and-node-js</guid><description>Jed Watson talks about Content Management Systems in Node.js.</description><pubDate>Wed, 18 Nov 2015 00:00:00 GMT</pubDate></item><item><title>Prototyping: Solutions vs. Interactions</title><link>https://www.thinkmill.com.au/blog/prototyping-solutions-vs-interactions</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/prototyping-solutions-vs-interactions</guid><description>Designing and prototyping the solution is different to prototyping interactions. Appreciating the distinction will help you pick your tools and be more effective at both.</description><pubDate>Thu, 22 Oct 2015 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/nzyo9mbi1mm3/prototyping-solutions-vs-interactions-prototyping-key-image?width=848&quot; alt=&quot;Prototyping structure&quot; width=&quot;848&quot; height=&quot;447&quot; /&gt;&lt;p&gt;As prototyping apps and tools become increasingly common, they are also becoming increasingly sophisticated. When I wrote the post about &lt;a href=&quot;https://medium.com/the-thinkmill/prototyping-ui-animation-2fe08e3a7932&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;prototyping UI animation&lt;/a&gt;, Origami had just been released, Pixate was in early private beta (and not yet owned by Google), and you could still count the number of dedicated prototyping tools on one hand.&lt;/p&gt;&lt;p&gt;Now, there’s an abundance of tools to suit your approach.&lt;/p&gt;&lt;p&gt;All of these tools are good at some things. Many of them are excellent at lots of things, and in the right hands, add immense value to the design and development of an application.&lt;/p&gt;&lt;p&gt;I head up UX and Interaction design at &lt;a href=&quot;https://www.thinkmill.com.au/&quot;&gt;thinkmill&lt;/a&gt; so I spend a good portion of my time translating ideas and concepts into interfaces and interactions. Over time the distinction between prototyping a &lt;em&gt;&lt;strong&gt;solution&lt;/strong&gt;&lt;/em&gt; vs. prototyping &lt;em&gt;&lt;strong&gt;interactions&lt;/strong&gt;&lt;/em&gt; has become very clear, and with it, the appreciation of which tools are best suit to each function.&lt;/p&gt;&lt;p&gt;While both solution and &lt;em&gt;interaction&lt;/em&gt; prototyping are part of the broader design function, but they are not the same thing. They are complimentary but distinct, like designing the database schema is complimentary but distinct from architecting the system.&lt;/p&gt;&lt;h3&gt;Why is the distinction important?&lt;/h3&gt;&lt;p&gt;There’s nothing revelatory above, so why does it matter? With better tools, those of us who were previously restricted to paper sketches, Photoshop/Sketch and hand waving can get a little over-excited. It becomes tempting to attempt to prototype everything. However well intentioned, &lt;strong&gt;avoid the temptation&lt;/strong&gt;. Diminishing returns kick in &lt;em&gt;very&lt;/em&gt; quickly when prototyping.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/opvzhj4cw36u/prototyping-solutions-vs-interactions-logic-prototype?width=848&quot; alt=&quot;Benefit vs logic prototype&quot; width=&quot;848&quot; height=&quot;552&quot; /&gt;&lt;p&gt;The trick is to prototype the right things — the necessary things — rather than all the things.&lt;/p&gt;&lt;h2&gt;The Solution Prototype&lt;/h2&gt;&lt;p&gt;In the very early stages of a project, the first milestone is articulating a sane, high-level solution. Put another way, I’m trying to answer two questions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;how could this system work?&lt;/li&gt;&lt;li&gt;how could the user interact with this interface? q &lt;em&gt;(Note could rather than should above. Assume you don’t know what anything should be like just yet).&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;At this stage we’re trying to articulate a solution that doesn’t have glaring holes, and weed out obvious problems in the user experience. A &lt;em&gt;solution&lt;/em&gt; prototype (as opposed to an interaction prototype) allows others to provide meaningful feedback with the benefit of a bird’s eye view.&lt;/p&gt;&lt;p&gt;I’ll use Sketch and Flinto to quickly stitch together static screens into a user flow, and sanity check the interface.&lt;/p&gt;&lt;p&gt;&lt;em&gt;(Side note: I used to hand sketch on the iPad and then jump into Photoshop, but the Sketch workflow has made this step largely redundant — in case you need another reason to use Sketch).&lt;/em&gt;&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/w3wwv8mkjhen/prototyping-solutions-vs-interactions-prototype-example?width=848&quot; alt=&quot;Prototype example in sketch&quot; width=&quot;848&quot; height=&quot;343&quot; /&gt;&lt;p&gt;It’s important to note that at this early stage, I’m not particularly concerned with optimising anything, only articulating the solution as completely as possible, based on limited information.&lt;/p&gt;&lt;p&gt;Parts that obviously don’t flow or make sense are quickly identified and fixed through iteration. With each iteration the prototype gets more refined and detailed (bot not &lt;em&gt;very&lt;/em&gt; detailed yet) and comprehensive. Then, at some point, with enough iteration and feedback, I reach that magical point when I can run through the solution prototype and have it “hang together”&lt;/p&gt;&lt;p&gt;This is the time to switch gears and dive into interaction prototyping.&lt;/p&gt;&lt;p&gt;&lt;em&gt;(I am, of course, over-simplifying here. There’s a ton of evaluation, testing and discussion of the solution prototype that goes on between these two stages).&lt;/em&gt;&lt;/p&gt;&lt;p&gt;When the system architecture and information architecture hangs together well enough, I use interaction prototyping tools like Edge Animate and more recently Flinto for Mac, to develop the feel and flow of the interface. This happens in “spots”, meaning I’ll take the key views and view transition and visualise those particular interactions. For example:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/prototyping-solutions-vs-interactions/prototype-example-nrl.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;p&gt;To repeat the point above: &lt;em&gt;do not try and build an app inside a prototyping tool&lt;/em&gt;. Worrying about managing application state while prototyping interactions is the worst kind of quicksand.&lt;/p&gt;&lt;h3&gt;How do the tools compare?&lt;/h3&gt;&lt;p&gt;While I could write a post for each — and &lt;a href=&quot;http://www.sketchappsources.com/blog/prototyping-solutions-vs-interactions/post/app-prototyping-tools-compared&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;others have done&lt;/a&gt; — here’s a few quick thoughts for now:&lt;/p&gt;&lt;h4&gt;Solution Prototyping&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;http://flinto.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Flinto&lt;/a&gt; / &lt;a href=&quot;http://www.invisionapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;InVision&lt;/a&gt; / &lt;a href=&quot;https://marvelapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Marvel&lt;/a&gt; / …others&lt;/p&gt;&lt;p&gt;Actually, Sketch is the most useful solution prototyping tool, but in order to validate UX flow, Flinto is my personal preference here. Between dropbox integration that automatically updates screens as you export, scroll slices and the magic connector, it’s very fast to string together screens into a navigable interface. It’s bare bones otherwise as far as features go. There’s a handful of views transitions available and a neat preview. The fact that I can’t get fancier than navigating between screens is a good thing at this stage. I get to a “yep this works at a high level” point faster.&lt;/p&gt;&lt;p&gt;InVision has been around the longest (&lt;em&gt;citation needed&lt;/em&gt;) and was a little late to the mobile prototyping game. I find the featureset richer, but the workflow slower. Marvel I’ve only recently started playing with and have little to say other than &lt;em&gt;it seems nice&lt;/em&gt;.&lt;/p&gt;&lt;h4&gt;Interaction Prototyping&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;https://www.flinto.com/mac&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Flinto for Mac&lt;/a&gt; / &lt;a href=&quot;https://www.adobe.com/au/products/edge-animate.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Edge Animate&lt;/a&gt; / &lt;a href=&quot;http://framerjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Framer&lt;/a&gt; / &lt;a href=&quot;https://facebook.github.io/origami/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Origami&lt;/a&gt; / …others&lt;/p&gt;&lt;p&gt;This field is a lot broader and your tool of choice will depend very much on how comfortable (or not) you are with writing code.&lt;/p&gt;&lt;p&gt;If you are familiar with JavaScript (erm, CoffeeScript specifically), there is arguably no better interaction design tool than &lt;strong&gt;Framer&lt;/strong&gt;. It has very good Photoshop and Sketch integration and writing code means there’s little you can’t do. On the other hand, if you can’t or don’t want to code, it’s not for you.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://medium.com/the-thinkmill/prototyping-ui-animation-2fe08e3a7932#.m6m9xl34m&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;I’ve written about working with &lt;strong&gt;Edge Animate&lt;/strong&gt; in-depth previously&lt;/a&gt;. It’s an under-rated (&lt;em&gt;un-cool&lt;/em&gt;) interaction design tool, and is all the more attractive because it caters to both comfortable coders and those who are not, with timeline-driven interactions. Gesture design can be harder to do compared to Framer, but that might not be a deal-breaker for you. Compositions can also easily be compiled into native apps using PhoneGap Build.&lt;/p&gt;&lt;p&gt;More recently, the folks at Flinto released &lt;strong&gt;Flinto for Mac&lt;/strong&gt;. The most striking thing about it is that it’s not just a desktop client for web app, rather it’s a fully-fledged interaction design tool, with an excellent &lt;em&gt;Export from Sketch&lt;/em&gt; plugin that preserves layers and groups from your Sketch files. I design with interaction in mind, so going from layer groups in Sketch to animatable layer groups in Flinto is a huge time saver. Perhaps most interestingly, there’s no concept of a timeline, as there is in just about every other tool. Instead you define a start and end states, and then control the delays + easing curves between the scenes. I’ll admit it took a while to wrap my head around the concept, but once I did, it makes a lot of sense.&lt;/p&gt;&lt;p&gt;&lt;em&gt;(I discovered later that there are good tutorial videos explaining everything on Flinto’s site).&lt;/em&gt;&lt;/p&gt;&lt;p&gt;What about Origami? Origami is actually a patch library for Apple’s Quartz Composer node animation tool. Credit to the Facebook team, Origami makes Quartz Composer usable/useful and being a node animation tool there is no code or coding required — everything is point-and-click. When you wrap your head around the sequences of patches that are required to achieve an effect or design an interaction, it becomes a pretty powerful tool. On the other hand it is one of the most frustrating interfaces you’ll ever use. Changing values requires a double click on a tiny number field. To add a patch shortcut you must hover a mouse pointer precisely over a ~5px circle, lest it insert the patch in the wrong place. And Quartz Composer is such a resource hog that my MacBook Pro (i7, 16gb ram) spins its fans hard enough to reach low earth orbit. It’s not for me, but I suggest you try it for yourself in any case.&lt;/p&gt;&lt;p&gt;It feels like Origami / Quartz Composer proved that there is clear and present need for dedicated interaction prototyping tools, based on which a a new wave of tools has come into existence. Kudos for that. It’s certainly getting better every day.&lt;/p&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Understand the difference between prototyping a &lt;em&gt;solution&lt;/em&gt; vs prototyping &lt;em&gt;interactions&lt;/em&gt; within that solution&lt;/li&gt;&lt;li&gt;Use the right tool for the job (you should use at least two different tools)&lt;/li&gt;&lt;li&gt;Avoid the temptation to build a complete app in prototype — only build the important stuff&lt;/li&gt;&lt;li&gt;Bonus point: try Origami and see if you can work out why anyone would.&lt;/li&gt;&lt;/ul&gt;&lt;/article&gt;</content:encoded></item><item><title>Going Mobile with React</title><link>https://www.thinkmill.com.au/blog/going-mobile-with-react</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/going-mobile-with-react</guid><description>Jed shares what we’ve learned approaching the unique challenges of building mobile web apps with React.</description><pubDate>Thu, 11 Jun 2015 00:00:00 GMT</pubDate></item><item><title>Making the case for Progressive Javascript</title><link>https://www.thinkmill.com.au/blog/making-the-case-for-progressive-javascript</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/making-the-case-for-progressive-javascript</guid><description>I’m crediting Mark Dalgleish for this one, but I want to make the case because it’s the best alternative I’ve heard to “Isomorphic Javascript”.</description><pubDate>Tue, 17 Feb 2015 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Read on to learn why.&lt;/p&gt;&lt;h2&gt;Quick background&lt;/h2&gt;&lt;p&gt;The web development community has been in love forever with the idea of writing code that can be shared across the server and the client. Or maybe that’s just me? Nope, &lt;a href=&quot;http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;not just me&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;We’ve flirted with technology for a while now that we’ve hoped will achieve this holy grail, but recently between node.js and React.js it’s become a reality we can reach out and touch. (er, write. In JavaScript.)&lt;/p&gt;&lt;p&gt;Which means we’re up to the &lt;em&gt;second&lt;/em&gt; &lt;a href=&quot;https://news.ycombinator.com/item?id=825997&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Hard Problem In Computer Science&lt;/a&gt;: naming things.&lt;/p&gt;&lt;p&gt;I mean, instead of “transferring JSON to and from the server without a page refresh” we have AJAX. There’s no way “writing JavaScript that we can run on the Server and the Client” is going to fly.&lt;/p&gt;&lt;p&gt;So far, for lack of a better term, we’ve mostly been calling it “Isomorphic Javascript”. And then &lt;a href=&quot;https://news.ycombinator.com/item?id=8414980&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;arguing about how&lt;/a&gt; that’s a terrible term for it.&lt;/p&gt;&lt;h2&gt;Remember Progressive Enhancement?&lt;/h2&gt;&lt;p&gt;It was a Good Thing. We started understanding how we could write web apps that were baseline-level functional without JavaScript, while being “progressively” enhanced with richer functionality as the visitors’ platform of choice (probably not IE6) supported it.&lt;/p&gt;&lt;p&gt;Apps like Flickr and Basecamp showed us the future, and jQuery helped us implement it ourselves. Capable browser? here’s a nice date picker! oh, no JavaScript? that’s cool, we’ll validate your input on the server.&lt;/p&gt;&lt;p&gt;Accessibility loved it! Good semantic markup makes screen readers happy.&lt;/p&gt;&lt;p&gt;Google loved it! Quality HTML is King for ranking in search results.&lt;/p&gt;&lt;p&gt;Users loved it! Apps were faster, and more interactive.&lt;/p&gt;&lt;p&gt;Web Developers loved it! … well … sort of.&lt;/p&gt;&lt;p&gt;Yes, it was great we could launch an MVP and &lt;em&gt;progressively enhance&lt;/em&gt; it with fancy client-side coolness. And oh, all those plugins!&lt;/p&gt;&lt;p&gt;But… all of a sudden we’re &lt;em&gt;writing all our code twice&lt;/em&gt;. For the client, and the server. We’re also testing our code twice. And designing two user experiences. And we’re forking our UI; some things work with JavaScript, others don’t.&lt;/p&gt;&lt;p&gt;Actually… that’s a lot of work.&lt;/p&gt;&lt;h2&gt;Enter Progressive Javascript&lt;/h2&gt;&lt;p&gt;The name works for me on a number of levels.&lt;/p&gt;&lt;p&gt;Firstly, it’s the best description I’ve heard of what’s going on.&lt;/p&gt;&lt;p&gt;Here’s the Wikipedia definition of Isomorphism:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Isomorphism&lt;/strong&gt; is a very general concept that appears in several areas of mathematics. The word derives from the Greek iso, meaning “equal,” and morphosis, meaning “to form” or “to shape.”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So I guess Isomorphic Javascript has equal shape on the server and the client? Nope. It doesn’t. Different code paths will run; different features are available. It’s just that you can write one set of code that executes in both environment and shares as much as possible.&lt;/p&gt;&lt;p&gt;The JavaScript runs on the server. Then it’s sent to the client and continues. There it does more. Handles interactivity. Takes over.&lt;/p&gt;&lt;p&gt;Here’s a definition of Progressive:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Progressive&lt;/strong&gt;; adj. happening or developing gradually or in stages.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Sounds pretty spot on to me.&lt;/p&gt;&lt;p&gt;Secondly, this whole concept seems like the natural evolution of what we were solving with Progressive Enhancement.&lt;/p&gt;&lt;p&gt;The Web Platform has come a long way. Chances are we’re supporting IE9 and wishing it was just IE10+. We’ve got grids for responsive resizing, and flex-box will soon help us forget we ever used tables for layout.&lt;/p&gt;&lt;p&gt;But mainly, it’s pretty safe to assume JavaScript will just work. Everywhere. On the client, and on the server (and the database, and your drone, and…)&lt;/p&gt;&lt;p&gt;There are still important reasons for server-side rendering. Deep links. Accessibility. Faster page loading. And, of course, SEO.&lt;/p&gt;&lt;p&gt;But since we can run JavaScript everywhere, we now have better solutions to the same problems we were encountering with Progressive Enhancement.&lt;/p&gt;&lt;p&gt;We can stop designing two user experiences. We can stop writing everything twice. And since we have less to worry about, we can be more productive. This will literally help us progress faster.&lt;/p&gt;&lt;p&gt;This is Progressive JavaScript.&lt;/p&gt;&lt;p&gt;Love it? Hate it? Have a better idea? Let me know on Twitter: &lt;a href=&quot;https://twitter.com/JedWatson&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@JedWatson&lt;/a&gt;&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>My first year of Open Source Commitment and KeystoneJS</title><link>https://www.thinkmill.com.au/blog/first-year-of-open-source-commitment</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/first-year-of-open-source-commitment</guid><description>If you want to do something, do it every day. This is the story of my first year writing open-source software.</description><pubDate>Mon, 01 Sep 2014 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;p&gt;Today marks one year of &lt;a href=&quot;http://github.com/JedWatson&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my GitHub streak&lt;/a&gt;, and coincidentally the first birthday of my company &lt;a href=&quot;http://www.thinkmill.com.au/&quot;&gt;Thinkmill&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;It’s also, roughly, a year since I released the first prototype of &lt;a href=&quot;http://www.keystonejs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;KeystoneJS&lt;/a&gt;, the open source Node.js CMS / web application framework I created.&lt;/p&gt;&lt;p&gt;When I started KeystoneJS it was to enable myself and others to develop node.js projects more efficiently, and I had no idea how it would be received by the Javascript community or other developers.&lt;/p&gt;&lt;p&gt;I was totally new to Open Source, and with no profile, followers or momentum I decided that committing to working on it every day (regardless of what I was doing commercially or personally) would be a great way to establish trust with other developers interested in using it. It was my way of saying “this isn’t just a throwaway project from someone you haven’t heard of that could be abandoned next month”.&lt;/p&gt;&lt;p&gt;It turned out to be so much more, and has been an amazing experience — definitely one of the best things I’ve ever done.&lt;/p&gt;&lt;p&gt;In the last year, Keystone has gained over 1,700 stars on GitHub, 260 forks, over 50 contributors, nearly a thousand followers on Twitter (@KeystoneJS), and is downloaded over 4,000 times a month.&lt;/p&gt;&lt;p&gt;We have used it to power websites and apps with hundreds of thousands of users in production, and other companies large and small are adopting it as a base for their own projects.&lt;/p&gt;&lt;p&gt;But the real surprise has been the impact that maintaining my streak has had on my life.&lt;/p&gt;&lt;p&gt;Most days its easy; I’ve got lots to do on Keystone, and love working on it.&lt;/p&gt;&lt;p&gt;Some days its hard; I’ve worked all-nighters, gone on a family holiday to China, or had nothing I can think of doing in the time I’ve got to do it.&lt;/p&gt;&lt;p&gt;But I’ve made a commitment so I do it anyway. And looking back, the hard days are the ones that have made the experience great.&lt;/p&gt;&lt;p&gt;When I’m not inspired, I’ll go through issues and find something minor that I can fix. Or I’ll write some tests. Or I’ll add something simple that’s been requested that I haven’t gotten around to while working on larger issues. This happens &lt;strong&gt;every day&lt;/strong&gt;. And the great thing is the sum of all these little things.&lt;/p&gt;&lt;p&gt;This story wouldn’t be complete without explaining my wife’s support. About a week in, I told her what I was doing, and she was on board.&lt;/p&gt;&lt;p&gt;She’s seen the excitement when I think I’ve done something cool. And the joy when someone I don’t know submits a great PR. And the pain when I’m flat out and stressed and tired and &lt;em&gt;need to do something in the next 30 minutes because otherwise I’ll break my streak&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Earlier this year, when we landed in China on holiday (and I thought I’d take a break) my wife challenged me about why I’m doing this, and encouraged me not to break my streak. She made me realise:&lt;/p&gt;&lt;p&gt;I’m no longer doing this to prove something and I’m not doing it for anyone else. I’m doing it for myself.&lt;/p&gt;&lt;p&gt;I’ve learned the power of sharing your work with others and becoming part of a community. I’ve had the opportunity to solve interesting and challenging problems. I’ve had the opportunity to give talks and workshops at meetups and conferences. And I’ve had the opportunity to meet (and collaborate with) amazing people from all over the world.&lt;/p&gt;&lt;p&gt;And now, almost every day, something happens with Keystone that &lt;em&gt;just makes me happy&lt;/em&gt;. All because of something I created, and my commitment to it.&lt;/p&gt;&lt;p&gt;About a hundred days in, I found out I wasn’t alone. John Resig (@jeresig, author of jQuery) wrote an &lt;a href=&quot;http://ejohn.org/blog/write-code-every-day/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;excellent post on his commitment to code every day&lt;/a&gt; and the impacts it has had (he is currently at 280 consecutive days).&lt;/p&gt;&lt;p&gt;These ideas aren’t actually specific to open source or even programming. If you have anything you’re passionate about — whether it’s writing, music, learning, or anything — committing to &lt;em&gt;doing something every day&lt;/em&gt; is a powerful way to achieve it.&lt;/p&gt;&lt;p&gt;And it’s surprisingly easy. You get used to it. It becomes part of your rhythm. You get over the hard parts, and enjoy the result.&lt;/p&gt;&lt;p&gt;My streak won’t continue forever; I’ll have a proper day off again when I’m ready. But the experience has changed my life for the better, and KeystoneJS has taken on a life of its own with new core contributors, and major improvements in the works.&lt;/p&gt;&lt;p&gt;To celebrate, and to acknowledge that KeystoneJS is now the sum of many developers, community members and users, I’ve graduated it out of my personal profile and &lt;a href=&quot;https://github.com/keystonejs/keystone&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;into its own organisation on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;We’ve got big plans for what to do with it from here. We’re rebuilding the Admin UI in React to enable developers to rapidly build web apps more effectively, and creating a whole plugin / customisation system. We’re abstracting common (or what should be common) functionality into separate packages. We’re going to enable model-based API creation with much less work, and a whole lot more.&lt;/p&gt;&lt;p&gt;I’m so excited about what we can do to make node.js so much more streamlined to develop apps with, as well as the teaching aspect of what we’re doing, that my wish for KeystoneJS on its first birthday is just that I can spend more time working on it.&lt;/p&gt;&lt;p&gt;In the meantime, it’s been a great first year!&lt;/p&gt;&lt;/article&gt;</content:encoded></item><item><title>Prototyping UI Animation</title><link>https://www.thinkmill.com.au/blog/prototyping-ui-animation</link><guid isPermaLink="true">https://www.thinkmill.com.au/blog/prototyping-ui-animation</guid><description>Interaction design with Edge Animate</description><pubDate>Sun, 03 Aug 2014 00:00:00 GMT</pubDate><content:encoded>&lt;article&gt;&lt;h2&gt;Interaction design with Edge Animate&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; this post is now a couple of years old. Tools for prototyping UI animation have (happily) &lt;em&gt;improved greatly&lt;/em&gt; since I wrote this, and certainly Edge Animate and PhoneGap belong more in 2014 than 2017. FWIW &lt;a href=&quot;https://www.sketchapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Sketch&lt;/a&gt; + &lt;a href=&quot;https://www.flinto.com/mac&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Flinto&lt;/a&gt; is my current preferred pairing. Happy reading.&lt;/p&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/x8n7z31lti9r/prototyping-ui-animation-prototyping-ui-key-image?width=848&quot; alt=&quot;Edge animate screen&quot; width=&quot;848&quot; height=&quot;512&quot; /&gt;&lt;p&gt;As an interaction designer who is more comfortable with pixels than code, I use &lt;em&gt;Animate&lt;/em&gt; to quickly turn sketches and Photoshop comps into interactive and animated UI prototypes that run as “native” apps on mobile devices.&lt;/p&gt;&lt;p&gt;Below is my process for designing the interactions and animations for a simple app–for &lt;a href=&quot;http://sydjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SydJS, a local JavaScript meetup&lt;/a&gt;–that allows members to confirm attendance, view upcoming talks, get reminders when meetups are scheduled, etc.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/BorisBozic/sydjs-assets/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Get the source PSD + Edge Animate composition&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/bladey/sydjs-app&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Get the app source code&lt;/strong&gt;&lt;/a&gt; (we open sourced everything)&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;The state of prototyping tools&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.flinto.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Flinto&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;http://www.invisionapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;InVision&lt;/a&gt; excel at quickly wireframing an app using only screenshots.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.framerjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Framer&lt;/strong&gt;&lt;/a&gt; is a prototyping framework targeted at designers that can do basic JavaScript (CoffeeScript)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Quartz Composer&lt;/strong&gt; is Apple’s node editor for masochists (kidding) that was recently popularised by the Facebook Paper team. However it’s quite old, buggy and comes with a particularly steep learning curve (though it’s very powerful once mastered).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href=&quot;http://html.adobe.com/edge/animate&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Edge Animate&lt;/strong&gt;&lt;/a&gt; is in a kind of sweet spot at the moment. You can do most things visually, but if you’re also comfortable with basic JavaScript, you can make sophisticated interactions and take many shortcuts.&lt;/p&gt;&lt;p&gt;My object here is not to persuade you to use any one tool over another. In fact &lt;strong&gt;I strongly encourage you to try them all&lt;/strong&gt;. Designing interactions and animations for user interfaces is a relatively young discipline and the ecosystem of tools is both very young and changing rapidly.&lt;/p&gt;&lt;h3&gt;Edge Animate 101&lt;/h3&gt;&lt;p&gt;Animate is the spiritual successor to Adobe’s Flash (complete with timeline, symbols and javascript) that outputs HTML5/CSS3/JS which can be quickly deployed as a native app using PhoneGap.&lt;/p&gt;&lt;p&gt;It’s &lt;em&gt;not&lt;/em&gt; for making production-ready apps. But it is very useful for designing and prototyping app interactions and animations as an input into the development process.&lt;/p&gt;&lt;p&gt;If you’re entirely new to Edge Animate, I recommend you check out the &lt;a href=&quot;https://helpx.adobe.com/creative-cloud/learn/tutorials/animate.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;getting started resources from Adobe&lt;/a&gt; and get familiar with the basics. There are a lot of great video tutorials to get you comfortable with the interface and basic concepts.&lt;/p&gt;&lt;p&gt;If you’ve used Flash before, you’ll feel right at home.&lt;/p&gt;&lt;h2&gt;Timelines &amp;amp; Keyframes&lt;/h2&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/njvsjn1cyhiw/prototyping-ui-animation-edge-animate-timeline?width=848&quot; alt=&quot;Here we’re saying: &apos;When we touch the btn-talks symbol, set its opacity to 50%&apos;&quot; width=&quot;848&quot; height=&quot;175&quot; /&gt;&lt;figcaption&gt;Here we’re saying: &apos;When we touch the btn-talks symbol, set its opacity to 50%&apos;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Timelines and Keyframes are central to the composition.&lt;/p&gt;&lt;p&gt;At its most basic, a timeline is simply visualisation of changes in properties over time.&lt;/p&gt;&lt;p&gt;Keyframes define the “extremes” — the start and end values of properties, such as X/Y coordinates, opacity, etc. Together they are the buildng blocks of all animations.&lt;/p&gt;&lt;h3&gt;Symbols&lt;/h3&gt;&lt;p&gt;Think of symbols as encapsulated objects that have their own independent timeline. In our context, symbols typically represent interactions or views within an app. Symbols are particularly useful because they can be manipulated with user-triggered actions (eg. tapping a button causes a view to slide in from the bottom).&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/py3jsk3m91nd/prototyping-ui-animation-edge-animate-triggers?width=848&quot; alt=&quot;Here we’re saying: “When we touch the btn-talks symbol, set its opacity to 50%”&quot; width=&quot;848&quot; height=&quot;283&quot; /&gt;&lt;figcaption&gt;Here we’re saying: “When we touch the btn-talks symbol, set its opacity to 50%”&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Triggers and Actions are what gives the composition interactivity. Triggers are events that exist on a timeline (e.g., at 500ms stop playback). Actions are usually user-triggered events (e.g., on touchstart, set opacity of element to 50%).&lt;/p&gt;&lt;h2&gt;Part 1. Setting up&lt;/h2&gt;&lt;p&gt;I prefer to get into Animate after a stint in Photoshop where I’ve designed the main views in the app and exported assets like icons and images.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/vtl1dw5tp7ri/prototyping-ui-animation-edge-animate-blank-canvas?width=848&quot; alt=&quot;Nothing like a blank canvas. Here we’re setting up the composition dimensions to represent an iPhone 5 portrait view.&quot; width=&quot;848&quot; height=&quot;532&quot; /&gt;&lt;figcaption&gt;Nothing like a blank canvas. Here we’re setting up the composition dimensions to represent an iPhone 5 portrait view.&lt;/figcaption&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;Nothing like a blank canvas. Here we’re setting up the composition dimensions to represent an iPhone 5 portrait view.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;We’re prototyping for iOS here, so I’ve set the dimensions of the composition to 640 x 1136px for an iPhone 5.&lt;/p&gt;&lt;p&gt;There’s an amount of pre-planning that should go into the composition before you start. I’ll typically break up the comp into its primary views:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Loading view (transitions to the Home screen)&lt;/li&gt;&lt;li&gt;Home screen (w/ RSVP buttons, reminder prompt)&lt;/li&gt;&lt;li&gt;Upcoming talks (overlay)&lt;/li&gt;&lt;li&gt;About the meetup (overlay)&lt;/li&gt;&lt;li&gt;With a mental map of how the comp will be organised, we can play.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Part 2. Animating&lt;/h2&gt;&lt;h3&gt;Home screen&lt;/h3&gt;&lt;p&gt;Before adding actions and triggers that control interactions, I start by building the animations that I want to see and to get the timing right.&lt;/p&gt;&lt;p&gt;When the app starts up, I want a seamless and continuous transition from the initial loading screen that displays a static logo while the app is loading, to the main view where the user can begin using the app.&lt;/p&gt;&lt;p&gt;As the Home screen is loaded, I want to move and fade several UI elements into place. I start by laying out the ‘end’ view, then I work backwards, plotting keyframes on the timeline to define when and how I want elements to transition into view.&lt;/p&gt;&lt;p&gt;For example, the RSVP buttons slide it from the bottom, followed shortly by the buttons at the top of the UI.&lt;/p&gt;&lt;p&gt;Here’s the keyframe view, slowed down:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/prototyping-ui-animation/edge-animate-keyframes-demo.mov&quot;&gt;Watch video&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Even though the entire transition lasts less than ½ of one second, animating elements in, rather than simply making them appear, gives the interface a far more natural feeling. Remember that in the real world, few things appear before your eyes instantly.&lt;/p&gt;&lt;h3&gt;RSVP buttons&lt;/h3&gt;&lt;p&gt;The buttons in this composition are a little fancier than your average button. I want them to transition from their neutral state to a representation of the user’s RSVP.&lt;/p&gt;&lt;p&gt;Here’s what we’re doing here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Changing the relative width of the two buttons and their text label&lt;/li&gt;&lt;li&gt;Changing the colour of the button to indicate state&lt;/li&gt;&lt;li&gt;Hiding the unnecessary label and showing an ‘x’ in its place&lt;/li&gt;&lt;li&gt;Showing the ‘add to calendar’ button (if attending)&lt;/li&gt;&lt;/ul&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/z5u04d9je12h/prototyping-ui-animation-states-and-animations?width=848&quot; alt=&quot;The 5 states and 4 animations for our buttons&quot; width=&quot;848&quot; height=&quot;172&quot; /&gt;&lt;figcaption&gt;The 5 states and 4 animations for our buttons&lt;/figcaption&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;The 5 states and 4 animations for our buttons&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;There are 5 states these buttons can be in, which gives us 4 sets of animations, like so:&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/r5n15pqn6taw/prototyping-ui-animation-states-animation-timeline?width=848&quot; alt=&quot;There are “stop” points between each transition that represent the 5 button states from the previous image.&quot; width=&quot;848&quot; height=&quot;266&quot; /&gt;&lt;figcaption&gt;There are “stop” points between each transition that represent the 5 button states from the previous image.&lt;/figcaption&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;There are “stop” points between each transition that represent the 5 button states from the previous image.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Next, we want to make sure the button labels animate with the button widths. The attending label will get wider, while the not attending label will be animated out of view. In its place we’ll fade in an &lt;strong&gt;‘X&lt;/strong&gt;’ to indicate that this area of the button will now cancel the previous action.&lt;/p&gt;&lt;p&gt;Rather than simply hiding the label and showing the ‘&lt;strong&gt;X&lt;/strong&gt;’, we’ll tween the transitions with easing.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assets.thinkmill.com.au/media/prototyping-ui-animation/state-animation-gif.mov&quot;&gt;Avoid hard cuts as much as possible. In the real world, nothing ever appears&lt;/a&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Avoid hard cuts as much as possible. In the real world, nothing ever appears&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Part 3. Adding interactions&lt;/h2&gt;&lt;p&gt;Triggers and Actions allow us to control the timeline based on predefined events and user input, which in turn adds interactivity to what would otherwise be a movie.&lt;/p&gt;&lt;p&gt;Depending on how interactive you want your app prototype to be, you can add as many or as few actions and triggers as you like.&lt;/p&gt;&lt;p&gt;I prefer to be conservative and add interactions only where necessary. The point of prototyping is to move rapidly and have the freedom to experiment broadly. Adding interactivity can quickly add unwanted overhead in the form of having to maintain an complex composition. Leave this to the development stage.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;My rule of thumb is if it’s obvious what action precedes the animation, it doesn’t need to be made interactive. Save your efforts for other things.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Let’s look at the RSVP button interactions in detail.&lt;/p&gt;&lt;p&gt;The RSVP button interaction is encapsulated in a symbol with an independent timeline. We have 5 keyframes representing all the states that these buttons can be in. At each point we have a &lt;em&gt;stop()&lt;/em&gt;; trigger.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/hnggunyx708u/prototyping-ui-animation-keyframe-timeline-stop-points?width=848&quot; alt=&quot;At each of the marked points above, the trigger is simply sym.stop();&quot; width=&quot;848&quot; height=&quot;165&quot; /&gt;&lt;figcaption&gt;At each of the marked points above, the trigger is simply sym.stop();&lt;/figcaption&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;At each of the marked points above, the trigger is simply sym.stop();&lt;/p&gt;&lt;/blockquote&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/ichv0yw367zg/prototyping-ui-animation-trigger-stop-animation?width=848&quot; alt=&quot;After 200ms, stop playing the timeline&quot; width=&quot;848&quot; height=&quot;283&quot; /&gt;&lt;figcaption&gt;After 200ms, stop playing the timeline&lt;/figcaption&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;“After 200ms, stop playing the timeline”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Why? Because we want the animation to be paused between state changes until the user taps on the button again.&lt;/p&gt;&lt;p&gt;To achieve this, we’ll bind a “play” action to the “on-click” event for the button. It looks like this.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/dpocqs3k5r0f/prototyping-ui-animation-animation-action?width=848&quot; alt=&quot;Animation action&quot; width=&quot;848&quot; height=&quot;339&quot; /&gt;&lt;figcaption&gt;Animation action&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The action above simply causes the playhead in the symbol’s timeline to play, until it reaches the next “stop” trigger.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Remember: we’re working within the timeline of the symbol and not the main timeline, so this action is relative to this symbol. These actions are not affecting the main stage timeline.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We now have an interactive element that plays through a scripted timeline in response to a user action.&lt;/p&gt;&lt;p&gt;Hit &lt;strong&gt;⌘+Return&lt;/strong&gt; to preview the comp and interactions in the browser. When you’re ready to see how it feels in your hand, you can deploy it as a native app relatively quickly and easily.&lt;/p&gt;&lt;h2&gt;Part 4. From Edge Animate to an App&lt;/h2&gt;&lt;p&gt;This is &lt;em&gt;&lt;strong&gt;the most valuable step&lt;/strong&gt;&lt;/em&gt; and one of the main reasons I like Edge Animate. It’s one of the easiest methods for quickly bringing an idea to life with interactions and animations that you can install as a native app.&lt;/p&gt;&lt;p&gt;Interactive prototypes are a great test of the hidden assumptions in a design. You’ll quickly discover poor UX and conceptual gaps, which is the main reason this kind of prototyping is so useful.&lt;/p&gt;&lt;p&gt;Prototyping consistently creates better interaction designers as well as better apps.&lt;/p&gt;&lt;p&gt;&lt;em&gt;You’ll need a PhoneGap account, and an Android and/or iOS developer account for the next part.&lt;/em&gt;&lt;/p&gt;&lt;h3&gt;Prepare your composition and assets&lt;/h3&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/xaz4acwnejb7/prototyping-ui-animation-publish-folder-structure?width=848&quot; alt=&quot;After you hit publish, you should have a folder that looks something like this:&quot; width=&quot;848&quot; height=&quot;537&quot; /&gt;&lt;figcaption&gt;After you hit publish, you should have a folder that looks something like this:&lt;/figcaption&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;After you hit publish, you should have a folder that looks something like this:&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;First, your composition must be saved as index.html. This is what PhoneGap will look for at runtime. When you’ve saved your comp, use the Publish option to compile all the assets you need in PhoneGap.&lt;/p&gt;&lt;p&gt;Go ahead and compress the folder now. The zip file is what you’ll upload to PhoneGap in a minute.&lt;/p&gt;&lt;h3&gt;Certificates &amp;amp; Keystores&lt;/h3&gt;&lt;p&gt;This is the only tricky part of this process. Because we’re effectively creating a “real” app, we need to have our papers in order.&lt;/p&gt;&lt;p&gt;If you’re deploying to &lt;strong&gt;iOS&lt;/strong&gt;, you’ll need a .p12 certificate and a provisioning profile. Check out the &lt;a href=&quot;http://docs.build.phonegap.com/en_US/3.1.0/signing_signing-ios.md.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PhoneGap docs&lt;/a&gt; for help with this step.&lt;/p&gt;&lt;p&gt;If you’re deploying to &lt;strong&gt;Android&lt;/strong&gt;, &lt;a href=&quot;http://www.lynda.com/Edge-Animate-tutorials/Creating-PhoneGap-Build-app-Edge-Animate/124307/127570-4.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Lynda.com has a handy video tutorial&lt;/a&gt;&lt;/p&gt;&lt;p&gt;To create the .keystore file that you’ll need, plug this into your terminal window &lt;em&gt;(replacing “app_name” with your app’s name)&lt;/em&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;keytool -genkey -v -keystore app_name.keystore -alias app_name -keyalg RSA -keysize 2048 -validity 10000&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You should end up with a &lt;em&gt;app_name.keystore&lt;/em&gt; file. Drag it into your folder above.&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://uipjbq4xwnkx.keystatic.net/jsifexqrc96n/images/gybido5y1403/prototyping-ui-animation-adobe-bd-screen?width=848&quot; alt=&quot;Adobe BD screen&quot; width=&quot;848&quot; height=&quot;467&quot; /&gt;&lt;figcaption&gt;Adobe BD screen&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;PhoneGap&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Create a new app in PhoneGap&lt;/strong&gt;. Install the .p12 certificate and then upload the .zip file you created above as the source. &lt;em&gt;(If you’re building an Android app,&lt;a href=&quot;http://www.lynda.com/Edge-Animate-tutorials/Creating-PhoneGap-Build-app-Edge-Animate/124307/127570-4.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;follow these steps in the Lynda video&lt;/a&gt;)&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Enable hydration&lt;/strong&gt;. This will check for a new build in PhoneGap every time you open the app on your device. A time saver when you deploy new builds. &lt;strong&gt;Upload an app icon&lt;/strong&gt;. There’s no excuse not to with &lt;a href=&quot;http://appicontemplate.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;icon templates aplenty&lt;/a&gt;. &lt;em&gt;Pro tip: don’t upload a rounded, transparent .png because you’ll get ugly edge ghosting in iOS. Upload a square icon and let iOS do the rounding for you.&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install the app to your device&lt;/strong&gt; by logging in to the PhoneGap website using your phone and simply hit the download button. If you’ve done it right, you should now see your Edge Animate comp installed as an app.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Rinse &amp;amp; Repeat&lt;/strong&gt;. As this will probably be the first time you get to touch your prototype, you’ll probably want to go back into Animate (or maybe even Photoshop) and start tweaking, iterating and redeploying. If you feel like you nailed it first go, you’re doing it wrong. I’ll typically go through this cycle ~20 times before I’m happy.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;To deploy a new build of the app:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Re-publish from Animate (don’t just hit ‘save’, use Publish)&lt;/li&gt;&lt;li&gt;Zip the folder as before&lt;/li&gt;&lt;li&gt;Upload new .zip to PhoneGap&lt;/li&gt;&lt;li&gt;Relaunch your app&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The hydra process should alert you to a new build and show you your latest version.&lt;/p&gt;&lt;h3&gt;To wrap up&lt;/h3&gt;&lt;p&gt;This is just one example of one process for prototyping animations and interactions for mobile apps. Edge Animate is one of the tools that suits my way of thinking and my skill set. (I’m learning JS so will play with &lt;a href=&quot;http://framerjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Framerjs&lt;/strong&gt;&lt;/a&gt; next). There’s no perfect tool for our discipline so open your mind and experiment until you find a tool (or two, or three) that suits you.&lt;/p&gt;&lt;p&gt;It’s a bloody exciting time to be an interaction designer!&lt;/p&gt;&lt;h2&gt;Recommended reading / watching&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://medium.com/design-ux/926eb80d64e3&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Transitional interfaces&lt;/a&gt; by &lt;a href=&quot;https://medium.com/@pasql&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pasquale D’Silva&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.youtube.com/watch?v=TMe0WnkF1Lc&amp;amp;feature=c4-overview&amp;amp;list=UURx1y52pfeMwbuer9Vh2u-A&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Designing with animation&lt;/a&gt; (video) by &lt;a href=&quot;https://medium.com/@pasql&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pasquale D’Silva&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/12_basic_principles_of_animation&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;12 basic principles of animation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://alistapart.com/article/ui-animation-and-ux-a-not-so-secret-friendship&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;UI Animation and UX: A Not-So-Secret Friendship&lt;/a&gt; by &lt;a href=&quot;http://alistapart.com/author/valhead&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Val Head&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://gibbon.co/mariusz/designing-ui-animation&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Designing UI Animation&lt;/a&gt; By &lt;a href=&quot;https://gibbon.co/mariusz&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mariusz Ciesla&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/article&gt;</content:encoded></item></channel></rss>