<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Evgeny Khoroshilov</title>
    <description>The latest articles on DEV Community by Evgeny Khoroshilov (@genedesign).</description>
    <link>https://dev.to/genedesign</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1094193%2F39673baa-7aef-41b1-8e65-2cb34082487b.png</url>
      <title>DEV Community: Evgeny Khoroshilov</title>
      <link>https://dev.to/genedesign</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/genedesign"/>
    <language>en</language>
    <item>
      <title>Beyond procrastination: the cognitive reasons behind unfinished projects</title>
      <dc:creator>Evgeny Khoroshilov</dc:creator>
      <pubDate>Mon, 23 Dec 2024 13:41:00 +0000</pubDate>
      <link>https://dev.to/genedesign/beyond-procrastination-the-cognitive-reasons-behind-unfinished-projects-oa7</link>
      <guid>https://dev.to/genedesign/beyond-procrastination-the-cognitive-reasons-behind-unfinished-projects-oa7</guid>
      <description>&lt;p&gt;From impostor syndrome to cognitive overload — mastering the invisible forces that prevent project success&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Remember this sensation: in your mind, a new brilliant project idea sparks, you're burning with excitement and 200% engaged into work, until… another project/framework/technology beckons. And I totally get it - it's always greener on the other side. Notebooks are filled with half-finished concepts, Github repos multiply with ambitious beginnings that won't ever reach their final stage. What starts as passionate creativity often transforms into a stockpile of unfinished potential, an accumulation of partially constructed projects. Sounds familiar, doesn't it? Well, it's not just you. In fact, it's quite common.&lt;/p&gt;

&lt;p&gt;Our relationship with incomplete work is &lt;strong&gt;far more nuanced than simple procrastination&lt;/strong&gt;. It's a complex relationship of creativity, neurological reward systems, and the profound psychology of potential. When we understand how it works, we can transform seemingly scattered efforts into a powerful attribute of self-improvement.&lt;/p&gt;

&lt;p&gt;Look at this from a different angle: those aren't graveyards of failed projects, but the living laboratories. Each unfinished activity represents a moment of intellectual curiosity frozen in time. Curious? Great! Let's dive into the complex psychological and neurological mechanisms driving technological exploration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short track:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Jump right to this chapter - “Strategies for Meaningful Project Development“.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Neurological Roots of Project Incompletion
&lt;/h2&gt;

&lt;p&gt;To try and reveal the roots of the new-projects-problem, we'll explore some ideas, backed up by psychological and neuroscientific works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dopamine-Driven Novelty Seeking: The Neural Reward Circuitry
&lt;/h3&gt;

&lt;p&gt;Our brain is evolutionarily wired to seek novelty as a survival mechanism. Dr. Andrew Huberman &lt;a href="https://hubermanlab.com/neural-dopamine-circuits-for-motivation" rel="noopener noreferrer"&gt;explains&lt;/a&gt; in his neuroscience study that this novelty-seeking behavior stems from our brain's dopamine reward system, which creates a powerful neurochemical response to new experiences.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Dopamine" rel="noopener noreferrer"&gt;Dopamine&lt;/a&gt; is a neurotransmitter. It is involved in many brain functions and well-known for its role in movement, motivation, and addiction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each &lt;strong&gt;new technological discovery triggers a dopamine release&lt;/strong&gt;, creating a neurochemical reward that far exceeds the more mundane satisfaction of completing existing work. Our neural circuits are designed to anticipate and seek out potential rewards, &lt;strong&gt;constantly pushing us towards new and exciting possibilities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ji45x4yoqr4qqu9dw3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ji45x4yoqr4qqu9dw3p.png" alt="Dopamine reward pathway in a simple form. Source - knowingneurons.com" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Naturally chasing neural circuits satisfaction, we tend to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;alternate projects/technologies/frameworks/etc, driven by the brain's reward prediction mechanism&lt;/li&gt;
&lt;li&gt;pursue bleeding-edge and freshly emerging libraries and languages&lt;/li&gt;
&lt;li&gt;start greenfield projects instead of maintaining existing repos&lt;/li&gt;
&lt;li&gt;immediately hop on to the new technique after a workshop/conference/article&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This naturally applies &lt;strong&gt;beyond the hobby or side projects&lt;/strong&gt;. Remember when half-way through refactoring of some legacy codebase you discover a new tech that &lt;em&gt;supposedly&lt;/em&gt; speeds up your work and &lt;em&gt;potentially&lt;/em&gt; grants your project 50% performance kick?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxee8w69xw55h672mzxig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxee8w69xw55h672mzxig.png" alt="Dr. David Eagleman and his bestseller book " width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In his book, &lt;a href="https://www.goodreads.com/book/show/9827912-incognito" rel="noopener noreferrer"&gt;"Incognito: The Secret Lives of the Brain"&lt;/a&gt;, Dr. David Eagleman describes a neural plasticity effect of &lt;strong&gt;activation of our brain's novelty circuits&lt;/strong&gt;. From developer's perspective the new technology seems then more exciting and potentially more valuable than the current mundane work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complexity Overwhelm and Cognitive Load
&lt;/h3&gt;

&lt;p&gt;While novelty seeking tends to propagate positive experiences, their amount can become a challenge. Especially when work is paralleled and/or task-switching engaged. Our brain has limited working memory capacity, a concept extensively explored quite a while ago by John Sweller in his &lt;a href="https://www.tandfonline.com/doi/abs/10.1207/s15326985ep2902_1" rel="noopener noreferrer"&gt;Cognitive Load Theory&lt;/a&gt;. Relatively recent famous work by Dr. Daniel Kahneman - &lt;a href="https://www.penguinrandomhouse.com/books/198148/thinking-fast-and-slow-by-daniel-kahneman/" rel="noopener noreferrer"&gt;"Thinking, Fast and Slow"&lt;/a&gt; - further elaborates on how our cognitive resources become depleted when faced with complex problem-solving tasks.&lt;/p&gt;

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

&lt;p&gt;For developers, this can be simply demonstrated as a &lt;strong&gt;progressive mental exhaustion&lt;/strong&gt;. The brain's working memory acts like a small workspace that becomes quickly overwhelmed by intricate technical challenges. As project complexity increases, cognitive load creates a &lt;strong&gt;psychological defense mechanism of avoidance and task-switching&lt;/strong&gt;. And, unfortunately, we've often been there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;initial excitement is replaced with cognitive exhaustion&lt;/li&gt;
&lt;li&gt;some complex architectural decisions become paralyzing&lt;/li&gt;
&lt;li&gt;technical debt creates increasing mental friction&lt;/li&gt;
&lt;li&gt;project scope tends to become overestimated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The prefrontal cortex, responsible for executive function, becomes progressively taxed. Stress and complexity directly impact our brain's ability to maintain focus and execute complex tasks. While some of these statements look obvious, there are probably even &lt;em&gt;more deeply rooted brain mechanisms&lt;/em&gt; worth exploring. We'll touch them in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fear-Based Project Avoidance and Psychological Barriers
&lt;/h3&gt;

&lt;p&gt;Fear is a primal emotion that shapes our most intimate professional decisions. Applied to our realm, fear transforms from a basic survival mechanism into a psychological barrier that &lt;em&gt;can paralyze even the most talented developers&lt;/em&gt;. Here are just a couple of the most common patterns that we can experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Perfectionism Paralysis&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Dr. Pauline Rose Clance's &lt;a href="https://www.paulineroseclance.com/impostor_phenomenon.html" rel="noopener noreferrer"&gt;groundbreaking research on the Impostor Phenomenon&lt;/a&gt; provides insight into this challenge. The fear of imperfect implementation stems from a deep-seated psychological mechanism where developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;doubt their technical capabilities&lt;/li&gt;
&lt;li&gt;engage in constant reevaluation or refactoring without meaningful progress&lt;/li&gt;
&lt;li&gt;compare potential output to an idealized mental model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Impostor Syndrome&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Going further with the impostor syndrome, Mike Cannon-Brookes, in his &lt;a href="https://www.youtube.com/watch?v=zNBmHXS3A6I&amp;amp;ab_channel=TEDxTalks" rel="noopener noreferrer"&gt;influential TED Talk&lt;/a&gt;, describes it as a &lt;strong&gt;universal experience among high-achieving professionals&lt;/strong&gt;. Often met between writers, artists, various creators, and of course developers this results in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;persistent doubt about technical abilities&lt;/li&gt;
&lt;li&gt;fear of external judgment and following blockers&lt;/li&gt;
&lt;li&gt;preventing project completion to avoid potential criticism&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx99gywujmtv0e4bltznt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx99gywujmtv0e4bltznt.png" alt="Mike Cannon-Brookes on the Ted Talk " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technology Obsolescence Anxiety&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Dr. Cal Newport's &lt;a href="https://calnewport.com/deep-work-rules-for-focused-success-in-a-distracted-world/" rel="noopener noreferrer"&gt;work on deep work and technological anxiety&lt;/a&gt; suggests that this fear is rooted in our rapidly changing technological landscape. Cobol, Perl and to the &lt;a href="https://endoflife.date/react" rel="noopener noreferrer"&gt;very recent time React.js&lt;/a&gt; developers are probably on the safe side in this regard. Nevertheless, jokes (?) aside, some of you can remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;constant worry about implementation becoming outdated&lt;/li&gt;
&lt;li&gt;perpetual desire to incorporate latest best practices&lt;/li&gt;
&lt;li&gt;endless loop of "one more optimization"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Incompleteness Addiction
&lt;/h3&gt;

&lt;p&gt;The quagmire of perpetual technological improvements goes arm-in-arm with another concept, hinted on previously. Unfinished projects represent &lt;strong&gt;infinite potential&lt;/strong&gt;. Completion on the other hand means &lt;strong&gt;confronting actual versus imagined capabilities&lt;/strong&gt;, which can be psychologically threatening.&lt;/p&gt;

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

&lt;p&gt;James Clear touches that aspect of human behaviour in the popular book &lt;a href="https://jamesclear.com/atomic-habits" rel="noopener noreferrer"&gt;"Atomic Habits"&lt;/a&gt;. He explores how our brains are wired to find comfort in potential rather than finality. This mechanism protects the developer's self-image by maintaining the project in a state of &lt;strong&gt;perpetual promise&lt;/strong&gt;, avoiding the potential disappointment of real-world constraints and limitations.&lt;/p&gt;




&lt;p&gt;If you find yourself in the place of endless proof-of-concept development or... exploring theories over practical implementations or... initializing multiple project versions without finalization you definitely know what's this is about. These neurological and psychological mechanisms &lt;strong&gt;aren't weaknesses but sophisticated cognitive strategies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And you know what else? You are not alone struggling with fear of accomplishment, collecting numerous incomplete projects or hopping styles, genres and technologies. You are, in fact, in a &lt;em&gt;legendary company&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Great Minds and Unfinished Symphonies
&lt;/h2&gt;

&lt;p&gt;Consider &lt;a href="https://en.wikipedia.org/wiki/Leonardo_da_Vinci" rel="noopener noreferrer"&gt;Leonardo da Vinci&lt;/a&gt;, whose notebooks were living documents of &lt;strong&gt;boundless intellectual exploration&lt;/strong&gt;. He is known primarily as painter; but as a draughtsman, engineer, scientist, theorist, sculptor, and architect he recorded 13000 pages of studies. Needless to say, majority of those works was unfinished. His approach reveals a critical insight: transformative developers are not those who persistently complete every project, but those who remain &lt;strong&gt;curious and adaptable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff935jr2ffhswvmjwrqjq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff935jr2ffhswvmjwrqjq.jpg" alt="Portrait of Leonardo Da Vinci" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Franz_Kafka" rel="noopener noreferrer"&gt;&lt;strong&gt;Franz Kafka&lt;/strong&gt;&lt;/a&gt; was the writer and an &lt;em&gt;existential experimenter&lt;/em&gt;. Kafka didn't write stories - he conducted psychological experiments. His fragmented manuscripts weren't incomplete — they were deliberate explorations of human uncertainty. The "&lt;em&gt;Dante&lt;/em&gt;" and one of the greatest writers of the 20th century finished none of his full-length novels and &lt;em&gt;burned around 90 percent of his work&lt;/em&gt;, leaving his dear friend Max Brod thousands of drafts after his death...&lt;/p&gt;

&lt;p&gt;If you aren't familiar with Sir &lt;a href="https://en.wikipedia.org/wiki/Alfred_Hitchcock" rel="noopener noreferrer"&gt;&lt;strong&gt;Alfred Hitchcock&lt;/strong&gt;&lt;/a&gt; - the film director and &lt;em&gt;the technological innovator&lt;/em&gt;, you would certainly recognize his movie by the iconic scene.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xfhibve88a5ge0xiuiv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xfhibve88a5ge0xiuiv.png" alt="Shower killer scene from the movie Psycho (1960) by Alfred Hitchcock" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Hitchcock, each film was an &lt;em&gt;experimental ground&lt;/em&gt;. He didn't just create movies; he continuously reimagined the language and shape of modern cinema as we know it. And this next quote is particularly curious, I'm sure it resonates in some of you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Once the screenplay is finished, I'd just as soon not make the film at all. All the fun is over. &amp;lt;...&amp;gt; When you finish the script, the film is perfect. But in shooting it you lose perhaps 40 per cent of your original conception."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For &lt;a href="https://en.wikipedia.org/wiki/Frida_Kahlo" rel="noopener noreferrer"&gt;&lt;strong&gt;Frida Kahlo&lt;/strong&gt;&lt;/a&gt;, &lt;em&gt;the personal technologist&lt;/em&gt;, art was a constant negotiation between personal narrative and technical exploration. She is famous for autobiographical paintings, where each painting was a &lt;em&gt;technological and emotional experiment&lt;/em&gt;. Kahlo's work has been celebrated internationally as emblematic of Mexican national and Indigenous traditions and by feminists for what is seen as its uncompromising depiction of the female experience and form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffy2a97s1etcu6btbkdwf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffy2a97s1etcu6btbkdwf.png" alt="Some self-portraits of Frida Kahlo in a chronological order" width="800" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In every creative domain, in every country there are famous figures, known for their works and, incidentally, working methodology. We only met a fracture of a percent of them in this article and I invite you to study more for the unbelievable insights.&lt;/p&gt;

&lt;p&gt;While dopamine-driven novelty seeking and cognitive challenges are not obstacles, but intricate neural mechanisms that have driven human innovation, what if they &lt;strong&gt;really pose a problem&lt;/strong&gt; of self-rumination cycles and &lt;strong&gt;blocking you&lt;/strong&gt; from progression to the next level? We'll now explore several critical strategies that can help you to channel your creative energy into meaningful outcomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for Meaningful Project Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Progressive Rendering
&lt;/h3&gt;

&lt;p&gt;Inspired by artistic techniques and incorporated in &lt;a href="https://web.dev/learn/images/jpeg/#progressive_jpeg" rel="noopener noreferrer"&gt;JPEG technology&lt;/a&gt;, progressive rendering is a methodological approach to project development that prioritizes &lt;strong&gt;incremental progress over perfection from the get go&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2rrtjl6orcuphsl89va.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2rrtjl6orcuphsl89va.png" alt="Draw the circles, then draw the rest of the owl meme compared with the artistic step-by-step sketch of owl drawing" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider the two practical phases - sketch and layering.&lt;br&gt;&lt;br&gt;
In the &lt;strong&gt;sketch phase&lt;/strong&gt; you create the most viable version of the project, focusing on the core functionality and basic structure. You simply don't need anything else beyond fundamental architecture and primary usability. If it's a UI library, forget rounding and line height alignment - focus on API, build process and delivery. If it's a landing page, don't mind the parallax effect and concentrate your efforts on the scalable accessible mobile-first layout.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;layering phase&lt;/strong&gt; you &lt;em&gt;progressively&lt;/em&gt; (layer by layer, iteration by iteration) add complexity and refinement, implementing features in order of priority and impact. At the end of each iteration you need to &lt;em&gt;validate it against the final project vision&lt;/em&gt; to understand the scope of the next layer. &lt;strong&gt;This stage is crucial&lt;/strong&gt;, as taking a larger scope into the next iteration (adding several features or underestimated chunk of work) can result in unexpected blockers following by scattered effort, losing focus, momentum, flow and satisfaction from the previously completed steps.&lt;/p&gt;

&lt;p&gt;The benefits of this technique are tremendous:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it reduces cognitive overload&lt;/li&gt;
&lt;li&gt;it provides early validation of the core concept&lt;/li&gt;
&lt;li&gt;it creates tangible progress that motivates toward further development&lt;/li&gt;
&lt;li&gt;it allows for flexible adaptation as project understanding hardens &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I foresee the silent question - "did you just reinvent &lt;a href="https://en.wikipedia.org/wiki/Scrum_(software_development)" rel="noopener noreferrer"&gt;Scrum&lt;/a&gt;?! interrobang, interrobang".&lt;br&gt;&lt;br&gt;
Well, fair enough, it's one of the key aspects. However, in practice working by Scrum doesn't always mean actually &lt;em&gt;understanding&lt;/em&gt; it. Let alone personal projects management when you are working alone constantly changing hats of angel ventures, stakeholders, product owners, scrum masters AND developers. But if you master or at least exercise that, your personal workflow improvements will be &lt;strong&gt;mirrored and rewarded as you grow professionally&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intentional Pruning
&lt;/h3&gt;

&lt;p&gt;Continuing with the efficient practices for project management, intentional pruning is a disciplined approach that involves &lt;strong&gt;deliberately limiting project scope&lt;/strong&gt; to ensure meaningful completion. In simple terms...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cut down everything enticing and shiny in favor of functional and achievable!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to apply this practice, stick to the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;evaluate features based on the core objectives&lt;/li&gt;
&lt;li&gt;prioritize functionalities with the highest user value&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ruthlessly eliminate nice-to-have-s&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latter is probably the hardest, at least from a personal experience…&lt;br&gt;&lt;br&gt;
However, once you push through this once or twice (practice makes perfect!), you'll see immense benefit. Not only you will be able to finish current projects, but your planning and architecture skills will be honed again and again, creating a great working habit. Eventually, feature elimination will be recognized as strategic optimization, not failure.&lt;/p&gt;

&lt;p&gt;Sometimes you need to let go to see the wood for the trees.&lt;/p&gt;

&lt;h3&gt;
  
  
  Graceful Project Closure
&lt;/h3&gt;

&lt;p&gt;Like not every story ends happily ever after, &lt;strong&gt;not every project is meant to reach full completion&lt;/strong&gt;, and acknowledging this is a mark of personal and professional maturity. This is essentially letting go on a BIGGER scale.&lt;/p&gt;

&lt;p&gt;To employ this practice you should most probably find yourself in a place of supporting multiple projects in parallel and finding hard time to either complete them or switch to the new ones.&lt;/p&gt;

&lt;p&gt;Tempered by the feature-culling experience you should be prepared to take the next steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;be honest with yourself, and conduct an objective project assessment&lt;/li&gt;
&lt;li&gt;identify and extract the learning opportunities from unfinished work&lt;/li&gt;
&lt;li&gt;document findings and salvage resources for the future applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to drop the guilt and transform disappointment intro strategic insight, you will need to separate personal worth from project outcomes and view unfinished work as learning experience. Each &lt;strong&gt;proper project closure&lt;/strong&gt; will be identified as another instrument in a toolbox of a professional, who values exploration and iteration.&lt;/p&gt;

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

&lt;p&gt;Having multiple unfinished projects is way more nuanced than simple procrastination. It often seems as a professional shortcoming and understandably is self-joked upon. But as we've explored together this is both personal challenge and a fundamental aspect of human creative cognition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faiwu0txptd2g9loqocl9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faiwu0txptd2g9loqocl9.jpg" alt="Memes from ProgrammerHumor subreddit about unfinished projects" width="800" height="791"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This sometimes poses a real issue for developers and creative minds, leading to professional stagnation, reduced productivity and personal frustration. However, by understanding the underlying psychological and neurological mechanisms, we can transform this pattern from a potential limitation into a strategic instrument of self development.&lt;/p&gt;

&lt;p&gt;Engage &lt;strong&gt;careful analysis, self-awareness and several targeted techniques&lt;/strong&gt;, and it will help you to channel your creative impulses more effectively, balancing with meaningful execution. The goal is not about a perfect completion, but meaningful progress. Stay curious and systematic and you will succeed!&lt;/p&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;&lt;br&gt;
If you find article useful please support by share and like. This significantly helps with the new articles.&lt;/p&gt;

&lt;p&gt;Stay tuned for more!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>productivity</category>
      <category>learning</category>
      <category>career</category>
    </item>
    <item>
      <title>Behind Fuji X Studio: Lessons in Web development, AI, Design and Accessibility</title>
      <dc:creator>Evgeny Khoroshilov</dc:creator>
      <pubDate>Mon, 25 Nov 2024 13:42:00 +0000</pubDate>
      <link>https://dev.to/genedesign/behind-fuji-x-studio-lessons-in-web-development-ai-design-and-accessibility-1e9i</link>
      <guid>https://dev.to/genedesign/behind-fuji-x-studio-lessons-in-web-development-ai-design-and-accessibility-1e9i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclaimer 1: This article was supposed to be a book, but ChatGPT quota is against it. If you didn’t get the joke, it’s probably the API latency.&lt;/p&gt;

&lt;p&gt;Disclaimer 2: Johan Guterman has fully reviewed, edited and approved this piece. Any copyright breach claims are preposterous, but will be tolerated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our application development journey started with a bit of a lazy for Johan and my growing fascination for automation. We both started using &lt;a href="https://fujifilm-x.com/en-us/" rel="noopener noreferrer"&gt;Fuji X cameras&lt;/a&gt; around the same time and very shortly after were fully charmed by the magic of &lt;a href="https://fujifilm-x.com/en-us/exposure-center/get-to-grips-with-film-simulation-modes/" rel="noopener noreferrer"&gt;JPEG film simulation recipes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5goilzkq90k6ep6f6muw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5goilzkq90k6ep6f6muw.png" alt="Fuji X Color Film Simulation Modes - some of our favourites" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Film Simulation modes are part of what make X Series cameras so special” &lt;a href="https://fujifilm-x.com/en-us/exposure-center/get-to-grips-with-film-simulation-modes/" rel="noopener noreferrer"&gt;fujifilm-x.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over time, our enthusiasm grew, and we found ourselves wanting something more tailored. Say, I watch some series or a movie and I enjoy the picture so much that I would like to take my photos with that &lt;em&gt;same color effect&lt;/em&gt; that I see on the screen. More importantly, I want to get the result &lt;strong&gt;before post-production&lt;/strong&gt; in the dedicated software.&lt;/p&gt;

&lt;p&gt;Replicating the cinematic quality of film or digital post-processing in a JPEG recipe is an ambitious challenge, often requiring extensive effort. It requires certain experience, and for beginners it’s a lot of work even in the specialized apps like &lt;a href="https://www.captureone.com/en" rel="noopener noreferrer"&gt;Capture One&lt;/a&gt; or &lt;a href="https://lightroom.adobe.com/" rel="noopener noreferrer"&gt;Lightroom&lt;/a&gt; (although it gets easier each year with the &lt;a href="https://support.captureone.com/hc/en-us/articles/22188770298269-Match-Look-Tool" rel="noopener noreferrer"&gt;new or improved tooling&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Despite the challenge, we can (and will) give it a try.&lt;br&gt;&lt;br&gt;
Sure, the results won’t be exactly the same, but:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;we can get a unique JPEG recipe named after the series or a movie&lt;/li&gt;
&lt;li&gt;it is time we put up AI to the challenge&lt;/li&gt;
&lt;li&gt;we can always tune the JPEG-s in the post-processing with a preset&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Don’t Blink
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“It’s quite simple, really” — Johan explains — “I get to the website, upload some pictures, set my camera features, wait couple of seconds and BAM — get a JPEG recipe similar to &lt;a href="https://fujixweekly.com/" rel="noopener noreferrer"&gt;Fuji X Weekly&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💭 Hm, so we need a form, collection of cameras parameters and some basic communication with AI.&lt;/p&gt;

&lt;p&gt;“That it?” — I ask.&lt;br&gt;&lt;br&gt;
“Well, It’d be also great to share the link with other photo enthusiasts! So when they get to the page…”&lt;br&gt;&lt;br&gt;
“Yup, let’s pause here for a second!”&lt;/p&gt;

&lt;p&gt;💭 We’ll also need a database to store and fetch recipe params and an image storage, apparently.&lt;/p&gt;

&lt;p&gt;“Okay, certainly doable. You were saying?”&lt;br&gt;&lt;br&gt;
“Right, so other people could remix existing recipes!”&lt;br&gt;&lt;br&gt;
“Remix? How does that work?”&lt;br&gt;&lt;br&gt;
“You just click a button…”&lt;br&gt;&lt;br&gt;
“…a-ha and it just works, sure”&lt;/p&gt;

&lt;p&gt;💭 Raising AI model temperature and tweaking some other params with the same input data should work.&lt;/p&gt;

&lt;p&gt;“Aa-and a nice landing page, of course!”&lt;br&gt;&lt;br&gt;
“Naturally”&lt;br&gt;&lt;br&gt;
“In the Fuji-retro-style, with grids and splashy gradients…”&lt;br&gt;&lt;br&gt;
“We’ll see”&lt;br&gt;&lt;br&gt;
“Oh, and I can write testimonials!”&lt;br&gt;&lt;br&gt;
“Yep… wait, is this all about promoting your Instagram?!”&lt;br&gt;&lt;br&gt;
“&lt;strong&gt;Alter… komm schon&lt;/strong&gt;”&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Implementation — Snap-Snap
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Hey there,ChatGPT
&lt;/h3&gt;

&lt;p&gt;To kick off, let’s prototype using a tool we already have at our disposal: &lt;a href="https://chatgpt.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The very first attempt was to find out the feasibility of the whole affair. To my genuine pleasure, it worked just fine! ChatGPT understood the requirements, correctly processed the images and output a decent starting point for the JPEG recipe… after several back-and-forth-s.&lt;/p&gt;

&lt;p&gt;Let’s visualize this:&lt;/p&gt;

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

&lt;p&gt;If we dig a bit deeper and extrapolate this to the application UX the process is not exactly that smooth. In our app we’ll be making a &lt;strong&gt;single request&lt;/strong&gt; and getting a &lt;strong&gt;single response&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What we’re looking forward to is something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fka9qkq68byocm2cyfz9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fka9qkq68byocm2cyfz9n.png" alt="Simplified communication between App and AI model" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get the better idea, just compare a casual conversation (&lt;em&gt;fast response, short messages, information builds on top of the previous data&lt;/em&gt;) to an old-school physical letter (&lt;em&gt;slow delivery, long sentences, all details covered to a single detail in one message&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Let’s get to it!&lt;/p&gt;
&lt;h3&gt;
  
  
  Hello, Open AI API
&lt;/h3&gt;

&lt;p&gt;We reached the important point of designing the request.&lt;br&gt;&lt;br&gt;
Since this is a huge topic on its own, I would only mention a few crucial details.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We evolved from the &lt;a href="http://chatgpt.com" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; dialogue to using &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;Open AI API&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;API request for the needs of Fuji X Studio is composed of &lt;strong&gt;text&lt;/strong&gt; and &lt;strong&gt;image data&lt;/strong&gt;. Images are passed in the base64 format, which is rather standard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Text,&lt;/strong&gt; however is a combination of several information bits. Remember, it’s a snail-mail-physical letter that should not fail. And when it does, failure should be predictable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Text (aka &lt;em&gt;instructions&lt;/em&gt;) consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;introduction and the point of request&lt;/li&gt;
&lt;li&gt;camera details from the form data, composed in a certain way&lt;/li&gt;
&lt;li&gt;JPEG recipe templates for &lt;a href="https://en.wikipedia.org/wiki/Fujifilm_X-Trans_sensor" rel="noopener noreferrer"&gt;different generations of camera sensors&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;templates for the output for every parameter&lt;/li&gt;
&lt;li&gt;response template&lt;/li&gt;
&lt;li&gt;error response templates&lt;/li&gt;
&lt;li&gt;instructions for images edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s not an exaggeration to note that the AI instructions is &lt;strong&gt;the most edited file in the project&lt;/strong&gt;. Fine-tuning of the text part for request took somewhat the same time as for creating the whole UI for the app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The full instruction takes around &lt;strong&gt;200 lines and about 7000 characters&lt;/strong&gt;, resulting in roughly &lt;strong&gt;1800&lt;/strong&gt; tokens for every request. Adding images tokens (I admit, we should’ve started with images) to that number will give us around &lt;strong&gt;3000–4000&lt;/strong&gt; tokens per request on average. Output tokens volume is somewhat negligible, we limit it to around &lt;strong&gt;250&lt;/strong&gt; per recipe.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Is that much?” — Johan chimes in to check the results.&lt;br&gt;&lt;br&gt;
“Well, it depends. For personal occasional usage it’s pretty much affordable, but for our free app we don’t want to get a hefty tokens bill out of a sudden”&lt;br&gt;&lt;br&gt;
“How hefty?”&lt;br&gt;&lt;br&gt;
“Any unpredictable credit is a risk, especially for a public app”&lt;br&gt;&lt;br&gt;
“We can add a «buy me a coffee» button…”&lt;br&gt;&lt;br&gt;
“Or we can try recently released &lt;a href="https://gemini.google.com/app" rel="noopener noreferrer"&gt;Gemini AI&lt;/a&gt;, it does have vision capabilities and it’s got a free tier. The only catch that it’s not available in EU yet”&lt;br&gt;&lt;br&gt;
“So, about that coffee button…”&lt;br&gt;&lt;br&gt;
“Although, access from EU is not an issue with the &lt;a href="https://vercel.com/docs/functions/configuring-functions/region" rel="noopener noreferrer"&gt;cloud functions&lt;/a&gt;. But we’ll need another round of testing.”&lt;br&gt;&lt;br&gt;
“I guess I’ll go take some photos then. And a coffee maybe.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Pivot #1
&lt;/h3&gt;

&lt;p&gt;Looking for alternative to Open AI’s token costs and release of Gemini AI aligned perfectly and resulted in the first turning point in development. In retrospect we can compare not only the &lt;strong&gt;price&lt;/strong&gt; but also the output &lt;strong&gt;quality&lt;/strong&gt;, which may be especially handy for other photo/development enthusiasts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So how and what are we actually comparing?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Same conditions as before, text request is around &lt;strong&gt;1800&lt;/strong&gt; tokens plus &lt;strong&gt;two&lt;/strong&gt; &lt;strong&gt;high-res images&lt;/strong&gt; on average interaction. However for different operators images cost varies.&lt;/p&gt;
&lt;h4&gt;
  
  
  🤖 Open AI
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;model: &lt;strong&gt;&lt;em&gt;gpt-4-vision-preview&lt;/em&gt;&lt;/strong&gt; (currently several cheaper models are available, i.e. &lt;strong&gt;&lt;em&gt;gpt-4o&lt;/em&gt;&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://platform.openai.com/docs/guides/vision#calculating-costs" rel="noopener noreferrer"&gt;image tokens calculation&lt;/a&gt;: “A 2048 x 4096 image in &lt;em&gt;detail: high&lt;/em&gt; mode costs &lt;strong&gt;1105&lt;/strong&gt; tokens”&lt;/li&gt;
&lt;li&gt;tokens per user session: (1800 + 2 images x 1100) x 3 retries = 12000 tokens&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openai.com/api/pricing/" rel="noopener noreferrer"&gt;tokens cost&lt;/a&gt;: $10/1M tokens ($2.5/1M for modern models)&lt;/li&gt;
&lt;li&gt;user session cost: $0.12 💰 ($0.03 for modern models)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  🤖 Gemini AI
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;model: &lt;strong&gt;&lt;em&gt;gemini-pro-vision&lt;/em&gt;&lt;/strong&gt;, &lt;a href="https://ai.google.dev/gemini-api/docs/changelog#06-12-204" rel="noopener noreferrer"&gt;later&lt;/a&gt; &lt;strong&gt;&lt;em&gt;gemini-1.5-flash&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/tokens?lang=node#images" rel="noopener noreferrer"&gt;image tokens calculation&lt;/a&gt;: “During processing, the Gemini API considers images to be a fixed size, so they consume a fixed number of tokens (currently 258 tokens), regardless of their display or file size.”&lt;/li&gt;
&lt;li&gt;tokens per user session: (1800 + 2 images x 258) x 3 retries = 6900&lt;/li&gt;
&lt;li&gt;free tokens — 1M tokens per minute and 1500 requests per day&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ai.google.dev/pricing#1_5flash" rel="noopener noreferrer"&gt;tokens cost&lt;/a&gt;: $0.075/1M tokens&lt;/li&gt;
&lt;li&gt;user session cost: free, then $0.0005 💰&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  And what about the output quality?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open AI&lt;/strong&gt; (&lt;strong&gt;&lt;em&gt;gpt-4-vision-preview&lt;/em&gt;&lt;/strong&gt;) seems to be having more proficiency in vision capabilities and in the photo specific area and produces more intricate and detailed output.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gemini AI&lt;/strong&gt; (&lt;strong&gt;&lt;em&gt;gemini-1.5-flash&lt;/em&gt;&lt;/strong&gt;) is more on the conservative side. It takes more effort, instructions and templates with examples to prompt out the desired effect. But it’s almost 60x times (yes, 6000%) cheaper!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s also worth mentioning that upgrading from &lt;strong&gt;gemini-pro-vision&lt;/strong&gt; to &lt;strong&gt;gemini-1.5-flash&lt;/strong&gt; resulted in around &lt;strong&gt;50% cut on response latency&lt;/strong&gt;. Great improvement!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, you see, it’s tricky. On one hand, you pay for better output result, spending fewer tokens. On the other hand, spending twice as much tokens and prompt development time for instructions, but it will be nearly free (depending on the volume of course). For personal usage I would still use Open AI for the same or comparable task.&lt;/p&gt;
&lt;h4&gt;
  
  
  Finally, let’s briefly compare the JS clients and developer experience
&lt;/h4&gt;

&lt;p&gt;The following snippets contain simplified excerpts of actual app code. Note that &lt;strong&gt;&lt;em&gt;imageData&lt;/em&gt;&lt;/strong&gt; is identical for both APIs to accommodate a convenient switch from one to another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ImageDataPart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ImageData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ImageDataPart&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;base64 data&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;image type&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Open AI&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
To work with the API you need an &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;API ke&lt;/a&gt;&lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;y&lt;/a&gt; and have &lt;a href="https://www.npmjs.com/package/openai" rel="noopener noreferrer"&gt;opena&lt;/a&gt;&lt;a href="https://www.npmjs.com/package/openai" rel="noopener noreferrer"&gt;i&lt;/a&gt; installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatCompletionContentPartImage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai/resources/index.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageMessages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChatCompletionContentPartImage&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image_url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4-vision-preview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Generate a JPEG film simulation recipe ...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;imageMessages&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;frequency_penalty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;presence_penalty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipe cannot be generated, service error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gemini AI&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Similarly, get the &lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;API ke&lt;/a&gt;&lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;y&lt;/a&gt; and install &lt;a href="http://twitter.com/google/generative-ai" rel="noopener noreferrer"&gt;@google/generative-a&lt;/a&gt;&lt;a href="http://twitter.com/google/generative-ai" rel="noopener noreferrer"&gt;i&lt;/a&gt; module to start working.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;GoogleGenerativeAI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;InlineDataPart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@google/generative-ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;genAi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenerativeAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;genAi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getGenerativeModel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-1.5-flash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageMessages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InlineDataPart&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// only the base64-encoded image data&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// it is recommended by Gemini docs to place image data &lt;/span&gt;
          &lt;span class="c1"&gt;// before text when it's one image&lt;/span&gt;
          &lt;span class="c1"&gt;// make sure to adjust the code accordingly for you needs&lt;/span&gt;
          &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;imageMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Generate a JPEG film simulation recipe ...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;topK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;topP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxOutputTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// correct, output is super humble&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipe cannot be generated, service error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strictly speaking, &lt;strong&gt;&lt;em&gt;there are no significant differences in the clients&lt;/em&gt;&lt;/strong&gt;. All nuances can be figured out in the docs or/and Github issues. Subjectively, &lt;strong&gt;Open AI&lt;/strong&gt; &lt;a href="https://platform.openai.com/docs/guides/moderation" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; seems to be more comprehensive and practically better organized. On the other hand, &lt;strong&gt;Gemini AI&lt;/strong&gt; &lt;a href="https://ai.google.dev/gemini-api/docs/safety-settings" rel="noopener noreferrer"&gt;docs&lt;/a&gt; are regularly updated and have significantly improved compared to the rather humble release stage.&lt;/p&gt;

&lt;p&gt;In addition, it’s worth mentioning one important aspect, such as &lt;strong&gt;moderation&lt;/strong&gt;. When dealing with user images, &lt;em&gt;especially&lt;/em&gt; the ones that are meant to be public, &lt;a href="https://platform.openai.com/docs/guides/moderation" rel="noopener noreferrer"&gt;especially&lt;/a&gt; when the service is paid, it’s crucial to setup a safety net. Both &lt;a href="https://platform.openai.com/docs/guides/moderation" rel="noopener noreferrer"&gt;Open AI&lt;/a&gt; and &lt;a href="https://ai.google.dev/gemini-api/docs/safety-settings" rel="noopener noreferrer"&gt;Gemini AI&lt;/a&gt; have &lt;a href="https://ai.google.dev/gemini-api/docs/safety-settings" rel="noopener noreferrer"&gt;dedicated capabilities&lt;/a&gt;. They have a slightly different approach to the setup, but conceptually are quite similar. Explore the docs to learn more.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Implementation — Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  It’s Alive!
&lt;/h3&gt;

&lt;p&gt;Following a 180 turnround with the AI model and meticulous fine-tuning of instructions we are able to get some consistent recipe outputs. At this point our app looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcxgza89v0t62vhp5g77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcxgza89v0t62vhp5g77.png" alt="Simplified app structure, featuring the user form, AI request and the output" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step was implementing services to save and share recipes effectively. Practically speaking, we need a simple yet efficient &lt;strong&gt;database&lt;/strong&gt; and an &lt;strong&gt;image storage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The backbone of our app is built on &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;. To manage the database we chose &lt;a href="https://vercel.com/docs/storage/vercel-kv" rel="noopener noreferrer"&gt;Vercel KV&lt;/a&gt;, a Redis-based solution that fits our needs just great. The implementation is not so different from the docs, but just for posterity, let’s have a look at writing and retrieving saved data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02yoojvdjby4onsv6xba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02yoojvdjby4onsv6xba.png" alt="Introducing KV storage for saving and retrieving recipe data" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we get a proper result from AI, we need to &lt;strong&gt;save it&lt;/strong&gt;, along with the form- and meta-data. Later when we &lt;strong&gt;retrieve&lt;/strong&gt; the recipe all we need is an ID. Note that every generation produces a unique ID, hence a new, &lt;strong&gt;unique recipe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To write (save) the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vercel/kv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/utils/getId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// unique ID is generated&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;camera settings&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;sensor settings&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;film&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;film settings&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;light settings&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;recipe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;recipe data as returned by AI&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timestampMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;latency, ms&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To read the recipe data we need to know it’s &lt;code&gt;id&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
We can get it from &lt;code&gt;params&lt;/code&gt; and generate 404 when it’s not located:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;notFound&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vercel/kv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipeData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hgetall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;recipeData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// return &amp;lt;RecipePage data={recipeData} /&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it’s done!&lt;br&gt;&lt;br&gt;
Let’s take care of the images in the next step.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pivot #2
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://vercel.com/docs/storage/vercel-blob" rel="noopener noreferrer"&gt;Vercel Blob&lt;/a&gt; was the very first service that we utilized, and it lasted around 2 days of not that really intense testing. And of course it’s not the question of reliability or anything else. We simply getting back again at the question of &lt;a href="https://vercel.com/docs/storage/vercel-blob/usage-and-pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmctjyc1wo2nf6qu6a0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmctjyc1wo2nf6qu6a0l.png" alt="Vercel Blob pricing table comparing Hobby and Pro plans" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first glance, the 250MB quota seemed sufficient, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw05azso08c7ffj9t1qd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw05azso08c7ffj9t1qd.png" alt="250MB quota seemed sufficient, right?" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not sure about you, but spoiled by other services pricing I was &lt;em&gt;convinced&lt;/em&gt; it’s the limit &lt;em&gt;per day&lt;/em&gt;. So when during testing responses started err on the server without any special premise, I discovered the huge red glaring issue in my Vercel storage. It said that the &lt;strong&gt;MONTHLY&lt;/strong&gt; limit was exhausted already.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Going with a plan B&lt;/strong&gt;. After some quick research &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt; emerged as the ideal alternative, despite its more involved setup process, including account and project setting. And if that’s not enough fun, you’ll also get a change to know the one and only &lt;a href="https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html" rel="noopener noreferrer"&gt;AWS SDK&lt;/a&gt;. Who needs &lt;a href="https://sst.dev/" rel="noopener noreferrer"&gt;SST&lt;/a&gt; anyway…&lt;/p&gt;

&lt;p&gt;You never asked, but I don’t want to be left alone with this.&lt;br&gt;&lt;br&gt;
Here’s how we can upload the images using &lt;strong&gt;&lt;em&gt;@aws-sdk&lt;/em&gt;&lt;/strong&gt; (extra code skipped for brevity). The crucial piece is the &lt;code&gt;id&lt;/code&gt;, which we figured out previously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;S3ClientConfigType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutObjectCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// proprietary util&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stringToBlob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/utils/stringToBlob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3Config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3ClientConfigType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3_REGION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_API_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_API_SECRET&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s3Config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blobPromises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringToBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;putObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;putObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blobPromises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, to retrieve the uploaded images we only need the &lt;code&gt;id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;S3ClientConfigType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListObjectsV2Command&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3Config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3ClientConfigType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3_REGION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_API_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_API_SECRET&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s3Config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listObjects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ListObjectsV2Command&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;MaxKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Fuji X Studio images limit&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Contents&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listObjects&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Contents&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;imageUrls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Contents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.s3.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;S3_REGION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.amazonaws.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our app starts to get closer to it’s final state:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpq8y3s11rye8oe2lxf2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpq8y3s11rye8oe2lxf2l.png" alt="Adding image storage to the app setup, allowing to save and retrieve image data" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the end, how does &lt;strong&gt;S3&lt;/strong&gt; compare to &lt;strong&gt;Blob&lt;/strong&gt;?&lt;br&gt;&lt;br&gt;
For our use case, the extended testing time alone made AWS S3 &lt;strong&gt;at least&lt;/strong&gt; 60 times more effective. And we are still counting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“So after two 180 pivots, does it mean we get back to the starting point?”&lt;br&gt;&lt;br&gt;
“Only if you haven’t watched «&lt;a href="https://en.wikipedia.org/wiki/Rick_and_Morty" rel="noopener noreferrer"&gt;Rick and Morty&lt;/a&gt;»"&lt;br&gt;&lt;br&gt;
“Yeah, I don’t really watch cartoons”&lt;br&gt;&lt;br&gt;
“Believe me, you &lt;strong&gt;do&lt;/strong&gt; in a parallel universe, Johan”&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Implementation — Post Processing
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;At this point, the app is functioning, but somewhat not complete. Let’s have a brief look on couple of other nice-to-have features.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Kill-Switch
&lt;/h3&gt;

&lt;p&gt;A simple &lt;strong&gt;kill-switch mechanism&lt;/strong&gt; is needed to temporarily disable the service while leaving essential pages and metadata accessible. This can be handy for several reasons, especially when one of our crucial services is no longer available.&lt;/p&gt;

&lt;p&gt;The maintenance kill-switch in our case is implemented via an &lt;strong&gt;env variable&lt;/strong&gt; that disables main features on the client and prevents unsolicited requests on the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isMaintenance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FLAG_MAINTENANCE&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;SETTING_TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMaintenance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sorry, the service is not available at the moment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server handling is the last resort in this case, so we coupled it with the rate limit checks, hence the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429" rel="noopener noreferrer"&gt;429 status&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is also a noticeable message in UI informing of the ongoing work on the website:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m2yb5q5593ad395scru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m2yb5q5593ad395scru.png" alt="Fuji X Studio landing page showcasing maintenance mode" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reporting
&lt;/h3&gt;

&lt;p&gt;After a while Johan comes up with a new idea — the feedback loop for the recipes — to gather user insights and refine AI outputs. It makes a lot of sense, since we need &lt;strong&gt;more&lt;/strong&gt; field data in order to improve our instructions. Furthermore, it’s sets up the stage for the new features, something like &lt;em&gt;recipes library&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately, it’s also additive to our codebase.&lt;br&gt;&lt;br&gt;
Reports page will be located in the new &lt;strong&gt;admin area&lt;/strong&gt; of the app. The most important piece of information is the recipe output. It needs to be easily accessible along with the input data and report information. We can browse reports and mark them as &lt;em&gt;read&lt;/em&gt; or &lt;em&gt;completed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf4q83zrgz4giq5c6mg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf4q83zrgz4giq5c6mg4.png" alt="Fuji X Studio admin area showcasing user reports for the recipes" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reporting backend relies on CRUD operations using &lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations" rel="noopener noreferrer"&gt;Next.js actions&lt;/a&gt;. The only tricky task to solve is how to identify and store the reports.&lt;/p&gt;

&lt;p&gt;Since we don’t really want to use another DB or overhaul existing code, the simple solution is to &lt;strong&gt;create a new KV record based on the recipe ID&lt;/strong&gt;. It naturally can be scaled further to accommodate more than 1 report per recipe, yet for our practical debugging needs it’s more than sufficient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2d9vy72ch7mjkfnyyzjh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2d9vy72ch7mjkfnyyzjh.png" alt="Implementation of the basic reporting system on top of existing architecture" width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vercel/kv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;makeKVReportKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-report`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getKVReportKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// create report&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addReportAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newReport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReportData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeKVReportKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newReport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;reportData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReportData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newReport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newReport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// other data&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// update actions is nearly the same, so we skip it&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reportKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reportData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not send the report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// get reports&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getReportsAction&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ReportData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReportData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getKVReportKeys&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;reportKeys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hgetall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dateAdded&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dateAdded&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// delete report&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deleteReportAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeKVReportKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reportKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have noticed certain simplifications for some operations.&lt;br&gt;&lt;br&gt;
The reason being is that “create report” is the only user-facing action. Other actions are rather contained and &lt;strong&gt;limited to local usage&lt;/strong&gt;. However if you intend to inherit this approach, make sure to catch and log the unexpected errors for various app roles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unicornix 🦄
&lt;/h3&gt;

&lt;p&gt;When Fuji X Studio just started, the color theme was inspired by Fujifilm brand green color with the sporadic red accents. The theme itself looked fine, but former approach could cause confusing workarounds for the accessible colors, especially when dark theme was involved.&lt;/p&gt;

&lt;p&gt;The design challenges with Fuji X Studio inspired the creation of 🦄 &lt;a href="https://www.npmjs.com/package/unicornix" rel="noopener noreferrer"&gt;Unicornix&lt;/a&gt;, which later became its theming backbone.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 &lt;a href="https://www.npmjs.com/package/unicornix" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/unicornix&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This transition resulted in a modern, versatile, and fully accessible UI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguswa6vm6nor7ybgcmdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguswa6vm6nor7ybgcmdc.png" alt="Fuji X Studio landing page in dark theme - before/after Unicornix updates" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28vtidwwd6p2od5nqodw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28vtidwwd6p2od5nqodw.png" alt="Fuji X Studio landing page in light theme - before/after Unicornix updates" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1wcq83rwak9my2jlyjm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1wcq83rwak9my2jlyjm.png" alt="Fuji X Studio recipe page in dark theme - before/after Unicornix updates" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2q68ylmhb3xid82nh8s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2q68ylmhb3xid82nh8s.png" alt="Fuji X Studio recipe page in light theme - before/after Unicornix updates" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other design tokens are created with the help of &lt;a href="https://www.design-tokens.dev/" rel="noopener noreferrer"&gt;Design Tokens Generator&lt;/a&gt; as usual.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 &lt;a href="https://www.design-tokens.dev" rel="noopener noreferrer"&gt;https://www.design-tokens.dev&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to have a deeper look at the tokens organization, welcome to explore the &lt;a href="https://github.com/XOP/design-system-starter" rel="noopener noreferrer"&gt;design system starter project&lt;/a&gt;, specifically the relevant package. It’s essentially the one used in Fuji X Studio for theming.&lt;/p&gt;

&lt;p&gt;Also, big kudos to &lt;a href="https://www.chakra-ui.com/docs/theming/overview" rel="noopener noreferrer"&gt;Chakra UI&lt;/a&gt; for creating &lt;strong&gt;fantastic theming solution&lt;/strong&gt;. It’s system prop approach is incredibly intuitive and it’s a struggle to switch back after working with it for a while. Highly recommended.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you wonder about the name differences, someone snatched the &lt;strong&gt;fujixrecipes.com&lt;/strong&gt; domain just a couple of days before I was about to commit a purchase. And you probably guessed what happened next — another pivot led to branding and content changes.&lt;/p&gt;

&lt;p&gt;“Frankly, I like the new name better.”&lt;br&gt;&lt;br&gt;
“Johan, you are just happy that the project is complete.”&lt;br&gt;&lt;br&gt;
“No, see, «Studio» implies a professional photography environment”&lt;br&gt;&lt;br&gt;
“And «Recipes» are some sketchy things that you never follow?”&lt;br&gt;&lt;br&gt;
“Replacing ingredients is creative!”&lt;br&gt;&lt;br&gt;
“I wonder why it’s not yet in testimonials…”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Imprint
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Freemited
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.fujix.app/" rel="noopener noreferrer"&gt;FUJI X Studio&lt;/a&gt; is entirely free — no ads, no signups, and no data sales. It’s designed for hobbyists and developers as an educational and creative tool. And it’s a fun project to work on.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 &lt;a href="https://www.fujix.app" rel="noopener noreferrer"&gt;https://www.fujix.app&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the same time it is relying on the high quality services like Vercel, Gemini and AWS S3, but respecting the virtual limits, imposed by aforementioned services. Luckily, given the predicted usage and &lt;strong&gt;rate limitation&lt;/strong&gt; it’s fairly enough for hobby experiments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Specs
&lt;/h3&gt;

&lt;p&gt;FUJI X Studio is based on &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; and powered by &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;, featuring &lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;Chakra UI&lt;/a&gt; and &lt;a href="https://ark-ui.com/" rel="noopener noreferrer"&gt;Ark&lt;/a&gt; for the interface, &lt;a href="https://lucide.dev/" rel="noopener noreferrer"&gt;Lucide icons&lt;/a&gt;, &lt;a href="https://fonts.google.com/" rel="noopener noreferrer"&gt;Google fonts&lt;/a&gt;, &lt;a href="https://www.design-tokens.dev/" rel="noopener noreferrer"&gt;Design Tokens Generator&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/unicornix" rel="noopener noreferrer"&gt;Unicornix&lt;/a&gt; for theming, &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt; for images storage, &lt;a href="https://gemini.google.com/" rel="noopener noreferrer"&gt;Gemini AI&lt;/a&gt; for images processing, &lt;a href="https://vercel.com/docs/storage/vercel-kv" rel="noopener noreferrer"&gt;Vercel KV&lt;/a&gt; for database. While additional services contribute to the project, this lineup offers a clear overview of the core stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  NextJS 15? Chakra3?
&lt;/h3&gt;

&lt;p&gt;In late October 2024, both Next.js and Chakra released major updates.&lt;br&gt;&lt;br&gt;
Coincidentally, just a couple of days before final polishing and launching the app, posing a precarious challenge of yet another tech update. After giving it a b/&lt;em&gt;g&lt;/em&gt;/rief overview and estimating the migration effort it was decided to stay somewhat behind the bleeding edge for a while. It’s totally okay. Let it be.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frame It
&lt;/h3&gt;

&lt;p&gt;Fuji X Studio started as a spark of curiosity, and evolved through the proof of concept, researches, pivots, and finally emerged as a tool that bridges AI innovation with the practical experimental photography.&lt;/p&gt;

&lt;p&gt;We certainly learned &lt;strong&gt;a lot&lt;/strong&gt; through this journey, which resulted in this very article. Of course some parts were skipped otherwise the story would never end. Same goes for the app development, and can apply to the photo editing as well. You need to know when done is done. It’s the essential skill that gets honed with the every next project.&lt;/p&gt;

&lt;p&gt;If you got some inspiration, had some fun, found something useful or new, please support the article via sharing or 💜. &lt;strong&gt;Thank you for reading!&lt;/strong&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;“Hey, Johan, do you know what are going to do today?”&lt;br&gt;&lt;br&gt;
“&lt;strong&gt;Na ja&lt;/strong&gt;, try to take over the world?”&lt;br&gt;&lt;br&gt;
“Wha…? Jeez. Let’s go take some photos first, shall we?”&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>a11y</category>
      <category>writing</category>
    </item>
    <item>
      <title>Design System Starter Template - All Technology You'll Ever Need</title>
      <dc:creator>Evgeny Khoroshilov</dc:creator>
      <pubDate>Thu, 15 Aug 2024 14:34:00 +0000</pubDate>
      <link>https://dev.to/genedesign/design-system-starter-template-all-technology-youll-ever-need-1o25</link>
      <guid>https://dev.to/genedesign/design-system-starter-template-all-technology-youll-ever-need-1o25</guid>
      <description>&lt;p&gt;There are several ways how Design System can be founded — down-top, top-down, external, originated from design or development needs and probably some others. One thing is certain — it’s important to &lt;a href="https://medium.com/@genedesign/design-systems-beyond-basics-unveiling-nuances-and-oversights-6c85807b1c26" rel="noopener noreferrer"&gt;start things small&lt;/a&gt;, while keeping an eye on the future setup, which is crucial for sustainable growth.&lt;/p&gt;

&lt;p&gt;The very &lt;em&gt;foundation&lt;/em&gt; you build &lt;em&gt;today&lt;/em&gt; must support current needs while being agile and thought through to suffice gradual evolution of requirements within a project or organization. This article focuses on the &lt;strong&gt;technical backbone&lt;/strong&gt;, essential for creating a Design System that can satisfy expanding ambitions.&lt;/p&gt;

&lt;p&gt;The 🚀 &lt;a href="https://github.com/XOP/design-system-starter" rel="noopener noreferrer"&gt;&lt;strong&gt;Design System Starter Template&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(DSS)&lt;/strong&gt; is built on the core components that establish a strong technical foundation for scalable and maintainable Design System. These components ensure that design and development are consistent, flexible, and efficient.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⭐️ &lt;a href="https://github.com/XOP/design-system-starter" rel="noopener noreferrer"&gt;&lt;strong&gt;https://github.com/XOP/design-system-starter&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the following sections we’ll overview primary and secondary modules of the DSS template, as well as its services and tooling. In addition we’ll explore their cost-effectiveness in the early stages of Design System and their potential in more mature stages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4bqxz7dpilx5a09ql9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4bqxz7dpilx5a09ql9a.png" alt="This illustration maps out a design system ecosystem. Core components include documentation and Storybook applications. Supporting elements like design tokens, UI libraries, icons, and fonts ensure consistency. The system facilitates efficient design and development through shared resources, Storybook stories and testing." width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  UI Library: The Heart of the Design System
&lt;/h3&gt;

&lt;p&gt;The DSS UI library serves as the central pillar of DSS Template, built with &lt;a href="https://react-spectrum.adobe.com/react-aria/" rel="noopener noreferrer"&gt;react-aria&lt;/a&gt; to ensure accessibility-first, headless UI components. While &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; is the &lt;em&gt;selected&lt;/em&gt; framework for the template, DSS is meant to be &lt;em&gt;easily adapted&lt;/em&gt; to other frameworks like &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt; or &lt;a href="https://www.solidjs.com/" rel="noopener noreferrer"&gt;Solid&lt;/a&gt;. This adaptability allows teams to choose the technologies that best align with their project needs without being locked into a specific stack&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;styling&lt;/strong&gt; DSS UI relies on &lt;a href="https://vanilla-extract.style/" rel="noopener noreferrer"&gt;vanilla-extract&lt;/a&gt;, which provides a robust scalable zero-runtime CSS base. Yet again, it’s a flexible choice, allowing for alternative approaches like &lt;a href="https://github.com/css-modules/css-modules" rel="noopener noreferrer"&gt;CSS modules&lt;/a&gt;, &lt;a href="https://panda-css.com/" rel="noopener noreferrer"&gt;Panda CSS&lt;/a&gt;, &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; etc.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;stability&lt;/strong&gt;, UI components rely on the &lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;testing library&lt;/a&gt;, focusing on functionality in the first place. Specific testing scenarios might not be relevant in case of themed (headless) components, but essential in other scenarios.&lt;/p&gt;

&lt;p&gt;Resulting component structure looks rather straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Switch/
  index.ts
  Switch.css.ts       - styles created with vanilla-extract
  Switch.spec.tsx     - tests using testing-library
  Switch.stories.tsx  - documentation with Storybook stories
  Switch.tsx          - component based on react-aria
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It’s worth mentioning that even though DSS UI is not following a &lt;em&gt;multi-package&lt;/em&gt; approach, it still allows for tree-shaking, leveraging respective &lt;a href="https://rollupjs.org/configuration-options/#output-preservemodules" rel="noopener noreferrer"&gt;rollup options&lt;/a&gt; in Vite config:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// named import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ds-starter/ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// default import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ds-starter/ui/components/Button/Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A critical aspect of the UI library is the &lt;em&gt;early incorporation of Design Tokens&lt;/em&gt;. Tokens are foundational to maintaining consistent styling across Design Systems, allowing even projects that do not utilize the full UI library to benefit from a cohesive design language. With the proper semantic tokens in place, colors can be easily changed without the need for massive refactoring. Also, with the modular approach we don’t really care &lt;em&gt;how&lt;/em&gt; Design Tokens are built, but rather &lt;em&gt;what&lt;/em&gt; is being output.&lt;/p&gt;
&lt;h3&gt;
  
  
  Design Tokens: The Backbone of Consistency
&lt;/h3&gt;

&lt;p&gt;Design Tokens are integral to the consistency and flexibility of the Design System. They provide a standardized approach to theming and styling across all modules and applications, ensuring that every element of the UI remains cohesive.&lt;/p&gt;

&lt;p&gt;DSS &lt;strong&gt;color tokens&lt;/strong&gt; are generated using 🦄 &lt;a href="https://www.npmjs.com/package/unicornix" rel="noopener noreferrer"&gt;Unicornix&lt;/a&gt;, a tool that allows to create accessible and customizable color palettes, providing for easy start with light and dark modes. Typography and some other tokens are created with 🎨 &lt;a href="https://www.design-tokens.dev/" rel="noopener noreferrer"&gt;Design Tokens Generator&lt;/a&gt;. Altogether this provides a solid foundation for further scaling without encountering major roadblocks.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.npmjs.com/package/unicornix" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--58xlbJJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/338e4905a2684ca96e08c7780fc68412.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.npmjs.com/package/unicornix" rel="noopener noreferrer" class="c-link"&gt;
          unicornix - npm
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Unicornix - themeable and accessible color palette generation. Latest version: 0.7.0-beta.0, last published: 9 days ago. Start using unicornix in your project by running `npm i unicornix`. There is 1 other project in the npm registry using unicornix.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHsc1BmO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/b0f1a8318363185cc2ea6a40ac23eeb2.png" width="32" height="32"&gt;
        npmjs.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;DSS Tokens are available in both &lt;strong&gt;CSS&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; formats, to reflect and support different project needs, from simple websites to complex web applications. Theming can be done in several ways, and here we fully rely on CSS custom properties.&lt;/p&gt;

&lt;p&gt;Here is an excerpt from the generated CSS.&lt;br&gt;&lt;br&gt;
It’s easy to note that theme can be swapped completely by changing a single data attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-content-strong&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;27&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-content-regular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;49&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-background-regular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-background-subtle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;237&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;237&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-content-strong&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-content-regular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;229&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;230&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;231&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-background-regular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--awsm-color-background-subtle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JS tokens can be consumed as CSS refs, containing the references to values, rather than the color strings. This approach is great for semantic variables and theming without sacrificing performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var(--awsm-color-content-strong)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;regular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var(--awsm-color-content-regular)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;regular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var(--awsm-color-background-regular)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var(--awsm-color-background-subtle)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Icons and Fonts: Modular Visual Language
&lt;/h3&gt;

&lt;p&gt;The Icons and Fonts modules add depth to the visual language. Icons are managed through an efficient process that generates components from SVG files using &lt;a href="https://react-svgr.com/" rel="noopener noreferrer"&gt;SVGR&lt;/a&gt; and &lt;a href="https://tsup.egoist.dev/" rel="noopener noreferrer"&gt;tsup&lt;/a&gt;. This ensures that icons are consistent and can be flexibly integrated across the system.&lt;/p&gt;

&lt;p&gt;Similar to UI components, icons can be also imported individually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// named import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;IconX&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ds-starter/icons&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// default import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;IconX&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ds-starter/icons/lib/IconX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Source (SVG) import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;IconXSrc&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ds-starter/icons/svg/x.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Fonts package offers a convenient solution for managing typography within the Design System. It supports both &lt;em&gt;base64-encoded&lt;/em&gt; fonts for quick setups and &lt;a href="https://fonts.google.com/" rel="noopener noreferrer"&gt;Google Fonts&lt;/a&gt; integration for more robust implementations, giving teams the flexibility to choose the best approach for their project’s needs while maintaining consistent typography across all digital products.&lt;/p&gt;

&lt;p&gt;It’s worth noting that while base64 encoding is efficient, it’s not effective for production setup. Yet in the early stages it can be a common denominator for consistent typography. Of course going further this should be replaced with the more appropriate &lt;a href="https://www.zachleat.com/web/comprehensive-webfonts/" rel="noopener noreferrer"&gt;fonts-loading strategy&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Now, the question arises — should you setup Icons and Fonts packages from the start? The answer naturally depends, however in most typical scenarios it will be a &lt;em&gt;“no”&lt;/em&gt;. More agile environment in the early stages is crucial and less dependencies is the key. Yet, &lt;em&gt;keeping in mind the upcoming structure&lt;/em&gt; and incorporating that in the early setup is a good idea, shaving off a couple of dozen “story points” in the future refactorings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation — Storybook and Beyond
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Storybook: A Multi-Purpose Development Tool
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt; is an important tool for UI development, serving primarily as a development environment and a documentation portal on early stages of Design System. It allows to visualize and interact with UI components in various states and configurations, resolving issues early in the development process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftp22fkskblidwgndf3sj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftp22fkskblidwgndf3sj.png" alt="DSS Storybook App in action, showcasing the Switch component in Light theme" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Storybook in DSS is a standalone app that &lt;em&gt;does not itself host any stories&lt;/em&gt; — they all are collected across the packages and composed in one central hub. This way DSS Storybook can document color palettes, typography, iconography etc. along with the UI components from different sources after a simple setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;💡 Note that there is no&lt;/em&gt; &lt;a href="https://storybook.js.org/docs/sharing/storybook-composition" rel="noopener noreferrer"&gt;&lt;em&gt;storybook composition&lt;/em&gt;&lt;/a&gt; &lt;em&gt;per se, &lt;br&gt;&lt;br&gt;
yet it’s also possible as one does not deny the other.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Explore the deployed demo here: &lt;a href="https://ds-starter-storybook.vercel.app/" rel="noopener noreferrer"&gt;&lt;strong&gt;https://ds-starter-storybook.vercel.app/&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beyond its direct functionality, DSS Storybook is additionally equipped with &lt;strong&gt;Visual Regression Testing (VRT)&lt;/strong&gt; and &lt;strong&gt;Accessibility Testing&lt;/strong&gt; using &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;. Such automation is &lt;em&gt;essential for large design systems&lt;/em&gt;, where manual testing could quickly grow ineffective and time-consuming. By integrating these tests into the development workflow (early), DSS ensures that the Design System can evolve fast without fear of regressions.&lt;/p&gt;

&lt;p&gt;While being an irreplaceable tool for &lt;em&gt;early-stage documentation&lt;/em&gt;, consolidating component documentation and visual examples into a single platform, Storybook is &lt;em&gt;not&lt;/em&gt; actually a documentation website. With time, more sophisticated, content-oriented and customizable solution is demanded, especially for the Design System consumers far apart from technology.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation Website: Design System Knowledgebase
&lt;/h3&gt;

&lt;p&gt;As the Design System matures, the need for more detailed and accessible documentation becomes paramount. The DSS Documentation Website (DSS Docs) addresses this need by providing a dedicated application for organizing and presenting information about the Design System.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen7wwplzqrpf7vocd8xv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen7wwplzqrpf7vocd8xv.png" alt="DSS Documentation Website in action, showcasing the same Switch component in Dark theme" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Explore the deployed demo here: &lt;a href="https://ds-starter-docs.vercel.app/" rel="noopener noreferrer"&gt;&lt;strong&gt;https://ds-starter-docs.vercel.app/&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DSS Docs is designed to be minimalistic yet highly functional and customizable. It includes several modules that can be tweaked and modified to meet the project purpose. Powered by &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; and enhanced with &lt;a href="https://github.com/nanostores/nanostores" rel="noopener noreferrer"&gt;nanostores&lt;/a&gt; state manager, DSS Docs implies two main types of content: &lt;em&gt;Articles&lt;/em&gt; and &lt;em&gt;Component Documentation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Articles&lt;/strong&gt; offer in-depth insights into Design System concepts, provide guidelines, patterns and describe foundational layers in detail. To add a new Article is as easy as simply to place a &lt;em&gt;Markdown&lt;/em&gt; file into the respective folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxeyp7tpzg6jr523i1ov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxeyp7tpzg6jr523i1ov.png" alt="This image shows the IDE window and showcases the new Markdown document placed in the proper location in the documentation project, specifically at src/content/core/spacing.md." width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component Documentation&lt;/strong&gt; includes interactive examples &lt;strong&gt;dynamically loaded from the Storybook stories&lt;/strong&gt;. This integration solves a couple of issues — it ensures consistency across the “Dev” and “Prod” documentation and avoids redundancy in content creation. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 As a bonus — component examples can be edited in the UI library and will be automatically picked up by Docs running in dev mode. Not a Storybook replacement, but can be useful for cosmetic updates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;New Component Documentation can be added with a &lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;MDX&lt;/a&gt; file, following a particular schema. Apart from the main description, extra sections can be added following the “Usage” pages example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mh74nui2c16p4dkpy6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mh74nui2c16p4dkpy6k.png" alt="This image shows the IDE window and showcases the result of running a component generator that adds an Alert component and respective documentation files in the respective location of the documentation project, specifically at src/content/components/Alert/index.mdx and usage.mdx." width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expandable structure of DSS Docs allows for easy updates and tweaks, making it an essential tool for teams looking to &lt;em&gt;step up from Storybook&lt;/em&gt; without significant effort and creating redundancy. The Documentation app is themed with DSS Tokens to ensure a consistent look and feel of the main product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation and Workflow Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scripts and Github Actions Automation
&lt;/h3&gt;

&lt;p&gt;DSS leverages a series of scripts to handle essential tasks like testing, linting, formatting, and dependency management. &lt;a href="https://turbo.build/repo/docs" rel="noopener noreferrer"&gt;Turborepo&lt;/a&gt; offers great help for running scripts effectively, especially when every module adheres to a unified standard.&lt;/p&gt;

&lt;p&gt;What’s more, everything that we run locally, including Visual Regression Testing — can be done on CI, thanks to &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;Github Actions&lt;/a&gt;. Apart from the thorough quality checks, Github Actions will take care of apps deployment too (powered by &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;). Naturally, all scripts are configurable and optional.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changelog and Release Management
&lt;/h3&gt;

&lt;p&gt;DSS uses &lt;a href="https://github.com/changesets" rel="noopener noreferrer"&gt;Changesets&lt;/a&gt; to automate the processes of changelog generation and packages releases, ensuring every change is tracked and properly versioned. Needless to say, both processes are supported by Github Actions as well.&lt;/p&gt;

&lt;p&gt;Here are some examples of published NPM packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@ds-starter/fonts" rel="noopener noreferrer"&gt;@ds-starter/fonts&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@ds-starter/icons" rel="noopener noreferrer"&gt;@ds-starter/icons&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@ds-starter/tokens" rel="noopener noreferrer"&gt;@ds-starter/tokens&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@ds-starter/ui" rel="noopener noreferrer"&gt;@ds-starter/ui&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component Generator
&lt;/h3&gt;

&lt;p&gt;To further enhance productivity, DSS includes a &lt;a href="https://turbo.build/repo/docs/guides/generating-code" rel="noopener noreferrer"&gt;Turbo-powered Generator&lt;/a&gt; that simplifies the process of scaffolding new UI components. Apart from saving time, this allows to greatly reduce the human-error-copy-paste factor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run a generator&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm run gen:ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After replying to a series of prompts, you will get the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;New component scaffolded in the &lt;strong&gt;DSS UI package&lt;/strong&gt;, containing all respective files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Same component added to the &lt;strong&gt;DSS Docs application&lt;/strong&gt;, with the correct MDX frontmatter data&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like &lt;em&gt;almost everything&lt;/em&gt; in DSS, generator template can and &lt;em&gt;most probably need to be tweaked&lt;/em&gt; to the project needs. This is a completely arbitrary operation, however using generator can be very beneficial for contributors, onboarding of team members and scenarios like codebase migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flexible Technology Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Main Design Principle
&lt;/h3&gt;

&lt;p&gt;Design System technological stack is an arbitrary matter, however it’s for sure &lt;em&gt;not random&lt;/em&gt;. It’s a natural effect of &lt;a href="https://medium.com/@genedesign/design-systems-beyond-basics-unveiling-nuances-and-oversights-6c85807b1c26" rel="noopener noreferrer"&gt;multiple contributing factors&lt;/a&gt;, including, but not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;product scope and project peculiarities&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;initial size and future ambitions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;teams expertise and proficiency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;contributors and consumers proficiency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;client requirements and technical stack&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;overall codebase age and historical reasons&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;existing technical debt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cross-platform and cross-browser support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;maintainability requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;existing or upcoming deadlines&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;industry trends and market volatility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;organization structural changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;and more…&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Would you be interested in a dedicated article on this? Let me know!&lt;br&gt;&lt;br&gt;
&lt;strong&gt;⭐️ Also, you are past 2000 words, reading champion!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The aim of the DSS template is not to comply with every single scenario, but to suggest the &lt;em&gt;industry average best practices&lt;/em&gt; that can be further tailored to the desired experience. Understandable, Template won’t fit a lot of systems, however presented patterns and snippets can be explored, re-used, improved and hopefully inspire new creations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Selected, Recommended, Opinionated
&lt;/h3&gt;

&lt;p&gt;Throughout the article we observed multiple technologies being used in order to compose the DSS Template and provide a holistic and functional developer experience. Actually, there’re more under the hood, welcome to explore the comprehensive &lt;a href="https://github.com/XOP/design-system-starter/blob/main/README.md" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Those technologies can be basically grouped into “&lt;strong&gt;Selected&lt;/strong&gt;”, “&lt;strong&gt;Recommended&lt;/strong&gt;” and “&lt;strong&gt;Opinionated&lt;/strong&gt;” categories, so that each next one is more biased than the previous.&lt;/p&gt;

&lt;p&gt;Consider examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;React&lt;/code&gt; is &lt;strong&gt;Selected&lt;/strong&gt; for being the &lt;a href="https://survey.stackoverflow.co/2023/#section-most-popular-technologies-web-frameworks-and-technologies" rel="noopener noreferrer"&gt;most popular&lt;/a&gt; solution for UI libraries, it’s perfect for demonstration of UI library scaffolding.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;React-Aria&lt;/code&gt; is &lt;strong&gt;Recommended&lt;/strong&gt;, because it’s the headless UI solution that prioritizes accessibility; we don’t need to invent the wheel with typical functional patterns and get lots of accessibility concerns sorted out of the box.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;Biome&lt;/code&gt; for &lt;a href="https://biomejs.dev/" rel="noopener noreferrer"&gt;linting and formatting&lt;/a&gt; is an &lt;strong&gt;Opinionated&lt;/strong&gt; choice (I appreciate the turn-key configuration and blazing performance) and can be replaced with &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; and i.e. &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of all other technological choices I would like to (&lt;em&gt;additionally&lt;/em&gt;) highlight the &lt;strong&gt;Opinionated&lt;/strong&gt; ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vanilla-extract.style/" rel="noopener noreferrer"&gt;Vanilla-extract&lt;/a&gt; as the CSS solution is great for scalable projects that prioritize performance and server-side rendering compatibility. While it has a somewhat higher entry threshold, it provides a very friendly CSS-in-JS experience &lt;em&gt;without&lt;/em&gt; &lt;a href="https://dev.to/srmagura/why-were-breaking-up-wiht-css-in-js-4g9b"&gt;CSS-in-JS downsides&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nanostores/nanostores" rel="noopener noreferrer"&gt;Nanostores&lt;/a&gt; is the go-to solution when it comes to minimalistic and effective global state management in the apps. It shines in the island-like architecture and &lt;code&gt;Astro&lt;/code&gt; projects in particular. DSS Docs feature &lt;code&gt;nanostores&lt;/code&gt; for basic operations like toggling theme or source code, but it’s capable of managing much more complex tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Typescript&lt;/strong&gt;&lt;/a&gt; is the technology that stands out being in &lt;em&gt;all three groups simultaneously&lt;/em&gt;. It has been around for a while to become industry standard, it is generally recommended for complex projects like Design System and I would also use it further for similar reasons.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Building any product requires a solid foundation, a clear roadmap, careful preparations, and timely revisions at every milestone. As requirements evolve over time, your technology should be resilient enough to adapt effectively to new settings.&lt;/p&gt;

&lt;p&gt;With Design Systems, it’s &lt;em&gt;all of that&lt;/em&gt;, &lt;strong&gt;plus&lt;/strong&gt; the element of perpetual motion. Can you think of a universal development approach that can handle project unpredictability and versatility beyond the good ol’ &lt;a href="https://www.lightningdesignsystem.com/guidelines/markup-and-style/" rel="noopener noreferrer"&gt;Markup and Style&lt;/a&gt;? Maybe &lt;a href="https://bradfrost.com/blog/post/a-global-design-system/" rel="noopener noreferrer"&gt;later&lt;/a&gt;, but for now, this is just the way of things.&lt;/p&gt;

&lt;p&gt;The 🚀 &lt;a href="https://github.com/XOP/design-system-starter" rel="noopener noreferrer"&gt;&lt;strong&gt;Design System Starter Template&lt;/strong&gt;&lt;/a&gt; can &lt;em&gt;help you&lt;/em&gt; in establishing a strong technological core and &lt;em&gt;may even become&lt;/em&gt; a great starting point, providing a modular and flexible solution for your next Design System challenge. However, for the most part, it’s the &lt;em&gt;groundwork for insights&lt;/em&gt; and sparks of inspiration. This has happened to me &lt;strong&gt;several times during the development of DSS&lt;/strong&gt; to the extent of pivoting on tools, which is why I think it’s useful. I’m definitely looking forward to and inviting you to the next encounters.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>design</category>
      <category>frontend</category>
      <category>ui</category>
    </item>
    <item>
      <title>Design Systems Consolidation</title>
      <dc:creator>Evgeny Khoroshilov</dc:creator>
      <pubDate>Mon, 25 Mar 2024 12:27:00 +0000</pubDate>
      <link>https://dev.to/genedesign/design-systems-consolidation-5bh4</link>
      <guid>https://dev.to/genedesign/design-systems-consolidation-5bh4</guid>
      <description>&lt;p&gt;Let's say you are working on a set of tools for your colleagues, it's getting traction, projects are built faster, your team grows and you decide it's time to make the right thing - make this the official Design System. The word spreads, you talk to the upper management and find out there are other similar initiatives in the company. Incidentally, one System exists for ages already, but your departments are so far apart you never knew. The other System is a niche thing, they use a different frontend framework for particular reasons. Oh, and they also have components for mobile platforms.&lt;/p&gt;

&lt;p&gt;Your head starts to spin, you are not alone who have been working on the Design System establishment! After a breather some new feelings and realizations emerge. What happens now? In a quarter? After a year?&lt;/p&gt;

&lt;p&gt;If this sounds familiar you've probably been there, or you might be there now while you are reading this. Worry not, you are on the right track. In this article we'll explore the scenarios of the upcoming potential developments of the Systems organization.&lt;/p&gt;




&lt;p&gt;To be fair, this multi-system case is not as rare as it may seem. Different teams serving different projects with different business value, historical reasons, budgeting, resources, time allocation, people expertise, stakeholders support etc - produce &lt;strong&gt;independent varying Design Systems&lt;/strong&gt; over time. There can be many similarities, but there will never be a total match, and that's ok. Then of course, time is another factor. Huge team of experts won't build a Design System of the &lt;strong&gt;same degree of maturity&lt;/strong&gt; in 3 months as a moderately developed team would do in 3 years.&lt;/p&gt;

&lt;p&gt;Over time leadership may (will!) come up with an idea of consolidation. For a number of reasons, of course, however something is pretty much on the surface - it's what Design Systems were built for! Overcome challenges at scale - less effort duplication, less resources spent, less redundancy, higher consistency. Sounds almost too easy, because it's surely not. And it may even get gnarly when independent systems prefer to stay independent...&lt;/p&gt;

&lt;h2&gt;
  
  
  A Schroedinger's System
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15cp91gprz9k0uiqjr1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15cp91gprz9k0uiqjr1p.png" alt="Systems Superposition - all sourced images belong to the respective authors" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So where do things go from here? Well, there are Options. Abrupt decision is a bad decision. Everything should be considered, analyzed, weighted and potentially POC-d in some cases. The final outcome is unique to your organization's circumstances and aspirations. Can it go back to the starting point? It sure can! Should we prepare for a 2.5-year-long migration? Maybe we should! At this point, it's like System appearing in &lt;a href="https://en.wikipedia.org/wiki/Quantum_superposition"&gt;&lt;em&gt;superposition&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And yet, potential developments are known to the extent. What's more, a lot of companies have been through similar challenges already and their experience is invaluable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Knowledge is power. Equip for the journey.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Options and Examples
&lt;/h2&gt;

&lt;p&gt;Let's have a look at the challenges that your organization might be facing with the help of a real-world analogy.&lt;/p&gt;

&lt;p&gt;Imagine an automotive conglomerate that operates multiple car brands, each serving different market segments but under the same corporate umbrella. Each brand has developed its unique way of building cars, utilizing different engineering teams, technologies, and suppliers. However, to optimize operations, reduce costs, and leverage synergies, the conglomerate considers strategies to consolidate its manufacturing and design processes while still catering to its diverse market segments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2mr7yr90osdcygj0uvo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2mr7yr90osdcygj0uvo.png" alt="Systems status quo" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll now explore several options of further advancement, each highlighting pros, cons, risks, costs and "quick wins", that aim to tactically improve the current situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Systems Continue Work as They Do
&lt;/h3&gt;

&lt;p&gt;In this scenario, the automotive conglomerate allows each of its car brands to &lt;strong&gt;operate independently&lt;/strong&gt;, utilizing their unique manufacturing processes, technologies, and market strategies. Each brand caters to its specific segment - luxury, economy, sports, etc. without altering their individual operations. This approach maintains brand identity and specialization but may lead to redundancy in research, development, and production across the conglomerate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0j190pec8n11gyhjq1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0j190pec8n11gyhjq1g.png" alt="Option 1: Systems Continue Work as They Do" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Each brand maintains its unique identity and specialized market focus. Innovation and experimentation are less restricted by overarching constraints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Duplication of efforts and resources across brands leads to inefficiencies. Opportunities for cost savings and standardization are missed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Risks&lt;/strong&gt;: Stagnation due to a lack of collaboration and knowledge sharing. Potential to fall behind competitors who optimize their operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;: Same, for maintaining separate development, production facilities, and support systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick Win&lt;/strong&gt;: Cross-brand communities of practice to share knowledge and best practices without altering existing structures.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good example of this approach is Apple, which continues to develop &lt;a href="https://developer.apple.com/macos/"&gt;macOS&lt;/a&gt; and &lt;a href="https://developer.apple.com/ios/planning/"&gt;iOS&lt;/a&gt; separately, catering to desktop and mobile markets with distinct systems. Although there are crossover features, the platforms remain independent.&lt;/p&gt;

&lt;p&gt;This option may seem like nothing happens from a practical perspective. However, it's incorrect to say there are no changes - while going through comparison and analysis, systems, stakeholders and leadership acquire a lot of knowledge. Sure, systems' status quo holds for now, but this information allows us to strategize over different areas of development. And most probably over time there will be other improvements and re-evaluations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Systems Start Sharing Subsystems
&lt;/h3&gt;

&lt;p&gt;Here, the conglomerate first identifies commonalities between its brands, such as the need for safety features, infotainment systems, or engine components. It decides then to &lt;strong&gt;consolidate these into shared subsystems&lt;/strong&gt;, developed centrally and then deployed across different car models and brands. This initiative reduces costs and streamlines operations while still allowing each brand to maintain its unique market positioning. Brands start benefiting from advancements in technology and design innovations developed by their sister brands, promoting a culture of knowledge sharing and efficiency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxxx9azkti7mq3gnl7ghn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxxx9azkti7mq3gnl7ghn.png" alt="Option 2: Systems Start Sharing Subsystems" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Efficiency gains from reusing components and knowledge across brands. Potential for a unified quality standard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Challenges in aligning different teams' priorities and methods. Risk of diluting brand identity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Risks&lt;/strong&gt;: Resistance from teams accustomed to working independently. Integration issues between disparate systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;: Moderate, investments in integration and standardization upfront but savings over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick win&lt;/strong&gt;: Shared organization of common components, like infotainment systems or safety features.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some time ago Adobe transitioned its standalone desktop applications into an integrated suite known as &lt;a href="https://www.adobe.com/creativecloud.html"&gt;Adobe Creative Cloud&lt;/a&gt;, allowing for seamless file and setting synchronization across different Adobe software. This shift enabled designers to work more fluidly between applications like Photoshop, Illustrator, and After Effects, effectively sharing subsystems such as cloud storage, font management, and asset libraries.&lt;/p&gt;

&lt;p&gt;In the context of a Design System environment instantaneously one thinks of &lt;a href="https://www.w3.org/community/design-tokens/"&gt;Design Tokens&lt;/a&gt; or other forms of system foundation and brand uniformity. You can also consider a common Documentation library, shared Accessibility best practices, Voice and Tone guidelines and more along these lines. From a technical perspective think of unifying the platform solutions, like release processes, assets distribution etc.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.design-tokens.dev/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--8x9rhJPG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.design-tokens.dev/images/design-tokens-hero.png" height="700" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.design-tokens.dev/" rel="noopener noreferrer" class="c-link"&gt;
          
      Design Tokens Generator - For Web Developers and Designers
    
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Streamline your design system with Design Tokens Generator for web development and design. Get efficient and consistent design elements effortlessly. Try now.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--_PLPA6PT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.design-tokens.dev/favicon.ico" width="48" height="48"&gt;
        design-tokens.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Option 3: Systems Retire and a New One Emerges
&lt;/h3&gt;

&lt;p&gt;To continue with analogy, the conglomerate decides to phase out several of its older, less efficient car brands or models and &lt;strong&gt;launches a new brand or model line&lt;/strong&gt; that incorporates the best technologies, processes, and market strategies learned from the entire group. This new brand is built from scratch to address current market demands and technological advancements, aiming to replace the older ones progressively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52kbos7gzqde76b709o8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52kbos7gzqde76b709o8.png" alt="Option 3: Systems Retire and a New One Emerges" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a &lt;strong&gt;&lt;em&gt;bold&lt;/em&gt;&lt;/strong&gt; move, involving significant investment and a strategic overhaul, but it's aimed at ensuring long-term relevance and competitiveness in the market.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Opportunity to build from the ground up with modern practices and technologies. Can address legacy issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Significant upfront investment. Risk of disruption to current operations and customer experiences.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Risks&lt;/strong&gt;: Adoption resistance, both internally and from existing users. Potential for project delays and overruns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;: High, both in development and in transitioning users and staff.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick Win&lt;/strong&gt;: Pilot programs to test and refine the new system before a full rollout.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2014 Google &lt;a href="https://en.wikipedia.org/wiki/Angular_(web_framework)"&gt;announced a transition from AngularJS to Angular&lt;/a&gt;. It was a significant shift spanning more than 1600 applications that involved retiring the old framework in favor of a new one built from the ground up, considering modern web development practices.&lt;/p&gt;

&lt;p&gt;Once again, it is quite a bold move, and even more bold if done at once, as that would be similar to &lt;em&gt;pulling the hand brake at 200km/h on the autobahn&lt;/em&gt;. Apart from investment and thorough planning, this strategy cries for RFC, POC, probably a couple, maybe a &lt;em&gt;dozen&lt;/em&gt;. And a carefully organized roadmap, transitioning from here to then and following the milestones. Depending on the organization size it can take years, and even after that you may find some outdated pages in your apps, simply due to the cost efficiency.&lt;/p&gt;

&lt;p&gt;More lean strategy may involve transition to the state in "Option 2" first and re-evaluate some time later. It may happen that this step would be satisfactory enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 4: One System Stays and Others Retire
&lt;/h3&gt;

&lt;p&gt;After a thorough assessment, the conglomerate decides that only the &lt;strong&gt;most advanced&lt;/strong&gt;, efficient, and market-responsive brand &lt;strong&gt;will continue its operations as before&lt;/strong&gt;. The other brands will gradually be phased out, with their best features, technologies, and market strategies being integrated into the surviving brand. This could mean, for example, that a high-efficiency engine developed for a luxury brand is adapted for use in economy models, or a successful design language is adopted across the board. This process includes retraining staff, retooling manufacturing processes, and transitioning the customer base to adopt the products of the consolidated brand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnx5v0ilquyakgjq2g9fd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnx5v0ilquyakgjq2g9fd.png" alt="Option 4: One System Stays and Others Retire" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Streamlines operations by focusing on the most successful or advanced system. Encourages uniform quality and experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;: May not meet all unique market segment needs. Could discourage innovation in the absorbed systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Risks&lt;/strong&gt;: Alienation of customers and teams attached to the retiring brands/systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;: Moderate to high, depending on the scale of integration and rebranding efforts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick Win&lt;/strong&gt;: Integrating branding and marketing efforts for a smoother transition and unified customer experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the tech world, Adobe's decision to &lt;a href="https://www.adobe.com/products/flashplayer/end-of-life.html"&gt;discontinue Flash in favor of HTML5&lt;/a&gt; is a pertinent example. Over time, Flash's limitations became apparent, including security vulnerabilities and lack of support on mobile devices. HTML5 offered a more secure, efficient, and flexible alternative, leading Adobe to announce the end of Flash support by the end of 2020. This shift required developers and content creators to migrate their content to the newer, more sustainable HTML5 standard.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 If you feel nostalgic, check out the &lt;a href="https://ruffle.rs/"&gt;ruffle&lt;/a&gt; project - the open source and safe Flash player emulator&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Choosing this approach can get quite nuanced if none of the systems shines bright enough to overshadow others. In practice that could mean lack in one or some disciplines with solid advantage in the others. For example, great specialists without dedicated time allocation, stable and developed UI library minus the accessibility practices, or excellence in all tech areas, except the library framework is a couple of generations behind.&lt;/p&gt;

&lt;p&gt;Naturally, to proceed with one system, it should &lt;strong&gt;stand strong&lt;/strong&gt; so it can be formed based on the best suitable choice, plus the best or lacking disciplines from the other systems (hybrid approach).&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;To the Next Steps&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Consolidation discovery requires patience, meticulous planning, and an openness to gradual progress, due to the inert nature of large-scale organizations and overall complexity. It can be very challenging to align different departments and systems due to established processes and the scale of existing operations. This inherent inertia means that consolidation is not a switch that can be flipped overnight. It's something that might unfold over several years or, in some cases, might lead to the realization that complete consolidation is not the optimal solution after all.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/genedesign" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4-O6ua9z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F1094193%252F39673baa-7aef-41b1-8e65-2cb34082487b.png" alt="genedesign"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/genedesign/design-systems-beyond-basics-unveiling-nuances-and-oversights-280m" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Design Systems Beyond Basics: Unveiling Nuances and Oversights&lt;/h2&gt;
      &lt;h3&gt;Evgeny Khoroshilov ・ Oct 16 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#design&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#designsystem&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#productivity&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;This is why the initial step in this journey is a &lt;strong&gt;comprehensive analysis and feature comparison&lt;/strong&gt; of the existing systems. This foundational work is critical, as it sets the stage for informed decision-making regarding the future of the organization's Design Systems. Further, the organization may proceed with drafting RFCs, developing POCs, or even forming new teams dedicated to lead the consolidation effort.&lt;/p&gt;

&lt;p&gt;These steps are not trivial, they require significant investment in terms of time, resources, and commitment from leadership and stakeholders across the organization. However, armed with a clear understanding of the potential options ahead and drawing on the experiences of others who have navigated similar challenges, organizations can approach the task of Design Systems consolidation with greater confidence and clarity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Acknowledgment
&lt;/h2&gt;

&lt;p&gt;This article is heavily inspired by &lt;a href="https://medium.com/eightshapes-llc/consolidating-design-systems-6bb7ce72f393"&gt;the work of Nathan Curtis&lt;/a&gt;. If you haven't read it yet, please indulge yourself!&lt;/p&gt;

</description>
      <category>design</category>
      <category>architecture</category>
      <category>learning</category>
      <category>designsystem</category>
    </item>
    <item>
      <title>How to build AWSM docs with Storybook and Astro</title>
      <dc:creator>Evgeny Khoroshilov</dc:creator>
      <pubDate>Mon, 26 Feb 2024 11:42:00 +0000</pubDate>
      <link>https://dev.to/genedesign/how-to-build-awsm-docs-with-storybook-and-astro-4ego</link>
      <guid>https://dev.to/genedesign/how-to-build-awsm-docs-with-storybook-and-astro-4ego</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the dynamic realm of UI development, a &lt;a href="https://dev.to/genedesign/design-systems-beyond-basics-unveiling-nuances-and-oversights-280m"&gt;well-documented design system&lt;/a&gt; is the key to success. Welcome to the AWSM Docs - a slingshot approach to get your Design System documentation website up and running in no time. In this short article, we'll delve into leveraging Storybook and Astro capabilities in order to save time on documentation effort and maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idea
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://storybook.js.org/"&gt;Storybook&lt;/a&gt; and &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt; is the dynamic duo behind the AWSM Docs powerful synergy.&lt;/p&gt;

&lt;p&gt;Storybook acts as an interactive playground for UI components, while Astro builds and enhances a static website with dynamic capabilities. Together, they empower UI library development. And the best part is - you get documentation website at (almost) no additional effort.&lt;/p&gt;

&lt;p&gt;Of course Storybook &lt;em&gt;alone&lt;/em&gt; can serve as a documentation website, especially at the early stages of Design System development, however it is limited in terms of customization, and rather opinionated in the sense of information architecture. Let the tool remain the tool for its purpose and embrace the beauty of the &lt;em&gt;dedicated&lt;/em&gt; documentation website, which can further become a single gateway for information about your Design System.&lt;/p&gt;

&lt;p&gt;Bid farewell 👋 to the challenge of maintaining consistency between stories and doc snippets. AWSM Docs approach eliminates redundancy and ensures your documentation mirrors the current state of your UI library or/and Design System.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Project structure is quite standard and straightforward. It's a pnpm-based monorepo, additionally enhanced with &lt;a href="https://nx.dev/"&gt;nx&lt;/a&gt; and a couple of formal configurations.&lt;/p&gt;

&lt;p&gt;Feel free to explore the &lt;a href="https://github.com/XOP/awsm-docs"&gt;repo&lt;/a&gt; and read along.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2apzbe2b7x5jima37h1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2apzbe2b7x5jima37h1i.png" alt="awsm-docs project structure" width="604" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;packages/awsm-ui&lt;/code&gt; represents the UI library that needs to be developed and documented. Development premises are provided by Storybook, located in &lt;code&gt;apps/storybook&lt;/code&gt;. Separate Storybook app is the best solution for scalability, as it can consume stories and MDX files from different packages. This is particularly useful for the modularized libraries, but even without that it offers a more clean code structure thanks to separation of concerns.&lt;/p&gt;

&lt;p&gt;Next to Storybook sits the &lt;code&gt;apps/docs&lt;/code&gt; app, (auto-)&lt;a href="https://awsm-docs.vercel.app/"&gt;documentation website&lt;/a&gt; built with Astro, the primary subject of this article. To mimic a real-life Design System website it has a formal structure and some demo pages, traditionally in the form of MD files. The only thing it does not have is the coded UI examples. Interesting. We'll delve into details shortly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6furtooeqwy2tuv6b3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6furtooeqwy2tuv6b3e.png" alt="awsm-docs website - Switch component page" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And last but not least, apps and packages depend on the custom &lt;a href="https://www.design-tokens.dev/"&gt;Design Tokens&lt;/a&gt;, exported by &lt;code&gt;packages/awsm-tokens&lt;/code&gt;. Note that there are two exported themes, suitable for both light (⚡️ &lt;em&gt;electric&lt;/em&gt;) and dark (🦄 &lt;em&gt;ultraviolet&lt;/em&gt;) color mode preferences.&lt;/p&gt;

&lt;h2&gt;
  
  
  UI Library
&lt;/h2&gt;

&lt;p&gt;At this point it's worth mentioning that there are a lot of things in this project "for demo purposes". Sure. I am not &lt;em&gt;totally&lt;/em&gt; opposed to the idea of using three different headless libraries for building a perfect UI, there can be more - yet sanity is the limit.&lt;/p&gt;

&lt;p&gt;For the same reasons I selected React as the framework for the task. Love it or hate it, &lt;a href="https://npmtrends.com/angular-vs-react-vs-svelte-vs-vue"&gt;numbers&lt;/a&gt; are talking, not to mention the foundational support of its full-stack endorsers - NextJS and Remix.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;awsm-ui&lt;/code&gt; you may find the 3 amigos components, each based on a different headless UI library (and I'm not going to repeat that further - &lt;em&gt;for demo purposes&lt;/em&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;(mandatory) &lt;code&gt;Button&lt;/code&gt; - based on &lt;a href="https://react-spectrum.adobe.com/react-aria/"&gt;React-Aria&lt;/a&gt; - represents the &lt;em&gt;atomic&lt;/em&gt; component, entity with the simple structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Switch&lt;/code&gt; - powered by &lt;a href="https://www.radix-ui.com/primitives"&gt;Radix UI&lt;/a&gt; - atomic component with internal state and slightly more complex structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Disclosure&lt;/code&gt; - featuring &lt;a href="https://headlessui.com/"&gt;Headless UI&lt;/a&gt; - more of a &lt;em&gt;molecule&lt;/em&gt; kind... perhaps because it's bigger&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6de0yn5ttks1oxi28un.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6de0yn5ttks1oxi28un.png" alt="react-aria, headless UI and radix + vanilla-extract" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All components are styled with &lt;a href="https://vanilla-extract.style/"&gt;vanilla-extract&lt;/a&gt; styling engine, effectively utilizing Design Tokens in JS format. Remember the (demo) purposes? Definitely applies here as well. Nothing is stopping you from using any other CSS magic of choice.&lt;/p&gt;

&lt;p&gt;As mentioned, all components are documented with Storybook (v^7.5). There is nothing fancy about the stories or config organization, as I try to keep things clear, unopinionated and ready for further tweaking.&lt;/p&gt;

&lt;p&gt;Here's a slightly reduced example of the Switch stories file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StoryObj&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Switch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Form/Switch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;StoryObj&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Checked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;defaultChecked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ColorPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we'll see further, more complex examples using templates and &lt;code&gt;render:&lt;/code&gt; are also supported, so it's not limited to plain args.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Docs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;Now let's explore the &lt;code&gt;docs&lt;/code&gt; app. In the project I'm using Astro v^4.0, and at the time of writing it's already updated to 4.4, however no breaking changes are expected, so feel free to use the latest version.&lt;/p&gt;

&lt;p&gt;In a nutshell, resulting app consists of the home page, couple of content sections - &lt;strong&gt;Core&lt;/strong&gt; and &lt;strong&gt;Patterns&lt;/strong&gt;, each containing an introduction and several inner pages, and the Components section, hosting static and dynamic documentation for the UI library.&lt;/p&gt;

&lt;p&gt;App is structured in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
    client
    components
    content
    data
    layouts
    pages
    shared
    store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;client&lt;/em&gt; contains the &lt;em&gt;client:only&lt;/em&gt; components, i.e. &lt;code&gt;CopyButton&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;components&lt;/em&gt; comprises traditional Astro building blocks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;content&lt;/em&gt; is the required element of the &lt;a href="https://docs.astro.build/en/reference/api-reference/#content-collections-astrocontent"&gt;Astro Collections&lt;/a&gt;, structured and filled in accordance with the navigation and website contents&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;data&lt;/em&gt; contains some additional MD(x) files for the section pages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;layout&lt;/em&gt; and &lt;em&gt;pages&lt;/em&gt; are standard and self-explanatory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shared&lt;/em&gt; hosts various utils and hooks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;store&lt;/em&gt; is the optional state management layer, powered by &lt;a href="https://github.com/nanostores/nanostores"&gt;nanostores&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Collections
&lt;/h3&gt;

&lt;p&gt;It is worth exploring collections and respective navigation for a little bit, as there is quite a high chance this will be expanded and customized. Luckily, it is actually quite simple.&lt;/p&gt;

&lt;p&gt;The easiest way to get familiar with the navigation structure is to check out &lt;code&gt;shared/globals.ts&lt;/code&gt; - there are a couple of constants that work in sync with the collections. For example the "Core" section needs the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// collection of documents
content/core/*.md

// shared/globals.ts
NAVIGATION_KEYS.core = 'core';
NAVIGATION[0] = { slug: NAVIGATION_KEYS.core, title: 'Core' }];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it for the basic collections!&lt;br&gt;&lt;br&gt;
Now, for the most interesting and challenging part.&lt;/p&gt;
&lt;h3&gt;
  
  
  Components
&lt;/h3&gt;

&lt;p&gt;Components documentation is basically split into "static" and "dynamic" parts. "Static" part is imported from the dedicated content collection, whereas the "dynamic" part is loaded directly from the UI library.&lt;/p&gt;

&lt;p&gt;The easiest way to follow how this alliance works is to start with the &lt;code&gt;pages/components/[...slug].astro&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Here's the part we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/components/[...slug].astro excerpt&lt;/span&gt;

&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`../../../../../packages/awsm-ui/src/components/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.stories.tsx`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;examplesData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;refineStories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Examples&lt;/span&gt; 
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;examplesData&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, we get the current Component entry data, defined in the frontmatter (we'll get back to that later).&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Second&lt;/strong&gt; - we &lt;a href="https://vitejs.dev/guide/features.html#dynamic-import"&gt;dynamically import&lt;/a&gt; and pass the stories data to the Examples component.&lt;/p&gt;

&lt;p&gt;Note, that it is a &lt;em&gt;server&lt;/em&gt; call and this data will be used further for rendering "slots" with names and other meta information in &lt;code&gt;Examples&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Examples.astro excerpt&lt;/span&gt;

&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;exampleName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Example&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;exampleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ExampleContent&lt;/span&gt;
        &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;only&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;exampleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;componentDir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Example&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Evidently, it's a simple presentational component. The one we are actually looking for is &lt;code&gt;ExampleContent&lt;/code&gt; - let's dive in for a closer look.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ExampleContent&lt;/code&gt; is meant to be loaded on the client, this is where the dynamic heavy lifting happens. What we need from the stories are in fact the &lt;em&gt;stories&lt;/em&gt; themselves - most typically the component variants, rendered with the different props.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Everything that we need for successful rendering can be found within the stories. Depending on the story format we might need different data.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client/ExampleContent.tsx excerpt&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ExampleContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExampleContentProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;componentDir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStoryLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;componentDir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AnyComponent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ElementType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;Variant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ElementType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;Variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Variant&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;struct&lt;/code&gt; is an arbitrary term for the &lt;code&gt;default&lt;/code&gt; object and it's contents look somewhat like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;default:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;title:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'Form/Button'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;component:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;'$$typeof':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Symbol(react.forward_ref)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;render:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Function:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;parameters:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;layout:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'centered'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;docs:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;tags:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'autodocs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Naturally, having Variant data we can render the source code if needed.&lt;/p&gt;

&lt;p&gt;Finally, let's have a look at the loading hook, however you won't find anything exceptional there, as it's pretty standard and predictable at this point. Note that &lt;code&gt;struct&lt;/code&gt; is not mentioned here, as it's almost identical to the &lt;code&gt;name&lt;/code&gt; import.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shared/hooks/useStoryLoad.ts excerpt&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useStoryLoad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ComponentStruct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;componentDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;_data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`../../../../../packages/awsm-ui/src/components/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentDir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.stories.tsx`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Binding
&lt;/h3&gt;

&lt;p&gt;We explored the main mechanism behind component examples rendering, and now it's time to get back to the documentation part.&lt;/p&gt;

&lt;p&gt;Components collection represent the static part of the documentation and organized in a particular way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;content/
  components/
    &amp;lt;ComponentName&amp;gt;/
      index.mdx
      [usage.mdx]
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;index.mdx&lt;/code&gt; contains the introductory information about component, whereas &lt;code&gt;usage.mdx&lt;/code&gt; is optional and may contain the docs about component application. If you wish to extend this structure with any custom pages, i.e. "Accessibility", it's easy to do. Check out the following files and adjust data accordingly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;shared/globals.ts&lt;/code&gt; - update &lt;code&gt;COMPONENT_NAVIGATION&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;components/Sidebar.astro&lt;/code&gt; - look for the &lt;code&gt;currentSlug&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What is important about both files is the &lt;strong&gt;frontmatter data&lt;/strong&gt;, usage of which you could already notice in the &lt;code&gt;pages/components/[...slug].astro&lt;/code&gt; file. We can define the schema ourselves and should follow it in all documented components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Button'&lt;/span&gt;
&lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Examples'&lt;/span&gt;
&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Form'&lt;/span&gt;
&lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Button'&lt;/span&gt;
&lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Button'&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;title&lt;/code&gt; - is the name of the component in the docs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;page&lt;/code&gt; - is the name of the tab in the same docs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;group&lt;/code&gt; - is the custom arbitrary grouping of components; see the usage in the &lt;code&gt;components/Sidebar.astro&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;component&lt;/code&gt; and &lt;code&gt;dir&lt;/code&gt; is what we actually use for the stories import&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Gimmick
&lt;/h3&gt;

&lt;p&gt;There's one more thing. It's more of a 🎁 bonus, actually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vbddzaojenkrxgt89j1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vbddzaojenkrxgt89j1.gif" alt="Just one more thing..." width="524" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we rely on the dynamic imports, Astro and Vite will automatically update components on changes - both components' and stories' code! It can give you extra flexibility on documenting (and sometimes &lt;em&gt;updating&lt;/em&gt;) components, especially if you decide to output them in different example containers.&lt;/p&gt;

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

&lt;p&gt;AWSM Docs presents a pragmatic approach to UI library or Design System documentation website development. While not claiming to reinvent the wheel, this solution aims to speed up and streamline the process. As with any tool, there's room for improvement, and I encourage further exploration.&lt;/p&gt;

&lt;p&gt;It's worth noting that at the time of writing there's an &lt;em&gt;upgraded and enhanced&lt;/em&gt; version of &lt;code&gt;awsm-docs&lt;/code&gt; being prepared, stay tuned for another article!&lt;/p&gt;

&lt;p&gt;Check out the project on &lt;a href="https://github.com/XOP/awsm-docs"&gt;GitHub&lt;/a&gt; and tailor the solution to your specific needs. Your insights and contributions will undoubtedly contribute to the evolution of this approach.&lt;/p&gt;

&lt;p&gt;Happy coding and documenting!&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>designsystem</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Design Systems Beyond Basics: Unveiling Nuances and Oversights</title>
      <dc:creator>Evgeny Khoroshilov</dc:creator>
      <pubDate>Mon, 16 Oct 2023 16:15:00 +0000</pubDate>
      <link>https://dev.to/genedesign/design-systems-beyond-basics-unveiling-nuances-and-oversights-280m</link>
      <guid>https://dev.to/genedesign/design-systems-beyond-basics-unveiling-nuances-and-oversights-280m</guid>
      <description>&lt;p&gt;The domain of Design Systems now is quite saturated with articles, books, insights, and post-mortems, each offering a glimpse into the multifaceted landscape. Since dipping my toes into this realm in 2017, I've experienced various evolution steps within different organizations, witnessing firsthand the maturation of tools and processes. With the teams we've been through various evolution stages and to this day, our created Design Systems are alive and well.&lt;/p&gt;

&lt;p&gt;While Design Systems have, in many respects, become a well-known initiative and occasionally an industry standard, overall system establishment isn't free from misconceptions that can misguide even proficient teams and companies. So in this essay featuring longer sentences and 0 button pictures, I would like to explore the ever-evolving Design System trends through the prism of practical experiences, focusing on the not-so-evident aspects. As in the end...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“All happy families are alike; each unhappy family is unhappy in its own way.”&lt;br&gt;&lt;br&gt;
― &lt;strong&gt;Leo Tolstoy,&lt;/strong&gt; &lt;a href="https://en.wikipedia.org/wiki/Anna_Karenina"&gt;&lt;strong&gt;Anna Karenina&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's course together through Design System definition, composition, benefits and, naturally, caveats.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Design System Defined&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In its essence, a Design System is a meticulously curated library, encompassing tokens, styles, components and many other digital aspects, guided by clear standards and accompanied by documentation. Fostered by a dedicated or federated team, it serves as the unseen engineer behind multiple products, orchestrating a harmonious digital user experience.&lt;/p&gt;

&lt;p&gt;The well-aged Design System is sitting robustly between &lt;em&gt;Brand Direction&lt;/em&gt; and &lt;em&gt;Digital Experiences&lt;/em&gt; while orchestrating the &lt;em&gt;Target Audience&lt;/em&gt;'s journey. Eventually, it becomes an integral, cohesive, and structural layer within an organization, not only implementing but defining product and organization design vision.&lt;/p&gt;

&lt;p&gt;Regardless of the requirements, resources, deadlines and ambitions, it's crucial to remember the key point - it should&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;always&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;start&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;small&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Design System pillars&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Hello, myself from 2019. That was the year when I coined the term "three pillars" of the Design System. Though slightly adjusted over time, I can confirm the concept is still legit in one or another form. Recently I stumbled upon a resemblance of that model in &lt;a href="https://sparkbox.com/foundry/design_system_makeup_design_system_layers_parts_of_a_design_system"&gt;Ben Callahan's course&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In short, the three pillars are &lt;strong&gt;Deliverables&lt;/strong&gt;, &lt;strong&gt;Maintainers&lt;/strong&gt; and &lt;strong&gt;Processes&lt;/strong&gt;. Here's the metaphor in action - when one of the pillars is wobbly or crumbly, the Design System either stumbles or collapses under pressure, becoming a blocker instead of the intended purpose. Not to mention when there's just &lt;em&gt;one pillar&lt;/em&gt; on its own - that should be either a dawn or soon decline of the initiative.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Side note - I'm not married to the "pillars" metaphor so much any longer, so let's come up together with a more viable and illustrative analogy (comments below).&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Deliverables&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With deliverables, it's rather obvious where to start and what to prioritize (UI libraries in any form) yet there are so many versatile options. Self-documenting libraries in Storybook, Figma-only documentation, code as the source of truth and more and beyond. Generally, there are 2 important things to focus on - &lt;strong&gt;Components&lt;/strong&gt; and &lt;strong&gt;Documentation&lt;/strong&gt; if you prefer alphabetical order. &lt;strong&gt;Documentation&lt;/strong&gt; and &lt;strong&gt;Components&lt;/strong&gt; if you are really being serious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code&lt;/strong&gt;: Including UI libraries, design tokens, iconography.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design Assets&lt;/strong&gt;: Think Figma libraries and UX patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: Spanning websites, code repos, Confluence etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional Assets&lt;/strong&gt;: Illustrations, fonts and such.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Maintainers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Maintainers establish and move the Design System forward. These dedicated individuals and/or teams champion its evolution in the beginning and sustain the fidelity and reliability of the system later. They ensure it adapts and resonates with time, project and organization requirements and the landscape of technology and user expectations.&lt;/p&gt;

&lt;p&gt;The more traditional approach in the early years - Design System originates from the &lt;em&gt;design&lt;/em&gt; environment and is pushed forward by &lt;em&gt;designer(s)&lt;/em&gt;. The frontend-first approach was considered a rarity. Lately, there is no big distinction between the two takes, in fact - coordination of minds and efforts would work the best. If you need a deep (much deeper) dive into the topic of people behind Design System development, refer to &lt;a href="https://eightshapes.com/articles/designing-a-systems-team/"&gt;Nathan Curtis's article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Designers&lt;/strong&gt;: mandatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI developers&lt;/strong&gt;: mandatory.&lt;/p&gt;

&lt;p&gt;Recommended ratio?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A) Highly depends. Usually a good start would be 2+ frontenders per 1 design specialist.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;B) Scale is non-linear. When in doubt refer to A).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Other competencies&lt;/strong&gt; may include but not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;UX designers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accessibility specialists&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;QA engineers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Motion designers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Content editors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Icon creators (this is a real thing)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What about the Product Manager? Applicable to Design Systems this role is highly versatile without traditional boundaries of the industry, ranging from roadmap communication to fully-fledged architecture. It's worth mentioning that the weight of PM responsibilities can be owned or even shared between key maintainers, especially at the early stages. However, a dedicated role can help to push the envelope faster and assist with the priorities churn, miscoordination, excessive bureaucracy, red tape and other fun aspects of large or conservative companies.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Processes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Processes signify the operational backbone of a Design System. They are &lt;a href="https://en.wikipedia.org/wiki/The_Big_Lebowski"&gt;Lebowski's valued rug&lt;/a&gt;, tying everything together with routines, workflows and collaborations and ensuring integration of the Design System within the organizational framework. They come especially useful at later stages, however, it's highly advised to consider at least the basic ones in advance.&lt;/p&gt;

&lt;p&gt;Another point of consideration is automation. You would like to save some precious time, wouldn't you? Automate everything possible, but &lt;em&gt;avoid de-humanization pitfalls&lt;/em&gt;. Talk to customers and each other first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request and Update Cycles&lt;/strong&gt;: It is crucial to establish and hone those as soon as feasible. For the team, it's velocity and transparency, for customers - trust and reliability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer Support&lt;/strong&gt;: Next in line - support - both technical and design-wise. Slack works for the most part!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contributions&lt;/strong&gt;: Prepare your fundamentals and encourage collaboration. Early insights are solid gold. Not &lt;em&gt;too&lt;/em&gt; early though!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onboarding&lt;/strong&gt;: Your system would definitely benefit from a regular guided process, considering how it can change over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Office hours&lt;/strong&gt;: When Slack &lt;em&gt;doesn't&lt;/em&gt; work. Have a weekly or 2-weekly session to chat and answer questions, provide support and announce something cool. Agenda is the key.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Features of a Design System&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Most integral elements that constitute a Design System are well known and I won't be going over them in detail again. Some less prominent building blocks might be overlooked though, so take a glimpse. Please note, that the list below only shows the &lt;em&gt;median&lt;/em&gt; slice of features, simply because not all Design Systems mature to the level of delivering not just elements, but the whole parts of the application as micro-frontends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual Style&lt;/strong&gt;: The aesthetic glue binding the UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI Components&lt;/strong&gt;: Reusable design elements ensuring efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UX Patterns&lt;/strong&gt;: Crafting a consistent user journey across platforms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;: Embedding inclusivity from the get-go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Editorial&lt;/strong&gt;: Sculpting the tone and content consistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charts&lt;/strong&gt;: Standardizing visual data representation.&lt;/p&gt;

&lt;p&gt;Plus more, should maturity and organization specifics demand for!&lt;/p&gt;

&lt;p&gt;Features really shine when 3 pillars of the Design System are working cohesively. Some of them may seem to match the deliverables, but essentially it's a more conceptual and product-oriented list. For example, "&lt;strong&gt;Accessibility&lt;/strong&gt;" can be achieved by putting together coding practices, inclusive design assets and guidelines, practical documentation and tutoring sessions. Take an element away and it falls apart. What is the value of high-contrast design if it's not respected in code? What is the value of aria props if they are neglected in the customers' projects? In both cases, it's a value multiplied by zero.&lt;/p&gt;

&lt;p&gt;Compare this to the steadier and slower but holistic approach. Yes, not all code components are compliant yet with &lt;a href="https://www.w3.org/TR/WCAG21/"&gt;WCAG 2.1 AA&lt;/a&gt;, but they match good practices in design and product teams know how to apply and test for accessibility. The value would not be 10/10 from the start, but it's more than 0.&lt;/p&gt;

&lt;p&gt;Finally, fully-fledged features don't appear out of thin air - they need strategy, communication, roadmap, tickets, prioritization and so forth. Interestingly enough, most features of Design Systems won't be finalized, but rather consistently grow in size and complexity over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Benefits of a Design System&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you are here and almost bored, jump to the next section &lt;em&gt;NOW&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Seriously, it's the most cliche part of the essay. It's so corny that we should change the heading immediately. Here goes nothing!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;- Benefits of a Design System&lt;/strong&gt;
&lt;/h3&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;+ The Bestestest part of a Design System&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;While some advantages of Design System installation might be immediately noticeable and appreciated by the stakeholders, others might not be so evident. In various sources you might find more aspects, however usually they would stem from one or another more prominent points.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Top 2:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: Ensuring a uniform visual and UX across UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;: Saving time and thereby, financial resources, in development.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Runner-ups:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;: Integrating and fostering best practices from the jump.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: A problem solver for scaling issues across various platforms.&lt;br&gt;&lt;br&gt;
Practically speaking, this is what Design Systems were created for in the first place. Ironically, it's often taking second place now...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faster Rebranding and Refactoring&lt;/strong&gt;: Making large-scale updates breezier.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Beyond the obvious:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Faster Onboarding&lt;/strong&gt;: Streamlining the integration process for development and design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recruitment&lt;/strong&gt;: Positioning the organization attractively for top-tier talent.&lt;/p&gt;

&lt;p&gt;Finally, there's a gimmick often named a "&lt;strong&gt;Common Language&lt;/strong&gt;" for an organization. Commonly, it's the poetic interpretation of the fact that teams strive for or have achieved effective collaboration and streamlined their production processes, not without the help of a well-established Design System. Should we put it in a separate category? &lt;em&gt;Sure&lt;/em&gt;, just remember to explain what is meant by that during a pitch. Instead of "hmm" you may count on "ohh!".&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Design System Caveats&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Well, here's the main reason why this very article was hatched in the first place. Honestly, I expected more content for this section, however, previous chapters already provide quite an extensive problematic coverage - &lt;strong&gt;in case you skipped it, consider going back&lt;/strong&gt;. Remember this statement, it’s a blunt solution for the caveat number N, so we'll see it in detail further.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Establishing a Design System demands both time and financial investment. This upfront cost, which requires management buy-in at company and project levels, predicates a future &lt;strong&gt;return on investment&lt;/strong&gt; through eventual time and cost efficiencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost-Effectiveness&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Design System is fundamentally crafted for &lt;strong&gt;scalability&lt;/strong&gt;, posing itself as a service particularly beneficial to multiple projects. While it can certainly manage the UI for a singular project, its cost-effectiveness truly shines when deployed across numerous ventures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Longevity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Longevity is intrinsic to a Design System’s makeup, operating as a &lt;strong&gt;perpetually&lt;/strong&gt; tuned and smoothly functioning entity. Should any of its fundamental pillars - deliverables, maintainers, or processes - become obstructed, it risks transforming into a barrier itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creativity Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Design System can intentionally or inadvertently become an operated valve for &lt;strong&gt;creativity&lt;/strong&gt;, often a focal point for critique. Nevertheless, in contemporary organizations, the potential stifling of creativity is typically mitigated in advance through strategic training and process establishment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;System Errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like all systems, a Design System is not immune to &lt;strong&gt;architectural pitfalls&lt;/strong&gt;. Common missteps include initiating numerous competing projects instead of starting small and failing to plan for long-term maintenance. Both provide a misleading sense of initial control but tend to manifest as obstructions in the long term.&lt;/p&gt;

&lt;p&gt;That's worth a short digress.&lt;/p&gt;

&lt;p&gt;What's a long-term vs short-term anyway? Let's put it on a virtual timeline. Roughly put, it's a threshold of 1 year or so. Usually, foundations should be established after 1 year of development, and then &lt;em&gt;the real work begins&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Design Systems are somewhat slow, multi-staged (also -layered and -faceted) projects, where each stage would be unique in aspects of length, growth, issues, requests, processes, maintenance effort and so on. Planning only for growth and overlooking long-term support is as dangerous as starting with all features and conflicting priorities, eventually making foundation establishment a never-ending resource-guzzler. Both examples relate to architecture and can be called system errors of Design System development. That's an oxymoron I would shy away from.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;It’s a wrap!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Creating and maintaining a Design System is naturally full of challenges. I reckon we should be more transparent and candid about possible roadblocks and considerations. Getting buy-in from stakeholders is one thing, return on investment is another.&lt;/p&gt;

&lt;p&gt;Design Systems have been generously contributing to the realm of consistency and efficiency in the digital development terrain since 2015 or so, marking &lt;strong&gt;not merely a trend but an industry staple&lt;/strong&gt;. Naturally, as we navigate the evolution of technology and design, the exploration, critique, and refinement of Design Systems forge ahead, too!&lt;/p&gt;

&lt;p&gt;This article is an interim stage where I take a chance to analyze personal and collective experiences with Design Systems, as that needs to happen from time to time. I also hope that it will encourage us to revisit how Design Systems contribute to our digital environments now and anticipate next-gen challenges and innovations with an open yet slightly more rational mind.&lt;/p&gt;

&lt;p&gt;❤️ Thank you for reading!&lt;br&gt;&lt;br&gt;
Get in touch on &lt;a href="https://www.linkedin.com/in/evgeniykhoroshilov/"&gt;LinkedIn&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>design</category>
      <category>designsystem</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Swift theming and design system foundation with Design Tokens Generator</title>
      <dc:creator>Evgeny Khoroshilov</dc:creator>
      <pubDate>Fri, 02 Jun 2023 16:30:00 +0000</pubDate>
      <link>https://dev.to/genedesign/swift-theming-and-design-system-foundation-with-design-tokens-generator-3bm6</link>
      <guid>https://dev.to/genedesign/swift-theming-and-design-system-foundation-with-design-tokens-generator-3bm6</guid>
      <description>&lt;p&gt;&lt;a href="https://www.design-tokens.dev/"&gt;Design Tokens Generator&lt;/a&gt; is a powerful tool that aims to simplify the management of design tokens for designers and web developers.&lt;/p&gt;

&lt;p&gt;The goal of the app is to make design tokens more &lt;strong&gt;approachable and user-friendly&lt;/strong&gt;. With Design Tokens Generator 👑😈🎨👽, you can focus on your creative vision while the app takes care of the tedious aspects of design token taxonomy. It provides a streamlined solution for managing design tokens, allowing you to &lt;strong&gt;effortlessly establish a strong foundation&lt;/strong&gt; for your Web Project, App or Design System.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufrumd8u9g08zgptqlmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufrumd8u9g08zgptqlmn.png" alt="Hero image of Design Tokens Generator — app logo is placed on the surface with the wording pattern" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Design Tokens Generator offers an intuitive interface and user-friendly features to streamline the process of working with design tokens and achieve greater design consistency and scalability in projects faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Tokens in a Nutshell
&lt;/h2&gt;

&lt;p&gt;Design tokens &lt;a href="https://www.youtube.com/watch?v=wDBEc3dJJV8&amp;amp;ab_channel=SalesforceDevelopers"&gt;have revolutionized the way&lt;/a&gt; we approach design systems. These &lt;strong&gt;platform-agnostic design values&lt;/strong&gt; serve as the &lt;em&gt;sub-&lt;a href="https://uxdesign.cc/atomic-design-101-8c5106d77717"&gt;atomic&lt;/a&gt; units of a design language&lt;/em&gt; (see illustration below), ensuring consistency and coherence across various platforms and projects. Design tokens also enable the creation of a cohesive and harmonious &lt;strong&gt;design language&lt;/strong&gt;. By establishing standardized values for &lt;strong&gt;colors&lt;/strong&gt;, &lt;strong&gt;typography&lt;/strong&gt;, &lt;strong&gt;spacing&lt;/strong&gt;, and other design elements, a consistent visual identity is maintained.&lt;/p&gt;

&lt;p&gt;By defining design attributes as tokens, updates or changes can be easily applied and propagated throughout the system. This eliminates the need for manual updates across multiple instances, saving time and effort while ensuring consistency. A &lt;em&gt;single modification to a design token can have a cascading effect&lt;/em&gt;, harmonizing the design elements across the entire project. In a nutshell, it is the concept of design tokens as the &lt;strong&gt;&lt;a href="https://ester.co/blog/design-tokens-powerful-design-system-engine#Understanding_Design_Tokens_The_Key_to_Scalable_Design_Systems"&gt;foundation for a design system&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5na7vn73b5asx9k9eoyb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5na7vn73b5asx9k9eoyb.png" alt="Atomic design illustration by Brad Frost (https://bradfrost.com/blog/post/extending-atomic-design/), left to right progression of icons represents design tokens, atoms, molecules, organisms and templates" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leveraging the benefits of design tokens, teams can streamline their design processes, enhance collaboration between designers and developers, create more efficient design systems and deliver exceptional user experiences.&lt;/p&gt;

&lt;p&gt;See also: &lt;a href="https://www.w3.org/community/design-tokens/"&gt;W3C Design Tokens Community Group&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Design Tokens no one talks about
&lt;/h2&gt;

&lt;p&gt;While design tokens are a simple and powerful concept in theory, their &lt;em&gt;practical implementation&lt;/em&gt; can often become daunting and complex. What initially seems like a straightforward solution for maintaining design consistency across projects can quickly turn into a roadblock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyxfrx7vqq6zrmj5opmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyxfrx7vqq6zrmj5opmn.png" alt="Material design palette sample overview — there are 3 color groups, each represented by 1 lead and 10 auxiliary colors" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With design tokens teams face the issue of finding the right balance between &lt;em&gt;generality and specificity&lt;/em&gt;. Existing open-source solutions often fall into two extremes. &lt;strong&gt;On one hand&lt;/strong&gt;, there are prominent and generic solutions that aim to cover a wide range of use cases. While these solutions may be suitable for larger projects, they can be &lt;a href="https://www.lightningdesignsystem.com/design-tokens/"&gt;overwhelming&lt;/a&gt; and &lt;a href="https://www.ibm.com/design/language/"&gt;impractical&lt;/a&gt; for smaller-scale projects. &lt;strong&gt;On the other hand&lt;/strong&gt;, there are &lt;a href="https://www.skyscanner.design/latest/foundations/product-typography/overview.html"&gt;specific&lt;/a&gt; &lt;a href="https://druids.datadoghq.com/foundations/color"&gt;solutions&lt;/a&gt; that originate from other team projects. While these may address &lt;a href="https://luna.sainsburys.co.uk/foundations/colour/sainsburys-colours/"&gt;certain unique requirements&lt;/a&gt;, they lack the flexibility needed for customization and adaptation.&lt;/p&gt;

&lt;p&gt;In both scenarios, without a proper framework or tool, managing and adjusting design tokens can become a &lt;strong&gt;taxonomy nightmare&lt;/strong&gt;, &lt;em&gt;hindering maintainability and scalability&lt;/em&gt;. Struggle with organization, support, and maintenance of their design tokens hampers productivity and can lead to inconsistencies across projects.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll get to know the Design Tokens Generator, a solution specifically designed to address these challenges and make working with design tokens more approachable and intuitive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing the Design Tokens Generator
&lt;/h2&gt;

&lt;p&gt;The Design Tokens Generator was developed as an approachable solution to &lt;strong&gt;bridge the gap&lt;/strong&gt; between the simplicity of design tokens and their practical implementation.&lt;/p&gt;

&lt;p&gt;It offers a streamlined and intuitive experience for designers and web developers, &lt;strong&gt;making design tokens more user-friendly and manageable&lt;/strong&gt;. The app also strives to simplify the customization of design tokens, empowering you to create and adjust tokens with ease while ensuring consistency and scalability across your projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kb51dpy727ir66vj9n3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kb51dpy727ir66vj9n3.png" alt="Design Tokens Generator logo with wording on the side" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Streamlined Design Tokens Naming Model&lt;/strong&gt;: The app offers an opinionated, simplified and intuitive naming model for design tokens. It allows for easier memorization and organization of tokens, enhancing the efficiency of tokens management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sufficient and Effective Tokens for Small- and Mid-Scale Projects&lt;/strong&gt;: The Design Tokens Generator provides a curated assembly of design tokens that are &lt;em&gt;suitable for small- to mid-scale projects&lt;/em&gt;. It eliminates the complexity of dealing with an overwhelming number of tokens, ensuring that you have the necessary tokens without unnecessary clutter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intuitive UI for Customization&lt;/strong&gt;: The app features an intuitive user interface that allows you to &lt;em&gt;customize design tokens&lt;/em&gt; according to your project requirements. You can easily adjust prefixes, typescale, color palette, unit formats, and more, enabling you to create a design system that aligns with your unique needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Interactive Preview Area&lt;/strong&gt;: The Design Tokens Generator includes a live preview area where you can &lt;em&gt;instantly visualize the adjusted tokens&lt;/em&gt;. This allows you to see the impact of your customization in real-time, making it easier to fine-tune and iterate on your design system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export to Popular Formats&lt;/strong&gt;: The app enables you to export your design tokens to popular formats, with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;CSS variables&lt;/a&gt; as the foundation. This makes it effortless to integrate the generated tokens into your frontend codebase and ensure consistency across different platforms.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now let’s dive deeper into the practical side of Design Tokens Generator. In the following section, we will explore how design tokens are organized within the &lt;em&gt;Sets and Collections&lt;/em&gt;. By understanding these concepts and interface controls, you will gain a deeper understanding of how to harness the power of design tokens for your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collections, Sets and their Purpose
&lt;/h2&gt;

&lt;p&gt;The Design Tokens Generator is organized in a straightforward and intuitive manner. The app groups design tokens into &lt;strong&gt;compact clusters known as Sets&lt;/strong&gt;. These Sets are then combined into &lt;strong&gt;larger groups called Collections&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are &lt;strong&gt;five Collections&lt;/strong&gt; in the App, each based on &lt;em&gt;design domain principles&lt;/em&gt;. Each Collection comprises one or more Sets, providing a robust and complete set of design tokens.&lt;/p&gt;

&lt;p&gt;To understand the &lt;strong&gt;contents&lt;/strong&gt; of the Collections, a quick glance at the Design Tokens Generator logo from both sides is sufficient:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2rkdw301zh4x8qi3hnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2rkdw301zh4x8qi3hnf.png" alt="Design Tokens Generator Logo 2-sided layout, left is combination of letters D and T, right is the group of 4 icons — crown, alien, devil and palette — around a cog icon element" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each Collection here is represented by a distinct icon or &lt;em&gt;emoji&lt;/em&gt;. These Collections serve as the foundation for organizing and managing your design tokens, revolving around a Core element that drives consistency and coherence throughout a project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Colors — Palette&lt;/strong&gt; (🎨) Naturally, the Palette icon represents the extensive &lt;em&gt;Colors Collection&lt;/em&gt;. This collection allows you to define and harmonize your color palette, creating a visually appealing and cohesive experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content — Crown&lt;/strong&gt; (👑) The Crown icon embodies the &lt;em&gt;Content Collection&lt;/em&gt;. It focuses on typography and text visualization, ensuring a regal prominence for your textual elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Space and Dimension — Alien&lt;/strong&gt; (👽) The Alien icon signifies the &lt;em&gt;Space and Dimension Collection&lt;/em&gt;. Space and dimension (along with &lt;em&gt;sizes&lt;/em&gt;) are combined for a generalized approach. This simplifies the management and organization of these design aspects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Details — Devil&lt;/strong&gt; (😈) The Devil icon personifies the &lt;em&gt;Details Collection&lt;/em&gt;. This collection adds that extra touch, injecting personality and flair into your visual language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generic Properties — Cog&lt;/strong&gt; (⚙️) Lastly, the centerpiece Cog icon represents the collection of &lt;em&gt;Generic Properties (the Core Collection)&lt;/em&gt;. These properties may not be design-specific or limited to mobile app development but play a fundamental role in shaping your project, such as defining the &lt;code&gt;root-size&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let’s dive deeper into each Collection and explore the unique Sets they contain. These Collections have been thoughtfully organized to cover various aspects of your design system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Palette Collection (🎨)
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, the Palette Collection contains the most Sets and tokens. Here’s a brief overview of the Sets within this Collection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Brand colors&lt;/em&gt;: &lt;strong&gt;Primary&lt;/strong&gt;, &lt;strong&gt;Secondary&lt;/strong&gt;, and &lt;strong&gt;Accent&lt;/strong&gt; color Sets representing a brand’s main color scheme.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Util colors&lt;/em&gt;: &lt;strong&gt;Safe&lt;/strong&gt;, &lt;strong&gt;Alert&lt;/strong&gt;, &lt;strong&gt;Info&lt;/strong&gt;, and &lt;strong&gt;Warning&lt;/strong&gt; color Sets for elements like loading indicators, error messages, and notifications.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Foundation colors&lt;/em&gt;: &lt;strong&gt;Gamma&lt;/strong&gt;, &lt;strong&gt;Base&lt;/strong&gt;, and &lt;strong&gt;Contrast&lt;/strong&gt; color Sets. Gamma offers a range of shades, while Base and Contrast define background and content colors for high contrast and accessibility.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Text colors&lt;/em&gt;: A standalone &lt;strong&gt;Text&lt;/strong&gt; Set with regular, strong, subtle, and accent variations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbtszfivk3ojigaseyzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbtszfivk3ojigaseyzu.png" alt="Screenshot of Design Tokens Generator app showcasing the Primary color Set interface and output" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a closer look at the structure of a color Set (excluding Foundation colors). Each Set consists of the following values: &lt;code&gt;base&lt;/code&gt;, &lt;code&gt;contrast&lt;/code&gt;, &lt;code&gt;tint&lt;/code&gt;, &lt;code&gt;shade&lt;/code&gt;, and &lt;code&gt;tone&lt;/code&gt;. The lead color (&lt;strong&gt;Primary&lt;/strong&gt; &lt;code&gt;#3675e8&lt;/code&gt; in this example) is tweakable, and other values are calculated automatically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;contrast&lt;/strong&gt;: the color that provides sufficient contrast to the base color (values are inherited from the Gamma Set)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tint&lt;/strong&gt;: a brighter and more intense variation of the base color&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;shade&lt;/strong&gt;: a darker and less vibrant variation of the base color&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tone&lt;/strong&gt;: a mixture of the base color with a neutral gray&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;strong&gt;Gamma&lt;/strong&gt;, you can also define the lead color and adjust the gamma progression control:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ua5dn302q3ryrs56jpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ua5dn302q3ryrs56jpz.png" alt="Screenshot of Design Tokens Generator app showcasing the Gamma Set of colors interface and output" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Text colors have regular, strong, subtle, and accent variations, with the option to switch or invert them depending on your primary theme. Experimenting with the Gamma and Text settings can help achieve the desired result for your project.&lt;/p&gt;

&lt;p&gt;To explore the Palette Collection further please &lt;a href="https://www.design-tokens.dev/concept#palette-collection"&gt;refer to the guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Collection (👑)
&lt;/h3&gt;

&lt;p&gt;The Content Collection focuses on three essential Sets: &lt;strong&gt;Font Size&lt;/strong&gt;, &lt;strong&gt;Line Height&lt;/strong&gt;, and &lt;strong&gt;Icon Size&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Font Size and Icon Size Sets share similar naming conventions and settings options. The tokens in these Sets follow a “&lt;em&gt;t-shirt&lt;/em&gt;” &lt;a href="https://en.wikipedia.org/wiki/Clothing_sizes"&gt;naming approach&lt;/a&gt;, ranging from &lt;code&gt;xs&lt;/code&gt; to &lt;code&gt;xxl&lt;/code&gt;, with a default base &lt;code&gt;n&lt;/code&gt; value of &lt;code&gt;1rem (16px)&lt;/code&gt;. The values for other tokens are calculated based on the chosen &lt;strong&gt;scale setting&lt;/strong&gt;, which determines the progression of the tokens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yzei3zh5q7s3ou7s4oh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yzei3zh5q7s3ou7s4oh.png" alt="Screenshot of Design Tokens Generator app showcasing the Font Size Set interface and output" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scale options represent multipliers that build upon the previous value, allowing for distinct or subtle differences in size. Practically this control uses the &lt;a href="https://spencermortensen.com/articles/typographic-scale/"&gt;type scale generation&lt;/a&gt; principles, allowing to have a quick all-in-one solution within the app.&lt;/p&gt;

&lt;p&gt;The Line Height Set, on the other hand, is controlled slightly differently due to the nature of this property. It typically requires fewer values. The tokens in this Set include &lt;code&gt;regular&lt;/code&gt;, &lt;code&gt;tight&lt;/code&gt;, and &lt;code&gt;loose&lt;/code&gt;, representing different line spacing options.&lt;/p&gt;

&lt;p&gt;In summary, the Content Collection provides you with the tools to control the sizes of fonts, line heights, and icons in your design system. To explore the Content Collection further please &lt;a href="https://www.design-tokens.dev/concept#content-collection"&gt;refer to the guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Space Collection (👽)
&lt;/h3&gt;

&lt;p&gt;The Space Collection in the Design Tokens Generator consists of two sets: the main Set — &lt;strong&gt;Spacing&lt;/strong&gt; and the auxiliary Set — &lt;strong&gt;Local&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Spacing&lt;/strong&gt; Set provides a range of spacing tokens, named with a numeric convention such as &lt;code&gt;space-075&lt;/code&gt;, &lt;code&gt;space-100&lt;/code&gt;, &lt;code&gt;space-125&lt;/code&gt;, and so on. The base token, &lt;code&gt;space-100&lt;/code&gt;, serves as the reference point, and other values are derived from it using a &lt;em&gt;progression factor&lt;/em&gt;. This approach offers a &lt;strong&gt;practical and intuitive way to work with spacing&lt;/strong&gt;, allowing you to easily calculate and adjust values based on the base token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febyt780t10qpvikkoary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febyt780t10qpvikkoary.png" alt="Screenshot of Design Tokens Generator app showcasing the Spacing Set interface and output, also it showcases the interactive tooltip informing about control usage" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The progression of the spacing scale is (almost) linear, unlike the typography generation style. It follows the design principle of using a standard grid with 2 or &lt;a href="https://uxdesign.cc/the-4px-baseline-grid-89485012dea6"&gt;4-pixel increments&lt;/a&gt;, which simplifies offset setups and ensures easy maintenance.&lt;/p&gt;

&lt;p&gt;However, if you need specific measurements that don’t align with the scale, you can calculate them on the spot using the base token (&lt;code&gt;space-100&lt;/code&gt;) and multiplying it by the desired factor.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Local&lt;/strong&gt; Set within the Space Collection introduces specific &lt;strong&gt;pattern-like tokens&lt;/strong&gt; that are particularly useful in web and mobile app development. These tokens can be customized and experimented with to meet the specific needs of your project. In addition to the predefined tokens, you also have the flexibility to add &lt;em&gt;custom spacing tokens based on specific use cases&lt;/em&gt;. Naming them &lt;strong&gt;according to their purpose&lt;/strong&gt; enhances their association with particular patterns in your project.&lt;/p&gt;

&lt;p&gt;Welcome to further explore the Space Collection in the &lt;a href="https://www.design-tokens.dev/concept#space-collection"&gt;guide document&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Details Collection (😈) aka “the Det😈ils”
&lt;/h3&gt;

&lt;p&gt;It’s getting hotter and more exciting!&lt;/p&gt;

&lt;p&gt;Details play a crucial role in enhancing the user experience. While &lt;em&gt;too many details can be overwhelming&lt;/em&gt;, carefully chosen details can &lt;strong&gt;elevate the overall design&lt;/strong&gt;. The Sets in the Details Collection are diverse and not directly related to each other, but they can inherit color and spacing values from other Sets within the design system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Radius&lt;/strong&gt;: This Set focuses on defining the radii of elements. It includes tokens for standard values like &lt;code&gt;small&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, and &lt;code&gt;large&lt;/code&gt;, as well as auxiliary tokens like &lt;code&gt;round&lt;/code&gt; and &lt;code&gt;pill&lt;/code&gt;, which determine the shape of the element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shadow&lt;/strong&gt;: The Shadow Set offers natural-looking shadow tokens along with the tint color. Tokens are named &lt;code&gt;small&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, and &lt;code&gt;large&lt;/code&gt; for consistent usage. The &lt;strong&gt;tint color&lt;/strong&gt; can be adjusted based on the design requirements of your project. Typically, it is derived from black or dark shades of gray, however you may need to adapt the tint color to ensure a more cohesive and natural appearance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfb0vwh11m4scywvo1ob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfb0vwh11m4scywvo1ob.png" alt="Screenshot of Design Tokens Generator app showcasing the Shadow Set interface and output, also it showcases the color picker interface for the tint color" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duration&lt;/strong&gt;: The Duration Set consists of tokens that control transitions and animations in your application and implement the time concept. The available tokens include &lt;code&gt;instant&lt;/code&gt;, &lt;code&gt;short&lt;/code&gt;, &lt;code&gt;regular&lt;/code&gt;, &lt;code&gt;long&lt;/code&gt;, and &lt;code&gt;extra&lt;/code&gt; values. When selecting a base value and scale for timing tokens, consider that the &lt;strong&gt;duration of transitions should be proportionate to the size of the element&lt;/strong&gt;. Larger elements generally require longer transitions, while smaller elements benefit from shorter and more responsive animations.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;strong&gt;Selection&lt;/strong&gt; Set enables you to control the colors for content selection in your project. While often overlooked, customizing the selection colors can easily add an individual touch to the overall design.&lt;/p&gt;

&lt;p&gt;Welcome to explore the Details Collection on a &lt;a href="https://www.design-tokens.dev/concept#details-collection"&gt;dedicated page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Collection (⚙️)
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Core&lt;/strong&gt; Collection is composed of a single Set that contains a crucial token for global control over the scale of your application. This token is named &lt;code&gt;root-size&lt;/code&gt; and represents the &lt;code&gt;font-size&lt;/code&gt; value that can be applied to the root element, typically the &lt;code&gt;html&lt;/code&gt; tag in web projects. By default, the root font size is set to &lt;code&gt;16px&lt;/code&gt; in most modern browsers.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;root-size&lt;/code&gt; token can be utilized in calculations for relative values. For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--awsm-root-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.25&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--awsm-root-size&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;font-size&lt;/code&gt; of the root element is set to the value of &lt;code&gt;--awsm-root-size&lt;/code&gt;, and for viewport widths larger than &lt;code&gt;1200px&lt;/code&gt;, it is increased by a scale factor of &lt;code&gt;1.25&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;rem&lt;/code&gt; unit in conjunction with the &lt;code&gt;root-size&lt;/code&gt; token ensures a consistent visual style for your application, irrespective of the device resolution or rendering medium. It also helps maintain accessibility compliance when users zoom in or out. By controlling the &lt;code&gt;root-size&lt;/code&gt; token, you can establish a consistent and responsive typography system across your entire application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flexibility and Control
&lt;/h2&gt;

&lt;p&gt;Each tokens Set consistently bears the minimum amount of similar controls, specifically the Prefix editor and Toggle control, allowing for more flexible organization of your design tokens output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zd78ekll2qbsju0u525.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zd78ekll2qbsju0u525.png" alt="Schematic illustration of the tokens Set showcasing the Prefix, Toggle and other controls" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefix editor&lt;/strong&gt; allows you to modify the prefix for the Set. When you change the prefix, all related tokens that use the current tokens as aliases will be updated in real time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Toggle control&lt;/strong&gt; allows you to include or exclude the current Set from the final output. By default, the Set is included in the output. Note, that the &lt;em&gt;Preview area will always visualize &lt;strong&gt;all&lt;/strong&gt; token values&lt;/em&gt; regardless of the toggle state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19w9zvm4055o0n21vec2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19w9zvm4055o0n21vec2.png" alt="Schematic illustration of the app Header featuring the Global Prefix and other controls" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we “zoom out” to the global scope of the app, we’ll find the &lt;strong&gt;Global Prefix editor&lt;/strong&gt; in the central area of the Header. This control allows you to customize the overall assembly of design tokens according to your specific project requirements. Global prefixes are commonly used in UI libraries and design systems to prevent variable clashes and accidental overrides. They also make it easier to distinguish between custom and inherited tokens in your codebase.&lt;/p&gt;

&lt;p&gt;Another global feature of Design Tokens Generator is the &lt;strong&gt;Auto-save functionality&lt;/strong&gt;. It’s convenient when you need to take a break between design sessions and &lt;em&gt;ensure consistency&lt;/em&gt; of your changes. Additionally app allows to quickly &lt;em&gt;reset all customizations&lt;/em&gt; to the original state.&lt;/p&gt;

&lt;p&gt;It’s worth noting that saved changes are local to your browser and may be reset if you clear your browser data. The app strives to preserve your data, but depending on the complexity of updates and release changes, there may be cases where everything is reset. Therefore, it’s &lt;em&gt;recommended to save your work externally&lt;/em&gt; to ensure its safety.&lt;/p&gt;

&lt;p&gt;Those and other useful controls can be further explored in the &lt;a href="https://www.design-tokens.dev/concept#app-interface"&gt;official guide&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;I hope that &lt;a href="https://www.design-tokens.dev/"&gt;Design Tokens Generator&lt;/a&gt; 👑😈🎨👽 can be used with fun and practicality to your everyday work. The aim is to provide you with a tool that enhances your creative process and makes your projects more enjoyable to develop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tx9pq690z142xoecjkv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tx9pq690z142xoecjkv.png" alt="Design Tokens Generator Logo vertical centered layout, at the top there is the group of 4 icons — crown, alien, devil and palette — around a cog icon element and at the bottom there is a title" width="800" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My primary focus with this App is to make design tokens and design system establishment &lt;strong&gt;more approachable for small- and mid-scale projects&lt;/strong&gt;, regardless of team size and expertise. It can be a perfect companion for the Design, Frontend, Full-stack and Product management needs in Web and App development areas.&lt;/p&gt;




&lt;p&gt;Your feedback is valuable and welcome. If you have any suggestions for improvement, feature requests, or general feedback, please &lt;a href="//mailto:hello@design-tokens.dev"&gt;reach out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also feel free to reach out to me for collaboration opportunities or to share your thoughts on how the app can be further enhanced to meet your needs. Please refer for more information to the &lt;a href="https://www.design-tokens.dev/about"&gt;About page&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bookmark &lt;a href="https://www.design-tokens.dev/"&gt;Design Tokens Generator&lt;/a&gt; and stay tuned for new releases, updates and useful tutorials!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And of course, your support is very much appreciated!&lt;br&gt;&lt;br&gt;
Check out the &lt;a href="https://www.producthunt.com/products/design-tokens-generator-2"&gt;ProductHunt page&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Welcome to spread the word about Design Tokens Generator via &lt;a href="https://twitter.com/share?url=https://www.design-tokens.dev&amp;amp;text=Streamline%20your%20design%20system%20with%20Design%20Tokens%20Generator%20-%20Get%20consistent%20and%20efficient%20design%20elements"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/sharer/sharer.php?u=https://www.design-tokens.dev"&gt;Facebook&lt;/a&gt; and other networks.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>css</category>
      <category>design</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
