<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Robb Owen Digital</title>
  <subtitle>Robb is an independent creative developer from South Wales who loves helping organisations turn exciting ideas into complex, creative products. Sometimes, he also writes in the third person.</subtitle>
  <link href="https://robbowen.digital/feed.xml" rel="self"/>
  <link href="http://robbowen.digital/"/>
  <updated>Sun Jun 01 2025 00:00:00 GMT+0000 (Coordinated Universal Time)</updated>
  <id>https://robbowen.digital/</id>
  <author>
    <name>Robb Owen</name>
    <email>hello@robbowen.digital</email>
  </author>
  
  <entry>
    <title>Looking elsewhere</title>
    <link href="http://robbowen.digital/wrote-about/looking-elsewhere/"/>
    <updated>2025-06-01T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/looking-elsewhere/</id>
    <content type="html">&lt;p&gt;There’s a word in the Welsh language that you might have come across, part of that wonderful class of words that defy direct one-to-one translation into English: &lt;strong&gt;Hiraeth&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Broadly speaking, hiraeth is a deep sense of longing for some other time, people or place where you felt at home. It has been described as the lingering presence of absence; not really nostalgia, but a sort of gnawing, idealised homesickness to be somewhere else and be part of something else that no longer exists or, perhaps, has yet to come.&lt;/p&gt;
&lt;h2&gt;Hype-driven development&lt;/h2&gt;
&lt;p&gt;I—as I suspect was the case for many of us—entered the industry wide-eyed and optimistic. Finding my feet in tech in the early 2010s was exciting and everybody talked with naïve passion and a pioneering spirit about building positive change for the future. There seemed to be a real drive to hone our craft, to work tirelessly for the benefit of our users, for our work to be taken seriously as more than just programmers and designers. We were makers. &lt;em&gt;Web Artisans&lt;/em&gt;. Shit, it seems almost laughably pretentious now. Somewhere along the way though, with a few success stories under the industry&#39;s belt, the profits just got too big and something fundamental shifted. Turns out that there&#39;s no time for craft when there&#39;s another round of funding to secure.&lt;/p&gt;
&lt;p&gt;Now, at the mid point of the 2020s, it&#39;s as if the big-tech manifesto of &amp;quot;move fast and break things&amp;quot; has reconditioned an entire generation of makers to prioritise speed and efficiency over quality and care. With our standards lowered, the shift inwards has also become more pronounced. Where industry peers were once chatting about &lt;em&gt;what&lt;/em&gt; they were making and &lt;em&gt;for whom&lt;/em&gt;, we now seem fixated solely on what we&#39;re building &lt;em&gt;with&lt;/em&gt;. We have sleepwalked into becoming an industry that makes tools for toolmakers; an unending tech-bro-robourus of solutions in search of problems, masking purpose with hype for the next big thing. And that hype-cycle is only getting faster. In just a few short years we&#39;ve seen the crest and crash of countless industry darlings from JavaScript frameworks, to Blockchain and NFTs, right up to the current flavour-of-the-week: AI. But, as with every well-intentioned new product or clever solution, that-which-wasn&#39;t-considered becomes a vector for harm and AI finally sees the tech industry going after its own workers. Not content to have disrupted hospitality, taxis and delivery-drivers-as-a-service, companies are now axing their contractor budgets or laying off record numbers of staff as they chase the spoils of generative AI.&lt;/p&gt;
&lt;p&gt;I want to make it clear that I am not &lt;em&gt;against&lt;/em&gt; AI, much in the same way as I am not against hammers—they are both tools—but I am equally not going to advocate that you use your hammer to demolish the work of another artist for personal financial gain. It&#39;s how these tools are being used makes the difference and, currently, the industry is drunkenly swinging its ill-gotten sledgehammer at the foundations of our societies. In the face of this reckless pursuit of hype-over-craft, I can&#39;t help but find myself longing to be part of something that makes good on the industry&#39;s early promise of responsible change. Thankfully, I know I am not alone in this. This time last month I attended &lt;a href=&quot;https://heypresents.com/&quot;&gt;All Day Hey&lt;/a&gt; in Leeds and talk tended towards how we might change the course of the industry. As I&#39;ve been gathering my thoughts in the days since, discussion online has also turned to many of same issues. At times it can feel like these conversations are eulogising our careers but I don&#39;t see any lack of desire to put the work in, rather a deep cultural burnout at the circumstances and motivations currently driving change in the industry. Despite it all, we still care.&lt;/p&gt;
&lt;p&gt;It all begs the question, what might the future look like for those of us who still commit ourselves to craft? Fortunately for us, tech is not the first industry forced to defend itself against dubious practices and it likely won&#39;t be the last. Perhaps those industries that survived could be an example. What could we learn if we were to look elsewhere?&lt;/p&gt;
&lt;h2&gt;Raising the standard&lt;/h2&gt;
&lt;p&gt;When it comes to painstakingly technical, user-centred work there are few industries more famous than the tailor-made fashion houses of Paris. Training for years with traditional methods, pushing the boundaries of their craft, making individual garments with great precision and with only the finest materials; for many, they represent the archetypal &amp;quot;Artisan&amp;quot;. But, for every artisan who cares about the craft, there&#39;s always someone else willing to compromise it for a quick buck. Throughout its storied history, the high-fashion industry has seen it all: corner cutting, poor quality materials, labour threats from mechanisation and rampant design piracy. Nowadays, we know many of these practices as fast-fashion, but they have been a thorn in the side of the fashion industry since the 1700s. That&#39;s not to say that the industry didn&#39;t fight back. Originally managed by a mercantile guild system these guilds would grow into an association who, by the late 1800s, would require that ateliers meet a baseline technical and material standard to be able to sell their garments within the city. Later, when piracy got out of hand, the heads of the industry implemented a further system of copy-protection by meticulously photographing and cataloguing each new design. Time after time the ateliers saw off threats to their livelihoods, but the industry&#39;s biggest challenge would come with the fall of Paris to Nazi Germany during World War II.&lt;/p&gt;
&lt;p&gt;Seeing the strong reputation of tailor-made fashion within France, the fascist occupiers immediately sought to seize the industry&#39;s design documentation, dismantle the ateliers for raw materials and forcibly relocate those involved in production to Berlin, or far worse. In the face of the new regime, some ateliers immediately capitulated and dismissed their workforces. Many others did whatever they were able to safeguard their industry and those working within it. When liberation eventually came in 1945, the surviving ateliers of Paris, led by &lt;a href=&quot;https://en.wikipedia.org/wiki/Lucien_Lelong#Under_occupation&quot;&gt;Lucien Lelong&lt;/a&gt;, came together again to codify their practices and ethics into law. In contrast to the association rules of the 1800s, this new legal standard would prevent any further attempts to undermine their craft by formally separating their work from fast-fashion practices. The law would not only ensure that only garments produced with certified materials and to exacting technical standards would be allowed to bear the name &amp;quot;Haute Couture&amp;quot;, but they would also stipulate the location and number of employees an atelier must have, thereby safeguarding many of the ancilliary professions and practitioners that contributed to their work.&lt;/p&gt;
&lt;h2&gt;Fast-software&lt;/h2&gt;
&lt;p&gt;Chasing trends and profits, cutting corners and undermining skilled workforces whilst providing ill-fitting, broken products with no regard for longevity; the modern software world sounds a whole lot like fast-fashion. With that in mind, two aspects of the ateliers&#39; pushback are particularly interesting to me - the first is the role of standards.&lt;/p&gt;
&lt;p&gt;So far I have made many references to &amp;quot;craft&amp;quot; and &amp;quot;quality&amp;quot;, but those terms can be hard to define. Motivations are more tangible. Compiling their accumulated knowledge &lt;em&gt;in service of the customer&lt;/em&gt; allowed the ateliers of Paris to define precisely what &amp;quot;good work&amp;quot; meant for their industry. With such a baseline in place, should the primary motivation for producing work be &lt;em&gt;anything&lt;/em&gt; other than meeting the customer&#39;s needs, the work fails by default. By also treating their standard as a living document, ateliers are able to focus on the betterment of their output, adapting and evolving their practice to meet the changing challenges of their industry.&lt;/p&gt;
&lt;p&gt;It should hopefully come as no surprise that web industry is already home to several evolving standards for the semantic use of HTML and Accessibility. The &lt;a href=&quot;https://www.w3.org/standards/&quot;&gt;web-standards&lt;/a&gt; movement and &lt;a href=&quot;https://www.w3.org/WAI/standards-guidelines/wcag/&quot;&gt;&lt;abbr title=&quot;Web Content Accessibility Guidelines&quot;&gt;WCAG&lt;/abbr&gt;&lt;/a&gt; are the cumulative work of years of user-focused research and provide us with a jumping off point for doing work in service of our users. Meeting standards must be intentional though and for a long time developers have neglected them. This presents us with an opportunity. Though AI has access to a vast dragons-hoard of code, much of that code does not follow standards and, by consequence, the output of LLMs and design-tooling tends to be a &lt;a href=&quot;https://www.youtube.com/watch?v=ZsFIvULxkHI&quot;&gt;chaotic rat-king of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s with no regard for semantics or accessibility&lt;/a&gt;. In this context it becomes clear that standards are a vital point of differentiation for craft-led practice. By design, these initiatives put the user experience above the developer experience. Following standards immediately shifts emphasis back to the &amp;quot;why&amp;quot; over the &amp;quot;how&amp;quot; and recentres the user within our work. If we collectively commit to following standards, we commit to &amp;quot;good work&amp;quot; by default.&lt;/p&gt;
&lt;p&gt;There is a common rebuttal to the use of standards, in that it is simply too hard and too time consuming for one maker to amass the level of knowledge to follow these rules end-to-end. The problem with this idea though, is that it assumes that only one maker should hold knowledge. This is the second aspect of the ateliers&#39; pushback - solidarity with adjacent professions. The heads of the industry understood that respect and accountability to one another would enforce their standards. To understand a bit more about how accountability to adjacent disciplines can enrich craft, let&#39;s take a look at another industry.&lt;/p&gt;
&lt;h2&gt;The case for blurred specialisms&lt;/h2&gt;
&lt;p&gt;To be honest, it&#39;s hard to keep mentioning the idea of web artisans without addressing all of the millenial-hipster jokes about organic, free-range TypeScript, artisanal hyperlinks and &lt;code&gt;farm-to-&amp;lt;table&amp;gt;&lt;/code&gt; layouts. The thing is though, the farm-to-table movement and its reinterpretation of the restaurant industry is a genuine example of how solidarity feeds craft. With that in mind, let&#39;s just lean into it for a moment.&lt;/p&gt;
&lt;p&gt;Widely attributed to the work of &lt;a href=&quot;https://en.wikipedia.org/wiki/Alice_Waters&quot;&gt;Alice Waters at Chez Panisse&lt;/a&gt;, farm-to-table dining presents cooperation as industry counterculture. In the face of questionable low-quality-high-profit practices overtaking the restaurant industry, the movement instead asked what might happen if they focused on sustainability, quality and shared knowledge. The big picture looks a little like this:&lt;/p&gt;
&lt;p&gt;In order to provide their diners with the best experience, a chef will spend years honing their technical skills. At the same time however, the chef understands that their execution is reliant on a foundation of quality ingredients, so seeks out growers with deep knowledge of their &lt;a href=&quot;https://en.wikipedia.org/wiki/Terroir&quot;&gt;terroir&lt;/a&gt;. Each knows their own limitations but, as their domains overlap, the chef and grower might then work together to enhance flavour or develop new cultivars that can stand up to different cooking techniques. With time, the process becomes reciprocal. The qualities of the grower&#39;s produce inspires the chef&#39;s work which influences the grower to apply their skills, which further elevates the chef. In this kind of scenario, it is not uncommon to see a grower&#39;s name featured prominently on a menu alongside the ingredients that they have worked hard to cultivate. The grower is not just a supplier, but respected as an equal artisan.&lt;/p&gt;
&lt;p&gt;My takeaway from this concept is that, in order to master your own domain, the boundaries of your specialism will often blur with specialists in other domains. With a mindset of mutual respect, the whole process of making is not a cycle of hype, but one of intentionality and growth.&lt;/p&gt;
&lt;h2&gt;But should designers code, though?&lt;/h2&gt;
&lt;p&gt;At this point you might quite rightly be thinking that blurred-specialisms sounds a lot like the T-shaped skillset touted by corporate leadership. You might also be thinking, &amp;quot;we&#39;ve already tried that, how can that be a future for the industry?&amp;quot;&lt;/p&gt;
&lt;p&gt;The difference, again, is one of intentionality.&lt;/p&gt;
&lt;p&gt;A top-down, KPI-driven approach rarely cares about craft over profits. This means, sadly, that the common implementation of T-shaped workers expects them to carry out the roles of other specialists in addition to their own. Take full-stack development or the age-old &amp;quot;should designers code&amp;quot; debate as examples - From a c-suite perspective, these are matters of efficiency; surplus roles can be eliminated if one worker can step up to do two or three functions within the organisation. By that same logic, if AI tooling can be trained well enough, it has the potential to replace all roles. Remember, line goes up if wages go down.&lt;/p&gt;
&lt;p&gt;The fundamental difference for the artisan model is that it operates in parallel, driven by the workers themselves. Each artisan retains full autonomy of their own specialism, but chooses to supplement their knowledge about how adjacent disciplines relate to theirs. Most importantly, this is done not with the intent to replace the other specialist, but to inform and elevate the quality of one&#39;s own work. This comes with a mutual accountability on doing what is right for the end-user rather than on convenience and expediency. Being an artisan is less about hoarding and regurgitating &#39;acquired&#39; knowledge, and more about being part of a distributed support network of talented individuals. Success is a matter of community. To focus on craft is to stand on the shoulders of those who came before and lift those who come after.&lt;/p&gt;
&lt;h2&gt;Craft is community&lt;/h2&gt;
&lt;p&gt;When reading into these examples and others, I quickly came to see a common thread of respect, intent, ethical standards and community. These human qualities, as I see it, are key to differentiating the craft from the hype-machine - They simply cannot be replaced or replicated by LLMs or design tooling. In her fantastic post, &lt;a href=&quot;https://whitep4nth3r.com/blog/the-promise-that-wasnt-kept/&quot;&gt;&amp;quot;The promise that wasn&#39;t kept&amp;quot;&lt;/a&gt;, Salma writes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Real value is delivered through vision, creativity, experimentation, and using human brains to solve human-centred problems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This really resonates with me. To dehumanise the craft is to remove the value of the work.&lt;/p&gt;
&lt;p&gt;As an industry we often talk about &#39;the supply chain&#39; in terms of code packages and modules that we depend upon, but I believe there is value in refocusing on the people who support us. Much like the couturiers and culinarians we&#39;ve already talked about, the web industry was built on the skill, care and labour of people and what makes it great &lt;em&gt;is&lt;/em&gt; those people. In the face of mass layoffs, it is no longer enough to show up and sweat the details of your own work. It&#39;s the time to champion each other. Taking inspiration from the examples above, that might look something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Prioritise working to standards. Standards aren&#39;t a restraint, they&#39;re a peer-reviewed baseline against which you can determine the quality of your work, especially when the people who use your works may depend on them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Take full ownership of what you control and freely share knowledge with those around you so that they may build upon your work and elevate it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the same time, look elsewhere and allow youself to be inspired by your contemporaries in adjacent (or even unrelated) disciplines.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remember that your network is a strength. Give people their dues and allow them credit where their work has lifted your own.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Stand up for adjacent disciplines when you see their knowledge and autonomy being infringed upon and, where work is directly affected in the form of layoffs, &lt;a href=&quot;https://ethanmarcotte.com/books/you-deserve-a-tech-union/&quot;&gt;form unions&lt;/a&gt; and push back against unreasonable demands.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More than anything, go out find like-minded people. Where you can, go to meetups or local events and support initiatives such as &lt;a href=&quot;https://piccalil.li/blog/we-need-your-support-to-do-free-projects-for-good-causes-and-publish-free-high-quality-education/&quot;&gt;Piccalilli&#39;s open collective&lt;/a&gt; that aim to give back to the community. For those readers in the UK, here are a few fantastic community-led events to get you started:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://heypresents.com/&quot;&gt;All Day Hey&lt;/a&gt; in Leeds&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://frontendnorth.com/&quot;&gt;Frontend North&lt;/a&gt; in Sheffield&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webdevconf.com/&quot;&gt;WDC&lt;/a&gt; and &lt;a href=&quot;https://pixelpioneers.co/&quot;&gt;Pixel Pioneers&lt;/a&gt; in Bristol&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://londonwebstandards.org/&quot;&gt;State of the Browser&lt;/a&gt; in London&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffconf.org/&quot;&gt;FF Conf&lt;/a&gt; in Brighton&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;s&gt;Hype&lt;/s&gt; Hope-driven development&lt;/h2&gt;
&lt;p&gt;Despite the questionable stewardship of our industry, the continued resilience of artisans in other fields gives me hope that the devaluation of crafting for the web is not inevitable, rather a matter of framing. Do I believe that any of the ideas presented here will be the miraculous savior of the web industry? Likely not, but then I equally do not believe that there is a single saviour to be found. Whatever the course, I do believe that we need to put in the work together, share our specialisms and then, perhaps, something will happen.&lt;/p&gt;
&lt;p&gt;I&#39;m in little doubt that, as long as there is big money to be made, the exploitative, VC-appeasing hype-cycle will rumble ever onwards. However, much in the same way that artisan-tailors and farm-to-table dining continue to work &lt;em&gt;separately&lt;/em&gt; from the exploitative worlds of fast-fashion and fast-food, I have to believe that there can be another place—another market—for those of us who still care about the people and the craft. Not really a hard-reset, but a parallel fork of the industry that does right by its users and by the people working in it. Maybe it&#39;s misplaced idealism or perhaps it&#39;s foolishness, but I can&#39;t shake the hope of that place; some odd kind of hiraeth.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Getting something from Nothing</title>
    <link href="http://robbowen.digital/wrote-about/getting-something-from-nothing/"/>
    <updated>2024-02-28T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/getting-something-from-nothing/</id>
    <content type="html">&lt;p&gt;In the last couple of years I have developed a complicated relationship with my phone. Whether it&#39;s scrolling past an endless wall of hate on Twitter or feeling my mood buckle under the weight of every TikTok or Reel that I push skywards, the tiny computer in my pocket has started to feel a bit like that box from Dune – there&#39;s nothing but pain in there.&lt;/p&gt;
&lt;p&gt;Increasingly though, it&#39;s not just &amp;quot;in there&amp;quot; that&#39;s the problem. Over-reliance on my phone has often left me feeling like I&#39;m not being forced to remember things as much as I&#39;d like and the addictive nature of notifications means that I&#39;m less present when my pocket starts to buzz. My hobbies have taken a hit too. Whilst I&#39;ve always been keen on travel photography, I&#39;ve noticed that on my recent trips I&#39;ve been taking fewer and fewer photos with my camera. At the same time, I&#39;ve taken hundreds of mediocre smartphone photos. The convenience of the phone in my pocket has started to outweigh the joy that I get from composing and shooting with the camera in my bag. Honestly, it&#39;s made me sad for all of the missed opportunities.&lt;/p&gt;
&lt;p&gt;As much as I joke about disconnecting and running off to a cabin in the woods though, I&#39;m still quite realistic that I &lt;em&gt;do&lt;/em&gt; need a phone for modern life and work. There are many fantastic and necessary things that I love about having a smartphone to hand, I&#39;d just like to use it a little less compulsively.&lt;/p&gt;
&lt;p&gt;When my Samsung S21 literally and unceremoniously fell apart back in January of this year, I was confronted with the reality that I was going to have buy a new phone. But what do you buy when you want to use something capable, but much less frequently? Well, it turns out that there is a UK-based company called &lt;a href=&quot;https://nothing.tech/&quot;&gt;Nothing&lt;/a&gt; who pitch exactly that. As a &amp;quot;midrange&amp;quot; device the Nothing Phone 2 isn&#39;t as powerful as the latest iPhone or Samsung devices, but something that came up time and time again in reviews was that Nothing aim to help people to make more intentional use of their phones. That claim, along with the industrial design, sufficiently piqued my interest. After finding a great deal on the 512gb model, I decided to take a punt on it.&lt;/p&gt;
&lt;p&gt;Tech has a real issue with chasing statistics and I&#39;m always aware that, working in the industry, there can be a pressure to always have the latest and greatest device. After four weeks of use, I thought I&#39;d share a few thoughts on taking a side-step.&lt;/p&gt;
&lt;h2&gt;Software that makes you want to stop&lt;/h2&gt;
&lt;p&gt;Nothing&#39;s &amp;quot;use your phone less&amp;quot; ethos was undeniably appealing in choosing their device, but it remains questionable when a company is ultimately still in the market of selling phones. With that in mind, I&#39;m pleasantly surprised that the Nothing Phone 2&#39;s OS actually makes good on some of the company&#39;s claims about minimising distractions.&lt;/p&gt;
&lt;div class=&quot;break-out&quot;&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nothing-phone-4.jpg&quot; style=&quot;padding-top: 68.65861411315957%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nothing-phone-4.jpg&quot; alt=&quot;The Nothing OS home screen, showing the minimal, greyscale aesthetic&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Nothing OS is a light-touch skin over stock Android that adds just enough personality without reinventing the wheel. No emphasis on AI-driven nonsense and no pre-installed app bloatware; just a dot-matrix font and icons, some smart app-grouping options and a few useful widgets, all tied together with custom UI animations. Amusingly, those animations and transitions have a decent heft and easing to them which, despite the supposedly inferior chipset powering this phone, leaves the phone feeling snappy or, dare I say it, &lt;em&gt;blazingly fast&lt;/em&gt;. It&#39;s all very refreshing to use.&lt;/p&gt;
&lt;p&gt;The most important change for me though, is the focus on minimalism baked into the OS. You&#39;ll find a few accent colours dotted here and there, but my homescreen is now sparse to the point of being almost-monochrome. This design choice isn&#39;t just for the vibe though - instead of a multi-coloured wall of app icons and notifications bubbles just waiting to distract me, I now have a list of discreet black and white icons. Where a dedicated app icon isn&#39;t available, the OS applies a contrasty greyscale filter that, so far, hits more than it misses (check out the Elk icon in the photo above).&lt;/p&gt;
&lt;p&gt;In practice, removing the colour from the icons means that the list of apps is harder to scan and the net result is that I&#39;m spending less time compulsively hopping between apps. When the UI forces me to stop and read, my usage starts to feel more intentional. Chuck in a screen-time widget on the always-on display and I am genuinely finding that I&#39;m unlocking my phone less frequently. Admittedly these are all subtle tweaks to the software but, given how much I&#39;ve struggled to use my phone less, they come together to feel like a win.&lt;/p&gt;
&lt;p&gt;So, a win for the software. But what about the hardware?&lt;/p&gt;
&lt;h2&gt;Business in front, party in the back&lt;/h2&gt;
&lt;p&gt;In my opinion, Android phone designs in 2024 are wholly uninspiring. For a company that once released a phone in Thanos-chic purple and gold, Samsung&#39;s latest phones have been reduced to characterless monotone slabs. Then there&#39;s the latest Pixel phones from Google which look like a from-memory sketch of Daft Punk and are equally loaded with enough AI intrusions to make them seem Human After All.&lt;/p&gt;
&lt;p&gt;The Phone 2 stuck out to me as doing things a little differently. With its sharp screen, uniform bezel and hole-punched camera the front looks a lot like an iPhone 15, but flip it over and the back is a different story.&lt;/p&gt;
&lt;div class=&quot;break-out&quot;&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nothing-phone-1.jpg&quot; style=&quot;padding-top: 65.97434331093464%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nothing-phone-1.jpg&quot; alt=&quot;A detail shot of the back of the Nothing Phone 2&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The back of the Nothing Phone 2 is a glass panel that shows through to a design that hints at the interior components without actually showing them. The end result is a finish that is shiny and smooth yet, at the same time, the components underneath create a surprising amount of texture (something that made more sense when I learned that Teenage Engineering had a hand in Nothing&#39;s design language). The whole thing is surrounded by matte metal rails and yet remains surprisingly lightweight, given how solid the materials feel.&lt;/p&gt;
&lt;p&gt;Then there are also the lights – sorry, the &amp;quot;Glyph&amp;quot;.&lt;/p&gt;
&lt;div class=&quot;break-out&quot;&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nothing-phone-2.jpg&quot; style=&quot;padding-top: 63.1578947368421%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nothing-phone-2.jpg&quot; alt=&quot;The Nothing Phone 2, with the glyph illuminated&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The back glass of the phone is home to an excessive number of white LED lights that etch out a pattern which looks like it could have been part of some glitchy Channel 4 ident from 2001. Coincidentally, that&#39;s exactly what it sounds like too as the phone&#39;s sound design (by Swedish House Mafia) is straight out of the 56k modem school of music. Think: lots of glitchy pops, buzzes, cracks and clicks.&lt;/p&gt;
&lt;p&gt;Look, I&#39;ll be real: these lights are an absolute gimmick and I&#39;m so unconvinced by their actual utility as &amp;quot;distraction free&amp;quot; notification lights that I swiftly disabled them in the settings. At the same time though, I&#39;m &lt;em&gt;really&lt;/em&gt; glad they&#39;re here.&lt;/p&gt;
&lt;p&gt;You see, comparing stats like chip speed, resolution and screen brightness will only get you so far but there&#39;s another side to any design that is much more unquantifiable - how the thing makes you &lt;em&gt;feel&lt;/em&gt;. It&#39;s entirely subjective, but one of things that drew me to this phone is that the design reminds me of a very specific era of consumer electronics when hardware could be fun and wasn&#39;t so beholden to market expectations. I&#39;m talking about the transparent Gameboy Color or the smoke-grey N64; Apple&#39;s first iMac and the legendary G4 Cube; Sony&#39;s burgundy-lacquered MZ-E620 MiniDisc Walkman and the aqua-camouflage Neo Geo Pocket; The goddamn &lt;a href=&quot;https://en.wikipedia.org/wiki/WonderSwan&quot;&gt;&lt;em&gt;WonderSwan&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The back of this device is leaning into the early-2000s transparent aesthetic and, to be honest, I&#39;m here for it. Hardware design used to be weird, creative and exciting to me and the Nothing Phone taps back into that long-dormant part of my brain quite nicely. The first time I saw the back of my phone light up like a football pitch, I just had to smile at the absurdity. Somehow, it feels good.&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nothing-phone-3.jpg&quot; style=&quot;padding-top: 78.05555555555556%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nothing-phone-3.jpg&quot; alt=&quot;The Nothing Phone 2 sitting on top of its cardboard packaging&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;So, should you buy this phone if you were already planning on getting the latest iPhone or Samsung? Probably not. I got this phone after deciding that it was right for my needs. If it seems like it might be right for you too then go for it.&lt;/p&gt;
&lt;p&gt;I want to be realistic and step away from any brand Kool-aid though: This phone is far from perfect and the design won&#39;t speak to everyone. The back glass is slightly pillowed which makes it feel a lot thicker in the hand than it actually is. With a 6.7&amp;quot; screen, the phone sadly &lt;a href=&quot;https://front-end.social/@Robb/111819006088344024&quot;&gt;remains the size of a small horse&lt;/a&gt;. The camera (as every review quite fairly warned) has the distinction of being &lt;em&gt;bang average&lt;/em&gt; in anything other than good light. And lastly, let&#39;s not forget that Nothing themselves are such a new company that there&#39;s no guarantee they&#39;ll still be around in 5-10 years.&lt;/p&gt;
&lt;p&gt;One month in, though, and I&#39;m really happy with my move to Nothing Phone 2. For my needs, the hardware has just enough charm that I&#39;m feeling happy to reach for my phone again and, at the same time, the thought that has gone into the software is helping me to break my phone addiction. Despite the industry&#39;s obsession with comparable stats, I haven&#39;t personally noticed any lack of power either. In fact, when I look at the reasons for upgrading my previous phones it has always been because of battery degradation or running out of storage and never because of a lack of power. In that regard – with 2 days on a single charge and 4 times the storage of my last phone – the Nothing Phone has actually been something of an upgrade.&lt;/p&gt;
&lt;p&gt;The jury is out on whether or not my usage will creep back up over time, or if the battery-life will stay steady, but I&#39;m already feeling less negativity about my phone. I&#39;m also happy to report that the lacklustre camera is making sure that my Fujifilm isn&#39;t staying in its bag anymore. So far, at least, I&#39;m pleased I went with Nothing.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>It&#39;s OK to abandon your side-project</title>
    <link href="http://robbowen.digital/wrote-about/abandoned-side-projects/"/>
    <updated>2024-02-22T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/abandoned-side-projects/</id>
    <content type="html">&lt;p&gt;The web industry is full to the brim with tales of side-projects that grew into successful businesses and, like many of us, I&#39;ll often find myself tinkering away on an idea or three after I&#39;ve finished with my day-job. Whilst it&#39;s definitely an enticing prospect, working on a side-project is not always sunshine and Lambos though – sometimes they just don&#39;t work out. If you&#39;re reading this, there&#39;s a chance that you might have recently abandoned (or are considering abandoning) a side-project. Many of us have been there. Hell, the neglected side-project has become something of a developer-meme at this point.&lt;/p&gt;
&lt;p&gt;That said, I often get emails from beginner developers looking for advice and one of the growing themes I&#39;ve noticed recently is concern that they they aren&#39;t shipping their side-projects as quickly or numerously as they would like. That anxiousness is totally understandable. When the prevailing wisdom of developer hustle-culture is &amp;quot;always be shipping&amp;quot; and tech-interviewers will routinely measure candidates by the output of their extra-curricular coding, those abandoned side-projects might not feel so funny anymore. That doesn&#39;t sit right with me. We hear about all the side-project success stories, but what if we talked more openly about the ones that tanked? Many of us do retrospectives at work, but personal projects don&#39;t get the same treatment. Instead, why don&#39;t we shine a light on all the time we spent on projects that didn&#39;t go anywhere? The seemed-like-a-good-idea-at-the-time abandonware; the graveyard of &lt;code&gt;node_modules&lt;/code&gt; folders still haunting our development environments.&lt;/p&gt;
&lt;p&gt;I&#39;d like to talk about a side-project I worked on a while ago; one that I abandoned the same day it was deployed.&lt;/p&gt;
&lt;h2&gt;The background&lt;/h2&gt;
&lt;p&gt;My partner is Latvian and, a few years back, I set out to learn her language. Being from a small country, detailed learning resources for the Latvian language are a bit sparse but I made decent progress regardless. That was, until I discovered that Latvian has &lt;a href=&quot;https://en.wikipedia.org/wiki/Grammatical_case&quot;&gt;grammatical cases&lt;/a&gt;. If you&#39;ve never encountered a &amp;quot;case&amp;quot; before, here&#39;s a little primer:&lt;/p&gt;
&lt;p&gt;A language like English uses word order and prepositions such as &amp;quot;for&amp;quot;, &amp;quot;to&amp;quot; or &amp;quot;in&amp;quot; to add meaning to each word in a sentence. If the order is wrong, or you miss a preposition, the sentence might no longer makes sense. For example, &amp;quot;Tom gives the book to Anna&amp;quot; sounds natural whereas, &amp;quot;Tom the book to Anna gives&amp;quot; doesn&#39;t. Cases change this up a bit. Instead of relying on word order and helper words, the end of each word itself changes to show what it is doing within the sentence. To return to the same example sentences in Latvian, &amp;quot;Tom&lt;b&gt;s&lt;/b&gt; dod grāmat&lt;b&gt;u&lt;/b&gt; Anna&lt;b&gt;i&lt;/b&gt;&amp;quot; (emphasis added to highlight the functional endings). Literally translated back to English, this sentence would be something like &amp;quot;Tom-&lt;b&gt;subject&lt;/b&gt; gives book-&lt;b&gt;object&lt;/b&gt; Anna-&lt;b&gt;towards&lt;/b&gt;&amp;quot;.&lt;/p&gt;
&lt;p&gt;Linguistically, cases are a pretty cool system because you no longer need to care about word order. As a learner though, this is a problem because you &lt;em&gt;do&lt;/em&gt; need to care about all of the various endings for each word you learn. Latvian has seven cases in total, two grammatical genders (each with three separate conjugation patterns), and nouns can be singular and plural. The TL;DR is that&#39;s something like 84 possible endings to memorize.&lt;/p&gt;
&lt;p&gt;So, cases can be a lot for a first-language English speaker. Thankfully though, I&#39;m also a developer and therefore I&#39;m hardwired to think that I can solve everything with code. What if I could build a quiz app to help me learn noun endings? This smelled like a side-project 🚀&lt;/p&gt;
&lt;h2&gt;The approach&lt;/h2&gt;
&lt;p&gt;I wanted to keep my app simple. Whilst I had a lot of other grammar to learn, I was only focusing on noun conjugations and that would help me whittle the initial concept down to an &lt;abbr title=&quot;Minimum Viable Product&quot;&gt;MVP&lt;/abbr&gt;. The quiz mechanism would present a series of Latvian nouns and the user would be required to conjugate the noun to the appropriate ending. To keep things interesting, the quiz would allow the user to make three mistakes before ending and I&#39;d throw in a simple high-score system to keep track of how I&#39;d performed in past quizzes.&lt;/p&gt;
&lt;p&gt;The tech stack would be simple too. At the time of planning, Svelte 3.0 was the new shiny so I decided to use it for my UI. I knew that I wanted to host everything on Netlify, so for the backend I&#39;d write a couple serverless functions to present the questions and check the answers. The main list of nouns could be served from a static JSON file and, as I&#39;d be the only user, I could safely persist previous quiz results and a high-score to local storage. I wouldn&#39;t need a database right now.&lt;/p&gt;
&lt;p&gt;As for how I would actually check answers, that would take a bit of research. After extensively reading about the conjugation patterns and how the various types of nouns are classified, I decided that my simplest option would be to build a system that leaned heavily on Regex to strip noun stems and append the appropriate suffixes.&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/lietvards-code.png&quot; style=&quot;padding-top: 56.67060212514758%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/lietvards-code.png&quot; alt=&quot;A screenshot of some of the code from my side-project, showing how I used regex to determine the noun class&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;With a decent plan in place, I started to code.&lt;/p&gt;
&lt;h2&gt;The realisation&lt;/h2&gt;
&lt;p&gt;After a full week of evenings working on the project, I put the finishing touches to the MVP. I deployed everything to Netlify and started my initial testing.&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/lietvards.png&quot; style=&quot;padding-top: 70.87988826815642%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/lietvards.png&quot; alt=&quot;A screenshot of the simple UI, showing the word zīle, and the dative singular form, zīlei&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;The UI was simple but passable and worked well on mobile devices. Quiz questions progressed smoothly and the session would end after three wrong answers, as designed. In between quizzes, the dashboard was correctly displaying stats for hits and misses on each word and the overall high-score was persisting between sessions. Happy that everything was working as planned, I cracked a beer and started training word endings.&lt;/p&gt;
&lt;p&gt;It quickly became clear that my app had a really big problem that I hadn&#39;t anticipated. The quiz was far too easy. Worse still, if I didn&#39;t make 3 mistakes, the quiz would keep going indefinitely. It just wasn&#39;t fun to use.&lt;/p&gt;
&lt;p&gt;I racked my brain for possible ways to make the quiz more fun but, eventually, the penny dropped: The issue couldn&#39;t actually be solved in code. It turns out that, in devising and coding all of the logic needed to test the various noun endings, I had passively learned the rules needed to form them.&lt;/p&gt;
&lt;p&gt;Over the last week I had worked long evenings to research and build an app – with a target audience of one person – and I didn&#39;t really need to use it anymore. Oops.&lt;/p&gt;
&lt;h2&gt;Maybe the real treasure is the code we wrote along the way&lt;/h2&gt;
&lt;p&gt;Out of all of my abandoned side-projects, this was the one that made me think differently. Even if I would never actually use the end &#39;deliverable&#39;, working on the project still indirectly achieved what I&#39;d set out to do. That led me to an important realisation: we talk a lot about abandoned side-projects as &amp;quot;failed&amp;quot;, but their success is really a matter of perspective.&lt;/p&gt;
&lt;p&gt;Despite what some tech recruiters might have you believe, the success of a side-project doesn&#39;t need to be defined by a beautiful, shipped product. We work in a practical medium and any build experience, good, bad or abandoned, is still valid experience. If you are able to remove the pressure to ship and instead approach them like throwaway prototypes, side-projects become a great scratch pad for experimentation. As I found when building my Latvian app, even the act of writing code itself can be a successful tool for solving problems.&lt;/p&gt;
&lt;p&gt;This is not all to say that we should dismiss the reasons these projects get abandoned – introspection is still important – but I find that focusing on the progress made can feel more constructive in the long-term. After abandoning my Latvian project, I dipped back into some of the other stalled side-projects languishing on my laptop. Where I&#39;d previously grumbled over a string of failures and wasted time, I could now refocus on what had gone well. On one project, I could see where I&#39;d first learned how to make an API in Go. Elsewhere I was impressed at how I had figured out how to work with GIS map data in Postgres. In another derelict directory, I saw not much more than a broken animation - one that I would later revisit and evolve into this website.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;I still regularly work on side-projects but my perspective and motivations are different now. My advice to a beginner dev struggling with their side-projects would be to always make sure that you&#39;re doing them for yourself, and for the right reasons. Instead of approaching your first project purely as a means to make it big or to impress recruiters, see it firstly as a means to learn and explore what&#39;s possible. Once you&#39;ve built up enough experience (i.e. abandoned a few projects) the rest usually follows. Side-projects should be creative and fun. If you find that shipping your project is starting to cause you stress or, worse yet, leaving you feeling burned out, then don&#39;t hesitate to cut it loose. Chances are that, if you look close enough, it has already brought you plenty of value.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>2023 Wrapped</title>
    <link href="http://robbowen.digital/wrote-about/2023-wrapped/"/>
    <updated>2023-12-31T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/2023-wrapped/</id>
    <content type="html">&lt;p&gt;For the past few years I&#39;ve tried to do a little retrospective post on the year that was. This year, inspired by the &lt;em&gt;endless&lt;/em&gt; &amp;quot;such-and-such wrapped&amp;quot; tracking-data-as-entertainment emails that I&#39;ve been getting, I&#39;m going to mix up the format a bit and throw in an extra rundown of some of the media I enjoyed this year.&lt;/p&gt;
&lt;p&gt;So, let&#39;s go. Here&#39;s my 2023 Wrapped (replete with overly-excitable subheadings)&lt;/p&gt;
&lt;h2&gt;&amp;quot;This year, your contracts were eclectic&amp;quot;&lt;/h2&gt;
&lt;p&gt;I had a really varied year of freelancing in 2023 - I&#39;ve worked on a bunch of brochure sites, a full design system, an app dashboard, I refactored part of a project written in Elm, I built a PWA with lots of time and timezone-dependent features and so much more that I struggle to recall.&lt;/p&gt;
&lt;p&gt;2023 also marked the milestone of being five-years-freelance and I worked extra hard this year. If I&#39;m honest, I worked too hard and burnout is once again knocking at my door. I still love the flexibility of the freelance life, but I&#39;m starting to get the itch to do my own thing. I&#39;m not sure what &amp;quot;my own thing&amp;quot; will be yet, but in 2024 I&#39;m going to take time out of client work to try my hand at a few personal projects.&lt;/p&gt;
&lt;h2&gt;&amp;quot;This year, the words came easier&amp;quot;&lt;/h2&gt;
&lt;p&gt;Every year I try to blog more and, whilst my drafts folder is still pretty well stocked, this year I published a tiny bit more than I did in previous years. This year I also challenged myself to write a little less technically and a little more conceptually. I think I just-about pulled it off.&lt;/p&gt;
&lt;p&gt;Here&#39;s a quick rundown of links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://robbowen.digital/wrote-about/hand-thrown-frontends/&quot;&gt;Hand-thrown frontends&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robbowen.digital/wrote-about/legacy-code/&quot;&gt;Legacy code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robbowen.digital/wrote-about/HTML-is-like-a-camera/&quot;&gt;HTML is like a camera&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robbowen.digital/wrote-about/locking-scroll-with-has/&quot;&gt;Locking scroll with :has()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&amp;quot;Another year, another framework&amp;quot;&lt;/h2&gt;
&lt;p&gt;In 2023 I did a hell of a lot of work with Nuxt and Vue. Moreso than any other framework or programming language. I &lt;em&gt;really&lt;/em&gt; enjoy using Nuxt and will continue to do so, but this year the fragmentation of Nuxt 2/3 compatibility has caused me quite a few serious headaches. In fact (and this is absolutely not intended as a slight on the work of the Nuxt team) this year I started questioning the use of frameworks altogether.&lt;/p&gt;
&lt;p&gt;But then, I did a couple of projects with Astro.&lt;/p&gt;
&lt;p&gt;Over the years I&#39;ve slowly come to the realisation that the thing I like most about front-end frameworks is not encapsulating JavaScript or declarative code or any of the usual &lt;abbr title=&quot;developer experience&quot;&gt;DX&lt;/abbr&gt; suspects. I like the componentisation of reusable HTML. Simply, I like not having to copy-paste a bunch of template code like we did in the old days. Because of that, Astro has been a great fit for me - Astro components allow me to assemble chunks of HTML and CSS and opt-in to all the benefits of a framework like React or Vue if and when I need them. No client-side JS by default? That&#39;s pretty damn cool and I&#39;m looking forward to more Astro projects in 2024.&lt;/p&gt;
&lt;h2&gt;&amp;quot;This year, you left the UK for the first time in 3 years&amp;quot;&lt;/h2&gt;
&lt;p&gt;For someone who loves to travel, I haven&#39;t been out of the UK since I returned from Japan in March of 2020. To be frank, it has been an absolute slog. Thankfully, this summer, I was finally able to break the streak (and the Great British Depression that came with it).&lt;/p&gt;
&lt;p&gt;My partner is Latvian and every five years Latvia holds a huge cultural celebration of traditional songs, dancing and costume – &lt;a href=&quot;https://en.wikipedia.org/wiki/Latvian_Song_and_Dance_Festival&quot;&gt;Dziesmu un deju svētki&lt;/a&gt;. In July we flew out for two weeks of  seeing her family and friends, eating great food and listening to fantastic musical performances.&lt;/p&gt;
&lt;p&gt;The highlight for me was the final concert, deep in a forest park and featuring a choir of 16,000 people. Add to that the crowd and you have close to 40,000 people singing long into the night. The next event is in 2028 and I can&#39;t recommend it enough. Just look at the size of this stage; you don&#39;t so much hear the songs as feel them in your chest. Magic.&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/dziesmusvetki.jpg&quot; style=&quot;padding-top: 46.44681839294008%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/dziesmusvetki.jpg&quot; alt=&quot;The main stage of the final concert, with a choir of 16,000 people&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;It&#39;s hard to understate the positive effect that travel has on my mental health, so I&#39;m really looking forward to travelling more in 2024.&lt;/p&gt;
&lt;h2&gt;&amp;quot;This year you got super sick of superheroes&amp;quot;&lt;/h2&gt;
&lt;p&gt;My extreme burnout from the endless Marvel content-machine peaked this year (Hi-and-bye, Quantumania) but thankfully there has been a wealth of decent alternatives this year. Here are my top film &amp;amp; tv picks from the last year:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Bear: season 2&lt;/strong&gt; - I &lt;em&gt;loved&lt;/em&gt; the first season and tbh, the ending of season one was so well handled that I&#39;d have been happy if it was a one-and-done show. Against my initials fears though, season two was better in almost every way (and not least because I&#39;m an absolute sucker for a redemption arc). My standout episodes were Honeydew and, of course, Forks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Past Lives&lt;/strong&gt; - Beautifully acted, absolutely gorgeous to look at, and with an ambiguous and nuanced plot. When it finished, my partner and I sat and discussed our interpretations for quite a while afterwards. I still can&#39;t get over how this was director Celine Song&#39;s first movie. Phenomenal talent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Godzilla Minus One&lt;/strong&gt; - This one slipped into my list right before Christmas. Some people grow up with Star Wars or Lord of the Rings or whatever, but for my brother and I we had the Godzilla movies. The recent US-made &#39;zilla flicks have been of diminishing quality but they still provide me some small amount of satisfaction for the lizard-brain. Minus One is a very different and very character-driven movie. Normally I&#39;d be rooting for the big lad, but here he was so intimidating and the characters so well drawn that I was cheering them on instead.&lt;/p&gt;
&lt;p&gt;Even if you&#39;re not a fan of kaiju movies, I&#39;d recommend giving this one a watch. It&#39;s a brilliant anti-war allegory and absolutely the best Godzilla has been since the 1954 original. On a personal level, hearing the original Godzilla theme blasting out in a cinema was a real treat too.&lt;/p&gt;
&lt;h2&gt;&amp;quot;In 2023 you played a whole bunch of games...or did you?&amp;quot;&lt;/h2&gt;
&lt;p&gt;I thought I played a lot of games this year, but statistics don&#39;t lie. As I wrote on &lt;a href=&quot;https://front-end.social/@Robb/111634430410952516&quot;&gt;Mastodon&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Of all the &amp;quot;isn&#39;t how we track you fun!!&amp;quot; Wrapped things this year, I enjoyed the Xbox Gamepass one the most because it showed that my girlfriend has basically worked a second job playing Age of Empires II&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That said, I did fit a couple of gems in the gaps between AOE2:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zelda: Tears of the Kingdom&lt;/strong&gt; – I&#39;ve been a Zelda fanboy since the &lt;abbr title=&quot;Link to the Past&quot;&gt;LttP&lt;/abbr&gt; days so, if that means anything to you, I probably don&#39;t need to elabourate much more on why I loved this game. I&#39;m completely in awe of how Nintendo managed to reuse so much of the game world from Breath of the Wild and still make it feel fresh and innovative. The level of creativity and fore-planning in this game is genuinely inspiring (web industry take note).&lt;/p&gt;
&lt;p&gt;Also, the music. OMG the music.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alan Wake II&lt;/strong&gt; - This was a curveball; the sequel I never knew I wanted. I played the original game back when it came out and I was honestly lukewarm about it. When AWII launched around Halloween, I knew very little about it, but decided to take a punt on it. Oh damn, is it good. It has a pulpy and totally overblown story, parts of it are genuinely scary and its blending of game and live-action looks phenomenal even if it creaks a little bit on my Xbox Series S. Seriously, the game crashes very frequently on Series S and yet I couldn&#39;t even be mad at it. I &lt;em&gt;needed&lt;/em&gt; to see what fresh madness would happen next.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;So that was my 2023. If I&#39;m entirely honest, it feels a little bit like I only just wrote one of these posts. This year has absolutely flown by for me, but I&#39;m feeling ready for the changes that the new year will bring.&lt;/p&gt;
&lt;p&gt;Cheers for reading this far, and here&#39;s to a happy and healthy new year in 2024! 🍻&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Locking scroll with :has()</title>
    <link href="http://robbowen.digital/wrote-about/locking-scroll-with-has/"/>
    <updated>2023-12-20T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/locking-scroll-with-has/</id>
    <content type="html">&lt;p&gt;It&#39;s finally happened – with the &lt;a href=&quot;https://www.mozilla.org/en-US/firefox/121.0/releasenotes/&quot;&gt;release of Firefox 121.0&lt;/a&gt; &lt;code&gt;:has()&lt;/code&gt; has landed in all browsers (though writing &amp;quot;&lt;code&gt;:has()&lt;/code&gt; has&amp;quot; is sadly no less awkward). In celebration, I thought it a good time to share one of my favourite practical uses of &lt;code&gt;:has()&lt;/code&gt; so far – scroll locks.&lt;/p&gt;
&lt;p&gt;Imagine that you need to open a modal window, or flyout menu. To prevent from losing the user&#39;s place in the page whilst that modal is open – particularly on mobile devices – it&#39;s good practice to prevent the page behind it from scrolling. That&#39;s a scroll lock.&lt;/p&gt;
&lt;p&gt;Let&#39;s take a look at how we might implement one.&lt;/p&gt;
&lt;h2&gt;Locking scroll&lt;/h2&gt;
&lt;p&gt;Probably the simplest way to lock the user from scrolling a document is to add &lt;code&gt;overflow: hidden&lt;/code&gt; to the document body.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body.lock-scroll&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This class will totally disable scrolling, so we can&#39;t just leave it in our HTML. It will need to be conditionally added and removed whenever a modal, flyout or scroll-locking element is active within the page.&lt;/p&gt;
&lt;p&gt;One way to solve this might be to adjust the body class when opening or closing the scroll-locking element:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;openModal&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// ...some code to open the modal&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// then lock the scroll&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;lock-scroll&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;closeModal&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// ...some code to close the modal&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// then unlock the scroll&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;lock-scroll&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method is generally fine, but there are some complications.&lt;/p&gt;
&lt;p&gt;What happens if you have multiple screen locks at the same time, for example? Let&#39;s say that our user clicks a hamburger menu to open a flyout whilst a modal is already open; both UI elements will lock the scrolling but, using the approach above, closing either the modal or the flyout would remove the &lt;code&gt;.lock-scroll&lt;/code&gt; class from the body and unlock the scrolling whilst the other element is still active.&lt;/p&gt;
&lt;p&gt;Similarly, if you&#39;re in a framework with clientside routing then navigating to a different page won&#39;t automatically remove the body class. Unfortunately, that means the scroll will still locked when the new page is loaded in.&lt;/p&gt;
&lt;p&gt;None of this is insurmountable by any means, but managing the body class to cope with edge-cases takes a bit of effort. CSS can make things simpler.&lt;/p&gt;
&lt;h2&gt;Solving edge-cases with &lt;code&gt;:has()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Because &lt;code&gt;:has()&lt;/code&gt; lets us modify a parent element based on its contents, handling scroll locks becomes a breeze. We can tweak the CSS declaration on our body element to use &lt;code&gt;:has()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body:has(.lock-scroll)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, rather than managing state directly on the body by toggling a &lt;code&gt;.lock-scroll&lt;/code&gt; class with Javascript, we can now manage it on the markup of any element that needs to lock the page scroll:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dialog&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lock-scroll&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- some wonderful modal content --&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dialog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of the lock being hinged on JS state-management, we&#39;ve tied it to the contents of the DOM itself. &lt;strong&gt;As long an element with &lt;code&gt;.lock-scroll&lt;/code&gt; is in the DOM, the scroll we be locked&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Need to unlock the scroll? remove the element from the DOM. If there are multiple instances of &lt;code&gt;.lock-scroll&lt;/code&gt;, then the scroll will &lt;em&gt;remain locked until all of them are gone&lt;/em&gt;. The same goes for route changes - if there&#39;s no &lt;code&gt;.lock-scroll&lt;/code&gt; class present in the new page then the scrolling will be automatically unlocked.&lt;/p&gt;
&lt;p&gt;...and that&#39;s kinda all there is to it. Lush.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The above is a very simple example, but it&#39;s one that I&#39;ve found useful. With support for &lt;code&gt;:has()&lt;/code&gt; now across all browsers, it&#39;s also one that is increasingly viable. The nature of CSS means it&#39;s pretty simple to expand the example to more elabourate use-cases too. You might, for example, wish to bind the scroll lock as a side-effect to changes in data or aria attributes:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body:has(.some-class[aria-expanded=&quot;true&quot;])&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whatever your application, &lt;code&gt;:has()&lt;/code&gt; looks to be a really handy tool and I&#39;m stoked to see it finally get full support.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>HTML is like a camera</title>
    <link href="http://robbowen.digital/wrote-about/HTML-is-like-a-camera/"/>
    <updated>2023-11-16T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/HTML-is-like-a-camera/</id>
    <content type="html">&lt;p&gt;Recently, as I started getting back into photography I read something that stuck with me (though sadly the source didn&#39;t). To paraphrase:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you were able to take a great photo on a camera from 1979 then that same camera can still take a great photo today. New and shiny tech may make certain things easier, but a good photo is a good photo. Ultimately, the viewer won&#39;t give a shit which camera you used&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a frontend developer, I think about this a lot.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Legacy code</title>
    <link href="http://robbowen.digital/wrote-about/legacy-code/"/>
    <updated>2023-04-17T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/legacy-code/</id>
    <content type="html">&lt;p&gt;Before he retired, my dad was an electrical and diagnostic engineer. In short, he dedicated most of his adult life to designing and building lasers and laser equipment – everything from laser-tag through to guidance systems for those massive drills that dig rail tunnels. After dad passed away, I stumbled across a box in my parent&#39;s garage containing prototypes and product specimens for many of the laser modules that he&#39;d built throughout his career. That box now serves as a tangible legacy of a long and varied career.&lt;/p&gt;
&lt;p&gt;Flash forward to a few months ago when I was chatting with a potential client. They asked me to show some of my past work, so I duly shared my screen and hopped over to the URL of a humanitarian campaign site that I was particularly proud of having worked on...&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/cant-be-reached.png&quot; style=&quot;padding-top: 57.154340836012864%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/cant-be-reached.png&quot; alt=&quot;A Google Chrome error page saying “this site can&#39;t be reached”&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;I often think about that box in the garage.&lt;/p&gt;
&lt;h2&gt;Let&#39;s get existential for a moment&lt;/h2&gt;
&lt;p&gt;This is not a new phenomenon. In my younger years I was keen to make a mark in the industry and would often find myself working to the point of exhaustion in order to meet unreasonable deadlines. What was I working on at the time? There&#39;s the kicker, I can&#39;t even remember. Whatever it was, I know that the site has probably been long since replaced. That is just how the web industry is.&lt;/p&gt;
&lt;p&gt;But, what does it actually mean to work in an medium that is so temporary? How does it shape us? When your work can literally disappear at any given moment, how do you reason with the effort that went into producing it?&lt;/p&gt;
&lt;p&gt;Personally, these are uneasy questions but, thankfully, I know that I&#39;m not alone in feeling this. I recently found myself nodding along with Jason Lengstorf&#39;s video &amp;quot;&lt;a href=&quot;https://www.youtube.com/watch?v=DOHZi6Em6H8&quot;&gt;Your job doesn&#39;t matter (and that&#39;s a good thing)&lt;/a&gt;&amp;quot; and, just this morning, I felt Eric W. Bailey&#39;s beautifully to-the-point &lt;a href=&quot;https://ericwbailey.design/published/sandcastles/&quot;&gt;Sandcastles&lt;/a&gt; in my bones.&lt;/p&gt;
&lt;h2&gt;Control structures&lt;/h2&gt;
&lt;p&gt;The more I think about the why, the more I believe my own sense of discomfort ultimately comes down to a lack of control in a job that is seemingly all about control.&lt;/p&gt;
&lt;p&gt;As developers we spend a lot of our time trying to take &lt;em&gt;nothing&lt;/em&gt; and give it the necessary structure and logic that turns it into &lt;em&gt;something&lt;/em&gt;. Once we have the something, the job shifts gears. We start to monitor uptime, we handle traffic spikes, triage error logs, make tweaks to the UI and do everything we can to keep the project ticking along. We are in charge of managing and maintaining the systems necessary for the project&#39;s life-support. And yet, despite those efforts, there&#39;s no system that can stop a project from just running its course.&lt;/p&gt;
&lt;p&gt;It&#39;s a truism that, from the point of launch, our work is no longer &amp;quot;ours&amp;quot;. We have full control, until suddenly we have none. Eventually, someone will make the decision that the time has come. The server will be shut off, the domain will be left to expire and the website will be gone, save for a trail of broken links.&lt;/p&gt;
&lt;p&gt;The industry has collectively come to call the closure of project, &amp;quot;sunsetting&amp;quot;. In a world of &lt;em&gt;blazing-fast&lt;/em&gt; bro-sy newness, it&#39;s an oddly melancholic bit of romanticism that seems to suggest that we knew the end was inevitable from the start. Regardless of any prior understanding, it can still feel uncomfortable that an entire body of work can just be ...gone.&lt;/p&gt;
&lt;h2&gt;Code conservation&lt;/h2&gt;
&lt;p&gt;So, if we know our projects will end, perhaps we can lessen any sense of existential unease by taking steps to preserve them. The question is, how can we do that?&lt;/p&gt;
&lt;p&gt;Well, the most obvious solution seems to be case studies, which is perhaps why so many of our portfolios are screenshot memorials to that sweet-spot between launching the site and handing over the keys to the CMS. Screenshots and videos definitely go some way towards the job, but its little more than a tribute – you can&#39;t really get a feel for the work in the same way as a live site. &lt;a href=&quot;https://set.studio/&quot;&gt;Set Studio&#39;s recent site launch&lt;/a&gt; included a beautiful and novel solution to this – it contains embedded, responsive snapshots of their work preserved in time. I&#39;m a huge fan of this approach but, as a freelancer, licensing and NDAs mean that it isn&#39;t always possible to do the same.&lt;/p&gt;
&lt;p&gt;What about retaining local versions of projects? Licensing issues aside, anyone who has spent any time struggling to update and run an old project&#39;s NPM dependencies will know the answer. Hell, even recording our work to physical media isn&#39;t a solution as external storage can fail – the box of useless, peeling DVDRs that I found alongside my dad&#39;s lasers is a testament to that.&lt;/p&gt;
&lt;p&gt;In times gone by I would have suggested that projects like the &lt;a href=&quot;https://archive.org/web/&quot;&gt;Internet Archive&#39;s Wayback Machine&lt;/a&gt; had our backs. By archiving websites long after the servers have been shut down, they have been ensuring that nothing is ever truly gone. Unfortunately though, it&#39;s not a perfect solution and we have a new problem. Perhaps it was a side-effect of having ceded final control of our work to external companies and corporations, but the industry&#39;s increasingly introspective focus on developer experience means we&#39;ve saddled ourselves with an over-reliance on JavaScript. Sadly, fantastic as they are, projects like Wayback Machine can&#39;t keep up with how we&#39;ve reshaped the industry. Many of the heavily JavaScript-based sites we&#39;re now making just can&#39;t be archived in the same way as HTML could. Just like all of the Flash-only sites of the early 2000s, what we&#39;re building today risks our work being totally lost to history.&lt;/p&gt;
&lt;p&gt;It&#39;s funny that, as developers, we often talk about &amp;quot;legacy code&amp;quot;. For us though, the word &amp;quot;legacy&amp;quot; isn&#39;t used in terms of something to preserve the past. More often than not, &amp;quot;legacy code&amp;quot; is something to be refactored, replaced or removed altogether in the name of progress. Without necessarily realising it, we have unconsciously accepted the temporary nature of our work into the language of our industry.&lt;/p&gt;
&lt;p&gt;This is all to say that, at the end of the day, perhaps it&#39;s the nature of digital things that they just aren&#39;t meant to last.&lt;/p&gt;
&lt;h2&gt;The ephemeral nature of (digital) things&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;It is the ephemeral nature of things that makes them wonderful &lt;em&gt;- Yoshida Kenkō, 1283 – 1350&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Up until now, you&#39;d be forgiven for thinking that this post was a bit of a downer. But, honestly, I don&#39;t want to see any of this in that light.&lt;/p&gt;
&lt;p&gt;Prior to joining the industry, I didn&#39;t formally study web development but rather social and cultural anthropology. The quote above is something I read in my university days which still sticks with me. Perhaps, rather than getting caught up in a bleak everything-ends sensibility, the steady destruction of our past work should be seen as a strength.&lt;/p&gt;
&lt;p&gt;That humanitarian campaign I worked on was only destined to run for a short time and, because of that, the team pulled together to make sure that we got it right in the time we had. If the initial impact is great enough, the need for preservation is secondary. In many ways, a finite shelf-life is an opportunity to be better.&lt;/p&gt;
&lt;p&gt;I want to get more comfortable with that idea, so I&#39;ve been thinking of how to get there. Here&#39;s my plan going forward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Wherever possible, use tried-and-tested tech. Stay as close to the platform as possible. Fewer moving parts means fewer chances to break and resiliance creates longevity. Look, I know that it&#39;s a massive cliché at this point, but there&#39;s a reason that &lt;a href=&quot;https://www.spacejam.com/1996/index2.html&quot;&gt;Space Jam&#39;s website&lt;/a&gt; is still online.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Longevity shouldn&#39;t always be the goal and you need to be comfortable with letting go. Your work is ultimately not your own and today&#39;s shiny new tool is tomorrow&#39;s technical debt. Tools are rarely worth fighting for, but the thing you&#39;re building should be. If the work is only going to be around for a short while, try to make the most impact on the people who will use it. Focus on their experience over your developer experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Code like you think no-one&#39;s reading. Try not to get hung up on &amp;quot;good code&amp;quot; and burn yourself out on over-engineering the perfect, scaleable solution. That scaled-up future may not happen so solve the user&#39;s problem first, then iterate it to solve the code. (&lt;a href=&quot;http://robbowen.digital/wrote-about/hand-thrown-frontends/&quot;&gt;Remember the bowl&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Back in December &lt;a href=&quot;http://robbowen.digital/wrote-about/so-long-2022/&quot;&gt;I mentioned how I wanted to make more things &amp;quot;for the sheer hell of it&amp;quot;&lt;/a&gt;. I realise now that this isn&#39;t just great for creativity, but it also helps with attachment. Make more things for fun. Prototype, build sites that are intentionally throwaway and play with your code. Things built for only yourself are no less important than the things built for others. Think of these projects as recycling – the project will live on in the new skills you use for your next bit of client work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lastly, if you do ever find yourself having to put in unreasonable hours to get a job done, make damn sure it&#39;s for a cause you&#39;ll care about and remember in fifteen years.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Will I stick to &lt;em&gt;all&lt;/em&gt; of this? probably not. Will doing &lt;em&gt;any&lt;/em&gt; of it have an effect? most definitely.&lt;/p&gt;
&lt;p&gt;After more than 15 years, I don&#39;t see the industry suddenly shifting to a preservation mindset but that&#39;s fine. I want to focus on doing the best work I can right now. In the end I probably won&#39;t have a box of my work in the garage but, ultimately, I think I&#39;ll be OK with that.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Hand-thrown frontends</title>
    <link href="http://robbowen.digital/wrote-about/hand-thrown-frontends/"/>
    <updated>2023-01-19T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/hand-thrown-frontends/</id>
    <content type="html">&lt;p&gt;I&#39;ve been thinking about my approach to frontend a lot recently and an analogy that comes up pretty consistently when talking about building user interfaces is that &lt;strong&gt;the frontend development process should be like efficiently assembling a model from Lego bricks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With the web industry&#39;s shift towards agile, feature-focused methodologies and componentisation, this analogy certainly makes sense. To be honest though, I&#39;ve personally never really seen frontend as an assembly job. Lego is admittedly awesome, but for me the mental model of assembling Lego bricks in the required order until a Jira ticket can be marked as &amp;quot;done&amp;quot; feels too linear and too rigid for how I like to work. And that&#39;s not to mention the pain that comes when you have to partially dismantle your bricks to correct some earlier misstep.&lt;/p&gt;
&lt;p&gt;I know that it&#39;s almost certainly because I came to frontend from a creative background rather than a compsci one, but I&#39;ve personally always seen frontend as more about iteration or refinement. Not to be &lt;em&gt;too&lt;/em&gt; artisan-code-wanker about it, but it helps me to reason about my process if I think about it more like working with clay:&lt;/p&gt;
&lt;p&gt;When you&#39;re working with clay, you always start out with the same, rough raw material. It definitely isn&#39;t perfect, but it has possibilities. If you manipulate, shape &amp;amp; tweak it the right ways, those possibilities will start to take form. You&#39;re no longer just working with clay, you have &lt;em&gt;a thing&lt;/em&gt;. Over time, and with enough coaxing, perhaps that thing starts to look like it might be a bowl. Sure, it might be a bit crude – it&#39;s lopsided and the structure is just all over the place – but it&#39;s definitely concave enough to hold water.&lt;/p&gt;
&lt;p&gt;You&#39;re not shooting for core functionality though, so you go back again and hone that shape so that &lt;em&gt;everyone&lt;/em&gt; knows without a shadow of a doubt that it&#39;s a bowl. Maybe you raise it up a bit to give it space to breathe and straighten out the edges so the shape reads better. You might give it more structure in the areas that were fragile and you might also thin out the parts where the clay was laid on too thick. Now, this might not happen in one pass, but with enough time and care you&#39;ll eventually arrive at something that could only ever be described as a bowl. You may still want to add some extra nuance and fine detailing at this point, but your bowl is ready to go into the kiln and be fired.&lt;/p&gt;
&lt;p&gt;Firing the bowl doesn&#39;t have to be the end of the process though. Your bowl is well-structured, the weight is balanced and it holds just the right amount, but you might want to give the aesthetics some love. Maybe you could look at painting it, or giving it a lush teal-amber glaze. Given enough time you could do pretty much anything you want but, though the tools might be different, the same rules apply. You&#39;ll still need to take care not to overdo it – if the bowl cracks it might leak, which would both undo all your hard work and make for a pretty crappy bowl.&lt;/p&gt;
&lt;p&gt;So, for me, the &lt;s&gt;frontend&lt;/s&gt; bowl-making process is a cyclical, non-linear one. On a surface-level it probably doesn&#39;t seem like it&#39;s as efficient as assembling Lego bricks to order, but over time you&#39;ll make more bowls and each phase of iteration will gradually shorten as your skill increases towards mastery. Where you once might have gotten hands-on and worked on-the-fly, you&#39;ll prepare all of your tools and materials in advance and you&#39;ll have the experience to foresee where things might go wrong. The steps of your process aren&#39;t always going to be the same – sometimes you&#39;ll be making a plate or a mug – but you have built up a feel for how the clay works, you respect its limits and you can shape it into most anything.&lt;/p&gt;
&lt;p&gt;I mean seriously, what even is a plate if not a slightly-flattened bowl?!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>So long, 2022</title>
    <link href="http://robbowen.digital/wrote-about/so-long-2022/"/>
    <updated>2022-12-31T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/so-long-2022/</id>
    <content type="html">&lt;p&gt;It&#39;s been a little while since I did a year in review post, so let&#39;s get that out of the way first. For all the &lt;a href=&quot;http://robbowen.digital/wrote-about/hindsight-is-2020&quot;&gt;wild unknowns of 2020&lt;/a&gt;, 2021 was a landmark year but for, sadly, all the wrong reasons.&lt;/p&gt;
&lt;p&gt;In September &#39;21, &lt;a href=&quot;https://for-dad.robbowen.digital/&quot;&gt;I lost my dad&lt;/a&gt;. Just over a month later, my partner lost her wonderful step-dad. Come the end of the year, and in the shadow of that combined grief, my drive to talk about &lt;em&gt;literally anything else&lt;/em&gt; that happened in 2021 was at absolute zero. I skipped the blog post.&lt;/p&gt;
&lt;p&gt;Now a year on, as we&#39;re closing out 2022, I&#39;ve realised that writing (publicly or privately) helps me to organise my thoughts and, in a roundabout way, helps me to reach some amount of closure. 2022 has absolutely blinked by so, in that spirit, I feel that this time a year-and-a-bit-in-review post might help to make things a bit less rushed.&lt;/p&gt;
&lt;h2&gt;Work&lt;/h2&gt;
&lt;p&gt;Please understand that, as I&#39;m British, I am hardwired from birth to hate any kind of own-horn-tooting. That said, experiencing such a huge loss in 2021 has made me realise that I need to properly celebrate my wins when they happen. In that spirit, here&#39;s a bunch of positive work-related things that made me feel good in 2022.&lt;/p&gt;
&lt;p&gt;Please forgive me as I commence tooting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;After dad died, I finished up my last contract and took some time off work to be with my family. When January 2022 rolled around, the year kicked back into gear with the launch of that final &#39;21 project – I had an absolute blast working with the talented team at &lt;a href=&quot;https://www.truedigital.co.uk/&quot;&gt;True Digital&lt;/a&gt; to build the frontend for the new Aardman website. As a teenager I had actually dreamed of studying animation and working at Aardman, so it was such a pleasure to work on something with so much scope for building fun little interactions and animations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Despite taking time off at the end of 2021, I managed to stay booked throughout the whole of 2022 and worked with some truly awesome people on everything from brochure sites to design systems and web apps.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I said no to a lot of projects this year. That might seem like an odd thing to celebrate, but in past years I have really struggled with burnout. Turning down more projects and forcing myself to take &lt;em&gt;at-a-bare-minimum&lt;/em&gt; a week between projects felt like a really positive step in staying healthy. I&#39;m not going to say that I didn&#39;t feel overwhelmed at times this year, but I&#39;m looking forward to saying &amp;quot;no&amp;quot; a lot more in 2023.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I&#39;m not really sure that I&#39;ve nailed my unofficial goal of blogging more in 2022, but I did manage to fit in a handful of blog posts this year. &lt;a href=&quot;http://robbowen.digital/wrote-about/css-blend-mode-shaders/&quot;&gt;One of them&lt;/a&gt;, based on a hologram technique that I&#39;d first tried out back in the skeuomorphic mists of 2010, proved quite popular. Ultimately that post was shared on the orange site, which culminated in a funny couple of days seeing my site&#39;s normally meagre bandwidth spike – no, &lt;em&gt;rupture&lt;/em&gt; – to around 60gb in 48 hours (remember that a compressed png can be much smaller than an SVG, folks 😅)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To my endless surprise, the continued enthusiasm for my Synthwave &#39;84 VS code theme &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=RobbOwen.synthwave-vscode&quot;&gt;carried it past one million downloads&lt;/a&gt; at the start of September. I really will do a light version one day, I promise.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In late summer I got a surge of Twitter DMs and emails from strangers. Normally that&#39;s a signal that Synthwave&#39;s glow has stopped working again, but this time it was thankfully more fun. Unbeknownst to me, YouTuber Gary Simon&#39;s DesignCourse channel featured this website in a &lt;a href=&quot;https://youtu.be/At6XyItIHsE?t=459&quot;&gt;roundup of developer portfolios&lt;/a&gt; and it was driving a lot of enthusiastic people my way...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;...and then, in October, that momentum got this site nominated for the &lt;a href=&quot;http://robbowen.digital/wrote-about/the-jammies-2022/&quot;&gt;&amp;quot;Personal website of the year&amp;quot; award at Jamstack conf&#39;s Jammies&lt;/a&gt;. I didn&#39;t win, but it was a huge confidence boost to know that people liked my work enough to place me alongside such talented finalists.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Also in October, my partner and I were able to attend &lt;a href=&quot;https://webdevconf.com/&quot;&gt;WDC in Bristol&lt;/a&gt; together. It was my first conference since New Adventures in January of 2020 and I&#39;ve definitely missed the conf buzz. Hearing such fantastic talks and hanging out with the speakers and attendees was both amazingly fun and hugely inspiring in equal measure. In 2023 I&#39;m definitely going to be attending more, and &lt;em&gt;* maybe *&lt;/em&gt; even think about writing a talk of my own.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Looking forward to 2023&lt;/h2&gt;
&lt;p&gt;I&#39;ve tried to avoid making this post about the sudden increased speed of big-tech-corporations imploding, but the whole thing and the subsequent shift to open-source alternatives like &lt;a href=&quot;https://front-end.social/@Robb&quot;&gt;Mastodon&lt;/a&gt; has left me feeling positive about the future of the web in 2023.&lt;/p&gt;
&lt;p&gt;I absolutely love my work, but for a long time now I&#39;ve been feeling a bit out of step with &lt;em&gt;the industry&lt;/em&gt; as a thing. The endless Twitter arguments about which new frontend framework makes you a Real Developer have left me disengaged at best and, at worst, almost drove me off of social media entirely. Now that the old world-as-we-knew it is starting to look a bit more fractured, I&#39;m feeling oddly more upbeat.&lt;/p&gt;
&lt;p&gt;As someone who cut their web-development teeth on Geocities, I couldn&#39;t be any more here for the sudden resurged interest in owning your own content. The momentum is infectious and in the coming year I&#39;d love to see conversation shift even more. Tools are hugely important of course, but I&#39;d much rather hear more about what we&#39;re making, than how we&#39;re making it. Even bigger than that, I&#39;m hoping to see less emphasis on the FAANG model of corporate gains, OKRs, KPIs and WTFs – I want to see industry focus shift to smaller projects and, more importantly, the lives, aspirations and emotions of the humans building them.&lt;/p&gt;
&lt;p&gt;For perhaps obvious reasons I haven&#39;t dwelled too much on my personal life in this post but, speaking for this human, everything I experienced has made me realise two fundamental truths: Life is short, and we don&#39;t have the time to take things too seriously.&lt;/p&gt;
&lt;p&gt;I&#39;m going to spend the next year making more time for my family, and creating more space for personal projects and building weird/cool stuff for the sheer hell of it. I&#39;m going into the new year feeling energised – Here&#39;s to a healthy and happy new year in 2023!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The Jammies 2022</title>
    <link href="http://robbowen.digital/wrote-about/the-jammies-2022/"/>
    <updated>2022-11-08T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/the-jammies-2022/</id>
    <content type="html">&lt;p&gt;To my surprise this website was recently nominated for the &lt;strong&gt;&amp;quot;Personal Website of the Year&amp;quot;&lt;/strong&gt; award at &lt;a href=&quot;https://jamstack.org/conf/jammies/&quot; target=&quot;_blank&quot;&gt;Jamstack Conf 2022&lt;/a&gt;! To be nominated as a finalist in this category, both by a public vote and alongside two seriously creative websites from &lt;a href=&quot;https://www.john-beresford.com/&quot; target=&quot;_blank&quot;&gt;John Beresford&lt;/a&gt;
and &lt;a href=&quot;https://lynnandtonic.com/&quot; target=&quot;_blank&quot;&gt;Lynn Fisher&lt;/a&gt;, is a real honour.&lt;/p&gt;
&lt;p&gt;I wanted to write a quick post to say a huge thank you to anyone and everyone who nominated or voted for this website. This site isn&#39;t just a personal website, it&#39;s also the online face of my business, and the recognition that this kind of event brings is such a huge boost. Cheers, you&#39;re awesome! 🍻&lt;/p&gt;
&lt;p&gt;Most importantly of all though, I want to say a massive congratulations to Lynn Fisher for bringing home the win. Lynn&#39;s annual website update is &lt;a href=&quot;https://lynnandtonic.com/archive/2020&quot; target=&quot;_blank&quot;&gt;always&lt;/a&gt; &lt;a href=&quot;https://lynnandtonic.com/archive/2019&quot; target=&quot;_blank&quot;&gt;super&lt;/a&gt; &lt;a href=&quot;https://lynnandtonic.com/archive/2018&quot; target=&quot;_blank&quot;&gt;creative&lt;/a&gt;, and I&#39;m stoked to see that she picked up a very well-deserved golden jam jar. Lynn&#39;s work is a constant source of inspiration for me, and I can&#39;t wait to see what the next iteration of her site brings!&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/clap.gif&quot; style=&quot;padding-top: 56.25%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/clap.gif&quot; alt=&quot;Me, clapping endlessly&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;(I might have missed out on the award this time but, in recording my conference clips, I did get to make this excellent reaction gif 😆)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Holograms, light-leaks and how to build CSS-only shaders</title>
    <link href="http://robbowen.digital/wrote-about/css-blend-mode-shaders/"/>
    <updated>2022-07-07T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/css-blend-mode-shaders/</id>
    <content type="html">&lt;style&gt;
  .spec-example {
    margin: 50px 0;
    display: flex;
    justify-content: space-between;
    gap: 2.5rem;
  }

  .spec-example__static,
  .spec-example__fixed {
    flex: 1;
    border: 20px solid white;
    height: 30rem;
    background-image: linear-gradient(to bottom, black 20%, #3c5e6d 35%, #f4310e, #f58308 80%, black);
  }

  @media (max-width: 512px) {
    .spec-example__static,
    .spec-example__fixed {
      border-width: 10px;
    }
  }

  .spec-example__fixed {
    background-attachment: fixed;
  }

  .frame {
    position: relative;
    margin: 25px auto;
    max-width: 830px;
    border: 20px solid white;
    background: black;
  }

  @media (max-width: 512px) {
    .frame {
      border-width: 10px;
    }
  }

  .frame img {
    display: block;
    width: 100%;
    max-width: 790px;
    height: auto;
    margin: 0;
  }

  .shader {
    backface-visibility: hidden;
    position: relative;
    display: block;
    overflow: hidden;
  }

  .shader.is-waiting .shader__layer,
  .shader.is-waiting .shader__layer::before,
  .shader.is-waiting .shader__layer::after {
    display: none;
  } 

  .specular,
  .specular::after,
  .mask,
  .mask::before,
  .mask::after {
    background-color: black;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    backface-visibility: hidden;
  }

  .specular {
    background-color: black;
    mix-blend-mode: color-dodge;
    background-attachment: fixed;
  }

  /* ASAKUSA */
  .frame--asakusa .specular {
    z-index: 0;
    background-color: black;
    background-position: center;
    background-size: cover;
    mix-blend-mode: color-dodge;
    background-attachment: fixed;
    background-image:
    linear-gradient(to bottom, black 20%, #3c5e6d 35%, #f4310e, #f58308 80%, black);
  }

  .frame--asakusa .mask {
    z-index: 1;
    background: white;
    background-size: 100%;
    mix-blend-mode: multiply;
    background-image: 
      linear-gradient(180deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)),
      url(/assets/img/articles/holo/tower_spec.jpg);
  }

  /* HOLOGRAM */
  .frame--sticker .specular {
    z-index: 0;
    background-position: top center;
    background-image:
      linear-gradient(125deg,#ff0099 30%, #fc8b00, #ffff00, #00ff8a, #00cfff, #cc4cfa 70%);
  }

  .frame--sticker .mask {
    background-repeat: no-repeat;
    background-size: 45%;
    background-position: center;
    z-index: 1;
    mix-blend-mode: multiply;
    background-image: 
      url(/assets/img/articles/holo/hologram.png);
  }

  .frame--sticker .mask::after {
    content: &#39;&#39;;
    background-position: center;
    background-attachment: fixed;
    z-index: 1;
    mix-blend-mode: color-burn;
    background-image: 
      linear-gradient(45deg, #333 40%, #ddd 60%, #333);
  }

  /* LIGHT LEAK */
.frame--leak .specular {
  z-index: 0;
  mix-blend-mode: color-dodge;
  background-color: black;
  background-position: top center;
  background-repeat: no-repeat;
  mix-blend-mode: color-dodge;
  background-attachment: fixed;
  background-image: linear-gradient(179deg,black 40%, red, orange, white 60%, black 80%);
}

.frame--leak .mask {
  mix-blend-mode: multiply;
  background-color: white;
  background-size: cover;
  background-image: url(/assets/img/articles/holo/pinelake_mask.jpg);
}

.frame--aurora .specular {
  z-index: 0;
  background-color: black;
  background-position: center;
  background-size: 100% 80%;
  mix-blend-mode: screen;
  background-attachment: fixed;
  background-image:
  linear-gradient(
    180deg,
    #3fd351,
    #0088ff,
    #8000ff,
    #0088ff,
    #3fd351
  );
}

.frame--aurora .mask {
  z-index: 1;
  background: white;
  background-size: cover;
  mix-blend-mode: multiply;
  background-image: 
    linear-gradient(to bottom, rgba(0,0,0,0.25) 15%, transparent),
    url(/assets/img/articles/holo/forest_spec.jpg);
}

.blend-example {
  margin: 50px 0;
  box-sizing: content-box;
  border: 20px solid white;
  height: min(352px, 45vw);
  display: grid;
  grid-template-columns: repeat(var(--cols, 2), 1fr);
  grid-template-rows: 1fr;
}

@media (max-width: 512px) {
  .blend-example {
    border-width: 10px;
  }
}

.blend-example__col {
  position: relative;
}

.blend-example__col img {
  width: 100%;
  height: min(352px, 45vw);
  object-fit: cover;
}

.blend-example__layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  mix-blend-mode: var(--blend);
  filter: brightness(0.99);
}

.example {
  margin: 50px 0;
}

.example--spec .specular,
.example--specmask .specular,
.example--mask .specular,
.example--mask .mask { mix-blend-mode: normal; }

.example--specbase .mask,
.example--off .specular,
.example--spec .specular .mask { display: none; }
&lt;/style&gt;
&lt;p&gt;I might be understating it a bit, but WebGL is a big deal. You only need to spend five minutes on one of the many design awards sites to see site-after-site fully leaning into the power of &lt;code&gt;canvas&lt;/code&gt;. Tools like &lt;a href=&quot;https://threejs.org/&quot;&gt;threejs&lt;/a&gt; make it easier to harness the power of 3D and &lt;abbr title=&quot;OpenGL Shading Language&quot;&gt;GLSL&lt;/abbr&gt; shaders and, with that, a whole new level of visual effects.&lt;/p&gt;
&lt;p&gt;This got me thinking though; why let JS have all the fun? With &lt;code&gt;mix-blend-mode&lt;/code&gt; finally gaining wide browser-support, we now have access to many of most common shading techniques in CSS. With some choice images and a bit of careful layering it&#39;s possible to build some surprisingly high-quality effects without the need for introducing any JS dependencies.&lt;/p&gt;
&lt;p&gt;Let&#39;s take a look at an example. As you scroll past the image below, the sunlight blooms a warm orange, before fading to a cool blue. You&#39;ll also briefly see some lens bokeh.&lt;/p&gt;
&lt;div class=&quot;frame frame--asakusa&quot;&gt;
  &lt;div class=&quot;shader&quot;&gt;
    &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/tower.jpg&quot; alt=&quot;Asakusa at dusk&quot;&gt;
    &lt;div class=&quot;shader__layer specular&quot;&gt;
      &lt;div class=&quot;shader__layer mask&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Ooooh shiny. Let&#39;s break it down.&lt;/p&gt;
&lt;h2&gt;What is a CSS &#39;shader&#39;?&lt;/h2&gt;
&lt;p&gt;Shaders in the WebGL world are complex GLSL scripts that determine how each individual pixel is rendered to the screen. We still don&#39;t have &lt;em&gt;that&lt;/em&gt; level of control in our CSS so, at its most basic level, our CSS &#39;shader&#39; is just an image with additional background-image layers above it. Yes, I&#39;m taking a few liberties with the name but with careful use of gradients, masking, nesting and &lt;code&gt;mix-blend-mode&lt;/code&gt; we can manage how these layers interact with both one-another and the image at the bottom of the stack.&lt;/p&gt;
&lt;p&gt;For the sake of visualising this core structure, the &#39;shader&#39; example above is set up with a few nested divs:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Asakusa at dusk&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader__layer specular&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader__layer mask&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To keep each layer aligned with the image at the base, we keep the nested content positioned with the following CSS:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.shader&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;backface-visibility&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* to force GPU performance. More on that later */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.shader__layer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, with the basic layout taken care of, lets take a look at the first layer of our effect - the lighting.&lt;/p&gt;
&lt;h2&gt;Simulating specularity&lt;/h2&gt;
&lt;p&gt;First of all we need to think about how light travels from light to dark across the surface of our image. We&#39;ll need an area of brightness where the light is at its most intense, falling off gradually into darkness as the light dissipates. We&#39;ll do this with a gradient.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.specular&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;180deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black 20%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #3c5e6d 35%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f4310e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f58308 80%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you imagine looking at a shiny surface, the light that reflects back is known as a specular reflection. How and where that highlight appears is dependant on the light source, but also your viewing angle - the highlight moves with you. Whilst our gradient is looking lovely, it&#39;s all a bit static. We&#39;ll need to introduce a bit of movement to really sell the effect.&lt;/p&gt;
&lt;p&gt;Fortunately, there&#39;s a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/background-attachment&quot;&gt;vintage CSS Level 1 property&lt;/a&gt; that can help with that; setting &lt;code&gt;.specular&lt;/code&gt; to &lt;code&gt;background-attachment: fixed&lt;/code&gt; means that, as the page scrolls, the gradient remains locked to the browser&#39;s viewport. This not only brings some much needed motion to our shader, but also means that we can very roughly simulate the changing view-angle without reaching for JavaScript.&lt;/p&gt;
&lt;div class=&quot;spec-example&quot;&gt;
  &lt;div class=&quot;spec-example__static&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;spec-example__fixed&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.specular&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-attachment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;180deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black 20%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #3c5e6d 35%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f4310e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f58308 80%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great! Now, let&#39;s get to applying this lighting to our base image.&lt;/p&gt;
&lt;h2&gt;Know your blend modes&lt;/h2&gt;
&lt;p&gt;As the name implies, &lt;code&gt;mix-blend-mode&lt;/code&gt; mixes the colours of each pixel in one element layer together with the layer directly below it. As with GLSL, CSS gives us a nice &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode&quot;&gt;long list of options to choose from&lt;/a&gt;, and creating the right effect means knowing which blend is going to give the results we need. But what do these blend modes actually do? Before we get stuck in with our shader, let&#39;s take a quick look at the blend modes we&#39;ll be using.&lt;/p&gt;
&lt;p&gt;Below you can see the images that we&#39;ll be using for these examples. On the left is the upper layer to be blended, and the right is the base image we&#39;ll be blending onto.&lt;/p&gt;
&lt;div class=&quot;blend-example&quot;&gt;
  &lt;div class=&quot;blend-example__col&quot;&gt;
    &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/mask-layer.jpg&quot; alt=&quot;Black and white concentric circles, with two grey semi-circles at the centre&quot;&gt;
  &lt;/div&gt;
  &lt;div class=&quot;blend-example__col&quot;&gt;
    &lt;img src=&quot;http://robbowen.digital/assets/img/about/lakebled.jpg&quot; alt=&quot;Lake bled from above&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;First off, let&#39;s look at a  &lt;code&gt;multiply&lt;/code&gt; blend. &lt;code&gt;Multiply&lt;/code&gt; takes the colour of each pixel in the current layer and multiplies it with the colour of the pixel directly beneath it. In practice this means that &lt;em&gt;darker colours in the current layer will obscure those in the layer below&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;blend-example&quot; style=&quot;--cols: 1&quot;&gt;
  &lt;div class=&quot;blend-example__col&quot;&gt;
    &lt;img src=&quot;http://robbowen.digital/assets/img/about/lakebled.jpg&quot; alt=&quot;Lake bled from above&quot;&gt;
    &lt;div class=&quot;blend-example__layer&quot; style=&quot;--blend: multiply&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/mask-layer.jpg&quot; alt=&quot;Black and white concentric circles, with two grey semi-circles at the centre&quot;&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Setting the blend to &lt;code&gt;screen&lt;/code&gt; takes the inverse of each pixel and multiplies them before inverting the result. This might sound complicated, but you can think of &lt;code&gt;screen&lt;/code&gt; as the opposite of &lt;code&gt;multiply&lt;/code&gt; - &lt;em&gt;Darker colours become transparent and only the lighter colours will show through to the layer below&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;blend-example&quot; style=&quot;--cols: 1&quot;&gt;
  &lt;div class=&quot;blend-example__col&quot;&gt;
    &lt;img src=&quot;http://robbowen.digital/assets/img/about/lakebled.jpg&quot; alt=&quot;Lake bled from above&quot;&gt;
    &lt;div class=&quot;blend-example__layer&quot; style=&quot;--blend: screen&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/mask-layer.jpg&quot; alt=&quot;Black and white concentric circles, with two grey semi-circles at the centre&quot;&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Lastly, &lt;code&gt;color-dodge&lt;/code&gt; and &lt;code&gt;color-burn&lt;/code&gt; are like taking &lt;code&gt;multiply&lt;/code&gt; and &lt;code&gt;screen&lt;/code&gt; into overdrive. Both modes divide the pixel colour on the base layer with the one in the current layer.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For &lt;code&gt;color-dodge&lt;/code&gt; this means that highlights and midtones get blown out whilst dark tones have no affect at all. &lt;code&gt;color-burn&lt;/code&gt; will boost shadows and darker midtones, whilst lighter tones have no affect at all&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In the image below, the left example is &lt;code&gt;color-dodge&lt;/code&gt; and the right is &lt;code&gt;color-burn&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;blend-example&quot; style=&quot;--cols: 2&quot;&gt;
  &lt;div class=&quot;blend-example__col&quot;&gt;
    &lt;img src=&quot;http://robbowen.digital/assets/img/about/lakebled.jpg&quot; alt=&quot;Lake bled from above&quot;&gt;
    &lt;div class=&quot;blend-example__layer&quot; style=&quot;--blend: color-burn&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/mask-layer.jpg&quot; alt=&quot;Black and white concentric circles, with two grey semi-circles at the centre&quot;&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;blend-example__col&quot;&gt;
    &lt;img src=&quot;http://robbowen.digital/assets/img/about/lakebled.jpg&quot; alt=&quot;Lake bled from above&quot;&gt;
    &lt;div class=&quot;blend-example__layer&quot; style=&quot;--blend: color-dodge&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/mask-layer.jpg&quot; alt=&quot;Black and white concentric circles, with two grey semi-circles at the centre&quot;&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Compositing layers&lt;/h2&gt;
&lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/composite-diagram.jpg&quot; alt=&quot;A diagram summarising how layers can be composited together to form a new image&quot;&gt;
&lt;p&gt;Now we know what we&#39;re working with, it might seem like the next step would be to slap a blend mode onto our gradient, place it over our base image and call it a day. That will definitely work, but it won&#39;t reach the level of quality we&#39;re going for.&lt;/p&gt;
&lt;p&gt;Blending the gradient directly to the base layer will mean that the lighting will be totally uniform across the image. Aside from chromed surfaces, that doesn&#39;t happen too often in nature. To really sell the effect we want to have control over the areas of the image where light can fall and where it can&#39;t. To simulate this, we can use a predominately dark image to mask out our gradient. This technique of using a dark image to mask off areas of a lighter one is often known as a &lt;em&gt;specular map&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You might be wondering how it will be possible to do this with CSS if &lt;code&gt;mix-blend-mode&lt;/code&gt; only affects the pixels in the layer directly below it, and we can only set one blend mode at a time. This is where our HTML structure starts to shine.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Asakusa at dusk&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader__layer specular&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader__layer mask&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By nesting &lt;code&gt;div&lt;/code&gt; layers inside of one another, we can work outwards applying additional &lt;code&gt;mix-blend-mode&lt;/code&gt;s to each wrapping &lt;code&gt;div&lt;/code&gt;. Essentially, this lets us add another &lt;code&gt;mix-blend-mode&lt;/code&gt; to the output of the previous blend. This process of layering different blend modes to produce a final output is known as &lt;code&gt;compositing&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s give it a try. Using a suitably dark &lt;code&gt;background-image&lt;/code&gt; we&#39;ll set &lt;code&gt;mix-blend-mode: multiply&lt;/code&gt; on our &lt;code&gt;.mask&lt;/code&gt; layer and throw away the parts of our gradient where we don&#39;t want light to show through.&lt;/p&gt;
&lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/composite-diagram-multiply.jpg&quot; alt=&quot;Two images are combined with a multiply blend to form a mask&quot;&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.mask&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;mix-blend-mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; multiply&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;url(/tower_spec.jpg)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.specular&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-attachment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;180deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black 20%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #3c5e6d 35%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f4310e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f58308 80%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have our specular map, we can apply the final lighting to the base image. We need to use one of the blend modes that ignores black and dark tones. This means that we should set our &lt;code&gt;.specular&lt;/code&gt; layer to use &lt;code&gt;mix-blend-mode: screen&lt;/code&gt; or &lt;code&gt;mix-blend-mode: color-dodge&lt;/code&gt;. Either would work in this case, but because we want the highlights to blow out into a nice sunlight-bloom effect, we&#39;ll go for &lt;code&gt;color-dodge&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s take a look:&lt;/p&gt;
&lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/composite-diagram-dodge.jpg&quot; alt=&quot;two images are combined with color-dodge blend to form a bloom effect&quot;&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.specular&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;mix-blend-mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; color-dodge&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-attachment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;180deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black 20%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #3c5e6d 35%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f4310e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f58308 80%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The final shader&lt;/h2&gt;
&lt;p&gt;And with that, the effect is complete! Here&#39;s the completed HTML and CSS:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Asakusa at dusk&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader__layer specular&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shader__layer mask&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.shader&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;backface-visibility&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* to force GPU performance */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.shader__layer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.specular&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;mix-blend-mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; color-dodge&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background-attachment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;180deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black 20%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #3c5e6d 35%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f4310e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #f58308 80%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.mask&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;mix-blend-mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; multiply&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;url(/tower_spec.jpg)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s take another look at the completed effect, but this time with the added ability to isolate each layer of the shader. Change the view mode dropdown to step through the effect and get a better idea of how the layers work together to produce the final image:&lt;/p&gt;
&lt;div class=&quot;example&quot;&gt;
  &lt;div class=&quot;controls&quot;&gt;
    &lt;label&gt;View mode:
      &lt;span class=&quot;selectbox&quot;&gt;
        &lt;select&gt;
          &lt;option value=&quot;off&quot;&gt;No effect&lt;/option&gt;
          &lt;option value=&quot;spec&quot;&gt;Specular only&lt;/option&gt;
          &lt;option value=&quot;specbase&quot;&gt;Specular + base image&lt;/option&gt;
          &lt;option value=&quot;mask&quot;&gt;Mask only&lt;/option&gt;
          &lt;option value=&quot;specmask&quot;&gt;Specular + mask&lt;/option&gt;
          &lt;option value=&quot;&quot; selected=&quot;&quot;&gt;Full effect&lt;/option&gt;
        &lt;/select&gt;
      &lt;/span&gt;
    &lt;/label&gt;
  &lt;/div&gt;
  &lt;div class=&quot;frame frame--asakusa&quot;&gt;
    &lt;div class=&quot;shader&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/tower.jpg&quot; alt=&quot;Asakusa at dusk&quot;&gt;
      &lt;div class=&quot;shader__layer specular&quot;&gt;
        &lt;div class=&quot;shader__layer mask&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Taking it further&lt;/h2&gt;
&lt;p&gt;In the example above, we used a greyscale version of the main image (with added scratches and bokeh) as a mask. That&#39;s a great way to add interest to an image, but shader layers can be whatever you want them to be. Let&#39;s look at a few more examples.&lt;/p&gt;
&lt;h3&gt;Aurora Borealis&lt;/h3&gt;
&lt;p&gt;In this example, repeating the gradient background gradient and reducing &lt;code&gt;background-size-y&lt;/code&gt; causes the light effect to move more quickly across the screen. When masked out with a specular map, this creates the illusion of an aurora rippling over the main image. Blown-out highlights with &lt;code&gt;colour-dodge&lt;/code&gt; creates the wrong effect here, so swapping our &lt;code&gt;.specular&lt;/code&gt; layer to &lt;code&gt;mix-blend-mode: screen&lt;/code&gt; maintains the sharp definition of the aurora.&lt;/p&gt;
&lt;div class=&quot;example&quot;&gt;
  &lt;div class=&quot;controls&quot;&gt;
    &lt;label&gt;View mode:
      &lt;span class=&quot;selectbox&quot;&gt;
        &lt;select&gt;
          &lt;option value=&quot;off&quot;&gt;No effect&lt;/option&gt;
          &lt;option value=&quot;spec&quot;&gt;Specular only&lt;/option&gt;
          &lt;option value=&quot;specbase&quot;&gt;Specular + base image&lt;/option&gt;
          &lt;option value=&quot;mask&quot;&gt;Mask only&lt;/option&gt;
          &lt;option value=&quot;specmask&quot;&gt;Specular + mask&lt;/option&gt;
          &lt;option value=&quot;&quot; selected=&quot;&quot;&gt;Full effect&lt;/option&gt;
        &lt;/select&gt;
      &lt;/span&gt;
    &lt;/label&gt;
  &lt;/div&gt;
  &lt;div class=&quot;frame frame--aurora&quot;&gt;
    &lt;div class=&quot;shader&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/forest.jpg&quot; alt=&quot;A pine forest at night&quot;&gt;
      &lt;div class=&quot;shader__layer specular&quot;&gt;
        &lt;div class=&quot;shader__layer mask&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Light-leak&lt;/h3&gt;
&lt;p&gt;Until now the examples have all used a greyscale specular map, but a full colour specular map can introduce new effects. In this example the mask image is created from an inverted and blurred version of the primary image, with a blue-red tone overlaid on top. When it&#39;s all layered together with a hot red-orange gradient, the resulting blend of colours results in something that looks a little like the light-leaks you get on vintage film cameras.&lt;/p&gt;
&lt;div class=&quot;example&quot;&gt;
  &lt;div class=&quot;controls&quot;&gt;
    &lt;label&gt;View mode:
      &lt;span class=&quot;selectbox&quot;&gt;
        &lt;select&gt;
          &lt;option value=&quot;off&quot;&gt;No effect&lt;/option&gt;
          &lt;option value=&quot;spec&quot;&gt;Specular only&lt;/option&gt;
          &lt;option value=&quot;specbase&quot;&gt;Specular + base image&lt;/option&gt;
          &lt;option value=&quot;mask&quot;&gt;Mask only&lt;/option&gt;
          &lt;option value=&quot;specmask&quot;&gt;Specular + mask&lt;/option&gt;
          &lt;option value=&quot;&quot; selected=&quot;&quot;&gt;Full effect&lt;/option&gt;
        &lt;/select&gt;
      &lt;/span&gt;
    &lt;/label&gt;
  &lt;/div&gt;
  &lt;div class=&quot;frame frame--leak&quot;&gt;
    &lt;div class=&quot;shader&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/pinelake.jpg&quot; alt=&quot;A lake with pine trees&quot;&gt;
      &lt;div class=&quot;shader__layer specular&quot;&gt;
        &lt;div class=&quot;shader__layer mask&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Hologram&lt;/h3&gt;
&lt;p&gt;Layering within the mask opens up even more possibilities. What would happen if we add another layer with &lt;code&gt;background-attachment: fixed&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;In this final example the mask layer has a background image SVG, and another black-white gradient running at the opposite angle to the specular gradient in the &lt;code&gt;.specular&lt;/code&gt; layer. Setting the nested mask layer to &lt;code&gt;color-burn&lt;/code&gt; causes it to blow out the definition of the SVG and you get this sweet two-way holographic look. CSS &lt;em&gt;is&lt;/em&gt; awesome.&lt;/p&gt;
&lt;div class=&quot;example&quot;&gt;
  &lt;div class=&quot;controls&quot;&gt;
    &lt;label&gt;View mode:
      &lt;span class=&quot;selectbox&quot;&gt;
        &lt;select&gt;
          &lt;option value=&quot;off&quot;&gt;No effect&lt;/option&gt;
          &lt;option value=&quot;spec&quot;&gt;Specular only&lt;/option&gt;
          &lt;option value=&quot;specbase&quot;&gt;Specular + base image&lt;/option&gt;
          &lt;option value=&quot;mask&quot;&gt;Mask only&lt;/option&gt;
          &lt;option value=&quot;specmask&quot;&gt;Specular + mask&lt;/option&gt;
          &lt;option value=&quot;&quot; selected=&quot;&quot;&gt;Full effect&lt;/option&gt;
        &lt;/select&gt;
      &lt;/span&gt;
    &lt;/label&gt;
  &lt;/div&gt;
  &lt;div class=&quot;frame frame--sticker&quot;&gt;
    &lt;div class=&quot;shader&quot;&gt;
      &lt;img src=&quot;http://robbowen.digital/assets/img/articles/holo/dash.jpg&quot; alt=&quot;The dashboard of a vintage car&quot;&gt;
      &lt;div class=&quot;shader__layer specular&quot;&gt;
        &lt;div class=&quot;shader__layer mask&quot;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;I&#39;ve said it before, but it bears repeating: modern CSS is such an amazing tool to work with and I&#39;m consistently impressed with the level of fidelity you can achieve. That said, It&#39;s time to address the potential elephant in the room. Depending on the device or browser you&#39;ve used to read this article, your scroll performance might be on the floor and your fans may have kicked up a notch.&lt;/p&gt;
&lt;p&gt;At the time of writing, blend modes in browsers are still pretty resource heavy. For more complex effects, with several layers of compositing, you will see a real performance hit. Add any CSS animations or transitions to the mix and it will really tank - particularly in Safari. After a bit of tweaking I eventually managed to claw back a bit of a performance boost by using &lt;code&gt;backface-visibility: hidden&lt;/code&gt;, but my initial impulse was to force GPU rendering by using the trusty &lt;code&gt;transform: translateZ(0);&lt;/code&gt; hack. Sadly, adding a transform revealed another quirk to be aware of.&lt;/p&gt;
&lt;p&gt;Due to the reliance on &lt;code&gt;background-attachment: fixed&lt;/code&gt;, applying CSS transforms to the shader can cause some strange side-effects. In Chrome it broadly works, but the gradients may appear offset depending on the transforms applied. Firefox on the other hand, will simply ignore the fixed positioning and your gradients will totally appear static. I&#39;m sure there are ways and means around that, but they&#39;d likely be testing the spirit of doing all of this without using JS.&lt;/p&gt;
&lt;p&gt;All-in-all this was really fun to explore. Sure, we can&#39;t get quite the same level of fidelity as GLSL, but for simpler effects this technique is a great alternative to introducing extra libraries to your project. As great as these effects can look though, I think that for now this is very much a case of: just because you can, doesn&#39;t mean you should.&lt;/p&gt;
&lt;p&gt;Until CSS filters and blend modes become more performant (or until browsers allow linking GLSL filters directly from CSS 🤞) restraint and subtlety might be the best way to go.&lt;/p&gt;
&lt;h2&gt;Image credits&lt;/h2&gt;
&lt;p&gt;The demos in this article use a collection of images or composited textures made available by the Unsplash photographers below. Please go check out their beautiful work.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://unsplash.com/photos/4ShXInrtdV4&quot; target=&quot;_blank&quot;&gt;Bokeh lights&lt;/a&gt; by &lt;a href=&quot;https://unsplash.com/@helloimnik&quot; target=&quot;_blank&quot;&gt;Hello I&#39;m Nik&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://unsplash.com/photos/SkLSOR5r3nc&quot; target=&quot;_blank&quot;&gt;Silhouette of pine trees under stars&lt;/a&gt; by &lt;a href=&quot;https://unsplash.com/@joshua_j_woroniecki&quot; target=&quot;_blank&quot;&gt;Joshua Woroniecki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://unsplash.com/photos/upXoQv5GAr8&quot; target=&quot;_blank&quot;&gt;Aurora&lt;/a&gt; by &lt;a href=&quot;https://unsplash.com/@followhansi&quot; target=&quot;_blank&quot;&gt;Johannes Groll&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://unsplash.com/photos/P8PlK2nGwqA&quot; target=&quot;_blank&quot;&gt;Lake under blue sky&lt;/a&gt; by &lt;a href=&quot;https://unsplash.com/@juanster&quot; target=&quot;_blank&quot;&gt;Juan Davila&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://unsplash.com/photos/I1C6CrPNyo8&quot; target=&quot;_blank&quot;&gt;Vintage dashboard&lt;/a&gt; by &lt;a href=&quot;https://unsplash.com/@ynwasso&quot; target=&quot;_blank&quot;&gt;Wassim Chouak&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://unsplash.com/photos/2jYK_Bloby4&quot; target=&quot;_blank&quot;&gt;Light leak&lt;/a&gt; by &lt;a href=&quot;https://unsplash.com/@seanwsinclair&quot; target=&quot;_blank&quot;&gt;Sean Sinclair&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://unsplash.com/photos/1k7vrumRHow&quot; target=&quot;_blank&quot;&gt;Scratches&lt;/a&gt; by &lt;a href=&quot;https://unsplash.com/@dancristianpaduret&quot; target=&quot;_blank&quot;&gt;Dan Cristian Pădureț&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  
  <entry>
    <title>Mind your language</title>
    <link href="http://robbowen.digital/wrote-about/minding-your-language/"/>
    <updated>2022-07-05T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/minding-your-language/</id>
    <content type="html">&lt;style&gt;
  *.flow p:lang(ja) {
    font-weight: bold;
  } 

  .highlight-padding {
    border-radius: 1rem;
    border: 2px dashed var(--fill);
    background-image: linear-gradient(180deg, var(--bg), var(--bg)), url(&quot;data:image/svg+xml,%3Csvg xmlns=&#39;http://www.w3.org/2000/svg&#39; viewBox=&#39;0 0 22.93 22.93&#39;%3E%3Cpolygon fill=&#39;%23ff99b3&#39; points=&#39;0 8.18 14.75 22.93 22.74 22.93 0 0.19 0 8.18&#39;/%3E%3Cpolygon fill=&#39;%23ff99b3&#39; points=&#39;22.93 8.37 22.93 0.38 22.56 0 14.56 0 22.93 8.37&#39;/%3E%3C/svg%3E&quot;);
    background-clip: content-box, padding-box;
    background-size: auto, 11px;
    box-shadow: inset 0 0 0 2px var(--bg);
  }

  p.highlight-padding {
    font-weight: bold;
  }

  .missing {
    color: transparent;
    border: 1px solid var(--text);
  }

  .character {
    color: var(--line);
    font-size: clamp(3rem, 8vw, 8rem);
    font-weight: normal;
    font-family: -apple-system,BlinkMacSystemFont,&#39;Segoe UI&#39;,Roboto,Helvetica,Arial,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;
  }

  p.vert {
    writing-mode: vertical-rl;
    -webkit-writing-mode: vertical-rl;
    -ms-writing-mode: vertical-rl;
    font-size: 2rem;
    font-weight: bold;
  }

  p.fake-vert {
    text-align: center;
    word-break: break-all;
    font-size: 2rem;
    font-weight: bold;
    width: 1em;
    white-space: break-word;
    line-height: 1;
  }

  p:lang(ar) {
    font-size: 2.5rem;
  }

  *:lang(zh),
  *:lang(jp),
  *:lang(ko) {
    font-family: -apple-system,BlinkMacSystemFont,&#39;Segoe UI&#39;,Roboto,Helvetica,Arial,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;
  }
&lt;/style&gt;
&lt;p&gt;The web has a diverse global audience and the websites and apps we build have to cater towards a wide range of languages, scripts and dialects. Developing for multiple languages isn&#39;t as simple as bashing the text onto the page though. As developers, we need to take extra care to present multi-lingual content in the most natural way possible. Fortunately, HTML and CSS present us with a few options to enhance the reading experience of our users.&lt;/p&gt;
&lt;p&gt;If you find yourself needing to design or develop content to work in multiple languages, here are five tips and gotchas that might help things run more smoothly:&lt;/p&gt;
&lt;h2&gt;1. Fonts and diacritics&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Have you ever heard the works of the Czech composer &lt;span lang=&quot;cs&quot;&gt;Anton&lt;b style=&quot;font-family: serif&quot;&gt;í&lt;/b&gt;n Dvo&lt;b style=&quot;font-family: monospace, system-ui&quot;&gt;ř&lt;/b&gt;&lt;i style=&quot;font-family: serif&quot;&gt;á&lt;/i&gt;k&lt;/span&gt;? Perhaps you might have visited the beautiful Latvian city of &lt;span lang=&quot;lv&quot;&gt;R&lt;b style=&quot;font-family: monospace&quot;&gt;ī&lt;/b&gt;ga&lt;/span&gt;, or strolled lazily through the &lt;span lang=&quot;fr&quot;&gt;Champs-&lt;span class=&quot;missing&quot;&gt;É&lt;/span&gt;lys&lt;span class=&quot;missing&quot;&gt;é&lt;/span&gt;es&lt;/span&gt; in Paris?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ok, so contrived example text aside, choosing a webfont with wide accent (or &#39;&lt;a href=&quot;https://en.wikipedia.org/wiki/Diacritic&quot;&gt;diacritic&lt;/a&gt;&#39;) support can be a real quick-win when designing for multi-lingual content.&lt;/p&gt;
&lt;p&gt;It&#39;s easy to take for granted, but not all fonts cover the full range of diacritic marks and this can be particularly noticeable with brand or display fonts. In fact, a quick look at Google Fonts &lt;a href=&quot;https://fonts.google.com/?category=Sans+Serif&amp;amp;sort=popularity&amp;amp;preview.text=T%C5%99inec.%20R%C4%ABga.%20Bucure%C8%99ti.%20%C5%81%C3%B3d%C5%BA.&amp;amp;preview.text_type=custom&quot;&gt;shows that even some of the most popular webfonts struggle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wherever possible, make sure to check support before deciding on your webfonts. If you&#39;re stuck with a font that doesn&#39;t include the diacritics you need, you needn&#39;t worry though. In the event that your primary font doesn&#39;t include a particular diacritic mark, browsers will render the missing character with the next available font in your &lt;code&gt;font-family&lt;/code&gt; stack.&lt;/p&gt;
&lt;p&gt;To avoid harming your design with clunky, mismatched text like the example above, make sure that the fonts in your stack are as close a visual match as possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Match the type of fonts&lt;/strong&gt;. If your primary font is a serif, your fallback fonts should all be serif fonts. The same goes for for sans-serif or monospace fonts. Swap like for like.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Match the x-height&lt;/strong&gt; of your fonts to ensure the size is similar. Mismatched fonts might appear taller than others, leaving your content looking ragged or uneven.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Match the contrast or weight&lt;/strong&gt; of your fonts to ensure that missing characters don&#39;t appear thicker or thinner than the primary font&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this might seem obvious but take a quick look through the font choices on Instagram stories and you&#39;ll see how easily font stack can slip through the net.&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/lang.jpg&quot; style=&quot;padding-top: 59.09598214285714%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/lang.jpg&quot; alt=&quot;A demonstration of the text &#39;Ikšķile&#39; rendered with fallbacks for diacritics. The first example shows mismatched sans and serif. The second shows mismatched x-height. The third and final shows mismatched contrast.&quot;&gt;
  &lt;/div&gt;
&lt;h2&gt;2. The &lt;code&gt;lang&lt;/code&gt; attribute&lt;/h2&gt;
&lt;p&gt;Another useful tool for improving the semantics of multi-lingual content is the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang&quot;&gt;&lt;code&gt;lang&lt;/code&gt; attribute&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The immediate benefit of setting a page-level &lt;code&gt;lang&lt;/code&gt; attribute on the &lt;code&gt;html&lt;/code&gt; tag is that search engines will know how to index your content. More importantly though, the &lt;code&gt;lang&lt;/code&gt; attribute will inform assistive technologies such as screen-readers which accent and dictionaries to use when reading the content aloud. When a language code is combined with a regional subcode, for example &lt;code&gt;en-GB&lt;/code&gt;, the results can get even more granular – as &lt;a href=&quot;https://twitter.com/MicheBarks&quot;&gt;Michelle Barker&lt;/a&gt; writes, the subcode &lt;a href=&quot;https://css-irl.info/dont-forget-the-lang-attribute/&quot;&gt;can also be used to specify the regional dialect of a screen reader&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Beyond assistive tech, setting the &lt;code&gt;lang&lt;/code&gt; attribute is also beneficial to our CSS. When the attribute is set, we can use the &lt;code&gt;:lang&lt;/code&gt; pseudo class to style content based on language. For example, the following CSS could be used to serve a specific font stack to any content that matches &lt;code&gt;:lang(ar)&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:lang(ar)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Noto Sans Arabic&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need more fine-grained control, the &lt;code&gt;:lang&lt;/code&gt; selector also stacks codes and regional subcodes. With the following CSS snippet, content nested under a &lt;code&gt;lang&lt;/code&gt; attribute of &lt;code&gt;en-GB&lt;/code&gt; would be both blue and bold, but content with &lt;code&gt;en-US&lt;/code&gt; would only be blue:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:lang(en)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:lang(en-GB)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The &lt;code&gt;lang&lt;/code&gt; attribute and character sets&lt;/h3&gt;
&lt;p&gt;There&#39;s one last feature that the &lt;code&gt;lang&lt;/code&gt; attribute provides for &lt;abbr title=&quot;Chinese Japanese Korean&quot;&gt;CJK&lt;/abbr&gt; languages in particular that might not be immediately obvious to people unfamiliar with those languages.&lt;/p&gt;
&lt;p&gt;Chinese, Japanese and Korean all use idographic characters but, as with all languages, usage has changed over time and so have the characters themselves. A hanzi character used in traditional Chinese, might be considerably different to a Japanese kanji or Korean hanja character of the same meaning. In a attempt to minimise the number of character variations, the unicode maintainers decided that all variations should be linked together under a single unicode point. This was known as the &lt;a href=&quot;https://en.wikipedia.org/wiki/Han_unification&quot;&gt;Han Unification&lt;/a&gt; and it&#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Han_unification#Rationale_and_controversy&quot;&gt;pretty controversial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Why does this matter for typesetting on the web? Let&#39;s take a quick look – Below is the same character (U+8FD4) repeated five times:&lt;/p&gt;
&lt;div class=&quot;repel&quot;&gt;
  &lt;span class=&quot;character&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot;&gt;返&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Now let&#39;s try that again, but this time setting the &lt;code&gt;lang&lt;/code&gt; attribute for each from left to right: &lt;em&gt;Simplified Chinese, Traditional Chinese (Taiwan), Traditional Chinese (Hong Kong), Japanese, Korean.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;repel&quot;&gt;
  &lt;span class=&quot;character&quot; lang=&quot;zh-ZH&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot; lang=&quot;zh-TW&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot; lang=&quot;zh-HK&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot; lang=&quot;ja-JP&quot;&gt;返&lt;/span&gt;
  &lt;span class=&quot;character&quot; lang=&quot;ko-KR&quot;&gt;返&lt;/span&gt;
&lt;/div&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;zh-ZH&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;返&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;zh-TW&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;返&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;zh-HK&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;返&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ja-JP&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;返&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ko-KR&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;返&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without setting the &lt;code&gt;lang&lt;/code&gt; attribute, the browser doesn&#39;t have any knowledge of which language you are using and will default to its best guess. These characters might look similar, but they&#39;re distinct between languages. For speakers of &lt;abbr title=&quot;Chinese Japanese Korean&quot;&gt;CJK&lt;/abbr&gt; languages, a word or phrase displaying the wrong character variant might still be readable, but it will look a bit jarring.&lt;/p&gt;
&lt;p&gt;Setting the &lt;code&gt;lang&lt;/code&gt; attribute ensures that browser renders the character appropriate for the target language. It&#39;s also important to note that the region subcode of the language definition is helpful here too - &lt;code&gt;zh-ZH&lt;/code&gt;, &lt;code&gt;zh-TW&lt;/code&gt; and &lt;code&gt;zh-HK&lt;/code&gt; are all under the &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO_639_macrolanguage&quot;&gt;zh macrolanguage code&lt;/a&gt;, but the character still differs by region.&lt;/p&gt;
&lt;p&gt;All-in-all, the &lt;code&gt;lang&lt;/code&gt; attribute is an vital tool in removing ambiguity from our multi-lingual content.&lt;/p&gt;
&lt;h2&gt;3. Direction and writing mode&lt;/h2&gt;
&lt;p&gt;Another common requirement of typesetting different languages is how to handle right-to-left scripts such as Arabic or Hebrew.&lt;/p&gt;
&lt;p&gt;Simply entering &lt;abbr title=&quot;right to left&quot;&gt;RTL&lt;/abbr&gt; text will flow the text properly, but the alignment of elements to the page itself will be off:&lt;/p&gt;
&lt;p lang=&quot;ar&quot;&gt; أنا أسف ، لا اتكلم عربي جيدا&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; أنا أسف ، لا اتكلم عربي جيدا&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fortunately HTML gives us a handy way to set the writing direction with the &lt;code&gt;dir&lt;/code&gt; attribute. Setting &lt;code&gt;dir=&amp;quot;rtl&amp;quot;&lt;/code&gt; on the &lt;code&gt;html&lt;/code&gt; tag will flip the text direction for the whole page, but you can also set it at text-level on individual elements. Let&#39;s take another look:&lt;/p&gt;
&lt;p lang=&quot;ar&quot; dir=&quot;rtl&quot;&gt; أنا أسف ، لا اتكلم عربي جيدا&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;rtl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; أنا أسف ، لا اتكلم عربي جيدا&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much better!&lt;/p&gt;
&lt;p&gt;There&#39;s more to text direction though – &lt;abbr title=&quot;right to left&quot;&gt;RTL&lt;/abbr&gt; is not the only alternative writing mode. Some scripts such as Japanese or traditional Chinese can have a vertical writing mode, written from top to bottom and from right to left. This was historically much harder to handle in CSS, and for a long time it was common to fake vertical text with a questionable combination of &lt;code&gt;word-break: break-all&lt;/code&gt; and a width of &lt;code&gt;1ch&lt;/code&gt;. Fortunately with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode&quot;&gt;&lt;code&gt;writing-mode&lt;/code&gt;&lt;/a&gt; gaining support in all major browsers, there&#39;s a better way. This property gives us the abillity to easily set lines of text to run vertically and in either direction. A value of &lt;code&gt;vertical-lr&lt;/code&gt; sets it from top to bottom, left to right. &lt;code&gt;vertical-rl&lt;/code&gt; flows lines of content top to bottom, right to left.&lt;/p&gt;
&lt;p&gt;But how much of a difference does this actually make vs the &#39;creative&#39; method of using word-break?&lt;/p&gt;
&lt;p&gt;Take a look at the Japanese-language examples below. On the left is vertical text faked with &lt;code&gt;word-break&lt;/code&gt;. On the right is text set correctly with &lt;code&gt;writing-mode: vertical-rl&lt;/code&gt;. The differences might seem subtle at first, but they&#39;re immediately obvious to anyone who reads Japanese:&lt;/p&gt;
&lt;div class=&quot;cluster&quot;&gt;
  &lt;p class=&quot;fake-vert&quot; lang=&quot;ja-JP&quot;&gt;
  1980年代のストリートファッション
  &lt;/p&gt;
  &lt;p class=&quot;vert&quot; lang=&quot;ja-JP&quot;&gt;
  1980年代のストリートファッション
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Ok, before we move on, you might have noticed the conspicuous split between controlling text direction with the &lt;code&gt;dir&lt;/code&gt; attribute in HTML and then &lt;code&gt;writing-mode&lt;/code&gt; in CSS. Personally, I find this to be a little confusing. Fortunately though, it looks like the CSS spec agrees and there is an experimental specification for &lt;code&gt;writing-mode&lt;/code&gt; that will bring &lt;code&gt;sideways-lr&lt;/code&gt; for &lt;abbr title=&quot;left to right&quot;&gt;LTR&lt;/abbr&gt; languages and &lt;code&gt;sideways-rl&lt;/code&gt; for &lt;abbr title=&quot;right to left&quot;&gt;RTL&lt;/abbr&gt; languages. Currently this is only supported in Firefox, but here&#39;s hoping it gains wider support.&lt;/p&gt;
&lt;h2&gt;4. Logical CSS properties&lt;/h2&gt;
&lt;p&gt;Building on the last point, we can now get our text orientated properly. But what about padding and margin?&lt;/p&gt;
&lt;p&gt;Traditional margin and padding CSS properties specify a direction: top, right, bottom or left. These are fine for &lt;abbr title=&quot;left to right&quot;&gt;LTR&lt;/abbr&gt; languages, but when writing mode changes, these properties won&#39;t take direction into account and may break your designs. Fortunately with CSS Logical properties, we now have access to direction-aware alternatives.&lt;/p&gt;
&lt;p&gt;Where you would have previously used &lt;code&gt;padding-left&lt;/code&gt; and &lt;code&gt;padding-right&lt;/code&gt;, you can instead use &lt;code&gt;padding-inline-start&lt;/code&gt; and &lt;code&gt;padding-inline-end&lt;/code&gt;. Similarly for &lt;code&gt;padding-top&lt;/code&gt; and &lt;code&gt;padding-bottom&lt;/code&gt; you can now use &lt;code&gt;padding-block-start&lt;/code&gt; or &lt;code&gt;padding-block-end&lt;/code&gt;. The same pattern works for margins.&lt;/p&gt;
&lt;p&gt;The great thing about these new properties is that for &lt;abbr title=&quot;left to right&quot;&gt;LTR&lt;/abbr&gt; languages, they&#39;re totally indistinguishable from the standard padding properties. Below is an example in English, the first uses padding-left and the second uses padding-inline-start.&lt;/p&gt;
&lt;p lang=&quot;en&quot; class=&quot;highlight-padding&quot; style=&quot;padding-left: 40px&quot;&gt;Half-fat decaf soy-milk caramel latté&lt;/p&gt;
&lt;p lang=&quot;en&quot; class=&quot;highlight-padding&quot; style=&quot;padding-inline-start: 40px&quot;&gt;Half-fat decaf soy-milk caramel latté&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style-attr language-css&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt; &lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Half-fat decaf soy-milk caramel latté&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style-attr language-css&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt; &lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;padding-inline-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Half-fat decaf soy-milk caramel latté&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you find yourself supporting an &lt;abbr title=&quot;right to left&quot;&gt;RTL&lt;/abbr&gt; language though, these new properties suddenly make all the difference. Let&#39;s compare:&lt;/p&gt;
&lt;p lang=&quot;ar&quot; dir=&quot;rtl&quot; class=&quot;highlight-padding&quot; style=&quot;padding-left: 40px&quot;&gt;القهوة العربية الأصيلة&lt;/p&gt;
&lt;p lang=&quot;ar&quot; dir=&quot;rtl&quot; class=&quot;highlight-padding&quot; style=&quot;padding-inline-start: 40px&quot;&gt;القهوة العربية الأصيلة&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;rtl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style-attr language-css&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt; &lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;القهوة العربية الأصيلة&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;rtl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style-attr language-css&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt; &lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;padding-inline-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;القهوة العربية الأصيلة&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The huge benefit of using logical properties is even more obvious when using a vertical &lt;code&gt;writing-mode&lt;/code&gt;. As with the previous examples, for the first example we&#39;re using &lt;code&gt;padding-left&lt;/code&gt; and on the second we&#39;re using &lt;code&gt;padding-inline-start&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;cluster&quot;&gt;
  &lt;p class=&quot;vert highlight-padding&quot; lang=&quot;ja-JP&quot; style=&quot;padding-left: 40px&quot;&gt;水出しコーヒー&lt;/p&gt;
  &lt;p class=&quot;vert highlight-padding&quot; lang=&quot;ja-JP&quot; style=&quot;padding-inline-start: 40px&quot;&gt;水出しコーヒー&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;At the time of writing, CSS Logical properties are &lt;a href=&quot;https://caniuse.com/css-logical-props&quot;&gt;really widely supported&lt;/a&gt; so there&#39;s every reason to start using them today. If you&#39;d like to know more, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties&quot;&gt;you can read more about logical properties on MDN&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;5. Form validation&lt;/h2&gt;
&lt;p&gt;This last point is where things get a bit tricky. The main point of contention is that, as web developers we need form validation to keep the web secure. At the same time though, we need to be mindful that our security measures don&#39;t unintentionally alienate or block users from using our websites.&lt;/p&gt;
&lt;p&gt;A cursory web search for name validation rules throws back regex patterns like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;firstname&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;^[a-zA-Z ,.&#39;-]+$&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This validation pattern definitely allows for a wide range of name punctuation but at the same time it would exclude a vast number of people who have accents, diacritics or non-Latin characters in their names. It&#39;s possible to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions&quot;&gt;extend this regular expression&lt;/a&gt; to include a wider range of accented characters, but making sure that all bases are covered can quickly become a maintenance nightmare.&lt;/p&gt;
&lt;p&gt;A common approach to this challenge is to simply ask users to write their name without diacritics. Whilst this would certainly pass validation rules, this is not a true solution. Rendering a character without its diacritic can sometimes change the meaning of a word and names are a deeply personal thing that vary widely by culture and by language. Regardless of the intent, to build with assumptions on what constitutes a valid name risks excluding those who fit outside of those assumptions.&lt;/p&gt;
&lt;p&gt;My recommendation for the most inclusive strategy? Make sure you do all of your usual security checks for script injection etc. but, wherever possible, don&#39;t validate the individual characters in a user&#39;s name. After all, as Patrick McKenzie wrote in the evergreen essay &lt;a href=&quot;https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/&quot;&gt;&amp;quot;Falsehoods programmers believe about names&amp;quot;&lt;/a&gt;, it&#39;s near-impossible to get it right.&lt;/p&gt;
&lt;p&gt;...Oh, and one last thing. If you absolutely &lt;em&gt;have to&lt;/em&gt; validate names then make sure that any feedback messages put the blame firmly on your systems. Telling a user that their name is &#39;invalid&#39; is rarely a good look.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;This post is just scratching the surface, but hopefully illustrates how important it is to handle multi-lingual content with care. Here&#39;s a quick summary of what we&#39;ve looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Choose fonts with good support for accents and diacritics and build a solid font stack to handle missing characters&lt;/li&gt;
&lt;li&gt;Always set the &lt;code&gt;lang&lt;/code&gt; attribute to let the browser know what language your content is in&lt;/li&gt;
&lt;li&gt;Set horizontal text direction with the &lt;code&gt;dir&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;Set vertical text direction with the &lt;code&gt;writing-mode&lt;/code&gt; css property&lt;/li&gt;
&lt;li&gt;Use logical properties for padding and margins to ensure that layout changes won&#39;t break your design&lt;/li&gt;
&lt;li&gt;Where possible, avoid validating name inputs and write conscientious, user-friendly error messages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTML and CSS will get us far, but it&#39;s worth noting that there are no one-size-fits-all solutions for getting multi-lingual content right. I&#39;d still advise that, wherever possible have your work reviewed by a native-speaker. That goes double if you&#39;re developing content for a language that you yourself don&#39;t understand.&lt;/p&gt;
&lt;p&gt;That said, over the years I&#39;ve found that following these steps can provide a solid foundation for building flexible, multi-lingual content.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Getting started with Go</title>
    <link href="http://robbowen.digital/wrote-about/getting-started-with-go/"/>
    <updated>2021-07-08T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/getting-started-with-go/</id>
    <content type="html">&lt;div class=&quot;break-out&quot;&gt;
  &lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/golang-banner.jpg&quot; style=&quot;padding-top: 39.86899563318777%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/golang-banner.jpg&quot; alt=&quot;The Go branding&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Lately, it seems like Go (or sometimes, Golang) is having a bit of a moment.&lt;/p&gt;
&lt;p&gt;Developed by Google, Go has been around for just over a decade now but over the last year it seems to have been steadily gaining in popularity on social media and Github. Given its versatility, It&#39;s perhaps easy to understand why - Go has been used for everything from &lt;abbr title=&quot;command line interface&quot;&gt;CLI&lt;/abbr&gt; tools to building high-profile devops software like Docker and Kubernetes.&lt;/p&gt;
&lt;p&gt;One thing that might be less immediately obvious though, is that Go is also equally well suited to building web apps and APIs. In fact, as a JavaScript (or TypeScript) developer, Go has a lot to love:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simple syntax&lt;/strong&gt; - Go was designed in a way that, once you know the core syntax, you should be able to read anyone&#39;s code. In fact, there&#39;s officially only one way to write Go code, and the excellent language tooling will keep you on the straight and narrow. There&#39;ll be no time wasted deciphering someone else&#39;s &amp;quot;clever&amp;quot; one-liners!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statically-typed, compiled language&lt;/strong&gt; - As with a language like TypeScript, typed variables mean that it&#39;s easier to manage bugs. The compiler does a lot of heavy lifting here too - you can&#39;t even compile your code if you have any unused variables knocking about in your codebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous code and concurrency&lt;/strong&gt; - Go was built with concurrency in mind and features &#39;go routines&#39; to manage multi-threaded, asynchronous code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-platform&lt;/strong&gt; - The Go compiler allows you to build cross-platform applications which are easy to deploy pretty much anywhere. You don&#39;t need even to deploy your sourcecode, just the compiled binary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As for me though, all of that stuff is kinda secondary.&lt;/p&gt;
&lt;p&gt;You see, Go comes with a very powerful standard library (&lt;abbr title=&quot;standard library&quot;&gt;stdlib&lt;/abbr&gt;) that means that you can really do a lot without needing to reach for a framework. Coming into the language from the JS world, which often relies heavily on third-party packages from NPM, it can initially seem really counter-intuitive to be writing so much low-level code. On the contrary though, I&#39;ve found that writing Go has led me to better understand certain concepts that other languages and frameworks such as Express or Rails have let me &#39;magic away&#39; in the past.&lt;/p&gt;
&lt;p&gt;The Go way of doing things feels very considered and deliberate; it makes me feel like I&#39;m learning more deeply about how the web works in general, and I love that.&lt;/p&gt;
&lt;p&gt;For me, writing Go is just – dare I say it – fun.&lt;/p&gt;
&lt;h2&gt;Let&#39;s build something&lt;/h2&gt;
&lt;p&gt;Ok, enough about why &lt;em&gt;I&lt;/em&gt; like Go. If any of the above sounds appealing but you&#39;re not sure where to start, then this post is for you.&lt;/p&gt;
&lt;p&gt;That said, this tutorial isn&#39;t intended for anyone who&#39;s already started writing Go - it&#39;s going to deliberately be very bare bones. For the sake of brevity, I also won&#39;t be spending much time covering core programming concepts too much. Prior to learning Go my primary language was JavaScript and I suspect that many of you who will be reading this will be somewhat familiar with that language. When I do make comparisons with other languages, it will be to JavaScript.&lt;/p&gt;
&lt;p&gt;With all of that out of the way, &lt;em&gt;let&#39;s go&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;(I&#39;m not even sorry for that pun)&lt;/p&gt;
&lt;h2&gt;Preparing to work with Go&lt;/h2&gt;
&lt;p&gt;Thankfully, getting set up for Go development is not as complicated as some other languages. Before we start, I&#39;d suggest completing the following three steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you&#39;re not already using it, I&#39;d strongly recommend downloading and using &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VS code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Download and run the &lt;a href=&quot;https://golang.org/dl/&quot;&gt;Go installer&lt;/a&gt;. At the time of writing the latest Go version is &lt;strong&gt;1.16&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Install the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=golang.Go&quot;&gt;Go VS code extension&lt;/a&gt; to enable intellisense, auto-imports, linting and more.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Starting a Go project&lt;/h2&gt;
&lt;p&gt;If you&#39;ve done any JS development, you&#39;re probably familiar with using third-party code from NPM.&lt;/p&gt;
&lt;p&gt;Go uses the concept of packages in a similar way, but for quite a while Go didn&#39;t have an official means of managing project dependencies. Several package management solutions popped up to try and make development easier but, in my experience, having a wide range of package managers made things a lot more confusing.&lt;/p&gt;
&lt;p&gt;Thankfully though, Go 1.13 introduced &lt;code&gt;go modules&lt;/code&gt; as the &lt;em&gt;de facto&lt;/em&gt; means of managing your application&#39;s dependencies. Coming from the JS world, Go modules uses the &lt;code&gt;go mod&lt;/code&gt; command in a way that might be familiar.&lt;/p&gt;
&lt;p&gt;Let&#39;s initialise a go modules project. Create a folder for your project and from the root open your terminal and run the &lt;code&gt;go mod&lt;/code&gt; command, replacing &lt;code&gt;&amp;lt;PACKAGENAME&amp;gt;&lt;/code&gt; with the desired project name:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;go mod init &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PACKAGENAME&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can choose whatever you want for this name, but be aware that &lt;strong&gt;it needs to be unique&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Later on we&#39;ll be importing external packages, so we will need to make sure that our application&#39;s package name doesn&#39;t clash with any future dependencies. Understandably, seeing into the future is a big ask. To mitigate name-clashes there&#39;s a Go convention of using your project&#39;s github path or a website url as the package name.&lt;/p&gt;
&lt;p&gt;Let&#39;s try it. You can use whichever name you want, but I might run something like:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;go mod init robbowen.digital/go-intro&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the command has finished, we will now have a &lt;code&gt;go.mod&lt;/code&gt; file in our project root. Inside, it will look a little something like this:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;module robbowen.digital/go-intro&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;go 1.16&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don&#39;t need to worry about this too much for now, but this file specifies the name of our root module (our application) and the version of go that it requires. If you know JS, the &lt;code&gt;go.mod&lt;/code&gt; file functions like a &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Our first file, and the main package&lt;/h2&gt;
&lt;p&gt;Go uses the concept of packages to group code and all Go applications start with a package called &lt;code&gt;main&lt;/code&gt;. You can think of it a bit like the entrypoint of a JS bundle - the main package runs first, and everything else branches out from there.&lt;/p&gt;
&lt;p&gt;We can declare which package a particular file belongs to with the &lt;code&gt;package&lt;/code&gt; keyword. Let&#39;s create a file in our project root called &lt;code&gt;app.go&lt;/code&gt;. Inside the file, let&#39;s declare that this code belongs to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The package declaration is the first step, but we will also need to tell the go compiler where our application actually starts. Inside of our main package, we will need to declare a function, also called &lt;code&gt;main&lt;/code&gt;. Functions in Go look a lot like they do in JavaScript and use the &lt;code&gt;func&lt;/code&gt; keyword.&lt;/p&gt;
&lt;p&gt;In our app.go file, let&#39;s prepare a main function for our application.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// application code will go here&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice, that&#39;s everything we need to get started.&lt;/p&gt;
&lt;p&gt;At this point you might have noticed that we&#39;re not calling &lt;code&gt;main()&lt;/code&gt; anywhere, and you&#39;d be correct. Go will automatically call &lt;code&gt;main()&lt;/code&gt; when our application runs. To see that in action we&#39;ll need to update our code so that it actually does something.&lt;/p&gt;
&lt;p&gt;Let&#39;s write a message to the terminal. For that we&#39;re going to need to use the &lt;code&gt;fmt&lt;/code&gt; a package from the Go standard library. The name &lt;code&gt;fmt&lt;/code&gt; stands for &lt;em&gt;format&lt;/em&gt; and, in case you were wondering how to say it out loud, is usually pronounced like &amp;quot;fumt&amp;quot;. Inside of this package there&#39;s a handy function called &lt;code&gt;Println&lt;/code&gt; that we can use to print a message to the console.&lt;/p&gt;
&lt;p&gt;Let&#39;s update our main function:&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Aw yiss&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re coding along in VS code, you might have noticed that you don&#39;t actually need to add that import line to the top of the file. Typing &lt;code&gt;fmt.&lt;/code&gt; should prompt intellisense to show you the available functions and, whenever you save a Go file, VS Code&#39;s language server should automatically add all of the appropriate imports.&lt;/p&gt;
&lt;p&gt;Let&#39;s build and run our application.&lt;/p&gt;
&lt;p&gt;From your terminal, you can run your new program with&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;go run main.go&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hell yeah, you&#39;re now officially a Go developer 🎉&lt;/p&gt;
&lt;h2&gt;Variables and types&lt;/h2&gt;
&lt;p&gt;At the moment we&#39;ve hardcoded that message, so let&#39;s move it to a variable.&lt;/p&gt;
&lt;p&gt;Variable definition in Go will be familiar to a JS dev, and even moreso if you&#39;ve dabbled with TypeScript:&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; myString &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Aw yiss&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; someNumber &lt;span class=&quot;token builtin&quot;&gt;int32&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; mmmPie &lt;span class=&quot;token builtin&quot;&gt;float32&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.14159&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; myBool &lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;myBool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;var&lt;/code&gt; keyword followed by a name defines a new variable. Being a typed language, the variable&#39;s type definition comes next (there&#39;s a &lt;a href=&quot;https://golang.org/ref/spec#Types&quot;&gt;full list of the available types in the Go documentation&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Lastly, you&#39;re free to specific an initial value but Go is statically typed, so bear in mind that the type of a variable cannot change type.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; someNumber &lt;span class=&quot;token builtin&quot;&gt;int32&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// this won&#39;t work&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;someNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;thirty seven&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Shortform variables&lt;/h2&gt;
&lt;p&gt;Writing var and the type out every time you need a new variable can get a bit tedious, so Go also provides a handy shortform syntax. This shortform doesn&#39;t include a type definition and instead assigns a type by inference. If that last sentence meant nothing to you, don&#39;t worry. The shortform allows us to set a variable and let the compiler worry about what type it is.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;message &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Aw yiss&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the time, you&#39;re going to be using this syntax. There are two caveats to be aware of when using this little walrus-looking lad though:
Firstly the &lt;code&gt;:=&lt;/code&gt; is &lt;strong&gt;only used when you first define a variable&lt;/strong&gt;. Reassignment the value is done with &lt;code&gt;=&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;message &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Aw yiss&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;some new value&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// this won&#39;t work as the variable is already defined&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;message &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yet another value&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second caveat is that the short syntax can only be used within a function. Defining package-scoped variables must use the &lt;code&gt;var&lt;/code&gt; syntax. We don&#39;t really need to worry about this one for now, though.&lt;/p&gt;
&lt;p&gt;Ok. Let&#39;s update our application to pass the message as a variable.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  message &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Aw yiss&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Functions&lt;/h2&gt;
&lt;p&gt;We&#39;ve already written a &lt;code&gt;main&lt;/code&gt; function but before we finish let&#39;s take a closer look at how to define our own functions in Go. Here&#39;s a simple function that adds two numbers:&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;func&lt;/code&gt; keyword is used the same way as our main function above, however within the parentheses we&#39;ve defined some &lt;em&gt;input parameters and their types&lt;/em&gt;. After the closing bracket we define the function&#39;s &lt;em&gt;return type&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s worth noting that, though it is generally frowned upon in the JavaScript world, there is something of a convention in Go for naming a function&#39;s input parameters with a single letter such as &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt;. Not everybody does this, and it&#39;s perfectly fine to use something more descriptive, but you&#39;ll see it in the stdlib so it&#39;s good to be aware.&lt;/p&gt;
&lt;p&gt;Ok, now we know a little bit more about functions, let&#39;s finish our application with a custom greeting.&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// where n stands for the name of a programming language&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayYiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Aw yiss, I know &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; n&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  message &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayYiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Golang&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If all goes well, running &lt;code&gt;go run app.go&lt;/code&gt; should print &amp;quot;Aw yiss, I know Golang&amp;quot; to your console. That&#39;s it, we&#39;re finished. From your terminal, you can compile a distributable binary of your application by running:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;go build main.go&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrapping up for now&lt;/h2&gt;
&lt;p&gt;What is essentially a riff on a &amp;quot;Hello World&amp;quot; might not seem like the most exciting application ever, but we&#39;ve actually covered quite a few core concepts of building Go applications.&lt;/p&gt;
&lt;p&gt;We&#39;ve learned how to start a go modules project. We&#39;ve learned about packages and how the main package acts as an entrypoint for Go applications. We&#39;ve learned a little bit about variables and functions and, to finish, we learned how to compile and run our application.&lt;/p&gt;
&lt;p&gt;Hopefully this tutorial has given you a taste for what it&#39;s like to write Go and, if you&#39;re coming from JS or TypeScript background, then it should be clear that learning Go will be building on top of common concepts that you already know.&lt;/p&gt;
&lt;p&gt;If you&#39;ve found this useful, then I&#39;m hoping to make this into a small series. Next time we&#39;ll take a look at structuring your Go applications and keeping things tidy with custom packages.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The year that was(n&#39;t)</title>
    <link href="http://robbowen.digital/wrote-about/hindsight-is-2020/"/>
    <updated>2020-12-31T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/hindsight-is-2020/</id>
    <content type="html">&lt;p&gt;Holy. Shit. Let&#39;s get it out of the way - &lt;strong&gt;2020 has been a prize dickhead&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Like many of you I started the year with &lt;em&gt;plans&lt;/em&gt;. I was going to learn X, Y and Z. I was going be more active with the industry on Twitter. I was going to work on my side-project and, after taking nowhere near enough holiday in 2019, I was going to travel more. Sufficed to say, that is not the year I had.&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;was&lt;/em&gt; fortunate to take a (very-surreal) trip to Japan at the start of the year, but I flew home with just enough time to get myself into lockdown. I haven&#39;t really been outside of Abergavenny since. Then, there was this website. I was super-excited to launch it back in January, and the positive feedback absolutely blew me away. &lt;a href=&quot;http://robbowen.digital/wrote-about/launch-day&quot;&gt;As I wrote back then&lt;/a&gt;, one of my big motivators in building the site was my intention of properly blogging for the first time. Well, a quick look at my &lt;a href=&quot;http://robbowen.digital/writing&quot;&gt;articles page&lt;/a&gt; will show that it didn&#39;t really pan out. Some of you might also have noticed that I have been a lot quieter on Twitter lately too.&lt;/p&gt;
&lt;p&gt;It needs to be said though, that all of the above is utterly inconsequential in the face of what we have collectively faced in a year that has been so painful for so many people.&lt;/p&gt;
&lt;p&gt;2020 has felt a lot like treading water. Like many of you I&#39;m sure, maintaining a business, my physical and mental health, and the health of those around me with everything that&#39;s going on has been utterly exhausting. Honestly, making it through the year is enough, and something that we can all be proud of.&lt;/p&gt;
&lt;p&gt;That said, in the spirit of reviving this blog I feel that writing a little more introspectively about my year might help me to make sense of the chaos.&lt;/p&gt;
&lt;h2&gt;Work&lt;/h2&gt;
&lt;p&gt;Working as an independent developer, maintaining a consistent work-stream is always playing on your mind. After a very busy December in 2019, I had a quiet start to the year before eventually hitting my stride during lockdown. From March onwards I was very fortunate to have a steady stream of client work right up until about two weeks ago.&lt;/p&gt;
&lt;p&gt;With hindsight though, I overdid it. I believe in doing things well and as a result, put a lot of myself into my work. The year’s continuous and over-arching sense of &lt;em&gt;oh shit, what now!?&lt;/em&gt; meant that ultimately I took on a massive amount but didn’t allow myself enough time between projects to decompress. The run up to Christmas is the closest that I&#39;ve come to burn-out since I went freelance, and I&#39;m definitely going to slow down for a while as a result.&lt;/p&gt;
&lt;p&gt;On the positive side though, this year I’ve done some of the most technically complicated work of my career. Whilst I mostly can’t share it publicly due to NDA (that old cliché!), I&#39;ve had a string of happy clients and I’m very proud of what I’ve achieved given the circumstances.&lt;/p&gt;
&lt;p&gt;Another unexpected silver-lining is that the pandemic has normalised remote work. Living in rural Wales, I usually work remotely to avoid a long commute. The sudden change has created new opportunities and it will be interesting to see how this shift pans out longer term, once it is safe for my clients to return to their offices.&lt;/p&gt;
&lt;h2&gt;Personal life&lt;/h2&gt;
&lt;p&gt;As a response to my overworking and having extra time at home due to the lockdown, I&#39;ve thankfully been able to prioritise more offline activities this year. Every Autumn, like clockwork, I start to miss being a student — no, not the heavy drinking — rather, the feeling of purposeful learning. This year was no different, except that I decided to do something about it.&lt;/p&gt;
&lt;p&gt;Around 2011, I studied Korean on-and-off for about a year. Since then I’ve mostly forgotten everything, beyond being able to read Hangul. In late October I bought a Korean grammar textbook and started regularly studying again. So far I&#39;m absolutely loving it. Whilst the language obviously has its own unique history and culture, on a purely linguistic-level Korean syntax and vocabulary are not a million miles removed from what I already know from having studied Japanese at university. It&#39;s empowering to feel like I&#39;m building on top a shared-foundation, rather than learning something totally new. Whenever it&#39;s safe to travel again, I can&#39;t wait to visit South Korea. Hopefully I can get to a conversational level by then.&lt;/p&gt;
&lt;p&gt;Lastly, the one thing in 2020 that has made the biggest positive impact is that, with the pandemic, my partner&#39;s job has become remote and she has been able to move in with me. Due to our working circumstances, my partner and I have been living apart on opposite sides of the country - it has been beyond amazing to finally spend out first Christmas together. Oh, we also got a kitten, &lt;a href=&quot;https://instagram.com/frida.friu&quot;&gt;Frīda&lt;/a&gt;, who is a constant source of joy. It&#39;s been a long while since I had a pet and I can&#39;t overstate the positive and motivating effect of having a little ball of chaotic energy in our home.&lt;/p&gt;
&lt;h2&gt;Looking forward&lt;/h2&gt;
&lt;p&gt;Well, we made it through a very difficult year. Unsurprisingly, I&#39;m not going to offer any predictions or goals for the new year. Doing our best day-by-day seems more attainable for now. If you’ve hired me to help out with your project, used any of my open source work, read my blog, or promoted my work in any way, I’d like to say thanks – you’re awesome.&lt;/p&gt;
&lt;p&gt;Stay safe, everyone. Happy new year!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Introducing Rekishi</title>
    <link href="http://robbowen.digital/wrote-about/introducing-rekishi/"/>
    <updated>2020-05-26T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/introducing-rekishi/</id>
    <content type="html">&lt;div class=&quot;break-out&quot;&gt;
  &lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/rekishi-banner.jpg&quot; style=&quot;padding-top: 26.595744680851062%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/rekishi-banner.jpg&quot; alt=&quot;Rekishi&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Imagine that you&#39;re building a website and you need all links to a particular type of content to open in a modal. On top of that, you would also like a nice transition animation to play whenever the user moves in and out of that content type.&lt;/p&gt;
&lt;p&gt;Working with a framework like React or Vue, there are a number of clientside routers and components available to help transition between different pages of content. However, whilst I&#39;m a fan of frontend frameworks, I&#39;m a big believer in incremental development. Instead of reaching straight for a framework, I tend to approach each project from a user-centric path of using the minimum-possible JS, only adding dependencies as I need them.&lt;/p&gt;
&lt;p&gt;If you&#39;re working closer-to-the-metal with Vanilla JS, coordinating the above example can be ...tricky. In order to capture and handle URLs as a user moves around the site, you need to use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/History&quot;&gt;History API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In a nutshell, the history API lets you capture each URL change, and listen to &#39;pop&#39; events that fire whenever the user navigates forwards or backwards through history. You are also able to associate a particular &#39;state&#39; with each change. However, there&#39;s a frustration that comes with this. When a &#39;pop&#39; event is fired the URL has &lt;em&gt;already changed&lt;/em&gt; and you no longer have any access the outgoing route, or the state object associated with it.&lt;/p&gt;
&lt;p&gt;If you&#39;re stringing different types of content together with page transitions, this can make it particularly tricky to choreograph everything. It&#39;s precisely this frustration that led me to build &lt;a href=&quot;https://github.com/robb0wen/rekishi&quot;&gt;Rekishi&lt;/a&gt; - a minimal pubsub wrapper for the history API.&lt;/p&gt;
&lt;h2&gt;Wrapping the API&lt;/h2&gt;
&lt;p&gt;Rekishi, like my &lt;a href=&quot;https://tornis.robbowen.digital/&quot;&gt;Tornis&lt;/a&gt; library before it, uses a pubsub pattern to listen to any changes in URL. This means that you can &lt;a href=&quot;https://github.com/robb0wen/rekishi#handler-functions&quot;&gt;&#39;subscribe&#39; handler functions to Rekishi&lt;/a&gt;. Whenever you push, pop or otherwise change the browser&#39;s URL, each of those subscribed functions will fire automatically.&lt;/p&gt;
&lt;p&gt;The key difference with the standard history API though, is that each of those subscribed functions will have access to an object containing information about both the incoming &lt;em&gt;and&lt;/em&gt; outgoing route. That object looks a little like this:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  incoming&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    hash&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    params&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Object&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  outgoing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    hash&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    params&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Object&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;REKISHI_PUSH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;          &lt;span class=&quot;token constant&quot;&gt;REKISHI_POP&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;          &lt;span class=&quot;token constant&quot;&gt;REKISHI__HASH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;          &lt;span class=&quot;token constant&quot;&gt;REKISHI_PARAMS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;          &lt;span class=&quot;token constant&quot;&gt;REKISHI_HASHPARAMS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;          &lt;span class=&quot;token constant&quot;&gt;REKISHI_NOCHANGE&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might also have noticed that this object contains an &#39;action&#39; property. This property contains a record of the type of URL change that has occurred. This could have been a change in hash fragment, an update to the query string, or whether the user has clicked a new link or travelled backwards through history.&lt;/p&gt;
&lt;p&gt;Similarly, both the incoming and outgoing routes can also have an associated data object. This functions a little bit like the History API&#39;s state object, however knowing both &lt;em&gt;incoming and outgoing information&lt;/em&gt; allows you to compare and respond accordingly.&lt;/p&gt;
&lt;p&gt;In isolation, actions and data information are useful, but in combination they provide a powerful means of choreographing content changes - Returning to our initial problem of transitioning between a modal and a page, Rekishi would allow you to handle that scenario as follows:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;handleRouteChange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; incoming&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; outgoing&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// on either history, or new link events...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;REKISHI_POP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;REKISHI_PUSH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;incoming&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; outgoing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;page&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// AJAX the content from incoming.path&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// set up your modal and inject AJAX content&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;incoming&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;page&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; outgoing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;//close your modal and remove the content&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Associating path data&lt;/h2&gt;
&lt;p&gt;Aside from handling URL changes, there&#39;s another reason that clientside frameworks like React make page transitions easier. Because they take the rendering of content away from the server entirely, they have access to information about each route ahead-of-time. When building an application with the History API and server-side routing, this approach isn&#39;t available to us.&lt;/p&gt;
&lt;p&gt;Rekishi also provides a means of bridging the two approaches. When initialising Rekishi, it&#39;s possible to &lt;a href=&quot;https://github.com/robb0wen/rekishi#registeredpaths&quot;&gt;pass an array of URL matchers&lt;/a&gt; that can associate default data with a particular path. This means that your app will know what data might be coming from a particular URL, before it is hit:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/posts/*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;post&#39;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/posts/riding-the-shinkansen&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;post&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      featured&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Putting it to the test&lt;/h2&gt;
&lt;p&gt;To put Rekishi to the test, I decided to refactor this website to use it instead of the (still very useful) &lt;a href=&quot;https://github.com/ReactTraining/history&quot;&gt;history module&lt;/a&gt;. Rekishi is small library (less than 2k when gzipped) and in making the switch I have managed to reduce my JS bundle size by a total of around 4kb. In isolation that figure might not seem much, but it made up roughly 23% of the pre-Rekishi bundle size of approximately 17kb. Not too shabby.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;All-in-all, I&#39;ve found that using Rekishi has greatly sped up my workflow for building vanilla JS websites. Using it in conjunction with &lt;a href=&quot;https://tornis.robbowen.digital/&quot;&gt;Tornis&lt;/a&gt; to handle interactions, I&#39;m finding that I&#39;m needing to rely less and less on frameworks in my day-to-day website builds. In terms of my end-user&#39;s bandwidth, I consider that a very good thing.&lt;/p&gt;
&lt;p&gt;I&#39;m hoping that others will find it useful, so I&#39;ve released Rekishi as an open-source project. The &lt;a href=&quot;https://github.com/robb0wen/rekishi&quot;&gt;source and documentation is available on GitHub&lt;/a&gt;. It is also available to install as a clientside dependency from &lt;a href=&quot;https://www.npmjs.com/package/rekishi&quot;&gt;npm&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to name your agency with NLP</title>
    <link href="http://robbowen.digital/wrote-about/how-to-name-your-web-agency-with-nlp/"/>
    <updated>2020-04-18T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/how-to-name-your-web-agency-with-nlp/</id>
    <content type="html">&lt;div class=&quot;break-out&quot;&gt;
  &lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nlp-diagram.jpg&quot; style=&quot;padding-top: 52.62008733624454%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nlp-diagram.jpg&quot; alt=&quot;An abstract representation of an NLP dependency graph&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;As with so many things these days, it starts with clickbait.&lt;/p&gt;
&lt;p&gt;&amp;quot;17 tricks to running a digital agency&amp;quot; was, for the most part, the same kind of unremarkable listicle that surfaces on Twitter from time to time. You know the sort - it keeps things light, you chuckle at a few zingers about office-dogs, exposed piping and wall-mounted fixies. You hit retweet and carry on with the day. This time though, there was one section that stood out to me. On naming your agency, it read:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Just pick a random word or two, and go with that&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Could it really be that simple? Let&#39;s find out.&lt;/p&gt;
&lt;h2&gt;Striking the right tone&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Random&lt;/em&gt; is a pretty strong word.&lt;/p&gt;
&lt;p&gt;Think about the web agencies that you admire. The best names might &lt;em&gt;seem&lt;/em&gt; random but usually carry some extra weight or depth behind them. This is all perhaps quite obvious, but it pokes an immediate flaw in our meme-ified advice. How can our choice be random, but still retain some deeper meaning?&lt;/p&gt;
&lt;p&gt;Fortunately for our soon-to-be-industry-leading web agency, there&#39;s a whole branch of machine learning that can help us automate exactly this kind of problem: &lt;strong&gt;Natural Language Processing&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;An introduction to NLP&lt;/h2&gt;
&lt;p&gt;Natural Language Processing, or &lt;strong&gt;NLP&lt;/strong&gt;, is all about training computers to process and understand human languages. At this point you might be thinking that teaching language to humans is already pretty difficult and, honestly, you wouldn&#39;t be wrong. NLP is a &lt;em&gt;really&lt;/em&gt; dense topic, but building even the smallest NLP applications can feel hugely empowering.&lt;/p&gt;
&lt;p&gt;That said, NLP tutorials have a tendency to get very technical, very quickly and they often assume that anyone entering the field is coming armed with a dual-PhD in linguistics and computer science. On top of that, NLP and machine learning are often cushioned by a thick layer of marketing BS that makes it hard to separate reality from building SkyNet. It&#39;s clear that NLP is an exciting topic but, for newcomers, all of the above means that it isn&#39;t always clear what you might actually want to do with it.&lt;/p&gt;
&lt;p&gt;In this tutorial, we&#39;re going to start slow and ease into a few core NLP concepts. We&#39;ll write a short script to &#39;borrow&#39; the tone of voice from other agencies that we like. We&#39;ll do this by scraping their websites and plucking a list of nouns and adjectives from the text content. Finally, as the original meme suggested, we&#39;ll randomly mash some of those words together to generate a few names for our awesome new web agency.&lt;/p&gt;
&lt;h2&gt;Pre-requisites and setup&lt;/h2&gt;
&lt;p&gt;Python is considered to be the de facto place to start for natural language processing and to generate our agency name, we&#39;re going to need Python 3.7. We&#39;ll also need to manage our dependencies with a Pipfile (for the JavaScript fluent amongst you: think &lt;em&gt;package.json&lt;/em&gt;). For that, we&#39;re also going to need &lt;strong&gt;pipenv&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nlp-python.jpg&quot; style=&quot;padding-top: 52.62008733624454%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nlp-python.jpg&quot; alt=&quot;The lemma for goes going went and gone in go&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;If you&#39;re new to Python, then you can install everything you need with the brilliant &lt;a href=&quot;http://docs.python-guide.org/en/latest/starting/installation/&quot;&gt;Hitchhiker&#39;s guide&lt;/a&gt;. Once you have installed Python itself, the guide has &lt;a href=&quot;https://docs.python-guide.org/dev/virtualenvs/#virtualenvironments-ref&quot;&gt;a handy page explaining how to install pipenv&lt;/a&gt;. This tutorial will assume some knowledge of Python syntax, but I&#39;ll keep the code as simple as possible.&lt;/p&gt;
&lt;p&gt;With everything installed, create a project directory and we&#39;re ready to get going.&lt;/p&gt;
&lt;h2&gt;The Corpus&lt;/h2&gt;
&lt;p&gt;Before we can process any text content, we first need to find a suitable a body of text. In NLP terminology this input text is known as our &#39;&lt;strong&gt;corpus&lt;/strong&gt;&#39;.&lt;/p&gt;
&lt;p&gt;In our case, we want to grab our text content from other web agencies that we admire, so we can kick things off by writing a function to grab text content from a remote url. Fortunately the Beautiful Soup package helps us to do this.&lt;/p&gt;
&lt;p&gt;Let&#39;s install Beautiful Soup, and the HTML5 library to our project dependencies. Open the project directory in your terminal and run:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;pipenv install bs4 html5lib&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a file called &lt;code&gt;main.py&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests &lt;span class=&quot;token comment&quot;&gt;# we&#39;ll need this to make requests to URLs&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; bs4 &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; BeautifulSoup&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Comment &lt;span class=&quot;token comment&quot;&gt;# HTML and comment parsers&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;read_urls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urls&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  content_string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; page &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; urls&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&#39;Scraping URL: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# Synchronous request to page, and store returned markup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    scrape &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# Parse the returned markup with BeautifulSoup&#39;s html5 parser&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    soup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; BeautifulSoup&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scrape&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;html5lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# loop script tags and remove them&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; script_tag &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; soup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_all&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;script&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      script_tag&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;extract&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# loop style tags and remove them&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; style_tag &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; soup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_all&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      style_tag&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;extract&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# find all comments and remove them&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; comment &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; soup&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Comment&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      comment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;extract&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# Join all the scraped text content&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;soup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;findAll&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# Append to previously scraped text&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    content_string &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; text&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; content_string&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You don&#39;t need to worry too much about the specific code of the function above, but it goes as follows: It read a list of URLs,
making a request to each before parsing the content with BeautifulSoup. The scraped markup needs to be tidied up a bit though, so next it removes any comments, script or style tags before pulling out the remaining text content and appending it to a string. When every URL in the list has been scraped, the string is returned as our corpus.&lt;/p&gt;
&lt;p&gt;Now we have our corpus, we can start to process it.&lt;/p&gt;
&lt;h2&gt;Lost in SpaCy&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://spacy.io/&quot;&gt;SpaCy&lt;/a&gt; bills itself as &#39;Industrial-strength Natural Language Processing in Python&#39;. Whereas packages like &lt;a href=&quot;https://www.nltk.org/&quot;&gt;NLTK&lt;/a&gt; offer immense amounts of flexibility and configuration, the flipside is a steep learning curve. SpaCy on the other hand, can be set up and ready to go in minutes.&lt;/p&gt;
&lt;p&gt;We need to add SpaCy to our project as a dependency. Open your project directory in your terminal and run:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;pipenv install spacy&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SpaCy doesn&#39;t do much by itself though - it needs a pre-trained language model to work with. Fortunately several are available out of the box. We&#39;ll also install the standard english model and save it to our Pipfile:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;pipenv install https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;explosion&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;spacy&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;models&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;releases&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;download&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;en_core_web_sm&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;en_core_web_sm&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gz &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With our dependencies installed, let&#39;s get back into the code. Open up &lt;code&gt;main.py&lt;/code&gt; and add SpaCy to our imports at the top:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests &lt;span class=&quot;token comment&quot;&gt;# we&#39;ll need this to make requests to URLs&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; bs4 &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; BeautifulSoup&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Comment &lt;span class=&quot;token comment&quot;&gt;# HTML and comment parsers&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; en_core_web_sm &lt;span class=&quot;token comment&quot;&gt;# SpaCy&#39;s english model&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, underneath our &lt;code&gt;read_urls&lt;/code&gt; function, we can define a new function to allow SpaCy to parse the corpus.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_tokens&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;corpus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# load the pre-trained english model&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  nlp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; en_core_web_sm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;load&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# parse the corpus and return the processed data&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; nlp&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;corpus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the name of our function, &lt;code&gt;get_tokens&lt;/code&gt;. What&#39;s that all about?&lt;/p&gt;
&lt;h2&gt;Parts of Speech and Tokenization&lt;/h2&gt;
&lt;p&gt;Most languages divide their words up into different categories. These usually comprise nouns, verbs, adjectives, adverbs and more. In linguistic terms, these categories are known as &#39;parts of speech&#39;, or POS. This is important to us as, to generate our agency name, we want to single out nouns and adjectives. But how can we get at them?&lt;/p&gt;
&lt;p&gt;One place to start would be to break each sentence up into its individual parts but this isn&#39;t a straightforward as using &lt;code&gt;String split()&lt;/code&gt;. We need a way to intelligently divide our text, whilst retaining the individual purpose of each word. Doing this manually would be too time consuming, so this is where machine learning becomes necessary. Most NLP libraries use trained language data to intelligently scan, compare and identify the characteristics of each word. Once identified, these words are separated out into &#39;tokens&#39;. This process is known as &lt;strong&gt;tokenization&lt;/strong&gt; and is one of the most important core principles of NLP.&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;get_tokens&lt;/code&gt; function tokenizes our corpus. Or, in simpler terms, SpaCy reads over the corpus, splits out individual words and returns a list of tokens. In creating that list, SpaCy also adds a lot of extra information to each extracted word, such as its relationship to other words in the sentence or which part of speech it is. We can use those attributes to filter the list down to find a specific part of speech.&lt;/p&gt;
&lt;p&gt;Let&#39;s create another function:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_word_list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tokens&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; part_of_speech&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# extract a list of words and capitalise them&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  list_of_words &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;capitalize&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; word &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; tokens &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pos_ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; part_of_speech&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# make the list unique by casting it to a set&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  unique_list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;list_of_words&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# cast it back to a list and return&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;unique_list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function receives the list of tokens, a part of speech code, and then filters the results using a &lt;a href=&quot;https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions&quot;&gt;list comprehension&lt;/a&gt; (again, for JS developers, a list comprehension is a bit like a cross between &lt;code&gt;Array.map&lt;/code&gt; and &lt;code&gt;Array.filter&lt;/code&gt;). To break this list comprehension down, it&#39;s best to work from the inside out – For every word in our tokens list, we grab &lt;code&gt;word.text&lt;/code&gt; and capitalise it, but only if the part of speech (&lt;code&gt;word.pos_&lt;/code&gt;) matches our POS code parameter. You can find a full list of these POS codes in the &lt;a href=&quot;https://spacy.io/api/annotation#pos-tagging&quot;&gt;SpaCy POS documentation&lt;/a&gt;. If you&#39;re curious about what other attributes might be available on each token, you can &lt;a href=&quot;https://spacy.io/api/token#attributes&quot;&gt;read the Token reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lastly, we can filter out any duplicated words by casting the results to a &lt;code&gt;set()&lt;/code&gt;. We&#39;ll need to perform some list actions later though, so it is cast back to a list before being returned.&lt;/p&gt;
&lt;p&gt;This is all looking great and we can now use this function to create a list of nouns, adjectives, verbs or adverbs. There&#39;s a slight problem though - if you were to use this function as-is, you might find that the list of words you get back is a bit of a mess.&lt;/p&gt;
&lt;h2&gt;Stop words&lt;/h2&gt;
&lt;p&gt;Most languages have a set of core words that occur frequently. In English, this might be words like &amp;quot;the&amp;quot;, &amp;quot;at&amp;quot;, &amp;quot;from&amp;quot;, &amp;quot;in&amp;quot;, &amp;quot;for&amp;quot; and so on. Whilst these words are useful for humans, they can quite quickly get in the way extracting meaning with NLP. As a result, it&#39;s common to filter them out and these kinds of words are known as &lt;strong&gt;stop words&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Our script is no different and the stop words need to go. Whilst we&#39;re here, we can also get rid of any rogue punctuation. Fortunately SpaCy tokens have two handy attributes that allow us to do this - the aptly named &lt;code&gt;is_stop&lt;/code&gt; and &lt;code&gt;is_punct&lt;/code&gt;. Let&#39;s update the list comprehension in our &lt;code&gt;get_word_data&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;  list_of_words &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;capitalize&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; word &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; tokens &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pos_ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; part_of_speech &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_stop&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_punct&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can extract a nice sanitised list of words, but there&#39;s a tiny bit more optimisation that we can do.&lt;/p&gt;
&lt;h2&gt;Lemmatization&lt;/h2&gt;
&lt;p&gt;Let&#39;s say you&#39;ve scraped your website content and in amongst your list of tokens are the words &amp;quot;goes&amp;quot;, &amp;quot;going&amp;quot;, &amp;quot;went&amp;quot; and &amp;quot;gone&amp;quot;. That&#39;s quite a few words to express the same base idea. We can make this simpler.&lt;/p&gt;
&lt;p&gt;These words could all be lumped together under the core concept: &amp;quot;go&amp;quot;. In linguistic terms, we call this a word&#39;s &#39;&lt;strong&gt;lemma&lt;/strong&gt;&#39; or the uninflected root form of a word.&lt;/p&gt;
&lt;p&gt;The simplest way to think of a lemma is to see it as what you might look for in a dictionary:&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nlp-lemma.jpg&quot; style=&quot;padding-top: 67.77277840269966%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nlp-lemma.jpg&quot; alt=&quot;The lemma for goes going went and gone in go&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;Out of the box, SpaCy provides a convenient means to convert any matched term back to its lemma. This is called &lt;strong&gt;lemmatization&lt;/strong&gt;. Let&#39;s hone our list of tokens down into a much tighter list of core ideas by updating our list comprehension. We&#39;ll change the first line to call &lt;code&gt;.lemma_&lt;/code&gt; instead of &lt;code&gt;.text&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Your final &lt;code&gt;get_word_list&lt;/code&gt; function should look like this:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_word_list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tokens&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; part_of_speech&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# extract a list of lemmas and capitalise them&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  list_of_words &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lemma_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;capitalize&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# &amp;lt;-- update .text to .lemma_&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; word &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; tokens &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pos_ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; part_of_speech &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_stop&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_punct&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# make the list unique by casting it to a set&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  unique_list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;list_of_words&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;# cast it back to a list and return&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;unique_list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Bringing it all together&lt;/h2&gt;
&lt;p&gt;Now we&#39;ve set up a few functions and we&#39;ve covered the core concepts, let&#39;s pull everything together. To generate the final name, we&#39;re going to need to be able to make random selections from our word lists. At the top of &lt;code&gt;main.py&lt;/code&gt;, add &lt;code&gt;random&lt;/code&gt; to your imports.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests &lt;span class=&quot;token comment&quot;&gt;# we&#39;ll need this to make requests to URLs&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; bs4 &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; BeautifulSoup&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Comment &lt;span class=&quot;token comment&quot;&gt;# HTML and comment parsers&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; en_core_web_sm &lt;span class=&quot;token comment&quot;&gt;# SpaCy&#39;s english model&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; random&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the end of the file, underneath your &lt;code&gt;get_word_list&lt;/code&gt; function, paste the following:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# create a list of urls to scrape&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# For illustration, I&#39;m using my website &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# but replace with any urls of your choice&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;CORPUS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; read_urls&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;https://robbowen.digital/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;https://robbowen.digital/work&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;https://robbowen.digital/about&#39;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# parse the corpus and tokenize&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;TOKENS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; get_tokens&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;CORPUS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# extract a unique list of noun lemmas from the token list&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;nouns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; get_word_list&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TOKENS&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NOUN&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# extract a unique list of adjective lemmas from the token list&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;adjectives &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; get_word_list&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TOKENS&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ADJ&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# create a random list of 500 nouns and adjective-noun combos&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# cast to a set to make sure that we only get unique results&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;list_of_combos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# String interpolation of an adjective and a noun...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;random&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;choice&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;adjectives&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;random&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;choice&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nouns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# 50% of the time...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; random&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;choice&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# else return a random noun&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; random&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;choice&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nouns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;# and repeat this 500 times&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Write list of combinations to a text file&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;output_file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./output.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;w&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; list_of_combos&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  output_file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;\r\n&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s break that code down.&lt;/p&gt;
&lt;p&gt;First of all, we pass a list of URLs into our &lt;code&gt;read_urls&lt;/code&gt; function and store the result in a constant for later. Be sure to change the example URLs to your own choices here, and remember: the more text on your chosen pages, the better your results will be.&lt;/p&gt;
&lt;p&gt;Next, we pass the corpus into our &lt;code&gt;get_tokens&lt;/code&gt; function. This is where SpaCy does its magic, tokenizing the corpus and returning a list of data-rich tokens.&lt;/p&gt;
&lt;p&gt;To generate our agency name, we want some random nouns. Nouns by themselves aren&#39;t much fun, so we&#39;ll grab some adjectives too. This uses the &lt;code&gt;get_word_list&lt;/code&gt; function we created earlier. The first argument is the list of tokens, and the second filters by a &#39;&lt;a href=&quot;https://spacy.io/api/annotation#pos-tagging&quot;&gt;part of speech code&lt;/a&gt;&#39;.&lt;/p&gt;
&lt;p&gt;Now we have our nouns and adjectives lists we can prep our final list of names. We want to generate a list of names – let&#39;s say 500 of them. We can use another list comprehension to do this.&lt;/p&gt;
&lt;p&gt;Remember the random library we imported above? It gives us access to the &lt;code&gt;random.choice&lt;/code&gt; function. As the name implies, it allows us to randomly choose a single item from a list. &lt;code&gt;random.choice&lt;/code&gt; also gives us a handy way to embed a 50/50 chance into our comprehension. Here, an if-else means each iteration of our loop will return either an adjective-noun combo or a single noun. Finally, with the comprehension complete, we wrap the list in &lt;code&gt;set()&lt;/code&gt; to ensure that we only have unique results.&lt;/p&gt;
&lt;p&gt;Ok, everything is ready. At the very end we create a file called &lt;code&gt;output.txt&lt;/code&gt;, loop through our list of names and write each to a new line.&lt;/p&gt;
&lt;p&gt;And...we&#39;re done! If you need to, you can double check your code against the &lt;a href=&quot;https://github.com/robb0wen/disruptive-breakfast/blob/master/main.py&quot;&gt;final source on github&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Generate the name&lt;/h2&gt;
&lt;p&gt;It&#39;s time to fire up the final script and generate some names. At your terminal, run the following command:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;pipenv run python main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If all goes well, you should have now a file named &lt;code&gt;output.txt&lt;/code&gt; chock full of multi-award-winning agency names.&lt;/p&gt;
&lt;h2&gt;And the winner is...&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;Just pick a random word or two and go with that&amp;quot;&lt;/strong&gt;. So, after all this, what name did I go with?&lt;/p&gt;
&lt;p&gt;Well, I had some real gems: &lt;strong&gt;Loaf&lt;/strong&gt;, &lt;strong&gt;Strategic Bear&lt;/strong&gt;, &lt;strong&gt;Rise&lt;/strong&gt;, &lt;strong&gt;Chaotic Rest&lt;/strong&gt;, &lt;strong&gt;Bold Tomorrow&lt;/strong&gt;. After a few minutes crawling through the output list though, I spotted &lt;em&gt;the one&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Exciting, yet confident. Like a life-raft in a sea of comedic nonsense:&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/nlp-disruptivebreakfast.jpg&quot; style=&quot;padding-top: 67.77277840269966%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/nlp-disruptivebreakfast.jpg&quot; alt=&quot;Disruptive Breakfast&quot;&gt;
  &lt;/div&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Ok, I get it. Like most clickbait, this might not have been a completely satisfying ending. Hopefully though, you&#39;ve learned a little about the potential application of even the most superficial Natural Language Processing scripts.&lt;/p&gt;
&lt;p&gt;To recap, in this introduction, we&#39;ve used and learned about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Corpus&lt;/strong&gt;: The body of text to be processed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parts of speech&lt;/strong&gt;: The name given to the categories of words comprising nouns, verbs, adjectives etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tokenization&lt;/strong&gt;: The act of splitting sentences into individual words, or tokens&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop words&lt;/strong&gt;: Small, frequently occurring words that we do not wish to include in our results i.e. &amp;quot;the&amp;quot;, &amp;quot;for&amp;quot;, &amp;quot;it&amp;quot; etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lemmatization&lt;/strong&gt;: Changing a word back to its root concept, or dictionary form i.e. the lemma of &amp;quot;best&amp;quot; or &amp;quot;better&amp;quot; is &amp;quot;good&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From these core concepts, the possibilities are vast. Imagine going into a pitch having scanned the client&#39;s website in advance to profile exactly how they talk about themselves. Or perhaps you could take the jump into sentiment analysis to scan how well received a particular topic is on Twitter. There&#39;s still a lot to learn but, with these core building blocks, you&#39;re on your way.&lt;/p&gt;
&lt;p&gt;If you enjoyed this article and have any ideas on how to extend this example, or if you generated a particularly badass name, then please &lt;a href=&quot;https://front-end.social/@Robb&quot;&gt;let me know on Mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&#39;re looking to learn more about NLP, I strongly recommend the &lt;a href=&quot;https://course.spacy.io/&quot;&gt;Advanced NLP with SpaCy&lt;/a&gt; course by Ines Montani - it&#39;s free, and really rather good.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Synthwave &#39;84 has changed</title>
    <link href="http://robbowen.digital/wrote-about/neon-dreams/"/>
    <updated>2020-02-10T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/neon-dreams/</id>
    <content type="html">&lt;div class=&quot;break-out&quot;&gt;
  &lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/synth-banner.png&quot; style=&quot;padding-top: 26.560726447219068%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/synth-banner.png&quot; alt=&quot;The Synthwave 84 logo banner&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It&#39;s been a wild ride since I first tweeted about Synthwave &#39;84. The support has been amazing and I&#39;d like to start by saying thanks everyone who has shared or used the theme since the last release. Particularly, I&#39;d like to thank you all for battling through the less than ideal installation process.&lt;/p&gt;
&lt;p&gt;It&#39;s been a bumpy road but it&#39;s been truly great to see so many people enjoying the theme.&lt;/p&gt;
&lt;h2&gt;Neon Dreams&lt;/h2&gt;
&lt;p&gt;This &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=RobbOwen.synthwave-vscode&quot;&gt;v0.1.0 update&lt;/a&gt; marks a changing point for the theme.&lt;/p&gt;
&lt;p&gt;As of writing, VS Code still doesn&#39;t support text effects in its themes, so the only way to apply the neon glow for Synthwave &#39;84 is to inject code directly into the application markup. The previous versions of Synthwave &#39;84 relied on a third-party extension to inject an extra stylesheet into the VS code editor. This was a hacky, if serviceable, method for adding extra styles, but I suspected it would never be long-term.&lt;/p&gt;
&lt;p&gt;With the v1.40.0 release of VS code, those suspicions were confirmed - the internal classes used for the glow had been changed. It was clear that another solution would be needed.&lt;/p&gt;
&lt;p&gt;This new update, called Neon Dreams, removes the need to use the Custom CSS and JS extension to enable the glow. It also rethinks how to apply the glow, so it should cope better with updates to VS code.&lt;/p&gt;
&lt;h2&gt;Before using&lt;/h2&gt;
&lt;p&gt;If you have been using Synthwave prior to v0.1.0, you will likely have used the Custom CSS and JS extension to enable the glow. Before enabling Neon Dreams, you will need to disable the prior method. You can do this by pressing &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; or &lt;code&gt;Shift + ⌘ + P&lt;/code&gt; and choose &amp;quot;Disable Custom CSS and JS&amp;quot;.&lt;/p&gt;
&lt;p&gt;In the name of simplifying the install process and mitigating update-related issues, the new version of Synthwave doesn&#39;t use a custom CSS file anymore. If you have been using a custom-modified version of the theme, then you might want to leave it as is for now.&lt;/p&gt;
&lt;h2&gt;Start dreaming&lt;/h2&gt;
&lt;p&gt;Firstly, if you are a Windows user, you may need to run VS Code with administrator privileges. For Linux and Mac users, Code must not be installed in a read-only location and you must have write permissions.&lt;/p&gt;
&lt;p&gt;Once you have admin permissions, open VS Code, and set your active colour theme to Synthwave &#39;84 - the glow is only active when the base theme is selected.&lt;/p&gt;
&lt;p&gt;press &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; or &lt;code&gt;Shift + ⌘ + P&lt;/code&gt; and choose &amp;quot;Enable Neon Dreams&amp;quot;&lt;/p&gt;
&lt;p&gt;You should be prompted for a restart. When the editor reloads, the glow should be active.&lt;/p&gt;
&lt;p&gt;As with the prior versions, VS Code may warn you that your installation is corrupt. You can suppress this message by choosing &#39;Don&#39;t show this again&#39; from the notification.&lt;/p&gt;
&lt;p&gt;Also as before, whenever VS code updates, you will need to rerun the enable command&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Launch day</title>
    <link href="http://robbowen.digital/wrote-about/launch-day/"/>
    <updated>2020-01-22T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/launch-day/</id>
    <content type="html">&lt;div class=&quot;break-out&quot;&gt;
  &lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/mountains.jpg&quot; style=&quot;padding-top: 52.62008733624454%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/mountains.jpg&quot; alt=&quot;Abstract logos based on the three mountains of Abergavenny&quot;&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Today is something of a milestone. For the first time in a long while, I have a new website. I&#39;m not talking about a holding page, or link to a resumé, but an actual honest-to-goodness home on the internet.&lt;/p&gt;
&lt;p&gt;More importantly than that, I have a personal blog for the first time ever.&lt;/p&gt;
&lt;p&gt;It&#39;s been a while coming. As a freelancer your website can often be the first impression your prospective clients will get, so the pressure to get it right can be &lt;em&gt;massive&lt;/em&gt;. Beyond that though, it felt important that I create something that reflects me and my values. Working on this site has been something of a labour of love for me, so what better way to kick off the blog than writing a little about how I got to this point?&lt;/p&gt;
&lt;h2&gt;Constraint breeds creativity&lt;/h2&gt;
&lt;p&gt;Without a shadow of a doubt, a blank canvas is an intimidating thing. It can feel especially daunting when it seems like there’s a new JS framework popping up every other day. Thankfully, over the years I’ve learned that I&#39;m at my most creative when I&#39;m working with restrictions. To get things moving, I sketched out a few constraints.&lt;/p&gt;
&lt;p&gt;The site should:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Convey a strong personality and a sense of place&lt;/li&gt;
&lt;li&gt;Showcase my &lt;a href=&quot;https://tornis.robbowen.digital/&quot;&gt;interaction library&lt;/a&gt; and emphasise the kind of work I enjoy&lt;/li&gt;
&lt;li&gt;Deliver all of the core site assets in 100kb or less&lt;/li&gt;
&lt;li&gt;Use progressive enhancement to keep the site usable when JavaScript is not available&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With those targets in place, I set to work.&lt;/p&gt;
&lt;h2&gt;Finding inspiration&lt;/h2&gt;
&lt;p&gt;The design direction was the first thing I had to nail down. I knew that I wanted to keep things simple, but full of character. I also wanted to ground everything with a sense of place. But where to start?&lt;/p&gt;
&lt;p&gt;I work remotely 80% of the time, so it made sense to try and bring something of my home environment into the design. I live in a small Welsh town, Abergavenny, which is surrounded by three distinctive mountains - Sugarloaf, Blorenge and Skirrid. No matter where you are in town, you can always see one of them. I figured that if these mountains can ground me, they could also ground my website.&lt;/p&gt;
&lt;p&gt;I got out my sketchbook and before long I had roughed out three abstract representations of the peaks. Rather than pick one mountain for my logo, I decided to mimic the town and show all three, randomly swapping them on each page of the site.&lt;/p&gt;
&lt;p&gt;The logo got things moving. The simple line-art style leant itself well to SVG illustrations, which would not only keep the page weight down, but also be a good way to inject a bit of character into the design. I sketched up a stylised self-portrait and several other scenes to accompany each page. To compliment the simple illustration style I opted for a 5-tone colour palette, adding a little texture with patterns and an &lt;a href=&quot;https://en.wikipedia.org/wiki/Printing_registration&quot;&gt;off-registration&lt;/a&gt; shading effect.&lt;/p&gt;
&lt;h2&gt;Hello, Eleventy&lt;/h2&gt;
&lt;p&gt;Before I started coding I knew that I wanted to build a &lt;abbr title=&quot;JavaScript APIs and Markup&quot;&gt;JAM&lt;/abbr&gt;stack site and host it on &lt;a href=&quot;https://netlify.com/&quot;&gt;Netlify&lt;/a&gt;. However the self-imposed 100kb limit meant that, right off the bat, using something like React or Gatsby would be tricky. Though it is possible with optimisation, the runtime JavaScript required for a single-page app would likely take up most, if not all of my initial load budget.&lt;/p&gt;
&lt;p&gt;If I wanted to keep this site small and snappy, I&#39;d need to find an alternative.&lt;/p&gt;
&lt;p&gt;At the time I started the build, I&#39;d been hearing a lot of buzz around a static site generator called &lt;a href=&quot;https://11ty.io/&quot;&gt;Eleventy&lt;/a&gt; and decided to check it out. On the surface, it&#39;s closer to something like &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; than it is to Gatsby. Pages are constructed from Markdown files using one or more templating languages and then compiled into vanilla HTML, CSS and JS files. It&#39;s hugely configurable and crucially there&#39;s no clientside bundle either, meaning you&#39;re entirely free to add as much or as little JavaScript as you like.&lt;/p&gt;
&lt;p&gt;Eleventy was the perfect fit.&lt;/p&gt;
&lt;p&gt;Beyond the standard templating features, Eleventy allows you to write custom nunjucks partials (or shortcodes as they&#39;re known here). On the surface that might not seem too exciting, but by using shortcodes I was able to approximate a React-like developer experience by breaking my UI into a series of neatly reusable components. The real magic comes with the way Eleventy stacks template formats though.&lt;/p&gt;
&lt;p&gt;If you write a markdown file it can optionally be parsed as one additional format defined in your &lt;code&gt;.eleventy.js&lt;/code&gt; config file - in my case, first as markdown, then as nunjucks. This means that these same shortcodes are also available within my markdown files allowing me to build more interesting layouts inside of blog content.&lt;/p&gt;
&lt;p&gt;In-keeping with Eleventy&#39;s ethos, I&#39;ve opted to kept clientside JavaScript to a minimum. To retain the feel of a single-page app, I&#39;m using the history api to capture internal links and transition between pages and, to spice things up a bit more, CSS custom properties swap out the colour palette on each page transition. Aside from &lt;a href=&quot;https://tornis.robbowen.digital/&quot;&gt;Tornis&lt;/a&gt;, I&#39;ve kept dependencies light and the final JS bundle clocks in at only 17kb when gzipped.&lt;/p&gt;
&lt;h2&gt;Getting animated&lt;/h2&gt;
&lt;p&gt;I love interaction design, and it was inevitable that my site would feature animation of some kind. The decision to use SVG throughout gave a simple way to add a little spark - I&#39;d use &lt;a href=&quot;https://tornis.robbowen.digital/&quot;&gt;Tornis&lt;/a&gt; to manipulate the position of the off-registration effect, and scatter an additional layer of interactivity throughout my illustrations.&lt;/p&gt;
&lt;p&gt;I wanted the homepage to stand out in particular. The concept was simple - I&#39;d have a stylised self-portrait track the user&#39;s mouse cursor around the site and a series of grouped paths and a bit of selective masking would create the illusion that the illustration was three dimensional.&lt;/p&gt;
&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/svglayers.png&quot; style=&quot;padding-top: 67.94462193823216%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/svglayers.png&quot; alt=&quot;A layer breakdown of how my illustrations work&quot;&gt;
  &lt;/div&gt;
&lt;p&gt;Animating nested SVG elements directly can cause performance issues on some low-end devices, but this isn&#39;t the case with standard HTML. To make sure the animation would be buttery smooth, I first needed to separate each section of the illustration into divs and then stack them with CSS. From there I could start the interaction:&lt;/p&gt;
&lt;p&gt;Tornis first captures the user&#39;s mouse position (or device tilt) as X and Y co-ordinates which I then convert into a percentage relative to the centre of the screen. Multiplying the positions of two extreme keyframes by this relative value, allowed me to gradually tween each section of the illustration between both poses.
To sell the effect, CSS transitions smooth out the movement and make each keyframe blend seamlessly.&lt;/p&gt;
&lt;p&gt;The results really surprised me. I found that approaching motion in this way - as a series of blended positions rather than individual animation sequences - makes for a much more organic feel. The gradual blending also means that you avoid any of the visible cuts you&#39;d get when swapping between CSS animations.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;So did I keep to my restrictions in the end? Broadly, yes.&lt;/p&gt;
&lt;p&gt;With a little help from &lt;a href=&quot;https://netlify.com/&quot;&gt;Netlify&lt;/a&gt; hosting, the initial page transfer on the homepage clocks in at ~80kb (gzip) and, should the site load without JavaScript, everything is fully usable with an alternative layout. Ultimately, I have used some images on blog articles and the like which brings the individual page weight higher but I feel like that&#39;s still acceptable as the core assets are well within my target.&lt;/p&gt;
&lt;p&gt;Most of all though, I&#39;m happy with the look of the site - &lt;em&gt;it feels like home&lt;/em&gt;. Setting myself constraints early on really helped to focus development whilst still allowing me to explore the design. Similarly, putting aside my default tech choices has meant that I&#39;ve been able to craft a comparable experience in a fraction of the filesize. I will definitely be using Eleventy for future projects.&lt;/p&gt;
&lt;p&gt;Finally, as with any personal website, I wouldn&#39;t call it totally &lt;em&gt;finished&lt;/em&gt; and I dare say that I&#39;ll keep on tweaking and updating things as it lives and grows. For now though, I’m really happy with how the final site has turned out. It&#39;s been a long road.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Synthwave &#39;84</title>
    <link href="http://robbowen.digital/wrote-about/synthwave-84/"/>
    <updated>2019-05-09T00:00:00-00:00</updated>
    <id>http://robbowen.digital/wrote-about/synthwave-84/</id>
    <content type="html">&lt;p&gt;Do you remember that endless summer back in &#39;84? Cruising down the ocean-highway with the top down, the wind in our hair and heads buzzing with neon dreams?&lt;/p&gt;
&lt;p&gt;No, I don&#39;t remember it either, but with this experimental theme we can go there.&lt;/p&gt;
&lt;div class=&quot;break-out&quot;&gt;&lt;div class=&quot;reveal-img dots&quot; data-src=&quot;/assets/img/articles/theme.jpg&quot; style=&quot;padding-top: 65.65349544072949%;&quot;&gt;
    &lt;img class=&quot;&quot; src=&quot;http://robbowen.digital/assets/img/articles/theme.jpg&quot; alt=&quot;A screenshot of VS Code showing Synthwave &#39;84 in action&quot;&gt;
  &lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This colour scheme is influenced by the music and the cover artwork of modern Synthwave bands like FM-84, Timecop 1983 and The Midnight. By association, that means I&#39;ve also taken heavy influence from the excellent &lt;a href=&quot;https://signalnoise.com/&quot;&gt;retro-tinged artwork of James White&lt;/a&gt; (check out his work, it&#39;s awesome).&lt;/p&gt;
&lt;h2&gt;But ...why?&lt;/h2&gt;
&lt;p&gt;I was a kid in the 80s but for most of my teenage life I strongly disliked nearly everything about the 80s aesthetic of my childhood. It was like, &lt;em&gt;so lame&lt;/em&gt;. With the hindsight of recent years though, I&#39;ve realised that it was actually pretty sweet and I wanted to celebrate it a little.&lt;/p&gt;
&lt;p&gt;Much the same way, in the modern web-development world of shaders, React and WebGL, I feel like it&#39;s easy to forget that the basics are actually pretty damn good. To that end, this theme goes back to basics - No Shader magic. No cloud-streamed WebGL render-farms. Just plain CSS.&lt;/p&gt;
&lt;h2&gt;Using SynthWave &#39;84&lt;/h2&gt;
&lt;p&gt;The theme can be downloaded from the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=RobbOwen.synthwave-vscode&quot;&gt;Visual Studio marketplace&lt;/a&gt; or from VS code itself, by searching for SynthWave 84 in the extensions tab. If you want to enable the glow, please carefully read the installation instructions for this, and the theme&#39;s dependencies.&lt;/p&gt;
&lt;h2&gt;Looking into the future&lt;/h2&gt;
&lt;p&gt;So, what&#39;s in store for the future of this theme? First and foremost, better language support. I primarily develop with JavaScript, Elixir and Python so whilst the theme looks good for those languages, tweaks have been needed for other languages. There&#39;s more to come.&lt;/p&gt;
&lt;p&gt;Many people have been also excited to see Synthwave &#39;84 ported to other software. Whilst I&#39;m not currently planning to port the theme myself, the community has already produced several fantastic ports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jnolis/synthwave85&quot;&gt;Synthwave 85 for Rstudio&lt;/a&gt; courtesy of &lt;a href=&quot;https://twitter.com/skyetetra&quot;&gt;Jacqueline Nolis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://carbon.now.sh/&quot;&gt;Synthave &#39;84 for Carbon&lt;/a&gt; courtesy of &lt;a href=&quot;https://twitter.com/etoxin&quot;&gt;Adam&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/diogocezar/hyper-synthwave84&quot;&gt;Hyper terminal&lt;/a&gt; courtesy of &lt;a href=&quot;http://www.twitter.com/diogocezar&quot;&gt;Diogo Cezar Teixeira Batista&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Perhaps most surprisingly for me, &lt;a href=&quot;https://panic.com/&quot;&gt;Panic&lt;/a&gt;, the team behind the legendary Mac editor Coda have recently announced their next-generation editor, called &lt;a href=&quot;https://panic.com/nova&quot;&gt;Nova&lt;/a&gt;. Turns out that the team at Panic liked Synthwave &#39;84 enough to create their own version, called Outrun. It&#39;s looking pretty sweet.&lt;/p&gt;
&lt;p&gt;Lastly, beyond editor support, the most popular feature-request has been for a &lt;strong&gt;light version&lt;/strong&gt;. To that I can say: It&#39;s in the works. Which reminds me, the eagle-eyed amongst you might have noticed that this page is sporting a lovely shade of sunset-peach...&lt;/p&gt;
</content>
  </entry>
</feed>