<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[WebArtisan.info]]></title><description><![CDATA[Tips, news, and career advice for full-stack web developers in the PHP, Laravel, and JavaScript worlds.]]></description><link>https://webartisan.info</link><generator>RSS for Node</generator><lastBuildDate>Tue, 12 May 2026 22:26:20 GMT</lastBuildDate><atom:link href="https://webartisan.info/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[JD's 2026 MacBook Neo Review]]></title><description><![CDATA[After a few days of using the 512GB MacBook Neo with TouchID, here are my impressions after some heavy, real-world use. I'll disclaim this review by saying that I consider myself a power user; on my m]]></description><link>https://webartisan.info/jd-s-2026-macbook-neo-review</link><guid isPermaLink="true">https://webartisan.info/jd-s-2026-macbook-neo-review</guid><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Sat, 11 Apr 2026 22:07:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6294a4c322c65adc00272246/7aacb746-daa5-4403-afef-e321a76b4b90.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After a few days of using the 512GB MacBook Neo with TouchID, here are my impressions after some heavy, real-world use. I'll disclaim this review by saying that I consider myself a power user; on my main workstation, I use many applications, Docker, virtual machines, developer IDEs, open many browser tabs, etc. But that is <em>not</em> what I bought this machine for, nor how I have been using it. My intended use case for this machine is as a backup machine for occasional noodling on the couch or in the kitchen. YouTube, browsing, email, text editing, Slack, and Teams chats.</p>
<p>I've owned an iPad Pro that has served this purpose for years, but I find I rarely take it out of its keyboard case or use it in portrait mode. I've basically been using an aging iPad as a limited, top-heavy laptop. A small Mac is way more practical (and can actually be cheaper) than an iPad Pro with a keyboard case. So a MacBook Neo? It seemed like a suitable purchase. The big question I had to answer for myself, however, is this: Is the cost savings of a Neo worth it compared to the similar but superior MacBook Air?</p>
<h2>Look And Feel</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6294a4c322c65adc00272246/de5675d5-594b-4ed7-a94c-6de4eefc5cc3.jpg" alt="" style="display:block;margin:0 auto" />

<p>There's no mistaking it — The MacBook Neo feels very much like an Apple product. It was designed with a clear, obsessive attention to detail. The colour is nice, elegant, and looks impressive under bright lights. I got "Indigo". While I think of indigo as having a purple hue, this colour is a desaturated blue-gray that reminds me of an old pair of blue jeans. Understated but not boring. The keycaps and even feet on the bottom are tinted to a matching hue. The chassis feels premium, solid. You can use one thumb to flip open the screen easily — and this sounds mundane, but you don't know you miss that until you use a lesser laptop with a hinge that requires you to awkwardly pry open the lid with both hands every time you use it. The Neo is just <em>nice</em>.</p>
<p>Comparing directly to the Air, MacBook Neo is about 1mm thicker but it it just has a 'friendlier' chassis feel, in my opinion. Rounded nicely, on both the top and bottom, where the Air's lid (the screen's back) has a sharper, square edge. It's also worth noting that the Air is just a little wider and deeper, and has the <em>exact</em> same weight as the Neo.</p>
<p>Not a big deal, but it's worth noting.</p>
<p>A tiny detail: The underside of the chassis states, in small print:</p>
<p>Designed by Apple in California A2 Model A3404</p>
<p>Product of Vietnam</p>
<p>The country of origin is interesting, as it seems to represent Apple's push to reduce its reliance on China and diversify its manufacturing.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6294a4c322c65adc00272246/3d8f1b66-9948-4548-bef6-64177bc5efc0.jpg" alt="" style="display:block;margin:0 auto" />

<h2>The Display</h2>
<p>The Neo has a nice, glossy 2408x1506 display that renders at 1408x881 points at 500 nits. It looks pretty good, but feels slightly fuzzy to me — I can tell it's not rendering natively at its full resolution, though macOS is good at scaling, so you don't notice unless you're looking for it. It is noteworthy that the <strong>13" MacBook Air</strong> also doesn't render at its native resolution out of the box, although it has a slightly bigger display. Overall, the screen is good. The reflectivity is annoying when a light source is behind you, but it's still plenty bright enough to read outdoors if you angle the screen to minimize glare. The top two corners are rounded to inlay perfectly with the chassis's rounded corners. There's no additional top area with a notch for the menu bar like Apple's high-end laptops. Some people are so irrationally annoyed by the notch that they may consider it a benefit, though in practice, it means you're losing desktop real estate to the menu bar.</p>
<h2>The Speakers</h2>
<p>I played some music through the Neo's built-in speakers, and they are fine. Nice sounding, not a lot of bass, but audio is clear and loud enough for regular laptop use. I later compared directly beside a MacBook Air, which has more speakers and I was surprised to note that the Neo's speakers actually sounded <em>very slightly better</em> for the music and samples I listened to. That said, the Neo's audio output won't blow you away like MacBook Pro speakers, but they are absolutely passable for watching YouTube videos and basic computer tasks.</p>
<h2>The Keyboard</h2>
<p>The keyboard is good. Nice to type on, basically feels like other recent MacBook keyboards. It's much better than the keyboards of the older TouchBar generation of laptops that most people hated. But here's where there's a big feature missing: backlighting. Every other Apple laptop has a backlit keyboard, but the Neo doesn't. It's some aggressive cost-cutting at play. One of my primary use cases is using this in bed at night while my wife sleeps. On the one hand, the lack of a backlight reduces the computer's brightness in the bedroom, but it also makes it harder to use. For regular typing, I'm 100% a touch typist. But entering long passwords with cryptic characters on shift+number keys? Annoying. I find I'm fumbling more than I expected to. For someone who can't touch-type and regularly uses their computer in the dark, I could see this being a deal-breaker.</p>
<p>The 512GB model I have includes TouchID. Sometimes it's nice. I haven't found TouchID very reliable on Macs so far — they seem to forget my fingerprint after a few days. But I could live without it since my Apple Watch also unlocks the device.</p>
<h2>Trackpad</h2>
<p>Apple has the best trackpads in the industry. Windows laptop trackpads have gotten better in recent years, but they haven't caught up. Apple's haptic trackpads are amazing, and I even own three of the Magic Trackpad 2 because I love them so much. The Neo isn't that. Apple has a pretty good design that distributes force around the trackpad to a button in the middle. It's okay. It's easy to use. It's a little louder than I'd like, and I miss the ability to adjust the clicking force in software, as you can on the haptic trackpads. In my view, this is a drawback, not a deal-breaker, since you can turn on tap-to-click.</p>
<h2>Performance</h2>
<p>Speed and performance are where things are a little hit-or-miss. Infamously, the Neo only has 8GB of RAM, which is a little scant for a real work machine in 2026. And since macOS has a good swap/paging story, it's mostly okay. But when setting this machine up, running a dozen apps, having lots of tabs open — yeah, the memory pressure will be a thing. I rarely show much free RAM on this machine in Activity Monitor — and that's not <em>inherently</em> a problem — but this thing is going to chug if you push it. For my use as a light text-editing/web browsing/emailing machine, that could suffice, but if I try to get real work done on this machine or install Docker, it's going to be painful.</p>
<p>Honestly, though, the RAM situation might not even be the most annoying aspect of this machine's performance — it's the SSD. The SSD on this machine benchmarks at about 1.5 GB/s, while the M5 MacBook Air can hit 7GB/s. That's a 4x difference! If you're doing anything disk-intensive — copying files around, installing software, performing heavy writes — you'll feel it. I've had a beachball more than a few times on this machine in a few days, especially when I was installing a ton of things and getting it set up. That's… not great in 2026. It was somewhat common on the netbook I hackintoshed <em>in 2009</em>, but I shouldn't be seeing that regularly from even a budget machine in 2026.</p>
<h2>Connectivity</h2>
<p>For ports, MacBook Neo only has one USB3.2 Gen2 10Gb port and one USB2 port. That means I can connect only one external monitor (limited to 4K) and only to the rear-most port. I'm not planning to connect it often, but I do have a Studio Display I can use with this and a pair of LG 5K monitors at the in-laws. It'd be nice to be able to plug those in and use them properly, but the Neo just can't do it. That right there might be the single biggest deal-breaker on the machine. If this had two Thunderbolt ports, that'd be fine. But as it is, the connectivity story is a bit sad.</p>
<p>The wireless situation is also not cutting-edge — WiFi 6, which is just okay. I'd hope for at least 6e in 2026, ideally WiFi 7 — which the M5 Air has, but not the Neo.</p>
<h2>Battery Life and Charging</h2>
<p>The battery life is good. Can I use this for multiple days without thinking about it? Probably not. On paper, the Air has slightly better battery life. Using it on the couch and in bed for several hours has not been a problem. I'm not a MagSafe zealot, so I can live without that connector — using ubiquitous USB-C cables I have lying around is usually easier. This machine doesn't have MagSafe, if that matters to you.</p>
<p>Charging is a little on the slow side as well, and I believe the specs indicate that the Neo draws a maximum 30W. (And only a 20W brick is included in the box, but just be thankful Apple hasn't completely taken away the included chargers as they have in Europe!) So it's not going to charge super quickly. It negotiates 45W USB-C PD.</p>
<h2>Conclusion</h2>
<p>The MacBook Neo is a really nice machine at a compelling price. Apple cut corners (figuratively and literally). Except maybe for the backlit keyboard and ports, they mostly cut the right ones. It feels like table stakes to offer proper support for Apple's own 5K displays, and get both ports up to USB-3.2 Gen2 (I retch whenever I have to type that). That way, you'd never have to think about which port works with which devices.</p>
<p>For me, a 256GB machine is insufficient. So I got the \(849 CAD (Education pricing) 512GB with TouchID. The next step up would be a base model MacBook Air for an additional \)510 CAD. That's a pretty big jump over $849 — but here's what I'd get and — totally arbitrarily, what I might value each thing at.</p>
<ul>
<li><p>$100: Thunderbolt 4 &amp; Dual External 6k display support</p>
</li>
<li><p>$30: Slightly bigger, better, internal display with auto brightness and TrueTone</p>
</li>
<li><p>$20: Slightly longer battery life and faster charging</p>
</li>
<li><p>$10: MagSafe Port</p>
</li>
<li><p>$5: Almost 1mm thinner chassis</p>
</li>
<li><p>$80: Double the RAM (16GB)</p>
</li>
<li><p>$40: Faster WiFi 7</p>
</li>
<li><p>$70: About 4x faster SSD</p>
</li>
<li><p>$50: Backlit Keyboard</p>
</li>
<li><p>$40: Nicer haptic trackpad</p>
</li>
<li><p>$0: 'Better speakers' (not meaningfully better on the Air)</p>
</li>
</ul>
<p>Total: \(445. Not quite \)510 but... <em>very</em> close. Each of those things is nice. Any one or two on their own could be dismissed. But the total overall package is going to be considerably nicer for someone who loves computers and uses theirs all the time.</p>
<p>That said, I bought this machine for one simple reason: I need a computer I can leave lying around the house and use in bed. I have a young kid running around who will mercilessly attack it. I'd be devastated if a tiny tyrannical toddler tossed my very expensive MacBook Pro. So a 'disposable' machine that's <em>mostly</em> good enough seems compelling — this machine is cheap enough that damage to it would be a bad day, not a bad month. And yet, overall… I think there's too much I'm leaving on the table, and a few too many compromises in the Neo for me. Since I actually would appreciate <em>all</em> the additional features the Air offers, it seems like a no-brainer for someone like me to get the Air instead.</p>
<p>Let's hope the next generation Neo includes a backlit keyboard and better ports — those two things alone would make a future Neo a <strong>much</strong> easier machine to recommend.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6294a4c322c65adc00272246/602be86c-ae2c-4532-be76-88da71b3cba1.jpg" alt="Baby using laptop" style="display:block;margin:0 auto" />

<p>Hands off my laptop, baby!</p>
]]></content:encoded></item><item><title><![CDATA[JD's 2024 Year in Review]]></title><description><![CDATA[2024 was a strange year for me that was a mix of trying new things, accomplishing the best work of my career, and stark transitions into unexplored territory.
I think it's important to take a moment every once in a while to step back and look at one'...]]></description><link>https://webartisan.info/jds-2024-year-in-review</link><guid isPermaLink="true">https://webartisan.info/jds-2024-year-in-review</guid><category><![CDATA[2024]]></category><category><![CDATA[yearinreview]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Wed, 01 Jan 2025 01:27:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736199147199/ed2bab86-c88d-40be-9158-e58700947328.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>2024 was a strange year for me that was a mix of trying new things, accomplishing the best work of my career, and stark transitions into unexplored territory.</p>
<p>I think it's important to take a moment every once in a while to step back and look at one's accomplishments from a distance. Sometimes, in the heat of the moment, we lose sight of what we have accomplished or achieved as tunnel vision sets in, and we're hyper-focused on what's next for us. Of course, the dawn of a new year is the traditional time to do this.</p>
<p>I'm mostly writing this post for myself to reflect, but sometimes it's fun to share my achievements (and, in some cases, setbacks) with others. Perhaps this will be interesting to someone else! In no particular order, here are the highlights of what I did in 2024.</p>
<h2 id="heading-built-a-lego-replica-of-my-house">Built a LEGO Replica of My House</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694226241/0ebfe242-f58e-41b5-a4f9-c8014bdd91e0.webp" alt class="image--center mx-auto" /></p>
<p>In 2023, I got into LEGO, which I hadn't owned since I was a kid. The toy has become very popular for adults in recent years, and many excellent adult-oriented sets have been released. In late 2023, I got the idea to build an accurate scale model of my house, and completed it in early 2024.</p>
<p>It is a large, custom build consisting of around 6,000 pieces. We keep it on a counter in the kitchen for the moment and everyone who visits is impressed by it. It took quite some effort to learn BrickLink Studio, design it based on careful measurements of my real house, source all the parts, attempt to put it together, and then tweak the design when it was clear my original design wouldn't work in the real world. In the process, I also amassed quite a collection of parts that are very well organized for other builds I want to do.</p>
<p>Even though the LEGO house was a silly hobby project, it's one of those things I always wanted to do as a child that I managed to make real as an adult, and I'm proud of the results. It is also symbolic of my pride in the home that my wife and I built, which I am also very happy with.</p>
<h2 id="heading-i-quit-my-job">I Quit My Job</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694250158/78135b49-5403-464e-b132-ace46026347f.webp" alt class="image--center mx-auto" /></p>
<p>At the end of 2023, I was required to take some vacation. I normally don't <em>enjoy</em> vacations, as I enjoy my work, enjoy being at home, and don't feel a need to go anywhere most of the time. But during my time off, I found myself listening to a lot of episodes of <em>My First Million</em>, an excellent podcast that talks to successful business owners. For weeks, all I could think about was the power and possibilities that there are for someone with my skillset, particularly with the advent of generative AI. I started to feel that if I didn't start focusing on building something of my own and spent my whole career working for someone else, I'd ultimately regret it.</p>
<p>With my wife (barely) on board, I submitted my resignation with ample time to prepare my employer for my departure and began winding down my work to leave.</p>
<p><a target="_blank" href="https://webartisan.info/a-life-of-freedom-part-1">I've written about this subject before</a>; but in a nutshell, I had no real ability to advance at my job, even though I liked it, and either wanted to get a job with opportunities for growth, or create my own business. I felt like I'd never do either if I continued to focus single-mindedly on my employee job.</p>
<p>To be clear, I don't recommend the approach I took to the vast majority of people, but I had the immense privilege of having both a wife with an excellent, established career and ample savings from years of working and barely spending any of my money. I figured that this was as good a time as any to make the plunge back into being a free agent, which is how I spent most of my young adulthood since starting a business at the age of 18.</p>
<h2 id="heading-some-of-the-best-work-of-my-career-at-epl">Some of the Best Work of My Career at EPL</h2>
<p>Despite having quit my job, to this day, I feel an immense sense of pride in my work at Edmonton Public Library and the codebase I contributed to during the 12 years I was there. It was in a shabby state when I started and still has a long way to go, but I vowed that before I left my job, I'd tackle a laundry list of issues, outdated dependencies, and modernizations that I had wanted to resolve so that the folks who took on my role in subsequent years had a fighting chance of being able to maintain those systems.</p>
<p>There are two specific developer tools I launched in the six months or so before I left EPL that I'm very proud of:</p>
<p>The first is <strong>FilterTable</strong>, which is a ColdFusion component that allows you to take database tables and, extremely easily, create a full user interface around them that provides search, sorting, pagination, editing, detail views of records, and more. It's compatible with MySQL and SQL Server and is extremely fast, using optimized vanilla JS on the frontend and a ColdFusion API on the backend to serve the data. This tool is pretty much only relevant to EPL, but I'd love to recreate an open-source Laravel version of this one day as an alternative to something like Nova. It became such a breeze to whip up the sort of applications that used to take me weeks to build at EPL but now took 15-60 minutes.</p>
<p>I also created a very slick NPM package called <a target="_blank" href="https://www.npmjs.com/package/@jdlien/validator">@jdlien/Validator</a>, which makes it a breeze to do extensive validation on forms in a way that is very powerful, user-friendly, and effective. Honestly, I was so sick of seeing forms that would error because you entered a phone number in a slightly different format, did a half-assed job of email validation, or made it super confusing or difficult to enter data correctly.</p>
<p>My Validator package is the best form validation implementation I've ever seen. It's well-thought-out, easy to use, and is applied in a way similar to built-in HTML validation browsers are supposed to do. (Such validation is usually implemented either poorly or inconsistently in browsers.)</p>
<p>I'm not sure if anyone else anywhere actually uses my package, but it was extremely useful for my work at EPL, saving me a ton of time while enhancing staff and customer user experiences. I'm proud of it.</p>
<p>With those tools in hand, I worked through the codebase of what we called the "Apps Legacy" platform at EPL and patched up dozens of the old apps and forms to use these new systems, which are much more robust and elegant than what they were using before. The most daunting part of this was an almost complete rewrite of the FormBuilder application I had created, which allows administrative staff to create their own forms for staff to use. It was commonly used in HR. That app was quite complicated as it allowed for forms with multiple stages filled by separate people, complicated logic within forms, saving everything to a database, and email routing based on different inputs.</p>
<p>Not only was I trying to rewrite big chunks of this extremely complicated app within a few months, but it was the most used application I had ever made at EPL, and almost all employees used it regularly. So this was a bit of a stressful thing to pull off right before I walked out the door, but barring one convoluted issue right after I released it, I pulled it off, and it was a huge user experience upgrade that significantly improved the codebase over the original.</p>
<h2 id="heading-job-applications">Job Applications</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694277789/37ec4fa6-92b9-4fda-b5ca-ac000c901e48.webp" alt class="image--center mx-auto" /></p>
<p>After leaving my job, things were uncertain and tense. It was a little scary. I didn't have paid work at first and was relying on keeping a minimal burn rate for my savings. For a while, I decided that the most obvious route forward was to apply for jobs that interested me and that I thought were an excellent fit for me.</p>
<p>I even worked with a coach, <a target="_blank" href="https://www.refactorcoach.com/">Jim Hodapp</a>, who helped me clarify my goals, refine my approach to applying for job postings, and devise what would hopefully be an effective strategy for finding work. Jim was excellent, and I heartily recommend him to anyone in such a position, but ultimately, I didn't get far with my job search. After applying to several companies, I only got one interview, and fortune was not in my favor with that process; I did okay in the technical portion of the interview, but I clearly didn't outshine my competition.</p>
<p>I think there were two big factors in my "failure" here: first, it's a tough job market now, and many big-tech/FAANG staff are being laid off, so there are so many talented software engineers looking for work—the competition is fierce. But even more importantly, my heart wasn't really in it. I just didn't want to apply for a whole bunch of jobs I didn't feel passionately about. As I went through this process, I realized even more that what I really wanted was the freedom and flexibility to work on what I wanted and eventually also to build something of my own. Although I feel like I could probably learn a whole lot from working for a bigger tech company, and I'd like an aspect of that, it's probably the time for me to do my own thing on my own terms.</p>
<p>The failed job search and the ensuing realization about what I want for my career were simultaneously the biggest disappointment and the biggest insight of 2024.</p>
<h2 id="heading-new-freelance-clients">New Freelance Clients</h2>
<p>After leaving my job, despite doing very little to market myself and connect with prospective clients, I was very fortunate that a few clients found me with potentially lucrative projects to take on. There have been some highs and lows with this process.</p>
<p>I found that I'd spend some time reviewing a project, putting together a project plan and a proposal, and then, well, wait. Although I had several projects come across my desk that I was happy to take on, I've found that these would often get stymied by some bureaucratic process. I'd get just to the starting line and was told that I would have to wait before getting a signoff on it and getting a signed contract that allowed me to start doing billable work.</p>
<p>I'm hoping that some of these still bear fruit in 2025, as there's potential there for me to have a lucrative year, but there lies one of the challenges of freelancing: You don't make money if you can't properly close a deal and get contracts signed.</p>
<p>That said, I did get one signed contract for a project I am actively working on (and it's something a bit different for me, a .NET project that I am updating and building a React-based frontend for), and another for a Public Library system that is near to my heart (and prior experience) that will be partly done with Laravel. So, I'm excited about getting those done!</p>
<h2 id="heading-aaron-francis-and-try-hard-studios">Aaron Francis and Try Hard Studios</h2>
<p>One of the most delightful surprises and opportunities that came my way in 2024 was when Aaron Francis sent me a message on Twitter/X asking if I'd be interested in helping his new business by writing some content for them, which hopefully will help to both establish their web properties as a source of excellent, well-written content about databases and drive some search traffic to their paid courses.</p>
<p>It has been a pleasure to get to know <a target="_blank" href="https://x.com/aarondfrancis">Aaron</a>, <a target="_blank" href="https://x.com/steve_tenuto">Steve</a>, and <a target="_blank" href="https://x.com/krpetrich">Kelsey</a> at Try Hard, and be in their inner-circle. They are some of the nicest and smartest people you could hope to meet. It was my pleasure to help out with the frenetic launch of the <a target="_blank" href="https://masteringpostgres.com/">Mastering Postgres</a> course and be a part of that, and I plan to work on a lot more content with them in 2025!</p>
<h2 id="heading-my-new-studio">My New Studio</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694296639/b1ea37ce-31d1-4b80-ba5e-d207836b530e.webp" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694301073/7a2f1419-0321-4f0f-9fe1-8ca708d31bb1.webp" alt class="image--center mx-auto" /></p>
<p>My new house had a bedroom dedicated to my office, which was quite nice, had a sizable window in front of it, and worked pretty well. But while our home is beautiful, we lost the neighbor lottery hard when a large, loud, obnoxious family moved in next door shortly after we did. They love screaming, swearing, operating power tools, running snowmobiles, and letting their 2-3 large dogs bark and howl incessantly. Consequently, it was very often noisy and loud in my office with the big window facing the back yard. I also live in a neighborhood where there's almost always new home construction happening.</p>
<p>Largely because of this, and because I had planned to record an audiobook this year and needed a quiet space to do that, I decided that I'd build another office in our basement, complete with a soundproofed window, acoustic treatment on the walls, acoustic panels, and some killer studio monitor speakers, and sexy lighting. This proved to be a bigger project than I anticipated, involving a lot of reorganization of the basement, buying and learning to use a miter saw, and some light construction.</p>
<p>In the end, I have a lovely, cozy space that is an excellent place to work and has everything I need to record audio, produce music, podcast, or maybe even make YouTube videos someday.</p>
<h2 id="heading-edited-and-recorded-an-audiobook-for-ash-allen">Edited and Recorded An Audiobook for Ash Allen</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694604604/cd5e46cc-77f6-4cb3-a476-5432c7dae688.webp" alt class="image--center mx-auto" /></p>
<p><a target="_blank" href="https://x.com/ashallendesign">Ash Allen</a> is an excellent Laravel Developer and a super nice guy, and I'm honored to have been the editor of his past two books. When he decided to start working on a new book about Freelancing as a web developer, he offered me a gig editing this book as well, which I was more than happy to accept.</p>
<p>While Ash's previous books were more programming-oriented books with a lot of code in them, this book was almost entirely prose, which was conducive to an audiobook. Ash didn't have any experience producing an audiobook, so I volunteered to take on the task, including doing the narration, editing, and packaging of the whole thing.</p>
<p>Voicing an audiobook is one of those personal challenges I've been wanting to take on for a long time, and it was fun to do, although it was a lot of work! I taught myself Logic Pro earlier this year to learn what I needed to know about audio production. I even hired an accent coach briefly to give me some tips on how to make my accent more "generic" and remove some of the personal/Canadian quirks in my speech to hopefully make the book a little easier to listen to and understand. It was an interesting personal journey to learn more about the subtle differences between Canadian and "General American" accents.</p>
<p>The editing and production of the audiobook are now all done, so I'm eager to see the book released in January. Hopefully, it's a big success for him and people like my rendition of <a target="_blank" href="https://web-dev-freelancing.com/">The Web Dev's Guide to Freelancing</a>. Definitely check it out if you're a freelancer looking to up your game, as there is a lot of great information in there!</p>
<h2 id="heading-wrote-generated-and-published-a-concept-synthwave-album">Wrote, Generated, and Published a Concept Synthwave Album</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694615071/d581b7ee-8f82-42a6-a26c-17f8eea243d2.webp" alt class="image--center mx-auto" /></p>
<p>If you thought the last item was a curveball, here's another bucket list item I've been wanting to tackle but never had the time or talent to get done. I like a lot of music, but one genre I've been captivated by for about six years is synthwave—a fusion of modern electronic production with 1980s synthwave and synthpop, wrapped in retro-futuristic nostalgia. I was a kid in the '80s, so I've always had a special place in my heart for the culture and music of the era; synthwave just hit me in the feels instantly.</p>
<p>More broadly, I've got a long history with music, dabbling in electronic music production and instruments, and was a professional DJ for over ten years. When generative AI music services popped up, I couldn't resist playing with it and was blown away by what you could make with little effort. I started making silly songs for my wife and made music of every genre that I could think of using <a target="_blank" href="https://suno.com/">Suno</a>.</p>
<p>Suno is very cool, but the instant you hear a song generated with it, you know it. It sounds scratchy and low-fi, like a poor-quality recording encoded with a bad encoder at a low sampling rate. Eventually, a competitor arrived called <a target="_blank" href="https://udio.com/">Udio</a> that had quality head and shoulders beyond Suno, but with a catch: It is much more difficult to generate well-structured, coherent songs on Udio.</p>
<p>After much tinkering, I started getting good results out of Udio, and at some point, inspired by the SpaceX Starship launches, I wrote a synthwave song called "Copilot" that I was quite happy with. It tells the story of a young woman leaving the Earth behind and imploring a mysterious other person to join them as her copilot.</p>
<p>I decided that I'd make a full concept album around this idea and genre, using consistent styles and vocals (which are all female and far beyond my singing ability). One song at a time, I wrote lyrics for several songs about this woman having a misadventure en route to Jupiter's moon Europa. I generated the music with Udio over a few weeks, making tweaks and edits in Logic Pro. I used DistroKid to publish the music, and my album, <a target="_blank" href="https://li.sten.to/europa">Europa, under the name Cosmic Cadet</a>, is available on all the streaming services!</p>
<p>I am proud of that album and think there are a few lovely songs on it, although I know I could do better. I purchased a small MIDI controller keyboard and taught myself a bit more about music theory and music production. I would love to make music using a hybrid of AI and traditional electronic/MIDI production techniques if I ever have time. (Stretch goal: get a female synthwave icon like Dana Jean Phoenix or Primo the Alien to sing on it!)</p>
<p>Additionally, I started writing pages and pages of notes and ideas to flesh out the story I created for the album into a full-length sci-fi novel. I even briefly went down the rabbit hole of learning to write better and structure a fiction story. Perhaps someday, I'll be able to cross that other bucket list item off my list and become a science fiction author, too!</p>
<h2 id="heading-business-network-and-surveillance-system-upgrade">Business Network and Surveillance System Upgrade</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694662406/28d3e2d0-6b8a-4424-aa24-d66b6fb0331d.webp" alt class="image--center mx-auto" /></p>
<p>I don't really do network administration professionally anymore (other than for my own stuff), but I still find the subject interesting. I got my Cisco CCNA cert at one point, and I have a fairly advanced network in my home based on the UniFi platform. My dad owns a bar and, unfortunately, has had a series of unfortunate events happen there that prompted us to upgrade his aging network and cameras.</p>
<p>I am a huge fan of Ubiquiti's UniFi products—they are not cheap, but they do many things enterprise networking equipment does for a fraction of the price. Hence, they are awesome for small businesses as well as computer nerds who want an excellent, reliable network that is extremely configurable and powerful.</p>
<p>I put in a proper rack, crawled around in the rafters through insulation pulling CAT6 cable, and installed new routing, switching, and camera hardware for the business that offers a vastly improved degree of quality, control, and power for both the cameras themselves as well as the ability to configure and administer the network. I'm really happy with the results, and I learned quite a bit during the process.</p>
<h2 id="heading-btransit-transit-api">bTransit Transit API</h2>
<p>One project I was quite proud of at Edmonton Public Library was a system that ingests GTFS transit data from Edmonton Transit Service into a database and provides a simple user interface to view transit schedules. At some point, we decided to shut down the web app for this because, with me gone, we didn't want anyone to have to maintain it, and it could, at times, be a maintenance burden.</p>
<p>When it went away, I thought that I'd take on the task of building a new system (based on Laravel, where the old system was mostly ColdFusion), that would ingest the data into a database and provide an API, which a number of different frontend user interfaces could then consume.</p>
<p>I got a pretty good start on this project right after leaving my work, but ultimately, I shelved it and haven't worked on it for some time. Perhaps in 2025 or 2026, I'll pick this project up again because it has some promise. The biggest problem is that, after a while, I realized that I don't have the personal interest in the project I once did—I almost never use public transit anymore now that I no longer live by a light rail station and commute to work daily. When I made it, it was incredibly useful for me and some people I care about, but I don't have the interest anymore, as much as I feel some sense of obligation to resurrect the handy tool I once made.</p>
<h2 id="heading-i-helped-make-a-baby">I Helped Make a Baby</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735694741151/cec61a78-47ab-4685-95c6-288c979778bb.webp" alt class="image--center mx-auto" /></p>
<p>My wife and I have been trying for a while to use Science™ to create a new human, and our efforts have been largely disappointing and unsuccessful. Yet, all of a sudden, we were blessed with a natural pregnancy, and my wife and I are prepared to welcome a new member to the family this coming January. Although I'm not officially a dad just yet, it's definitely huge that we've gotten a healthy human this far. 2025 will be a very different year for me, that's for sure!</p>
<p>My wife and I attended a course walking us through the birth and first few months of caring for a baby, which is a little overwhelming. We got to practice things on this creepy doll from 1990. Hopefully, we can figure it out, and our tiny human will survive!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Despite some setbacks and my occasional feelings of failure and lack of accomplishment, 2024 was actually a pretty eventful year for me. I completed some fun and interesting personal projects and spent some time finding my footing in a new employment situation and exploring new territory, although things didn't always work out as I hoped. The next year will be exciting as I dive deeper into freelancing, start figuring out this parenting thing, and hopefully take on a few new and exciting projects that I have in mind.</p>
<p>If you have read this far, I appreciate you taking the ride with me, and I wish you an amazing 2025!</p>
]]></content:encoded></item><item><title><![CDATA[A Life of Freedom: Part 2]]></title><description><![CDATA[In part 1 of this series, I discussed how I made a dramatic decision to leave a job I enjoyed to pivot to freelancing and working on personal projects. I was fortunate enough to make this decision voluntarily and while on good terms with my former em...]]></description><link>https://webartisan.info/a-life-of-freedom-part-2</link><guid isPermaLink="true">https://webartisan.info/a-life-of-freedom-part-2</guid><category><![CDATA[Freelancing]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Fri, 23 Aug 2024 04:42:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724418399598/c326b5bc-4e77-4d74-9221-921f55cf4354.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target="_blank" href="https://webartisan.info/a-life-of-freedom-part-1">part 1 of this series</a>, I discussed how I made a dramatic decision to leave a job I enjoyed to pivot to freelancing and working on personal projects. I was fortunate enough to make this decision voluntarily and while on good terms with my former employer, although I didn't have a comprehensive plan for what I was going to do after I left.</p>
<h2 id="heading-how-its-going">How It's Going</h2>
<p>I've now been self-employed for a third of a year. This time has had its ups and downs and has been something of a wild ride. I didn't have a detailed plan for what I was going to do at first, but it's interesting to reflect on what I've been up to during that time.</p>
<h3 id="heading-technical-writing-gigs">Technical Writing Gigs</h3>
<p>I have done a few technical writing gigs for various clients; I enjoy writing and educating. It's different from coding but is usually related to my experience in software engineering domains. I find it can pay well. I'm a decent writer, but despite my ability and experience writing, it can still be surprisingly difficult and mentally taxing work — particularly when you're writing about an area you don't necessarily have deep expertise in already, as considerable effort needs to be put into research, reading, and creating tests or benchmarks. I pride myself on being accurate and thorough and writing with a very high level of quality. So even with tools like ChatGPT to create outlines or "proofread drafts" (which it is not very good at, by the way), it still can take a surprisingly long time to write professional, high-quality technical content.</p>
<h3 id="heading-job-hunting">Job Hunting</h3>
<p>During a lull in work and with mounting anxiety from my wife, I decided it would make sense to apply for full-time employment. Unfortunately, I was getting no responses to my applications. Apparently, it's not the easiest time to find software engineering work. (This may foreshadow a project I am about to start working on.) Disheartened, I went down a rabbit hole of improving my résumé and employability, enlisting the help of a coach specializing in software engineering, <a target="_blank" href="https://refactorgroup.com/">Jim Hodapp</a>. As I reflected on what I wanted for my career and worked with my coach, I felt more strongly that the right path toward my long-term goals is for me to go the self-employed/small business route.</p>
<h3 id="heading-consulting-for-my-former-employer">Consulting for My Former Employer</h3>
<p>As the first couple of months went by, my former employer didn't contact me at all since I left, dispelling the delusion I once had that I was indispensable. It took them a while to hire a new developer, who was only able to start when the existing dev had his first baby, so they had me come in for one day to help with training. Not nearly enough for the poor new guy, who was struggling with such a short onboarding, but so far, this was as much as they were interested in using me.</p>
<h3 id="heading-client-projects">Client Projects</h3>
<p>Fortunately, friends and contacts directed clients to me, and I started a project rebuilding an older PHP site and a new project for a library system here in Alberta. It was exciting to get started on these projects, do the initial analysis, and get a crude proof of concept working. After submitting contracts and project plans, I found myself waiting. Then, waiting more. I'm still waiting. This is an unfortunate reality of contract work. Sometimes, it takes clients an awfully long time to make a decision, get approval, or get funding, and until then, you don't even know if the work you put in will even pay off.</p>
<h3 id="heading-fun-projects">Fun Projects</h3>
<p>Being self-employed (especially when you're waiting for clients to respond) gives me ample opportunity to explore other interests and hobbies. For instance, I've been experimenting with writing lyrics and creating songs using generative AI.</p>
<p>I undertook a "serious" music production process, learned to use a digital audio workstation (DAW), and created a full synthwave concept album with a story about an astronaut who travels to Jupiter's moon, Europa. It is <a target="_blank" href="https://li.sten.to/europa">available in all the streaming services and music stores</a>! Creating a music album is one of those things that has been on my bucket list for a while, and I'm thrilled that I got it done. I would like to turn the story I came up with for the song lyrics into a full-length novel, and to that end, I have been studying the art of fiction writing, which I find fascinating.</p>
<p>Some of this "diversion" into my hobbies might have been a bit much for my wife, who seriously questioned me for putting efforts in so many directions.</p>
<h2 id="heading-ultimate-freedom-and-all-its-perils">Ultimate Freedom — And All Its Perils</h2>
<p>I currently live a life of great freedom and flexibility, though with a less consistent income than I was used to. While that may sound great to those with an inflexible employment situation, it comes with some drawbacks. For one, it's very easy to question whether you're making the best choices. Am I doing what I should with my days? Am I being productive enough, making enough money, and doing things that are ultimately fulfilling to me?</p>
<p>At times, I found myself struggling with a sense of aimlessness and a lack of purpose. I felt a bit lost, not having the structure of a 9–5 job nor the direction of managers and directors above me. When I do have freelance work, I have to balance the needs of multiple clients and have to figure out on my own how to handle administrative duties, from replying to emails to creating contracts and determining the appropriate paperwork to complete.</p>
<p>All the while, I also have to weigh the value of personal projects or activities I <em>want</em> to spend my time on, like my open source and SaaS projects, making music, or even wasting time watching YouTube. This may mean low-urgency client work falls by the wayside, and I may feel guilty about that. So, what do I work on at any given time? It can be tough to determine. Decision paralysis is a real thing — there are <em>too many</em> things I can do, so I ultimately do unproductive things and then feel bad for getting nothing done!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>So now you can see what I've been up to — and it has been several loosely related things, a combination of freelance work, passion projects, and fun projects. I'm challenged both by finding work and balancing work I do have with other things I want to do in life while having a relatively unstructured life.</p>
<p>In part 3 of this series, I will discuss doing the work of reflecting on what you want, setting goals, and creating a plan to achieve those things. All very important to have done when you're a free agent, as I am now.</p>
]]></content:encoded></item><item><title><![CDATA[A Life of Freedom: Part 1]]></title><description><![CDATA[In early 2024, I made a huge life decision. I was going to leave my job of 12 years — an extremely secure job that I loved. I was the senior web developer at Edmonton Public Library, responsible for internal applications and systems used by staff the...]]></description><link>https://webartisan.info/a-life-of-freedom-part-1</link><guid isPermaLink="true">https://webartisan.info/a-life-of-freedom-part-1</guid><category><![CDATA[Freelancing, SelfEmployment]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Thu, 22 Aug 2024 13:42:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724253447362/908deab3-ad28-4570-b699-2b76298f3f0d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In early 2024, I made a huge life decision. I was going to leave my job of 12 years — an extremely secure job that I loved. I was the senior web developer at Edmonton Public Library, responsible for internal applications and systems used by staff there. It was mostly a wonderful place to work, offered flexibility in my work, and I built a lot of great web applications that I'm proud of. But I put in my notice (three months of it, in fact) and finally left my job on April 26, 2024.</p>
<h2 id="heading-why-leave-my-job">Why Leave My Job?</h2>
<p>What was I leaving my cushy, stable job to do? Well, I wasn't sure. But a number of factors led me to leave.</p>
<h3 id="heading-1-exploring-new-possibilities">1. Exploring New Possibilities</h3>
<p>The space of generative AI has been both exciting and terrifying. It <em>seems</em> truly intelligent and offers the ability to create many things with less effort than ever.</p>
<ul>
<li><p>Want an article that dives deep into something you barely understand? ChatGPT has got you.</p>
</li>
<li><p>Need help coding in a language you're unfamiliar with? Claude.ai will fix your bugs, suggest refactors, and write complex logic.</p>
</li>
<li><p>Want to create scenes from a futuristic world that doesn't exist? Midjourney et al. are amazing.</p>
</li>
<li><p>Want to create a song with your own lyrics in an obscure music genre but are short on time and musical talent? <a target="_blank" href="https://suno.com">Suno</a> and <a target="_blank" href="https://udio.com">Udio</a> make something like this possible.</p>
</li>
</ul>
<p>These tools are amazing, and while not perfect, they open up new creative possibilities that didn't exist before. On the other hand, this stuff is changing so rapidly that it's impossible to see what's around the corner. In a few years, we could have realistic long-form generated videos, artificial general intelligence that can outsmart any human in most tasks, or capable robots that can automate almost any kind of human labor. As a programmer, it's exciting because I can code things in hours that would have taken me days before. However, less experienced developers can do that as well, so there's an argument that this devalues experienced developers. Artists and musicians, and writers alike are crapping their pants because it appears the value of the skills they have honed for an entire lifetime is about to trend toward zero.</p>
<p>In this environment, I feel like you either have to embrace this new technology and learn to make the most of it or die. My employer seemed to encourage the exploration of it more than many, but my heart goes out to folks in organizations that prohibit the use of generative AI. In my opinion, that's like a construction company banning power tools for its employees and contractors. It's going to make the work take longer than for those who use the best tools.</p>
<p>During vacation time at the end of 2023, I spent a lot of time watching episodes of <a target="_blank" href="https://www.youtube.com/@MyFirstMillionPod">My First Million</a>, which got my gears turning about the possibility of creating businesses and products using these new technologies. I felt that if I didn't try to harness this soon, I'd miss out and deeply regret it later. I wrote down dozens of ideas of how generative AI could be used in a number of products or ideas. Frankly, most of those are bad or implausible without major funding, but it was exciting to consider.</p>
<p>There were other things besides experimenting with AI that I've long wanted to do, but my obsessive focus on my work as an employee made it rather difficult for me to put significant effort into such directions. "Some business using AI" was really a placeholder for all the things I've wanted to try and explore over the past few years since I finished my computer science degree. Could I have just "quiet quit", doing the minimum possible work I could get away with while putting real effort into my own projects? Yeah. But to me, that is just gross. I'm the type who gives whatever I'm working on my all, and I loathe such disingenuousness. I'm certainly not the type of person who could have three full-time jobs I'm overqualified for at the same time.</p>
<h3 id="heading-2-improving-my-skills-in-new-areas">2. Improving My Skills in New Areas</h3>
<p>My prior job involved working on a lot of legacy applications in ColdFusion. I got into a groove working on ColdFusion applications and got really good at the language, but I felt I couldn't completely break away from doing this and want to focus on more modern and widely used languages and frameworks, like PHP/Laravel on the backend and TypeScript/React on the frontend. I felt like I would never become a truly excellent Laravel and PHP developer while I was spending so much time in ColdFusion. I had initiated a pivot to a new Laravel-based platform, but as the seasoned programmer on the team, I became the guy who handled all the legacy stuff. It was tough for me to divert my attention from it. I was addicted to working on the old stuff — it was my baby. It was time for a clean break so I could do things better for my career than working on crufty old apps in a rarely-used language.</p>
<h3 id="heading-3-fear-of-stagnation">3. Fear of Stagnation</h3>
<p>My senior in my prior job was a man who worked there for about 20 years by the time he quit, and at the end of that, he was completely miserable. Long ago, he had lost the passion for his craft, was uninterested in learning new things or improving, and didn't care much about the quality of his work. He felt like he was in prison awaiting "release", meaning that he needed to hit the experience and age combo that would give him the pension he wanted.</p>
<p>At various times, he warned me not to become like him. I wondered if I'd get to a point where the only career move that made sense was to ride out my employable years in the same stagnant but secure union job accumulating pension, whether I enjoyed it or not. There's nothing <em>wrong</em> with that, but this idea just seemed depressing.</p>
<p>I couldn't advance my career as a web developer there. People in other roles I worked with earned slightly more, but the organization had no interest in upgrading my position, and I was even paid less than my disgruntled former coworker and others who had less experience than me. This was frustrating and demotivating.</p>
<p>To be clear, I still liked my job, had great coworkers, and felt a sense of pride and purpose in what I did. But I felt like the conditions were right for me to leave on good terms at this point since I had a successor in place who could handle most of my work.</p>
<p>I thought that if things didn't work out with my own business, I could always seek another full-time job that offered more opportunities for advancement and beneficial new experiences before I got too ossified.</p>
<h3 id="heading-4-creating-something-of-my-own">4. Creating Something of My Own</h3>
<p>I enjoyed creating software for a large organization, but I felt saddened that, after long nights and weekends working on projects just because I felt driven to make progress and do excellent work, I was ultimately building something I had zero ownership of. When I left EPL, my account was shut down, and I couldn't even see or use the software I built again if I wanted to.</p>
<p>If I'm going to slavishly devote the prime years of my career to building something, I'd much rather that it was something that I <em>own</em> — something that I want to make for my own reasons. If such a project becomes a business that generates reliable revenue, that's great, but even passion projects I build for their own sake, at least I could do it under my terms, and no one could take it away from me in the end.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>So here I am — I left my secure employment, and now you know my reasons for doing so: Curiosity, ambition, and a hunger for new opportunities. And a side of sheer stubbornness. I have been in this position for four months now, and this time has been a wild ride, both fun and scary, filled with interesting opportunities and projects and some very uneasy feelings as well, which <a target="_blank" href="https://webartisan.info/a-life-of-freedom-part-2">I discuss in Part 2 of my journey</a>!</p>
<p>Follow me on Twitter/X at <a target="_blank" href="https://x.com/jdlien">@jdlien</a> to stay in touch!</p>
]]></content:encoded></item><item><title><![CDATA[Engineering Emotional Experiences for the Next Generation]]></title><description><![CDATA[With a few glasses of wine in me and some nostalgia-fueled synthwave playing, I found myself reflecting on my past, and my history with computers. I got to thinking of my childhood, and more specifically, my personal history with computers.
I love te...]]></description><link>https://webartisan.info/engineering-emotional-experiences-for-the-next-generation</link><guid isPermaLink="true">https://webartisan.info/engineering-emotional-experiences-for-the-next-generation</guid><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Mon, 27 May 2024 05:18:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/p0j-mE6mGo4/upload/1cd6ea4e1ab0329dffeb9b8211ce47b6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>With a few glasses of wine in me and some nostalgia-fueled <a target="_blank" href="https://www.youtube.com/watch?v=1wBNgz8ciZQ">synthwave</a> playing, I found myself reflecting on my past, and my history with computers. I got to thinking of my childhood, and more specifically, my personal history with computers.</p>
<p>I love technology and the promise of what it can do for humanity. In our current moment of fearing that our jobs will all be erased in the blink of an eye by generative AI, and that the world will end in a decade or two due to whatever existential threat pundits claim will wipe us out, I still love technology. I love the promise of the possibilities that technology and software unlocks. I love the unbridled innovation that the anguished efforts of hard-working engineers brings us. But amidst arguing about software best-practices and which framework or programming language is best, I reflected on a simpler time in my life (perhaps everyone's life, when viewed through a certain wistful set of rose-colored glasses). I remembered a time when tech wasn't just about making a buck, about us sacrificing ourselves to ostensibly "make the world a better place" but about having fun, and about <em>feeling something</em>.</p>
<p>When I reflect on my life in the 1990s and my introduction and early fascination with computers at that time, it wasn't about any of that. It wasn't like I had some obligation to crack open a two-inch thick software manual and learn the nuances of MS-DOS because I wanted to increase my salary; I was a nerdy 10-year-old kid who just thought it was freaking cool. I just wanted to figure out whatever I had to so that I could play video games and get them working on my primitive 386 PC, with all 256 colours of chunky pixels and PC speaker bleeps and bloops that PCs of that era could muster.</p>
<p>I loved playing video games back then, and I'm sure that's a path that many people took that ultimately led to them embarking on a career in technology. But when I really think deeply about it all, it's just that there was a certain <em>feeling</em> that I had. The feeling that an exciting revolution was a moment away. We could be entertained, we could be captivated, and it seemed like something big was happening. We'd read magazines showing us that the next revolution in technology was afoot, and, in hindsight, they weren't wrong. It was amazing—year after year, we'd see huge jumps in what computers could do. One year we'd have PC speaker beeps, the next we'd have pulse-code-modulated audio with CD-quality sound. We'd double the resolution of computers, get Windows 95 bringing a full-blown GUI to all our software, be able to download any song we wanted as MP3 files on Napster, and we'd see huge increases in the amount of RAM and the complexity of software from one month to the next.</p>
<p>In a sense, this is all still true today, and I love following the rapid advancement of technology just as much as any geek, but sometimes I wonder if, growing up, getting into the tech industry, and becoming a "professional", I've lost that sense of wonder and that incredibly feeling of discovery that I had as a child that kept me intrigued and fascinated and that made me want to learn how to code. The passion that led me to spend hours writing custom versions of <code>autoexec.bat</code> and <code>config.sys</code> for boot disks as a kid who could barely even type, let alone program a computer.</p>
<p>Over the course of my career as a developer, I've spent weeks of 12-hour days coding to build software that helps employees get their work done, and I loved that stuff — I love making tools that help people become productive, and love how computers can enable that stuff. And the thought occurred to me that, while there's nothing wrong with any of that, what if we just put some of that passion and effort into creating something that makes people <em>feel</em>. As much as shaving a few minutes of people's day-to-day work is a noble cause, maybe software can make people feel a certain way that is kind of amazing. Just make an experience that is cool to watch, engages people, captivates minds, and inspires them to want to dive into a whole other world just because it is intriguing and cool.</p>
<p>I think a lot about how I could take my personal projects to that level; not just by making something that is well-engineered, and that follows best practice. Not just something that uses the programming language du jour... but something that is an experience that creates a feeling of amazement and a sense of wonder in the next generation of engineers and leaders and creators who will craft the future of this world.</p>
]]></content:encoded></item><item><title><![CDATA[How Does AI Affect A New Developer's Ability to Learn?]]></title><description><![CDATA[There are many hot takes on how using AI and LLMs affects your ability to learn programming. Some, like Kyle Gawley, imply that using AI to write code is cheating and that you won't learn anything. Others say it is a great way to learn and that it ca...]]></description><link>https://webartisan.info/how-does-ai-affect-a-new-developers-ability-to-learn</link><guid isPermaLink="true">https://webartisan.info/how-does-ai-affect-a-new-developers-ability-to-learn</guid><category><![CDATA[#ai-tools]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Fri, 03 May 2024 17:32:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/_0iV9LmPDn0/upload/1febcea7538a11bda40d7a6a09066609.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are many hot takes on how using AI and LLMs affects your ability to learn programming. Some, like Kyle Gawley, imply that using AI to write code is cheating and that you won't learn anything. Others say it is a great way to learn and that it can help you become a better programmer.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/kylegawley/status/1786350057824092201">https://twitter.com/kylegawley/status/1786350057824092201</a></div>
<h2 id="heading-is-hands-on-coding-critical-for-learning">Is Hands-On Coding Critical for Learning?</h2>
<p>I mostly disagree with this take, but at the same time, it's probably true that you need to type lines of code as a complete beginner, or you won't learn the fundamentals. I was taught to literally type out code from a textbook in order to learn. This sort of approach certainly has some benefits, especially before smart code completion was available — being able to type basic programmatic structures and functions and have the muscle memory for good syntax was incredibly useful. In my career, I got a lot of value just from being able to type some repetitive things like &lt;!DOCTYPE html&gt; without thinking about it. I do think it's helpful to have the basics down to that level.</p>
<h2 id="heading-how-chatgpt-helped-me-evolve-as-a-programmer">How ChatGPT Helped Me Evolve as a Programmer</h2>
<p>On the flip side, as an experienced developer, I've improved my skills and knowledge significantly and at a faster pace than ever by working with ChatGPT and learning new techniques and design patterns through it. Previously, I might have written barely functional code and shrugged it off. Now, I can iterate with ChatGPT to come up with good designs that incorporate best practices, and I've produced the best work of my career in no small part thanks to ChatGPT.</p>
<h2 id="heading-make-chatgpt-your-teammate-not-your-oracle">Make ChatGPT Your Teammate, not your Oracle</h2>
<p>The thing is, the iteration is key. Utley and Gohar of Stanford's d.school researched how teams integrate AI into existing workflows and found something really fascinating: the most successful teams were those that iterated with AI, treating it like a teammate, not those that just used it like an "Oracle" to generate a solution. This means that the best way to get better results with AI is to use it as a tool to help you learn, a way to bounce your ideas off something that can rephrase your thoughts in new ways, and a way to dig deeper and get more information or further your understanding — not as a crutch to do the work for you.</p>
<p>There is a huge difference between those who lazily do the minimum possible to have AI write some code without giving a thought to how it works and someone who has an idea and iterates multiple times, asking themselves (and the AI) questions like:
How does this work?"
How could this be better?
What are the trade-offs?
What are the implications of this design?
What are the edge cases?
How can this be optimized?</p>
<p>If you are fortunate enough to work with a team of experienced developers, you can ask others such questions; therein lies much of the power of pair programming or mob programming. On the other hand, if you are working alone or with less experienced developers, you might not have that luxury. In that case, AI can be a great tool to help you learn and improve your skills. It is an excellent second set of eyes for you to check your work and find mistakes that may otherwise take a lot of time for you to find when they cause some weird edge-case bug.</p>
<h2 id="heading-opening-up-coding-to-the-masses">Opening Up Coding To The Masses</h2>
<p>The barrier to entry for beginners is now lower. This could be the difference between someone building something interesting and useful and someone giving up on programming at the outset because they can't get basic things to work. Maybe people will be more likely to persist until they learn and figure things out. Maybe that's the difference between an okay programmer who relies heavily on AI and someone who tried for ten minutes and swore off coding forever.</p>
<p>When I started doing web development in 1999, I met people who said things like, "I took a programming in college and hated it." Maybe people like that would stick with it a lot longer if using AI meant it wasn't such an epic struggle just to get started. Perhaps many such people have more to offer the world than merely the tenacity to struggle with complex technical problems until they understand the innermost workings of Von Neumann architecture, have read Cormen, Leiserson, Rivest, and Stein's 'Introduction to Algorithms' cover-to-cover, and memorized the method signature of every broadly useful Java class.</p>
<p>Some folks might be brilliant at understanding how people think and making great user experiences. Maybe they have an artistic flair that allows them to create some gorgeous graphics and designs, or maybe they are expert writers who can engage and inspire readers. The products of their coding may provide a canvas for these other creative endeavors. Not having an expert-level ability to understand programming without any tooling doesn't inherently make what they do less valuable, does it?</p>
<p>Like in so many other communities, I think a lot of the "people who use AI to code are amateurs" sentiment just amounts to hostile gatekeeping. "You're not good enough to be one of us because you're not like us" is more likely what this really amounts to in many cases.</p>
<h2 id="heading-future-programmers">Future Programmers</h2>
<p>Large language models are like many other tools: they can be a crutch, or they can help you learn and increase productivity. Until we can plug our brains in Matrix-style, learning anything to a high level will always require reading books, documentation, and tutorials. And, of course, one must write code and struggle with solving problems. That won't likely change, no matter how advanced tooling (like AI) becomes. It takes time and effort, even as new tools make things easier.</p>
<p>In ten years, the industry will be filled with young folks who have never coded without AI tools, and more experienced developers will have long-used them to maximize their productivity and output quality. It'll be hard to be competitive without them. It's like someone today insisting on coding by hand in machine code, refusing to use high-level languages or IDEs with syntax highlighting or code completion. Such a person might be brilliant at understanding how a computer works, and they might create highly efficient small programs, but how fast could they build complex, modern software?</p>
<p>It will definitely be interesting to see studies of how LLM-based tooling affects learning outcomes and skill development, but it's still in the early days.</p>
<p>What do you think? Should young developers immediately reach for these tools, or should they learn things the hard way until they develop basic competence? Is it risky to rely too heavily on LLMs?</p>
]]></content:encoded></item><item><title><![CDATA[Choosing A Programming Language to Learn]]></title><description><![CDATA[Whether you are new to programming, looking for your first language to learn, or an experienced developer looking for a new challenge to take your career to the next level, choosing a programming language to learn can be daunting. There are just so m...]]></description><link>https://webartisan.info/choosing-a-programming-language-to-learn</link><guid isPermaLink="true">https://webartisan.info/choosing-a-programming-language-to-learn</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week4]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Mon, 12 Sep 2022 19:58:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/4-EeTnaC1S4/upload/v1663003956009/5DngLkftE.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Whether you are new to programming, looking for your first language to learn, or an experienced developer looking for a new challenge to take your career to the next level, choosing a programming language to learn can be daunting. There are just so many options. It usually takes years to go from zero to hero in a given language, so you don't want to feel like you're wasting your time.</p>
<h2 id="heading-factors-to-consider">Factors to Consider</h2>
<p>A number of subjective and objective factors may weigh into your motivation for learning a specific language — and in the end, what really matters is staying motivated and having a <em>good reason</em> to learn a new thing, or else you're unlikely to stick with it. That said, here are typical motivations for learning a language.</p>
<h3 id="heading-1-you-need-it-for-work">1. You Need It for Work</h3>
<p>This often goes unmentioned, but this is probably one of the most important and motivating factors. If you already have a job (or just landed a job) that requires you to know a particular language, most likely because you'll be working on an existing codebase, that's probably the most motivation you can get. Learn this language or possibly get fired!</p>
<h3 id="heading-2-you-have-friends-or-connections-using-it">2. You Have Friends or Connections Using It</h3>
<p>If you already have good friends or all your social media connections use a language, this can help tremendously — when you get stuck you might know who to turn to for help or to discuss your problem. If you are part of a community that seems to gravitate towards a particular language, that will significantly affect your motivations for learning it.</p>
<h3 id="heading-3-overall-popularity">3. Overall Popularity</h3>
<p>If a language is extremely popular, this will likely sway your opinion about which you should learn. The more popular the language, the more likely you are to find high-quality resources for learning it and getting help. There will be more packages written in the language that you can use so you don't constantly have to reinvent the wheel. And there will be more, better tooling available to help you code in that language.</p>
<p>Here is the percentage of responses of which languages respondents to the Stack Overflow 2021 survey said they used extensively in the past year. JavaScript is king here, followed by Python, with Java remaining quite popular as well.</p>
<p>PHP barely makes the long list, before more niche languages like Go, Rust, Ruby, and so on. This list gives you a good indication that JavaScript (or Node) and Python are great languages to learn for a first or second language simply because of their tremendous popularity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663009822078/uSqYSw6Lq.png" alt="Stack Overflow 2021 Survey — Most Popular Technologies" class="image--center mx-auto" /></p>
<h3 id="heading-4-job-opportunities">4. Job Opportunities</h3>
<p>If your goal is to get a job, you may want to learn a new language to make you more employable. Exactly which languages are the strongest contenders could depend on the jobs available in your area, what industry you'd prefer to work in, and what vacancies are available. For example, JavaScript is very popular and there are a lot of jobs that require JavaScript — but there is also a lot of supply of coders who know JavaScript, so that doesn't necessarily mean your prospects of getting a job with that language are better.</p>
<h3 id="heading-5-highest-paying-jobs">5. Highest Paying Jobs</h3>
<p>People proficient in one language over another tend to make more money. There are many factors that could contribute to this, such as the popularity of a language in particularly lucrative industries.</p>
<p>Stack Overflow's 2021 survey found that Clojure was, by a good margin, the highest paying language (although it's far from the most popular language) and Dart is the lowest paying language. PHP was the second lowest. I'm not sure why PHP performed so badly, but perhaps this is because it's a language many junior devs learn to work with WordPress at beginner-level jobs.</p>
<p>I don't know that this is necessarily a <em>good</em> reason to learn one particular language over another, however, because there's no guarantee that you can <em>find</em> a job that requires one of these languages, and even if you do, the salary range could vary wildly depending on how senior the position is, the responsibilities associated with it, and the nature of the company hiring you.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663010894524/MCH6Y2NR_.png" alt="Median Salary by Programming Language" class="image--center mx-auto" /></p>
<h3 id="heading-6-you-love-it-and-enjoy-the-developer-experience">6. You Love It — And Enjoy the Developer Experience</h3>
<p>One reason people would take one language over another is simply because they enjoy using it versus other languages they know. A language that makes sense, has good tooling, has clear syntax and is easy to learn and use without tricky bugs cropping up all the time will be one that many people would choose to use, all else being equal.</p>
<p>From Stack Overflow's 2021 survey, Rust and Closure are strong Winners, followed by TypeScript. PHP actually performs quite poorly, worse than PowerShell and barely ranking above C. COBOL performs the worst by far with only 16% of respondents claiming to love the language and 84% dreading it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663011425212/qv3mUuOnA.png" alt="Most Loved Langauges" /></p>
<p>It's interesting to look at frameworks, as having a great framework may be a reason to choose a language. The Stack Overflow survey shows that Svelte, ASP.NET Core, FastAPI, and React are well-loved frameworks, and jQuery, Drupal, and Angular are the most dreaded.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663011746589/U4ZaGmPql.png" alt="Most Loved Frameworks" /></p>
<h3 id="heading-its-a-personal-choice">It's a Personal Choice</h3>
<p>In the end, the decision to learn and use a particular language is a very personal one and only you can make the decision about which makes sense for you. There are some interesting data points from surveys like the Stack Overflow survey, and solely going by that, it might look like Clojure or Rust make a lot of sense as they are well-loved and working with them often pays well, or JavaScript just because it's so popular and essential in so many areas.</p>
<p>In my case, there are a few languages that I've spent most of my career learning, and I'd like to explain how I came to work with them.</p>
<h4 id="heading-php">PHP</h4>
<p>When I started learning web development, PHP was everywhere and was (and still is) extremely popular. I had friends who could program in it and they taught me a bit and I worked on projects with them. Later on, when I got the job I've now had for ten years, it was one of the languages used by my employer, as we had a few Drupal-based websites.</p>
<h4 id="heading-coldfusionlucee">ColdFusion/Lucee</h4>
<p>ColdFusion is not very popular anymore, so much so that it doesn't even make any of the lists in this survey. But my employer had an extensive legacy codebase made with ColdFusion and it's just what the existing developers knew and used. So that's what we used. When their existing developer retired I couldn't find any compelling reasons to keep using it so we primarily transitioned to PHP and the Laravel framework.</p>
<p>That said, I've found ColdFusion pretty easy to work with and friendly, but there are a lot of drawbacks to using ColdFusion, and many of those are remedied by using a more popular language. Since we already had PHP in our environment and I already knew PHP, we decided to use the best, most popular PHP framework for new development.</p>
<h4 id="heading-sql-mysql-and-sql-server">SQL (MySQL and SQL Server)</h4>
<p>SQL was essential for any backend development, and in our legacy stack we used pure SQL queries inside of ColdFusion or PHP. Having very good knowledge of SQL and relational database concepts is probably more important for my job than specific knowledge of any particular programming language. Well designed databases and queries mean that you have good data integrity and performance, but just about any language could send that SQL to the server and turn it into HTML and JavaScript.</p>
<p>We are a Microsoft-heavy environment so we used a lot of SQL Server databases, but we are using MySQL for most new web development for cost-saving reasons and because it works a little better with Laravel.</p>
<h4 id="heading-jquery">jQuery</h4>
<p>When I started my job, jQuery was <em>the</em> way most people used JavaScript. JavaScript was tricky to get working between different browsers and it was limited, making even simple operations complicated. We still have a lot of legacy code using jQuery.</p>
<h4 id="heading-vuejs">Vue.js</h4>
<p>For new development, we are primarily working with Vue.js. Why Vue and not, say, React? Mainly because it seemed more popular and well-liked in the Laravel community, and most of the people I know and interact with use Vue. I know of and use many projects that use Vue heavily, and many jobs in my space require knowledge of Vue — despite React being more popular overall.</p>
<h4 id="heading-jshtmlcss">JS/HTML/CSS</h4>
<p>If you're a full-stack web developer like I am, it's hard to avoid knowing any of these languages, and I make extensive use of them on any front-end code I write.</p>
<p>If you found this article interesting, please follow me on Twitter at <a target="_blank" href="https://twitter.com/jdlien">@jdlien</a> for more articles like this!</p>
]]></content:encoded></item><item><title><![CDATA[A New Way to Learn Tech Skills]]></title><description><![CDATA[Something has been on my mind for a little while. An idea for a large project that could revolutionize education — or at least make learning a lot more fun and engaging.
The Problems with Typical Education Approaches
Since I was a kid I've thought th...]]></description><link>https://webartisan.info/a-new-way-to-learn-tech-skills</link><guid isPermaLink="true">https://webartisan.info/a-new-way-to-learn-tech-skills</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week3]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Tue, 06 Sep 2022 04:02:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/BEEyeib-am8/upload/v1662435132319/blS61LSmEe.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Something has been on my mind for a little while. An idea for a large project that could revolutionize education — or at least make learning a lot more fun and engaging.</p>
<h2 id="heading-the-problems-with-typical-education-approaches">The Problems with Typical Education Approaches</h2>
<p>Since I was a kid I've thought the way we learn is broken. We have arbitrary exams that measure little of consequence and we forget what we've learned as soon as it's over. We are largely taught not to cooperate when completing assignments, and we are taught that looking things up or doing things the easy way is "cheating". When you grow up and enter the real world, this education doesn't prepare us for the real world. When we get jobs, what matters isn't really how hard we work, it's the results we get and how well we can solve employers' and customers' problems.</p>
<p>Education is a frustrating experience for many people; it's not often pleasant or fun, and so often we feel like we have to learn things that don't matter. There are so many things I studied in university that I've never had cause to bring to mind since I finished the final exam.</p>
<p>One thing I really like about software development as a profession is that you don't need a formal education. There's no governing body that tells you that you are or are not allowed to be a programmer. If you can sit down at a computer, read some documentation, and get code running that solves a problem for yourself or someone else then congratulations — you're a developer.</p>
<p>But learning to be a software developer, especially a good and well-rounded one, is <em>hard</em>. There are many skills one has to learn — like working in a command line, algorithms and data structures, program construction, analysis, database optimization, markup languages, user interface design, and so on. Tech is a fast-moving industry so there are always new and (ostensibly) better ways of doing things. This is exciting but sometimes frustrating. It's likely a person could burn out trying to keep up.</p>
<h2 id="heading-imagine-a-world">Imagine a World...</h2>
<p>A better way to learn might be a community that can take some of the best parts of learning in a real educational institution, but combine that with some of the best parts of online video game worlds. Instead of plodding your way through textbooks and stressful final exams, what if a developer education were a series of smaller, but meaningful tasks that could be completed for some kind of reward?</p>
<p>A good model for this could be an online role-playing game. Imagine playing World of Warcraft but it makes you a kick-ass web developer. You are a "character", you choose a "class" or career path (which, of course, you could switch at any time), and you complete "quests" which might involve building web pages for other people (or maybe non-player characters or actual businesses). Maybe some of these tasks are even real-world tasks that have a bounty on them that you can earn real money for completing. What if a small business wanted a home page? They could put it up for a bounty in this game.</p>
<p>As you complete tasks, you gain experience points or other various rewards that give you a standard way to show off how knowledgeable you are. And you could earn and learn as quickly or as slowly as you're comfortable.</p>
<p>I think this "world" could have many people working towards common goals and the player/students would be encouraged to help others with their tasks and would be rewarded for doing so, maybe a bit like on Stack Overflow. Players could learn and grow together and foster friendships as they progress towards their goals and become more skilled in their chosen fields.</p>
<p>I don't think this system needs to have high-budget video game style graphics or elaborate fantasy stories, necessarily — but that could be appealing to some as an enhancement to this system. </p>
<h1 id="heading-interested">Interested?</h1>
<p>Would an environment like this be a good learning environment for you? Would this be a fun and engaging way to learn by racking up experience points and "achievements" in a social game-like environment that encourages you to be awesome at JavaScript, promotes your PHP skills, and challenges you to take your CSS to the next level? Or perhaps there are already things out there a little bit like this. Let me know in the comments!</p>
<p>If you find this sort of content interesting, <a target="_blank" href="https://twitter.com/jdlien">I'd love to connect with you on Twitter!</a>.</p>
]]></content:encoded></item><item><title><![CDATA[From Beginner to Expert — Resources for Upping Your Web Development Game]]></title><description><![CDATA[It can be overwhelming learning everything you need to know to get to the next level in your career as a developer. There are always so many things to learn and keep on-top-of in the world of web development. You can see what skills are relevant to d...]]></description><link>https://webartisan.info/from-beginner-to-expert-resources-for-upping-your-web-development-game</link><guid isPermaLink="true">https://webartisan.info/from-beginner-to-expert-resources-for-upping-your-web-development-game</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[PHP]]></category><category><![CDATA[#week2]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Mon, 29 Aug 2022 19:49:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/C3V88BOoRoM/upload/v1661784959457/zlf3Ko96f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It can be overwhelming learning everything you need to know to get to the next level in your career as a developer. There are always so many things to learn and keep on-top-of in the world of web development. You can see what skills are relevant to different career paths as a developer on roadmap.sh. For instance, a junior frontend dev needs to understand things like:</p>
<ul>
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
<li>Git &amp; GitHub</li>
<li>Package Managers</li>
<li>Command Line Interface</li>
<li>How the Internet and servers work</li>
<li>How to use an integrated development environment (IDE)</li>
<li>Integrating linters and formatters into your IDE</li>
<li>Testing frameworks</li>
</ul>
<p>Even that list could be enough to make your head spin! </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661790242366/ZWfc0Ibbc.webp" alt="confused_chloe.webp" class="image--center mx-auto" /></p>
<p>It's quite daunting to look at this list as a beginner, not knowing what many of these items are! But keep this in mind:</p>
<ol>
<li>Some of these topics are learned quickly once you understand fundamentals.</li>
<li>Some of these take many years to master and fully-understand—but are more helpful than necessary to know.</li>
<li>All of these have many great resources online to help you learn them.</li>
</ol>
<p>One thing that's great about the field of web development is that the Web is jam-packed of excellent resources for learning about this field. The challenge is finding the best stuff <em>for you</em>. While there is some great material on YouTube or various websites that turn up in search results, I don't highly recommend this as a core-learning resource for the following reasons:</p>
<ul>
<li>YouTube is <em>extremely</em> distracting. How many times have you gone to YouTube looking for something and an hour later realized you still haven't looked for what you went there for because you got distracted by something totally random?</li>
<li>Unless you pay for a premium subscription, you'll have video ads pretty frequently.</li>
<li>The quality and scope of YouTube videos varies wildly. Some YouTubers produce top-notch, expert material, while some throw together super quick videos using cheap robot voices, low quality microphones, and don't even edit their videos for concision.</li>
<li>It's worth paying for quality. You may not be flush with cash in your situation, but I'll recommend a few options that cost $15-$30/mo, which is affordable if you are serious about learning. By paying for a product you have much higher confidence that the quality is good.</li>
<li>It's great to have a coherent track that can teach you a topic from beginning to end. If you learn bits and pieces from various sources, you might waste time re-learning what you already know or wind up with critical holes in your knowledge.</li>
</ul>
<p>This article lays out what I think are the best websites and resources for learning web development in 2022. These resources will cover starting from the basics of putting a web page together, through to learning several programming languages and frameworks. Given the focus of this blog, special mention will be given to learning advanced PHP and Laravel concepts. There are many great resources available for free, but if you're serious about becoming a skilled developer it is well-worth spending money on high-quality resources that can ultimately lead to a lucrative career. Note that any prices mentioned are in US dollars.</p>
<h2 id="heading-learning-the-basics-freecodecamporg-free">Learning The Basics — freeCodeCamp.org (Free)</h2>
<p>It's trendy to attend a "Developer Bootcamp" where you spend many hours a day learning all the things as fast as possible. This can be a good way to dip your toes into development if you don't yet know much, but a true "bootcamp" where you do in-person sessions with instructors can be quite expensive, not to mention difficult to attend if you have a job or family commitments.</p>
<p>The next best thing is doing a comprehensive online program that you commit to working on consistently for a period of time. And one of the most well-known and best programs allowing you to get started (and for free) is freeCodeCamp.org.</p>
<p>FreeCodeCamp gives you comprehensive, project based courses walking you through each step of learning the languages necessary to build web applications, starting from the very basics. If you're an absolute beginner, I think that this is a great place to start learning fundamentals.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661793492017/A26E77H23.png" alt="image.png" /></p>
<p>There are lots of different programs you can take with FreeCodeCamp to that you learn about whatever type of software development you happen to be interested in.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661793277248/wTc_ASIAb.png" alt="image.png" /></p>
<p>My biggest complaint with freeCodeCamp is probably that, as a more experienced developer, it feels like a little too much "hand-holding" to allow you to really grow rapidly. In order to become a real developer, you're going to want to get your own development environment set up on your computer and build real applications that you can run locally or on a server. This brings us to the next resource.</p>
<h2 id="heading-the-odin-project-free">The Odin Project (Free)</h2>
<p>Another free resource that I would recommend for learning the basics of development is <a target="_blank" href="https://www.theodinproject.com/">The Odin Project</a>. It reads like a book that walks you through learning all the necessary fundamentals, and is less hand-holding than something like freeCodeCamp, but it covers the fundamentals. It is quite text heavy, so if you have a short attention span or limited capacity to read long, text-heavy websites, it may not be the most entertaining way to go.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661794196644/XCbirwqpW.png" alt="image.png" /></p>
<h2 id="heading-code-with-mosh-video-courses-dollar29mo-or-dollar249year">Code with Mosh Video Courses ($29/mo or $249/year)</h2>
<p>Mosh Hamedani of CodeWithMosh.com is an instructor who has an extremely comprehensive collection of video courses teaching a wide range of web development topics starting with the fundamentals, and covering topics such as databases, React, C++, Java, Python, and many more. (Sadly, he does not cover PHP or Laravel, but we will get to this shortly).</p>
<p>Mosh's videos, hosted on the Teachable platform, are made with high production value and they do a good job of explaining concepts in a beginner-friendly way. And you get a ton of value for your subscription fee as there are so many topics covered. If you're a beginner to intermediate developer, CodeWithMosh.com is a resource well worth your time and money.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661794520312/YY-AAZlLX.png" alt="image.png" /></p>
<h2 id="heading-laracasts-dollar15mo-dollar99year-dollar399-forever">Laracasts ($15/mo $99/year $399 forever)</h2>
<p>If you have web dev basics down and are interested in being a full-stack (or backend) developer working with PHP, there are fewer better resources out there than Jeffrey Way's <a target="_blank" href="https://laracasts.com">Laracasts</a>. Laracasts has something approaching 200 series of video courses that are designed to get you up-to-speed quickly with practical, real-world skills working in a real development environment. Many excellent instructors have contributed to the material on Laracasts, including well-known developers who have created frameworks or tooling that are popular in the Laravel world.</p>
<p>The production quality is excellent, and the custom Laracasts.com website and community platform is gorgeous and very well made. Not only is it a great platform for learning from videos, but there's a vibrant community of other developers who help each other solve problems with their code in the discussion forums.</p>
<p>Many courses are available for free, so you can get a lot of value from Laracasts without paying anything. But for $15/mo it's a no-brainer for a working Laravel dev, but sometimes you can even get deals on the lifetime plan around Black Friday (so follow <a target="_blank" href="https://twitter.com/laracasts">Laracasts</a> and stay tuned for any deals). Having a lifetime plan for Laracasts is a resource that has been invaluable for me to learn and improve my skills with Laravel and PHP over the years.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661795653892/qdiypXx91.png" alt="image.png" /></p>
<h2 id="heading-laraveldaily">LaravelDaily</h2>
<p>Another resource for PHP and Laravel developers that has to be mentioned is the treasure in our community  of <a target="_blank" href="https://twitter.com/povilaskorop">Povilas Korop</a> and his website LaravelDaily.com. This man is a content-creating machine, and virtually every day he puts out great content showing how to solve problems and write better code in Laravel. It's well worth following him on Twitter or checking out <a target="_blank" href="https://laraveldaily.com">his site</a>. If you've developed in Laravel, you've probably stumbled upon his blog posts explaining one concept or another.</p>
<p>Povilas has a <a target="_blank" href="https://laraveldaily.teachable.com/">very good series of courses</a> on the Teachable platform available for $99/year) which is well worth a look if you enjoy his teaching style. You can find many of his videos on <a target="_blank" href="https://www.youtube.com/c/LaravelDaily">his YouTube channel</a> as well to get an idea of the kind of content he creates. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661796344521/ctJCui8TH.png" alt="LaravelDaily Teachable" /></p>
<h2 id="heading-program-with-gio-learn-php-the-right-way-free">Program With Gio — Learn PHP The Right Way (Free)</h2>
<p>I don't recommend YouTube for focused learning, but I do want to mention one YouTuber who has been putting out great PHP content. <a target="_blank" href="https://www.youtube.com/c/ProgramWithGio/">Program with Gio</a>. He has an ongoing series called "<a target="_blank" href="https://www.youtube.com/watch?v=sVbEyFZKgqk&amp;list=PLr3d3QYzkw2xabQRUpcZ_IBk9W50M9pe-">Learn PHP The Right Way</a> which has many hours of content showing you the basics of PHP and Laravel, and he delves into some quite advanced topics that I have personally used as supplementary material to fill in holes in my knowledge.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661798249129/VE2KWFGI4.png" alt="Program With Gio" /></p>
<h2 id="heading-scrimba-free-for-basics-dollar15mo-for-pro">Scrimba (Free for basics, $15/mo for pro)</h2>
<p>Another platform I've seen highly recommended for beginners is <a target="_blank" href="scrimba.com">Scrimba</a>. This is a well-designed platform for learning web technologies that combines a video player with video content along with  a web-based IDE that lets you edit the code along with the video. It feels a lot like pair programming with a real instructor!</p>
<p>It's certainly worth mentioning because it's probably one of the most engaging ways for a beginner to learn. You can try their HTML and CSS, JavaScript, and React courses for free. For more advanced courses a subscription is required, and if you want to really commit there is even a Bootcamp program for $200 per month that offers code reviews and group study, which might be an excellent option if you're motivated by the social aspect of a school.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661796778092/Uujf9v50s.png" alt="image.png" /></p>
<h2 id="heading-twitter">Twitter</h2>
<p>Social media can be a huge distraction but it can also be a great way to meet and casually interact with other people who share your interests. By following people who are interested in or experts in web development, it can help inspire you to keep learning and keep up to date with your field, no matter what level you are at. And by sharing your knowledge and journey into web development, you can also develop your own following which gives you credibility and opens up a lot of doors.</p>
<p>If you're interested breaking into the world of tech-Twitter, I've created a list called <a target="_blank" href="https://twitter.com/i/lists/1564322477211516928">Web Dev Rockstars</a> that has many great accounts who have a reputation for posting excellent content or helping others learn or break into careers in web development.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661802330589/_0nCi3Y_F.png" alt="Twitter" /></p>
<h2 id="heading-your-own-blog">Your Own Blog</h2>
<p>As you learn about web development, it's a great exercise to write about it and share things with others. You don't have to be an expert nor do you necessarily have to be an amazing writer to create valuable content — people often learn best from other who are a step or two ahead of them as they can identify with the struggles of someone who doesn't understand things they only recently learned.</p>
<p>As with being active on Twitter, creating a library of content detailing your understanding and your achievements gives you great credibility in the tech space, and the opportunity to help others with things that you may have struggled with.</p>
<p>Creating a blog is not necessarily difficult—this very blog is hosted on <a target="_blank" href="https://hashnode.com">Hashnode</a> which makes it really easy to get started and write content without managing a complicated CMS or building a website. But if you do want an exercise to advance your skills, you could try setting up a WordPress site or build a completely custom blogging platform as a learning exercise.</p>
<h2 id="heading-online-degree">Online Degree</h2>
<p>You certainly don't need a degree to be a software developer or have a great career in the field. And I don't necessarily recommend going for this if you aren't very sure this is what you want to do with your life and have the means to handle the associated expenses or get loans. But if you are serious about getting promoted to a very high level in the field and you want to work for the big tech companies or big business, getting a degree in computer science (aka CompSci or CS) can open a lot of doors.</p>
<p>I completed a degree almost 100% online through Athabasca University (in Canada) while working full-time. It was an incredible amount of work and stress, took over five years, and cost about as much as a new car. But am I a better developer now because of that? Maybe a bit. I definitely have more skills in mathematics, English writing, and tangential knowledge in a variety of areas as a consequence of all the books and courses I went through and was tested on. Projects as part of the program (particularly the big capstone project at the end) were good practice. But could I have done all of that sort of stuff on my own? Absolutely. <em>Would I have?</em> Hard to say. It's unlikely I would have ever had the motivation to complete some of that sort of work without the big stick of tight deadlines promising to turn my expensive tuition into a waste of money if I didn't get the work done.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661801282885/BxbPjt12j.jpeg" alt="JD's Degree" /></p>
<p>Online university is certainly not for everyone. I had the significant advantage that, as an older student with tech industry experience, most of the material in my courses was familiar to me. For someone who is new to tech, it could be difficult to get through some of the courses without a support network of teachers, assistants, or other students to give them a hand every now and then. Personally, I struggled with Calculus as well as (the very math-heavy) senior-level data structures and algorithms course, and paid for a tutor to help me with the math.</p>
<p>However you prefer to learn, there should be some resources out there that allow you to learn about, practice, and master the many skills and concepts one needs to understand to be an excellent developer. I hope that you find something useful among the resources listed in this post. If you are aware of anything that helped you learn or improve as a developer, please share it, as I'm always looking for great resources to share.</p>
<p>If you found this post interesting, please <a target="_blank" href="https://twitter.com/jdlien/">follow me on Twitter</a>.</p>
]]></content:encoded></item><item><title><![CDATA[How and When To Use @apply in Tailwind]]></title><description><![CDATA[If you've used CSS for long, you're likely used to creating CSS components for reusable styles. A big drawback of Tailwind appears to be code duplication — imagine having to add in all the classes for a button hundreds of times in an application: bac...]]></description><link>https://webartisan.info/how-and-when-to-use-apply-in-tailwind</link><guid isPermaLink="true">https://webartisan.info/how-and-when-to-use-apply-in-tailwind</guid><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[apply]]></category><category><![CDATA[Vue.js]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Mon, 22 Aug 2022 23:54:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660938452901/Y4XanDyrk.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you've used CSS for long, you're likely used to creating CSS components for reusable styles. A big drawback of Tailwind appears to be code duplication — imagine having to add in all the classes for a button hundreds of times in an application: background colors, font colors, padding, margins, borders, and all the variants for hover, active, focus and so on. It would be maddening.</p>
<h2 id="heading-behold-apply-our-savior-maybe">Behold <code>@apply</code>, Our Savior!   Maybe.</h2>
<p>Fortunately, Tailwind gives us a tool for this: <code>@apply</code>. This allows us to use existing Tailwind (and custom) classes and create CSS components of our own.</p>
<p>Here's a button class I've used:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;

<span class="hljs-selector-class">.btn</span> {
        @apply inline-flex items-center justify-center
            rounded
            border-x border-t border-b
            border-x-zinc-300/70 border-t-zinc-100 border-b-zinc-400/80
            bg-gradient-to-b from-zinc-200 to-zinc-300
            px-2 py-1
            shadow
            <span class="hljs-attribute">hover</span>:text-opacity-<span class="hljs-number">100</span>
            focus:outline-none
            focus:ring-<span class="hljs-number">2</span>
            focus:ring-blue-<span class="hljs-number">400</span>/<span class="hljs-number">50</span>
            focus:ring-offset-<span class="hljs-number">0</span>
            focus:ring-offset-transparent
            active:-translate-y-px
            disabled:text-opacity-<span class="hljs-number">100</span>
            disabled:opacity-<span class="hljs-number">60</span>
            disabled:active:translate-y-<span class="hljs-number">0</span>;
}
</code></pre>
<p>That's a lot of classes for a single element that is repeated many times. In this case, I have just applied the Tailwind classes and created my own component so I can simply use a class of <code>.btn</code> on anything that should be styled like a button.</p>
<p>You can even <code>@apply</code> <code>.btn</code> in another selector if you wanted, making <code>@apply</code> even more powerful. Imagine an inactive button that should look disabled, without disabling it, so it can still trigger an error message.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn-inactive</span> {
   @apply btn opacity-60;
}
</code></pre>
<h2 id="heading-wait-do-you-you-really-want-to-apply-all-the-things">Wait... Do You You <em>Really</em> Want to <code>@apply</code>  All the Things?</h2>
<p><code>@apply</code> sounds really helpful — you can clean up all those messy classes cluttering up your HTML. But you should carefully weigh whether you <em>actually</em> need to create your own CSS component classes. After all, the whole point of Tailwind is that it gives you an easy, well-documented way to use a <em>utility-first</em> approach to designing CSS.</p>
<p>I have <a target="_blank" href="https://phpprotips.com/the-pros-and-cons-of-tailwindcss">discussed the pros and cons of using Tailwind</a> and its utility-first approach <a target="_blank" href="https://phpprotips.com/the-pros-and-cons-of-tailwindcss">in another article</a>, but to summarize, there are big advantages for maintainability by using Tailwind such as:</p>
<ul>
<li>No thinking of class names</li>
<li>Less switching between CSS and front-end code</li>
<li>Easily making style changes</li>
<li>Ease of maintenance</li>
</ul>
<p>If you begin to use <code>@apply</code> all over the place, you've obviated all of those benefits where you've used them.</p>
<ul>
<li>You now have to think of names for your new components.</li>
<li>You have to agree on those names with other developers and remember them when you need that style. </li>
<li>Overriding these styles can become difficult if you need something a bit different.</li>
<li>Developers won't immediately understand what the class does, and must look up your class and figure out what it's for and where it's used if it needs changing.</li>
</ul>
<p>For these reasons, you ought to think twice before reaching for <code>@apply</code>; the majority of the time, there are better solutions. Here is a guide on when to use <code>@apply</code>.</p>
<h2 id="heading-are-you-using-a-component-based-framework">Are You Using A Component-Based Framework?</h2>
<p>If your frontend code uses components, it makes sense to use this feature in lieu of <code>@apply</code>. This way, you get to leverage the power and maintainability of utility-first design <em>and</em> combine that with the maintenance benefits and DRY-ness of extracting commonly used bits of code to components.</p>
<p>Components can even work for simple, single-elements like buttons or links, as you could have props or parameters you pass to give different styles and behaviours of buttons re-use a lot of code. But this is <em>particularly</em> true when you have more complicated components, like forms, tables, modals, etc. — this way you can combine your structural HTML with the style so that they are in one place that you go to make updates. That means that you can also build in the logic to make multiple variants. For example, you could have a "size" attribute that allows you to have large, medium, or small buttons.</p>
<p>See the following simplified example VueJS component: a radio button styled as a slider toggle.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- An input with a few size options --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-start"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"relative inline-flex"</span>
      <span class="hljs-attr">:class</span>=<span class="hljs-string">"{'h-4 w-8': size === 'sm', 'h-6 w-12': size === 'md', 'h-8 w-16': size === 'lg'}"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">:class</span>=<span class="hljs-string">"{'h-4 w-8': size === 'sm', 'h-6 w-12': size === 'md', 'h-8 w-16': size === 'lg'}"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"peer absolute cursor-pointer rounded-full border-0 !bg-none outline-none checked:border-0 bg-zinc-300"</span>
        <span class="hljs-attr">v-bind</span>=<span class="hljs-string">"$attrs"</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
        <span class="hljs-attr">:for</span>=<span class="hljs-string">"id"</span>
        <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>
        <span class="hljs-attr">:class</span>=<span class="hljs-string">"{'h-3 w-3 top-0.5 left-0.5 peer-checked:translate-x-4': size === 'sm', 'h-5 w-5 peer-checked:translate-x-6': size === 'md', 'h-7 w-7 peer-checked:translate-x-8': size === 'lg'}"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute top-0.5 left-0.5 transform cursor-pointer rounded-full bg-white shadow ring-0 duration-200"</span>
      &gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">size</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-string">'md'</span>,
    },
  },
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>We <em>could</em> have made a CSS component for the checkbox, but as this Vue component has a few HTML elements, and since my project uses it many times over, it makes sense to create a component. At this point, there will be no repetition of the used styles, so using <code>@apply</code> wouldn't make sense. Additionally, we can use Vue conditional classes to tweak several styles based on a specified size.</p>
<p>There would be many drawbacks and few advantages to <code>@apply</code>ing styles here. We can apply similar concepts to Laravel Blade components, React components, Angular, etc.</p>
<h2 id="heading-are-you-using-a-server-side-language">Are You Using a Server-Side Language?</h2>
<p>If you're not using a frontend framework like this (or Laravel), but you're just using vanilla PHP in a simple project, you could do similar extractions of components using PHP includes and such.</p>
<h2 id="heading-are-the-classes-actually-repeated-in-many-places">Are The Classes <em>Actually</em> Repeated in Many Places?</h2>
<p>In many cases, we think that we are repeating a lot of exact Tailwind classes in a lot of places, but it often turns out that either</p>
<ol>
<li>There are subtle differences in many cases between instances of this "repeated" class</li>
<li>All of the repetition occurs in one place.</li>
</ol>
<p>If there are differences, then herein lies the benefit of Tailwind - if there were differences between them, you might have to fight against a CSS component to get the changes you are looking for. In this case <code>@apply</code> isn't your friend.</p>
<p>Further, if the repetition occurs in one place or in a single file, you can use multi-cursor editing to select and edit all instances of this at once — this isn't particularly more difficult or time consuming than going back to a separate CSS file and editing a class then coming back. If you don't understand how to use multi-cursor editing, selecting several words at once, or selecting several lines at once in your IDE, you should definitely learn this. Let me know if you'd like a tutorial on this if you have no idea what I'm talking about!</p>
<p>Here's a perfect example of where multi cursor editing could allow you to adjust all these <code>&lt;img&gt;</code> elements with only a few keystrokes:</p>
<pre><code class="lang-html">  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-3 flex -space-x-2 overflow-hidden"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-block h-12 w-12 rounded-full ring-2 ring-white"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-block h-12 w-12 rounded-full ring-2 ring-white"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-block h-12 w-12 rounded-full ring-2 ring-white"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2.25&amp;w=256&amp;h=256&amp;q=80"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-block h-12 w-12 rounded-full ring-2 ring-white"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-block h-12 w-12 rounded-full ring-2 ring-white"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>/&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-3 text-sm font-medium"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-blue-500"</span>&gt;</span>+ 198 others<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In reality, most the "duplication of styles" isn't in your source code, the output of it that the browser receives. In the source code it lives as, loops in your backend or frontend code. So the apparent duplication is of little or no concern when it comes to maintaining your source code. So in these scenarios, it's  not worth using <code>@apply</code>. You may think that this could at least reduce the payload to the browser, but in reality this sort or repetitive output compresses well and the actual difference to user experience is almost unmeasurable and thus isn't worth the maintenance burden it will incur.</p>
<h2 id="heading-are-your-repeated-classes-causing-you-maintainability-problems-right-now">Are Your Repeated Classes Causing You Maintainability Problems <em>Right Now</em>?</h2>
<p>At the end of the day, if you aren't solving a problem with <code>@apply</code>, then you're probably creating problems for no reason. If your code is fine the way it is and isn't actually more work to maintain, you're best leaving it and saving the extra weight and complexity in your application by avoiding creating extra classes you don't really need.</p>
<h2 id="heading-okay-youre-really-sure-you-want-to-use-apply">Okay, You're Really Sure You Want To Use <code>@apply</code>!</h2>
<p>If you're not using a framework with components, you're not using a server-side language, you're mainly just creating a style for a single-element, that element is widely reused in exactly the same form over and over, and you're struggling with maintainability problems — then you can use <code>@apply</code> in good conscience. <em>This</em> is what it's for.</p>
<p>As a review, see this flowchart Tweeted by <a target="_blank" href="https://twitter.com/adamwathan/">Adam Wathan</a>, the creator of TailwindCSS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661211722624/b-AR476QC.jpg" alt="Should_you_Use_Apply.jpg" /></p>
<p>I hope you found this article interesting. For more content like this, <a target="_blank" href="https://twitter.com/jdlien">follow me on Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[What Is Dependency Injection?]]></title><description><![CDATA[When you start working with frameworks like Laravel or .NET, a concept you frequently come across is "dependency injection". What dependency injection amounts to is that when one thing (like a class) needs something else, like an object, it gets it.
...]]></description><link>https://webartisan.info/what-is-dependency-injection</link><guid isPermaLink="true">https://webartisan.info/what-is-dependency-injection</guid><category><![CDATA[PHP]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[SOLID principles]]></category><category><![CDATA[dependency injection]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Fri, 19 Aug 2022 15:50:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660875748443/CqFdhmTO5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you start working with frameworks like Laravel or .NET, a concept you frequently come across is "dependency injection". What dependency injection amounts to is that when one thing (like a class) needs something else, like an object, it gets it.</p>
<p>But what does this mean? How do we use it, and how does it work? Why is this even something we would care about? As I started learning MVC frameworks like Laravel, it took me a while to understand these concepts. This article gives a brief introduction to these concepts to help answer these questions.</p>
<h2 id="heading-what-is-meant-by-dependency">What Is Meant by 'Dependency'?</h2>
<p>Let's look at a real open source Laravel app called <a target="_blank" href="https://github.com/stefanzweifel/screeenly">Screenly</a> by Stefan Zweifel. It has a <code>CaptureService</code> class that takes screenshots. <code>CaptureService</code> itself needs, or 'depends on', a <code>ChromeBrowser</code> object. In this case, we say that <code>ChromeBrowser</code> is a <em>dependency</em> of <code>CaptureService</code>.</p>
<p>The first way we may think to handle this situation would be to create a new instance of the <code>ChromeBrowser</code> dependency on our own within the <code>CaptureService</code> when needed, like so:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CaptureService</span>
</span>{
    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@var</span> Screeenly\Services\Browser
     */</span>
    <span class="hljs-keyword">protected</span> $browser;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;browser = <span class="hljs-keyword">new</span> ChromeBrowser();
    }
}
</code></pre>
<p>This approach does <em>not</em> use dependency injection.  What's wrong with that?</p>
<p>This creates what we call "tightly coupled" classes, because the <code>CaptureService</code> has to handle using and creating instances of <code>ChromeBrowser</code>. There are a few problems with this:</p>
<ul>
<li>Adding new features or changing <code>ChromeBrowser</code> may require us to update <code>CaptureService</code> and any other classes that use <code>ChromeBrowser</code>.</li>
<li>We have to create a new <code>ChromeBrowser</code> instance every time we make an <code>CaptureService</code>. What if we already have one somewhere? This would be inefficient.</li>
<li>Creating automated tests is difficult because we can't test <code>CaptureService</code> without it creating an actual <code>ChromeBrowser</code>.  A problem with the latter could make the <code>CaptureService</code> impossible to test or work on.</li>
<li>What if we later want another kind of browser, like a "FirefoxBrowser"?</li>
</ul>
<p>Such tight coupling creates maintenance headaches when we later are required to make changes to <code>CaptureService</code> for updates to <code>ChromeBrowser</code>. Instead, we would prefer our classes to be 'loosely coupled'.</p>
<p>A technique we use to achieve this is <strong>dependency injection</strong>. Here's what it looks like to rewrite the above example with the use of dependency injection (which is how the real project works):</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Screeenly</span>\<span class="hljs-title">Contracts</span>\<span class="hljs-title">CanCaptureScreenshot</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CaptureService</span>
</span>{
    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@var</span> Screeenly\Services\Browser
     */</span>
    <span class="hljs-keyword">protected</span> $browser;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">CanCaptureScreenshot $browser</span>)
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;browser = $browser;
    }
}
</code></pre>
<p>Above, we type-hinted the <code>$browser</code> with the <code>CanCaptureScreenshot</code> <em>interface</em> (not a real class type)  and set it as a parameter for the constructor. This is enough to tell the framework to create an instance of the type of browser we want, such as <code>ChromeBrowser</code>.</p>
<p>Laravel's <strong>service container</strong> contains the smarts (via a PHP feature called <em>reflection</em>) to understand how to create an instance of <code>ChromeBrowser</code> with just this information. An instance of a class that fulfills this <code>CanCaptureScreenshot</code> contract (that is, a browser) is then automatically resolved and 'injected' into the <code>__construct</code> constructor method.</p>
<h2 id="heading-dependency-inversion">Dependency Inversion</h2>
<p>Dependency injection is a key way we use the concept of "Dependency Inversion" (the D in the "SOLID" programming principles).</p>
<p>The key points of dependency inversion are these:</p>
<ul>
<li>High-level modules should not depend on low-level modules</li>
<li>Both should depend on abstractions (interfaces)</li>
<li>Abstractions should not depend on details</li>
</ul>
<p>Going back to our Screenly example, note that <code>$browser</code> is dynamic, as it is type hinted as the <code>CanCaptureScreenshot</code> interface which requires the <code>capture</code> method:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Screeenly</span>\<span class="hljs-title">Contracts</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Screeenly</span>\<span class="hljs-title">Entities</span>\<span class="hljs-title">Url</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">CanCaptureScreenshot</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capture</span>(<span class="hljs-params">Url $url, $storageUrl</span>)</span>;
}
</code></pre>
<p>There are multiple classes that <em>implement</em> the <code>CanCaptureScreenshot</code> interface: <code>ChromeBrowser</code> and <code>InMemoryBrowser</code>. So by type-hinting the <em>interface</em>, we can actually potentially use either of these classes within the <code>CaptureService</code>. Other browsers could potentially be added in the future that use this interface.</p>
<p>Laravel knows <em>how</em> to implement this interface, and what class to create because a <em>service provider</em> is configured that tells Laravel about this. See the following ScreenlyServiceProvider class</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ScreeenlyServiceProvider</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ServiceProvider</span>
</span>{

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">boot</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;app[<span class="hljs-string">'view'</span>]-&gt;addNamespace(<span class="hljs-string">'screeenly'</span>, base_path().<span class="hljs-string">'/modules/Screeenly/Resources/views'</span>);

        <span class="hljs-comment">// Note that this binds ChromeBrowser to the CanCaptureScreenshot Interface</span>
        <span class="hljs-keyword">$this</span>-&gt;app-&gt;bind(CanCaptureScreenshot::class, ChromeBrowser::class);

        auth()-&gt;extend(<span class="hljs-string">'screeenly-token'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">$app, $name, <span class="hljs-keyword">array</span> $config</span>) </span>{
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ScreeenlyTokenGuard(
                auth()-&gt;createUserProvider($config[<span class="hljs-string">'provider'</span>]),
                <span class="hljs-keyword">$this</span>-&gt;app[<span class="hljs-string">'request'</span>]
            );
        });
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">register</span>(<span class="hljs-params"></span>)
    </span>{
    }
}
</code></pre>
<p>We can also observe that, for purposes of testing, we don't necessarily want to use <code>ChromeBrowser</code> so we can see that in the API test suite, this same interface is resolved with a <em>different</em> class, the <code>InMemoryBrowser</code> class.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiV2ScreenshotTest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">BrowserKitTestCase</span>
</span>{
    <span class="hljs-comment">/** <span class="hljs-doctag">@test</span> */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_returns_base64_representation_of_screenshot</span>(<span class="hljs-params"></span>)
    </span>{
        Storage::fake(config(<span class="hljs-string">'screeenly.filesystem_disk'</span>));

        Storage::disk(config(<span class="hljs-string">'screeenly.filesystem_disk'</span>))-&gt;put(<span class="hljs-string">'test-screenshot.jpg'</span>, file_get_contents(storage_path(<span class="hljs-string">'testing/test-screenshot.jpg'</span>)));

        $apiKey = ApiKey::factory()-&gt;create();

        <span class="hljs-comment">// Note that for testing we bind a *different* class to the CanCaptureScreenshot interface</span>
        <span class="hljs-keyword">$this</span>-&gt;app-&gt;bind(CanCaptureScreenshot::class, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">$app</span>) </span>{
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> InMemoryBrowser(<span class="hljs-string">'http://foo.bar'</span>, <span class="hljs-string">'/path/to/storage'</span>);
        });

        <span class="hljs-keyword">$this</span>-&gt;json(<span class="hljs-string">'POST'</span>, <span class="hljs-string">'/api/v2/screenshot'</span>, [
            <span class="hljs-string">'key'</span> =&gt; $apiKey-&gt;key,
            <span class="hljs-string">'url'</span> =&gt; <span class="hljs-string">'http://google.com'</span>,
        ])
        -&gt;seeJsonStructure([
            <span class="hljs-string">'data'</span> =&gt; [
                <span class="hljs-string">'path'</span>, <span class="hljs-string">'base64'</span>,
            ],
        ]);
    }
}
</code></pre>
<p>The above demonstrates that by using an <em>interface</em>, we have the flexibility to free a class from needing to know anything at all about how that interface is implemented, and this allows for easier testing and more maintainable code.</p>
<h2 id="heading-inversion-of-control">Inversion of Control</h2>
<p>Dependency injection is a specific implementation of a more general concept called inversion of control (IoC), which has to do with whether it is <em>us (via our own code)</em> or the <em>framework</em> that decides when to create classes, run methods, and such.</p>
<p>In procedural code, the code works through a series of operations in order, and the code fully controls when functions are called. This is inflexible and trickier to maintain as you end up with big chunks of tightly coupled code.</p>
<p>A benefit of a framework like Laravel, (or also a GUI framework like for desktop apps) is that the framework can decide when and how to create classes and run the functions (or methods) contained within them when needed. This is the primary job of the service container. The control is "inverted" to the framework, which allows your code to focus on the important responsibilities, making your classes more focused and easier to maintain.</p>
<p>It's like if you had a chauffeur drive you to work, rather than you operating every aspect of your car yourself. It frees your mind for something you'd rather focus on.</p>
<p>IoC is one way in which software is made more modular and extensible. Laravel makes it so easy to use dependency injection just by type hinting variables passed into class methods which will either create the objects or use existing "singleton objects" (of which there can be only one).</p>
<p>I hope this introduction to the concepts of dependency injection and inversion of control has been helpful. Feel free to ask questions in the comments. And if you enjoy this kind of content, you can <a target="_blank" href="https://twitter.com/jdlien">follow me on Twitter at @jdlien</a>. </p>
]]></content:encoded></item><item><title><![CDATA[When You Get So Close...]]></title><description><![CDATA[I used to think I was great at interviews. I was trained on them as a teenager participating in (and winning) scholarships and contests. I usually come across as likeable — and I've gotten almost every job I've seriously interviewed for.
Except for t...]]></description><link>https://webartisan.info/when-you-get-so-close</link><guid isPermaLink="true">https://webartisan.info/when-you-get-so-close</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[week1]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Tue, 16 Aug 2022 07:13:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660625936729/YCa4ZMSBj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I used to think I was great at interviews. I was trained on them as a teenager participating in (and winning) scholarships and contests. I usually come across as likeable — and I've gotten almost every job I've seriously interviewed for.</p>
<p>Except for this one.</p>
<p>This is the story of my worst interview failure and the lessons I learned from the experience. By reading this, I hope you can learn from my mistakes and do much better than I did in your next interview.</p>
<p> </p>
<p>While working full-time as a developer, I completed a computer science degree. As I finished, an opportunity for exactly the kind of job I wanted fell into my lap. It was for a company that worked with Laravel, which is a framework I love. It seemed like the stars were aligned — and I was finally about to make the big career jump I had wanted!</p>
<p>I submitted my résumé and cover letter to the company. Shortly after, I was informed that they wanted to do a short introductory interview with me for us to get to know each other.</p>
<p>This went well, and I had a lovely conversation. Eventually an email came back saying that they wanted me to interview over Zoom with the owner of the company, Norman!</p>
<p>This is where things really went off the rails for me, I'm afraid. What follows is a list of things <strong>not</strong> to do in an interview based off this experience.</p>
<p>I first figured that I'd establish rapport by explaining my familiarity with the company.</p>
<p><em>Me:</em> "So I saw your company's promo video."</p>
<p><em>Norman:</em> "Oh yeah? What did you think?"</p>
<p><em>Me:</em> "It was fun, but pretty <em>goofy</em>."</p>
<p><em>Norman:</em> "Oh? Goofy, hey? Yeah, I guess..."</p>
<p>I quickly realized that this was a... suboptimal choice of words.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660627579794/4FwSDd9Hw.png" alt="goofy_crop.png" /></p>
<h3 id="heading-interview-pro-tip-1-do-not-insult-the-interviewers-work-within-the-first-five-minutes-of-the-interview">Interview Pro Tip 1: Do not insult the interviewer's work within the first five minutes of the interview.</h3>
<p> </p>
<p>Next, we went on to discuss the company and what they do which went well enough. Then we got to talking about my experience. As we discussed my specific skills with Laravel and some of the frameworks they work with, I kept reminding him that I'm not very experienced and mentioning things that I didn't know very well.</p>
<p>"I'm an experienced developer but I only really have a year of experience working with Laravel, and I definitely don't know as much as the people you have working for you already."</p>
<p>I actually was more skilled and knowledgeable with Laravel than I was probably making myself sound. I had learned a lot and done some complex projects with it by that point. What was I thinking?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660627911329/iFQ1c26tM.gif" alt="rambling.gif" /></p>
<h3 id="heading-interview-pro-tip-2-do-not-undersell-yourself-or-point-out-your-shortcomings-unprompted">Interview Pro Tip 2: Do not undersell yourself or point out your shortcomings unprompted.</h3>
<p> </p>
<p>We then got to the standard interview questions. These are some of the easier questions to prepare for because they are quite common.</p>
<p>Norman asked "What is the hardest thing you've ever worked on and how did you handle that?"</p>
<p>I was stumped. I sat there for what felt like an entire minute in awkward silence, my mind racing as I began to perspire. Finally, I rambled off some obscure and probably insignificant details of a caching system I had built years ago, which was probably not a good example and not particularly relevant to the work this company does. Norman seemed somewhat unsatisfied.</p>
<p>Instead, I should have had a list of good talking points like this prepared in advance. A list of things that were really challenging that led to euphoric moments when I conquered them, resulting in notable advances in my abilities as a developer. When interviewers ask this sort of question, they want to find out how you solve problems, how you perform under pressure, and to get an idea of what you are capable of at your best.</p>
<p>My response offered little or none of this. I should have been better prepared.</p>
<h3 id="heading-interview-pro-tip-3-do-not-fail-to-prepare-for-common-questions-do-satisfy-the-interviewers-reasons-for-asking">Interview Pro Tip 3: Do not fail to prepare for common questions. Do satisfy the interviewer's reasons for asking.</h3>
<p> </p>
<p>Because I realized I was failing hard, I thought I could fall back on demonstrating some of my work. Demoing a project you have worked on is very common in interviews.</p>
<p>"How about I show you something I built?" I said.</p>
<p><em>Norman:</em> "Yeah, sure."</p>
<p><em>Me:</em> (<em>clicking frantically, realizing that my computer hasn't granted Zoom permission to share my screen</em>) "Oh, uh... I'm getting an error. It says I don't have permission to share my screen. I'd have to hang up and call back after restarting Zoom. I don't use this very often!"</p>
<p><em>Norman:</em> "It's okay, let's just move on."</p>
<p>I should have tested this before the interview, even calling a friend and checking everything out. This just made me look incompetent at using a computer.</p>
<h3 id="heading-interview-pro-tip-4-do-not-start-an-interview-without-testing-your-software-and-ensuring-everything-works">Interview Pro Tip 4: Do not start an interview without testing your software and ensuring everything works.</h3>
<p> </p>
<p>Soon after this we got to everyone's favorite awkward part of the interview. The salary negotiation! "How much are you looking to make if you are hired?"</p>
<p>Me: "Well, I'm really more interested in the opportunity to learn than the money but I guess I'd like to make more than I make where I work now." I began, as I rambled on.</p>
<p>I continued to list a few numbers that I thought I'd be satisfied with, all of which were probably a bit lower than they should have been.</p>
<p>This likely devalued me in the interviewer's eyes. I should have just named a number that was significantly higher than what I made before, and that is commensurate with what the industry pays based on my research. If you're a bit too high they will simply offer you less. If you're way, way too high you might seem like you don't have a clue what you're doing.</p>
<h3 id="heading-interview-pro-tip-5-do-not-beat-around-the-bush-with-salary-just-name-a-salary-higher-than-what-youd-be-happy-with">Interview Pro Tip 5: Do not beat around the bush with salary. Just name a salary higher than what you'd be happy with.</h3>
<p> </p>
<p>As the interview closed out, Norman asked "Where are you at in your job search right now?"</p>
<p>I responded "I'm not really looking that much right now, I just thought you'd be an interesting company to work for."</p>
<p>Yikes, what a terrible way to answer! I made it sound like I'm not actually serious about taking on a new job and that I'm not actively about to be scooped up by anyone else. This would have been a perfect time to take a completely different approach to answering this question by applying scarcity principle and social proof to put some pressure on the interviewer.</p>
<p>I should have answered something like "I've found some interesting prospects and I'm evaluating my options. I hope to make a decision by the end of next month". That would have</p>
<ol>
<li>Made me seem like I'm serious about taking a new job.</li>
<li>Made it sound like I'm actually desired by other companies.</li>
<li>Put a set time on the interviewer's decision-making, spurring him to get back to me.</li>
</ol>
<p>I did none of these things and may have just put the nail in the coffin of that interview.</p>
<h3 id="heading-interview-pro-tip-6-do-not-make-it-seem-like-you-arent-serious-about-the-job-or-that-no-one-else-is-interested-in-you">Interview Pro Tip 6: Do not make it seem like you aren't serious about the job or that no one else is interested in you.</h3>
<p> </p>
<p>We concluded the interview with traditional pleasantries and it was over. I reflected on some of what had just transpired, thinking about how some of that could have gone better.</p>
<p>Now, despite the tone and focus of this article, it wasn't actually a <em>bad</em> interview. It was pretty good overall, but for all I know there may have been some stiff competition. And who knows <em>why</em> exactly they never ended up offering me a job. Well, actually, you know who knows? The guy that interviewed me. But <em>I</em> don't. Because I never followed up. And I never asked him. And I never heard from that company again.</p>
<p>If you're interested in the job, it behooves you to follow up 2–5 days later if you haven't heard back. In many cases, taking such initiative is exactly what is needed to make you seem like you're seriously interested. I've heard of interviewers who don't even hire people who don't follow up after.</p>
<p>If you reach out and they inform you that you didn't get the job, you can (and should) ask why not, and what you could have done better. Most people want you to be successful, so interviewers are often happy to give you some detail. This feedback could be invaluable for your next interview.</p>
<h3 id="heading-interview-pro-tip-7-do-not-forget-to-follow-up-with-the-company-a-few-days-after-the-interview">Interview Pro Tip 7: Do not forget to follow up with the company a few days after the interview.</h3>
<p> </p>
<p>If you're on the hunt for a new job, I hope you've found some useful advice in this article when it comes to doing your interview. It sucks to have to learn these sorts of lessons the hard way, but in the end, you can become better with each of your failures. I hope my failed interview helps you ace yours.</p>
<p>If you liked this article, consider <a target="_blank" href="https://twitter.com/jdlien">following me on Twitter</a>.</p>
]]></content:encoded></item><item><title><![CDATA[How I Made My '80s Retro-Style Homepage - Part 2]]></title><description><![CDATA[In Part 1 of this series we learned how to create a glowing CRT monitor effect. In this part, we'll use JavaScript to make an interactive terminal that simulates a slow command line interface.
This will be a good review of several essential concepts ...]]></description><link>https://webartisan.info/how-i-made-my-80s-retro-style-homepage-part-2</link><guid isPermaLink="true">https://webartisan.info/how-i-made-my-80s-retro-style-homepage-part-2</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Sun, 07 Aug 2022 21:39:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659903874813/uuYMLXLnK.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target="_blank" href="https://webartisan.info/how-i-made-my-80s-retro-style-homepage-part-1">Part 1</a> of this series we learned how to create a glowing CRT monitor effect. In this part, we'll use JavaScript to make an interactive terminal that simulates a slow command line interface.</p>
<p>This will be a good review of several essential concepts in JavaScript, because to understand this, we must learn the following concepts:</p>
<ul>
<li>Asynchronous programming using promises and async/await</li>
<li>Immediately invoked function expressions</li>
<li>Manipulating HTML element contents</li>
<li>Switch statements</li>
<li>Event listeners</li>
</ul>
<p>To continue where we left off in part 1, borrow the code from the <a target="_blank" href="https://codepen.io/jdlien/pen/yLKoGZp">Part 1 Codepen</a> to get the CSS and some HTML boilerplate.</p>
<h2 id="heading-slowly-writing-text-to-the-screen">Slowly Writing Text to The Screen</h2>
<p>Let's create a function called <code>writeText</code> that can take a string write it to a target element with a specified delay between characters.</p>
<p>We'll use a default delay of 10ms between characters.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">writeText</span>(<span class="hljs-params">target, content, delay = <span class="hljs-number">10</span></span>) </span>{

}
</code></pre>
<p>We use <code>split</code> to turn the string into an array (separated by nothing so each letter is its own element). And we create a counter variable, <code>current</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> contentArray = content.split(<span class="hljs-string">''</span>);
<span class="hljs-keyword">let</span> current = <span class="hljs-number">0</span>
</code></pre>
<p>Now we loop through the contentArray, incrementing the counter until we have gone through all the characters. Inside the loop the main thing we need to do is call <code>setTimeout</code> to have it draw each character after a progressively increasing delay.</p>
<p>But we can't just call <code>setTimeout</code> a bunch of times, because by the time any of the <code>setTimeouts</code> run, the counter will already be at the maximum value and nothing will work! It would just write "undefined" to the screen many times after a long pause.</p>
<p>We can solve this by using an IIFE (immediately invoked function expression). This looks a lot like a normal function except it doesn’t need a name and is enclosed in parentheses and will execute as soon as it is declared.  This allows us to pass an argument to setTimeout without the value of that argument changing, because it has its own internal, separate scope.</p>
<p>Note: If you don't end JS lines with semicolons, beginning a line with a parenthesis can cause issues, so I add a semicolon before a line starting with a parenthesis or square bracket.</p>
<p>The arrow function used as the first parameter to <code>setTimeout</code> below adds one character at a time to the HTML content of the target to build it up. And the time delay of each setTimeout will be incremented by the specified delay. This gives the effect of 'writing in' all the characters at a slow, consistent speed.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">while</span> (current &lt; contentArray.length) {
  ;(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">curr</span>) </span>{
    <span class="hljs-built_in">setTimeout</span>(
      <span class="hljs-function">() =&gt;</span> {target.innerHTML += contentArray[curr]},
      delay * curr <span class="hljs-comment">// increase delay with each iteration</span>
    )
  })(current++)
}
</code></pre>
<p>This should work to slowly write to the screen.</p>
<p>If we are doing anything more than writing text once, we will want way to run a series of these commands in sequence, and then be able to do something else when we are done. This is a perfect use case for promises.</p>
<p>When we use a promise, we are returning a "promise" of something happening at a later time, rather than anything concrete. This "later time" is when the promise's <code>resolve</code> method is executed, which we can call when the last character is written.</p>
<p>Let's revise the writeText method to include this call. While we are at it, we can switch our IIFE with an arrow function, and use <code>scrollTo</code> to ensure that the scrolling text is always in view:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Write text to a target element with a specified delay in ms</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">writeText</span>(<span class="hljs-params">target, content, delay = <span class="hljs-number">5</span></span>)
</span>{
  <span class="hljs-comment">// Loop through array of content characters</span>
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve</span>) =&gt;</span> {
    <span class="hljs-comment">// Make an array of the specified content</span>
    <span class="hljs-keyword">const</span> contentArray = content.split(<span class="hljs-string">''</span>)

    <span class="hljs-comment">// Keep track of the character currently being written</span>
    <span class="hljs-keyword">let</span> current = <span class="hljs-number">0</span>

    <span class="hljs-keyword">while</span> (current &lt; contentArray.length) {
      ;(<span class="hljs-function">(<span class="hljs-params">curr</span>) =&gt;</span> {
        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
          target.innerHTML += contentArray[curr]
          <span class="hljs-comment">// Scroll to the bottom of the screen unless scroll is false</span>
          <span class="hljs-built_in">window</span>.scrollTo(<span class="hljs-number">0</span>, <span class="hljs-built_in">document</span>.body.scrollHeight)

          <span class="hljs-comment">// Resolve the promise once the last character is written</span>
          <span class="hljs-keyword">if</span> (curr === contentArray.length - <span class="hljs-number">1</span>) resolve()
        }, delay * curr) <span class="hljs-comment">// increase delay with each iteration</span>
      })(current++)
    }
  })
}
</code></pre>
<p>Excellent! Now <code>writeText</code> returns a promise, which allows you to chain calls to writeText using the Promise <code>then()</code> method. To make code even more readable, we shall create an <code>async</code> function and then simply invoke calls to <code>writeText</code> one after the other by preceding the calls with <code>await</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">writeStuff</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">await</span> writeText(element1, <span class="hljs-string">'some text'</span>)
  <span class="hljs-keyword">await</span> writeText(element2, <span class="hljs-string">'more text'</span>)
  <span class="hljs-comment">// Anything else you want to happen after this</span>
}
</code></pre>
<h2 id="heading-setting-up-the-command-prompt">Setting up the command prompt</h2>
<p>Let's create some boilerplate HTML that we can use to create our command prompt. We will have a 'banner' section with some ASCII art, a section for instructions, a section that output is written to, and a prompt. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"asciiText"</span>&gt;</span>
  _    _      _ _        __          __        _     _ 
 | |  | |    | | |       \ \        / /       | |   | |
 | |__| | ___| | | ___    \ \  /\  / /__  _ __| | __| |
 |  __  |/ _ \ | |/ _ \    \ \/  \/ / _ \| '__| |/ _` |
 | |  | |  __/ | | (_) |    \  /\  / (_) | |  | | (_| |
 |_|  |_|\___|_|_|\___/      \/  \/ \___/|_|  |_|\__,_|
  <span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"instructions"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"output"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"prompt"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"command-input"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"cursor"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blink"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>The <code>command-input</code> span above will be where the user's keystrokes will show up and the contents of this will be interpreted as a command that we can run.</p>
<p>To get this working, we'll have to listen for keystrokes on the <code>keydown</code> event, then run a <code>handleKeypress</code> function when a keystroke is found. We will pass input and  output elements as arguments to this function.</p>
<p>The first thing we'll need is to check which keystroke was entered, as we want to print the entered key to the screen <em>unless</em> it was enter or backspace. We handle those differently.</p>
<p>If the user presses enter, we want to save the command, then execute it, then we can use <code>writeText</code> to output it.</p>
<p>Else if the user presses backspace, we want to erase the last typed in character.</p>
<p>Finally, for any other key, we just print the keystroke to our 'input' span.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleKeypress</span>(<span class="hljs-params">e, input, output</span>)
</span>{ 
    <span class="hljs-comment">// Enter clears the input and executes the command</span>
    <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">'Enter'</span>) {
      <span class="hljs-keyword">const</span> command = input.innerText
      input.innerHTML = <span class="hljs-string">''</span>
      <span class="hljs-comment">// reprint the entered command</span>
      output.innerHTML += <span class="hljs-string">'&lt;br&gt;&lt;strong&gt;'</span> + command + <span class="hljs-string">'&lt;/strong&gt;&lt;br&gt;'</span>
      writeText(output, execute(command))
    }

    <span class="hljs-comment">// Backspace causes last character to be erased</span>
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">'Backspace'</span>) {
      input.innerHTML = input.innerHTML.substring(<span class="hljs-number">0</span>, input.innerHTML.length - <span class="hljs-number">1</span>)
    }

    <span class="hljs-comment">// For any other key, print the keystroke to the prompt</span>
    <span class="hljs-keyword">else</span> input.insertAdjacentText(<span class="hljs-string">'beforeend'</span>, e.key)

  <span class="hljs-comment">// Accept a command, execute it, and return any output</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">execute</span>(<span class="hljs-params">command</span>)
  </span>{
    <span class="hljs-keyword">switch</span>(command) {
      <span class="hljs-keyword">case</span> <span class="hljs-string">'test'</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">'Test successful!'</span>

      <span class="hljs-attr">default</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">'Unknown command'</span>
    }
  }
}
</code></pre>
<p>Note that our function includes another function that can contain a list of commands: we use a switch block that allows us to add any commands we want. We can also perform any other operations within each <code>case</code> that can be performed before returning the output text.</p>
<h2 id="heading-putting-it-all-together">Putting It All Together</h2>
<p>To put together the final program, we can create an eventListener for the <code>DOMContentLoaded</code> event so a function will be run when the page is loaded. This function will draw text to the screen, then add another event listener to the document for the <code>keydown</code> event that invokes <code>handleKeypress</code>. This would look like so:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Execute page loading asynchronously once content has loaded</span>
<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'DOMContentLoaded'</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> instructions = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'instructions'</span>)
  <span class="hljs-keyword">const</span> prompt = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'prompt'</span>)
  <span class="hljs-keyword">const</span> cursor = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'cursor'</span>)  

  <span class="hljs-keyword">await</span> writeText(instructions, <span class="hljs-string">'Enter a command'</span>)
  prompt.prepend(<span class="hljs-string">'&gt;'</span>)
  cursor.innerHTML = <span class="hljs-string">'_'</span>

  <span class="hljs-keyword">const</span> input = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'command-input'</span>)
  <span class="hljs-keyword">const</span> output = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'output'</span>)
  <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'keydown'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> handleKeypress(e, input, output))
})
</code></pre>
<p>By adding more <code>case</code>s to the <code>switch</code> block, you can make a retro-looking command line interface that accepts a number of commands and allows the user to interact with it.</p>
<p>To see a full, working example, check out my Codepen below.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/jdlien/pen/VwXXEgV">https://codepen.io/jdlien/pen/VwXXEgV</a></div>
<p>By tweaking this program, you should be able to make a fun and unique command line application on a webpage that has some personality and fun elements. </p>
<p>If you found this article interesting and educational, <a target="_blank" href="https://twitter.com/jdlien">follow me, @jdlien, on Twitter</a> for more content like this!</p>
]]></content:encoded></item><item><title><![CDATA[How I Made My '80s Retro-Style Homepage - Part 1]]></title><description><![CDATA[I was a kid fascinated by technology and computers in the 1980s. Part of me loves that era where computers held promise that was understood by few. I envied the hackers depicted in films, shrouded in darkness, oily faces illuminated with the flickeri...]]></description><link>https://webartisan.info/how-i-made-my-80s-retro-style-homepage-part-1</link><guid isPermaLink="true">https://webartisan.info/how-i-made-my-80s-retro-style-homepage-part-1</guid><category><![CDATA[CSS]]></category><category><![CDATA[retro]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Tue, 26 Jul 2022 06:05:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658807218016/HZiYeiAS3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was a kid fascinated by technology and computers in the 1980s. Part of me loves that era where computers held promise that was understood by few. I envied the hackers depicted in films, shrouded in darkness, oily faces illuminated with the flickering green glow of a primitive CRT monitor. The sound of modems screeching into telephone lines, as letters slowly stream down the screen from a faraway server they had no right to be in.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658806968452/Sb3NgCEAV.jpeg" alt="David Lightman dialing a remote server in the 1983 film &quot;WarGames&quot;" />
<em>David Lightman dialing a remote server in the 1983 film "WarGames"</em></p>
<h2 id="heading-ascii-art">ASCII Art</h2>
<p>One day I stumbled upon a neat <a target="_blank" href="https://www.coolgenerator.com/ascii-text-generator">ASCII text generator</a>, and thought it would be fun to make something with it. I love ASCII art from the days before photographs could be displayed on most computers. Some people put so much effort into creating fancy letters and images using only simple letters and characters. I also wanted to couple this with an '80s retro aesthetic.</p>
<p>To get started, we could simply copy the text and put it into a <code>&lt;pre&gt;</code> tag to preserve the line breaks, but we want to give it a "glowing" look, like the phosphors on an old, low resolution CRT.</p>
<h2 id="heading-glowing-effect">Glowing Effect</h2>
<p>Fun fact: In the early days of computers, green phosphor was often used for monitors because it had the longest afterglow, and the human eye is the most sensitive to that color, so it appeared bright. It was cheaper this way!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658808187898/AqfncfTZl.jpeg" alt="green_crt.jpeg" />
<em>This is the basic look we are going for</em></p>
<p>To start, we can select a green color from the above image. To make it look bright and glowing, we will use a slightly lighter color than what we are going for.</p>
<p>When using colors in CSS, it's convenient to use the 'hsl' model because it means we can easily tweak the hue, saturation, and lightness of the color separately, so we can easily make different shades of the same color without complex math. For the body Let's try <code>color: hsl(154 84% 70%);</code>, then make a darker, less saturated shade of that green for the background, <code>background: hsl(154 50% 5%);</code>.</p>
<p>Now to give it the characteristic fuzzy glow of green phosphor, we can add <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow">text-shadow</a> with 0 x and y offsets, a 5px blur-radius, and the same color we use for text. You can tweak the intensity of the glow by using more lightness or changing the blur radius.</p>
<p><code>text-shadow: 0 0 5px hsl(154, 84%, 70%);</code></p>
<p>Here's what we have so far. Not bad!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658842814064/kJidgMFpP.png" alt="glowing green ASCII-art letters spelling &quot;Hello World&quot;" /></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
  <span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">50%</span> <span class="hljs-number">5%</span>);
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">84%</span> <span class="hljs-number">70%</span>);
    <span class="hljs-attribute">text-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">5px</span> <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">84%</span> <span class="hljs-number">70%</span>);
  }  
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">pre</span>&gt;</span>
  _    _      _ _        __          __        _     _ 
 | |  | |    | | |       \ \        / /       | |   | |
 | |__| | ___| | | ___    \ \  /\  / /__  _ __| | __| |
 |  __  |/ _ \ | |/ _ \    \ \/  \/ / _ \| '__| |/ _` |
 | |  | |  __/ | | (_) |    \  /\  / (_) | |  | | (_| |
 |_|  |_|\___|_|_|\___/      \/  \/ \___/|_|  |_|\__,_|
<span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<h2 id="heading-curve-and-glare">Curve and Glare</h2>
<p>Next, we can give our background the illusion of a curvalicious CRT monitor with a bit of glare by using a radial gradient in the middle. For this, we'll create a <code>position: fixed</code> div that takes up the entire viewport by setting <code>top</code>, <code>right</code>, <code>bottom</code>, and <code>left</code> all to zero.</p>
<p>For the radial gradient, you can use a lighter, less saturated version of the color in the middle at 0%, and end with our dark green background color a little ways from the edge around 70%.</p>
<p><code>background-image: radial-gradient(hsl(154 5% 15%) 0%, hsl(154 50% 5%) 70%);</code></p>
<p>Finally, ensure you use <code>z-index: -1;</code> so that the body text appears above this effect.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658843304780/d3urlc8-M.png" alt="Radial gradient that looks like glare off a curved screen" /></p>
<pre><code class="lang-css"><span class="hljs-comment">/* The curvy screen glare effect */</span>
  <span class="hljs-selector-id">#glare</span> {
    <span class="hljs-attribute">position</span>: fixed;
    <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">right</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>; <span class="hljs-comment">/* ensure the effect doesn't cover the text */</span>
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">radial-gradient</span>(hsl(<span class="hljs-number">154</span> <span class="hljs-number">5%</span> <span class="hljs-number">15%</span>) <span class="hljs-number">0%</span>, <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">50%</span> <span class="hljs-number">5%</span>) <span class="hljs-number">70%</span>);
  }
</code></pre>
<h2 id="heading-low-resolution-effect">Low-Resolution Effect</h2>
<p>Next we try to simulate the low-resolution look of an old, interlaced monitor (interlacing is where the cathode ray draws every other line with each pass). We create another full-screen div as with the glare effect, except this time it must be above the text, so it gets a <code>z-index</code> of 1. To ensure that this doesn't interfere with clicking other elements on the screen, we also add <code>pointer-events: none;</code>.</p>
<p>We add <code>repeating-linear-gradient</code> that is transparent for two pixels, then a translucent black for the next two pixels, like so:</p>
<p><code>background: repeating-linear-gradient(transparent 0px 1px, hsl(154 0% 0%/.35) 3px 4px);</code></p>
<h2 id="heading-flicker-effect">Flicker Effect</h2>
<p>To add a flickery interlacing effect that looks all kinds of glitchy (or possibly seizure inducing when taken to an extreme), use CSS animation to move the background 2 pixels up and down rapidly. Repeat this every 1/30 of a second to emulate a typical CRT at '60i' (60 Hz interlaced). (Modern LCDs usually update at 60Hz, so this should update twice with every real refresh).</p>
<p>Depending on the exact colors you use, the effect may not be very noticeable. This may also look quite different depending on the resolution and refresh rate of the display you are viewing. You can tweak the effect by decreasing the opacity of the dark (non-transparent) color in the <code>repeating-linear-gradient</code> or, alternatively, try using a 1/15s cycle (0.66666666s). (This will look very flickery and not be conducive to long-form reading.)</p>
<pre><code class="lang-css"><span class="hljs-comment">/* low-resolution screen overlay interlacing */</span>
<span class="hljs-keyword">@keyframes</span> lines {
  0% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span>}
  50% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span>}
  51% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">2px</span>}
  100% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">2px</span>}
}

<span class="hljs-selector-id">#interlaced</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">repeating-linear-gradient</span>(transparent <span class="hljs-number">0px</span> <span class="hljs-number">1px</span>, hsl(<span class="hljs-number">154</span> <span class="hljs-number">0%</span> <span class="hljs-number">0%</span>/.<span class="hljs-number">3</span>) <span class="hljs-number">3px</span> <span class="hljs-number">4px</span>);
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">pointer-events</span>: none;
  <span class="hljs-attribute">animation</span>: lines <span class="hljs-number">0.066666666s</span> linear infinite;
}
</code></pre>
<h2 id="heading-blinking-command-prompt">Blinking Command Prompt</h2>
<p>For the last retro computer detail this tutorial covers, we can create a command prompt using <code>&amp;gt;</code> and an underscore. We'll create one more <code>@keyframes</code> for a blink effect and apply it to the cursor, repeating infinitely every 0.4s.</p>
<pre><code class="lang-css">  <span class="hljs-keyword">@keyframes</span> blink {
    0% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>}
    30% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>}
    70% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>}
    100% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>}
  }

  <span class="hljs-selector-class">.blink</span> {
    <span class="hljs-attribute">animation</span>: blink <span class="hljs-number">0.4s</span> linear infinite;
  }
</code></pre>
<h2 id="heading-the-finished-product">The Finished Product</h2>
<p>Check out the following CodePen to see what this demo looks like so far:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/jdlien/pen/yLKoGZp">https://codepen.io/jdlien/pen/yLKoGZp</a></div>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
  <span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">50%</span> <span class="hljs-number">5%</span>);
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">84%</span> <span class="hljs-number">70%</span>);
    <span class="hljs-attribute">text-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">4px</span> <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">84%</span> <span class="hljs-number">70%</span>);
    <span class="hljs-attribute">font-family</span>: monospace;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
  }

  <span class="hljs-selector-tag">pre</span> {
    <span class="hljs-attribute">margin</span>: auto;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10vh</span>;
    <span class="hljs-attribute">display</span>: table;
  }

  <span class="hljs-selector-id">#glare</span> {
    <span class="hljs-attribute">position</span>: fixed;
    <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">right</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>; <span class="hljs-comment">/* ensure the effect doesn't cover the text */</span>
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">radial-gradient</span>(hsl(<span class="hljs-number">154</span> <span class="hljs-number">5%</span> <span class="hljs-number">15%</span>) <span class="hljs-number">0%</span>, <span class="hljs-built_in">hsl</span>(<span class="hljs-number">154</span> <span class="hljs-number">50%</span> <span class="hljs-number">5%</span>) <span class="hljs-number">70%</span>);
  }

  <span class="hljs-keyword">@keyframes</span> lines {
    0% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span>}
    50% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span>}
    51% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">2px</span>}
    100% {<span class="hljs-attribute">background-position</span>: <span class="hljs-number">0px</span> <span class="hljs-number">2px</span>}
  }

  <span class="hljs-selector-id">#interlaced</span> {
    <span class="hljs-attribute">position</span>: fixed;
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">repeating-linear-gradient</span>(transparent <span class="hljs-number">0px</span> <span class="hljs-number">1px</span>, hsl(<span class="hljs-number">154</span> <span class="hljs-number">0%</span> <span class="hljs-number">0%</span>/.<span class="hljs-number">3</span>) <span class="hljs-number">3px</span> <span class="hljs-number">4px</span>);
    <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">right</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">z-index</span>: <span class="hljs-number">10</span>;
    <span class="hljs-attribute">pointer-events</span>: none;
    <span class="hljs-attribute">animation</span>: lines <span class="hljs-number">0.066666666s</span> linear infinite;
  }

  <span class="hljs-keyword">@keyframes</span> blink {
    0% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>}
    30% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>}
    70% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>}
    100% {<span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>}
  }

  <span class="hljs-selector-class">.blink</span> {
    <span class="hljs-attribute">animation</span>: blink <span class="hljs-number">0.4s</span> linear infinite;
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">pre</span>&gt;</span>
  _    _      _ _        __          __        _     _ 
 | |  | |    | | |       \ \        / /       | |   | |
 | |__| | ___| | | ___    \ \  /\  / /__  _ __| | __| |
 |  __  |/ _ \ | |/ _ \    \ \/  \/ / _ \| '__| |/ _` |
 | |  | |  __/ | | (_) |    \  /\  / (_) | |  | | (_| |
 |_|  |_|\___|_|_|\___/      \/  \/ \___/|_|  |_|\__,_|
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Would you like to play a game?<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-symbol">&amp;gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blink"</span>&gt;</span>_<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"interlaced"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>  
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"glare"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>That covers this tutorial of the retro computer screen effect using CSS.</p>
<p>In the <a target="_blank" href="https://webartisan.info/how-i-made-my-80s-retro-style-homepage-part-2">Part 2 of this series</a>, we cover using JavaScript to</p>
<ul>
<li>create an effect that slowly draws text onto the screen</li>
<li>making a simulated command-prompt that accepts text input</li>
<li>responding to command input</li>
</ul>
<p>If you had fun with this demo, and want more content like this, you can follow me on Twitter at <a target="_blank" href="https://twitter.com/jdlien">@jdlien</a>.</p>
]]></content:encoded></item><item><title><![CDATA[The Pros and Cons of TailwindCSS]]></title><description><![CDATA[In the world of front-end web development, Tailwind has become the CSS framework that has taken off more than any other recently. Despite this, it has its detractors.
The premise of Tailwind is that instead of creating a set of selectors and bespoke ...]]></description><link>https://webartisan.info/the-pros-and-cons-of-tailwindcss</link><guid isPermaLink="true">https://webartisan.info/the-pros-and-cons-of-tailwindcss</guid><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[CSS]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Thu, 21 Jul 2022 16:52:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658422298826/I_x6dCKNu.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the world of front-end web development, Tailwind has become the CSS framework that has taken off more than any other recently. Despite this, it has its detractors.</p>
<p>The premise of Tailwind is that instead of creating a set of selectors and bespoke classes to style your code (as has been common practice for 20 years), you use a large set of single-purpose utility classes that each do one thing. In essence, you add nearly all your style right into your frontend HTML, React, or Vue code.</p>
<p>So instead of something like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-body"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title pricing-card-title"</span>&gt;</span>$29 <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-muted"</span>&gt;</span>/ mo<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"list-unstyled"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>30 users<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>15 GB<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>&gt;</span>Buy<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>You might instead have this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-12 space-y-4 sm:mt-16 sm:space-y-0 sm:grid sm:grid-cols-2 sm:gap-6 lg:max-w-4xl lg:mx-auto xl:max-w-none xl:mx-0 xl:grid-cols-4 border border-gray-500"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg leading-6 font-medium text-gray-900"</span>&gt;</span>$20 <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm"</span>&gt;</span>/ mo<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-6 space-y-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex space-x-3 text-sm text-gray-500"</span>&gt;</span>30 users<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex space-x-3 text-sm text-gray-500"</span>&gt;</span>15 GB<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-8 block w-full bg-gray-800 border border-gray-800 rounded-md py-2 text-sm font-semibold text-white text-center hover:bg-gray-900"</span>&gt;</span>
  Buy<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>If you've never used Tailwind, your first reaction might be <em>"Yikes! Holy #@&amp;%ing classlists! How is this a better way to style my content?"</em></p>
<ul>
<li><p>It looks weird.</p>
</li>
<li><p>Some of those class names are long.</p>
</li>
<li><p>There's a lot of repetition — imagine having several of these blocks on the page.</p>
</li>
<li><p>In many cases, it looks and works a lot like using <code>style</code> tags with inline styles on elements.</p>
</li>
</ul>
<p>Despite these apparent drawbacks, there are many benefits to the approach that Tailwind offers. And I assure you, Tailwind offers a lot more than shorter ways to add inline styling.</p>
<p>Here are the main benefits of using TailwindCSS.</p>
<h2 id="heading-1-no-more-thinking-of-names-for-style-classes">1. No more thinking of names for style classes</h2>
<p>Using traditional methods of styling require you to consider what types of content and styles you might have and then attempt to create classes allowing you to apply these styles. Or you have base styles that work most of the time but you sometimes might need to override them. This can get quite overwhelming, begging questions like:</p>
<ul>
<li><p>Will I use this more than once?</p>
</li>
<li><p>If I use this again, will it need to be a bit different in some way?</p>
</li>
<li><p>What naming conventions do I use?</p>
</li>
<li><p>Should I name this after the appearance, the function, or something else?</p>
</li>
<li><p>What conventions should I use to name and organize my CSS?</p>
</li>
</ul>
<p>Naming might not sound all that challenging, but consider all the above and try to be consistent about it over time and across team members. It is actually <em>very</em> difficult! A tremendous amount of cognitive load is incurred, and this also often leads to "bikeshedding" amongst team members as they argue about the best names for things.</p>
<p>When using TailwindCSS, there's far less of a need to name classes, as you just apply the utility classes specified in the documentation. Once you start using Tailwind, most of the class names become fairly intuitive, and lesser used ones are easily looked up in <a target="_blank" href="https://tailwindcss.com">Tailwind's excellent documentation</a>.</p>
<h2 id="heading-2-much-less-switching-contexts-between-css-filesstyle-blocks-and-html-code">2. Much less switching contexts between CSS files/style blocks and HTML code</h2>
<p>Normally when you’re writing your core webpage structure, you’ll be writing HTML and JS, and often think things like</p>
<ul>
<li><p>“I just need to add some margin to this”.</p>
</li>
<li><p>“This text needs to be bigger.”</p>
</li>
<li><p>“I have to make a class for this new type of element.”</p>
</li>
</ul>
<p>Every time you do that, you have likely needed to either switch to another tab of your IDE with your CSS, or scroll all the way up to the top of your document head where you’ve got a <code>&lt;style&gt;</code> block.</p>
<p>Whenever you do this, you have this context switching and have to spend time moving around, making the change to your CSS, then getting back into the document.</p>
<p>When you use TailwindCSS, most of the time you are styling things inline, right in your CSS in the class attributes, so you don’t have to switch to this other section of your code. That makes you much faster. If you’re already experienced enough that you’re used to working the former way, you may not even realize how much time you’re spending switching between documents and finding your place, but this is a tremendous benefit to the utility-first approach to working with CSS.</p>
<h2 id="heading-3-you-can-use-variants-with-media-queries-pseudo-classes-and-child-selectors">3. You can use variants with media queries, pseudo-classes, and child-selectors</h2>
<p>Inline styles cannot use media queries. For instance, if you want an element to display one way on small screens, and another way on large screens, you would normally need to create different sets of selectors in different parts of your CSS. This can be quite cumbersome because you have to find multiple sections of your stylesheet and edit them.</p>
<p>Fortunately, Tailwind allows you to do all of this kind of styling inline without having to create different selectors for each variant. Here are just a few:</p>
<ul>
<li><p>Size classes (sm, md, lg, xl)</p>
</li>
<li><p>hover</p>
</li>
<li><p>focus</p>
</li>
<li><p>active</p>
</li>
<li><p>dark mode (through either parent class or media query)</p>
</li>
<li><p>first, last, odd, even</p>
</li>
<li><p>form states like required, invalid, disabled</p>
</li>
<li><p>group and peer to modify things in child or adjacent elements</p>
</li>
<li><p>before and after to modify pseudo-elements</p>
</li>
<li><p>placeholder (for form elements that aren’t filled in)</p>
</li>
<li><p>markers to style bullets in lists</p>
</li>
</ul>
<p>There are a ton and more of these variants are being added all the time. Check <a target="_blank" href="https://tailwindcss.com/docs/hover-focus-and-other-states">the variants page on the docs</a> for a more complete list with examples.</p>
<h2 id="heading-4-tailwind-helps-enforce-a-coherent-design-system-without-being-too-restrictive">4. Tailwind helps enforce a coherent design system (without being too restrictive)</h2>
<p>If you’ve ever tried to design a large, complex site, or created a design system for a company to use, you might know that it can be tricky to enforce design decisions across a team or many pages over a long time.</p>
<p>Tailwind really helps with this because you can set up Tailwind to allow only specific values, instead of just having everything set to an infinite range of possible values.</p>
<ul>
<li><p>A color palette is defined with reasonable steps between shades.</p>
</li>
<li><p>Spacing values are only allowed at certain intervals so spacing looks consistent. You have to choose between 12, 14, 16, 20 etc, and not every possible value, so you are less likely to end up with slightly different spacing between very similar elements.</p>
</li>
<li><p>There’s a set of fonts, weights, and font-sizes to use.</p>
</li>
<li><p>The width breakpoints are predefined for your responsive design so you don’t have many breakpoints. (You can easily add more).</p>
</li>
</ul>
<p>Of course, you can and probably will customize any of these values as required, but the point is that you don’t have to think “Hmm, do I need 18 or 19 pixels of spacing here?” or “Should this color be #1122FF or #1128F0?”. You have a defined list of values to choose from, and this makes styling more straightforward and being consistent easier.</p>
<h2 id="heading-5-your-production-css-only-has-styles-you-actually-use">5. Your production CSS only has styles you actually use</h2>
<p>If you’ve ever maintained a massive stylesheet, you might know that over time, you can end up with a lot of cruft in there. Some styles were put in there with the expectation they’d be used a lot, but the code it applies to might not even exist in your codebase in time. When that happens, it can be a lot of work to look for those classes and elements and prune the unused style from your CSS. Or worse, you just leave it in there for fear of breaking something.</p>
<p>In Tailwind, you’re going to be generating your CSS automatically using a build process, so your stylesheet will only have the utility classes you actually have in your code. This means that, for smaller sites, your CSS stays pretty small and compact. And for larger sites, it still remains reasonable.</p>
<h2 id="heading-6-utility-classes-cache-well">6. Utility classes cache well</h2>
<p>Inline styles can’t really be cached because they have to be reloaded along with the HTML content. A CSS file can be more aggressively cached, improving page-load speed.</p>
<p>The flip-side of this is that the actual HTML may be larger with Tailwind because of all the utility classes in the content, compared to making large classes that extract the complexity out into a stylesheet. But this ends up being a pretty good tradeoff, because the repetitive nature of utility classes compresses quite well at the transport layer used to transfer webpages, so the resulting difference in data transfer time and page load performance is extremely negligible.</p>
<h2 id="heading-7-its-easy-to-make-style-changes-without-overriding-things">7. It’s easy to make style changes without overriding things</h2>
<p>With a utility-only approach, you rarely have to worry about CSS precedence mucking things up or complicated and annoying style conflicts. If you have large hierarchies of style with nested selectors, then you want to make a change, you might find yourself having to resort to using <code>!important</code>, which is rarely a good idea, because it’s impossible to override that later.</p>
<p>Using Tailwind, your styles are usually applied directly to the thing you are styling, and if you want to change a thing, you just change it, you don’t have to fight with higher-priority styles that have higher specificity somewhere in the chain that you have to find and contend with, like in the example below.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">form</span><span class="hljs-selector-class">.master</span> <span class="hljs-selector-tag">input</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span>;
}

<span class="hljs-comment">/* Later on in the stylesheet… */</span>


<span class="hljs-comment">/* This style will not be applied in a form with class “master” because the above has higher specificity */</span>
<span class="hljs-selector-tag">form</span> <span class="hljs-selector-tag">input</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">4px</span>;
}
</code></pre>
<h2 id="heading-8-extracting-to-components-helps-keep-code-dry">8. Extracting to components helps keep code DRY</h2>
<p>What about the repetitive nature of Tailwind? If you have a bunch of styled list items, for example, would you actually have to apply a big list of classes every time?</p>
<p>Yes and no. If you were coding a large document completely by hand, this could certainly get tedious, although in those cases, modern IDEs would let you select and edit all of them simultaneously anyways. (Multi-cursor editing is an absolute godsend!) But in most practical websites, highly repetitive parts of a document are usually generated programmatically through a loop anyways. So you only really have to write such code once.</p>
<p>In cases where you may need to reuse a block of code, it makes more sense to extract this to reusable components in your framework (like React, Vue, or even Blade). This way you can also combine the relevant structural HTML with all the styling for it, and have one source of truth for this bit of code that might appear in many pages.</p>
<p>Finally, if extracting to components doesn’t make sense in your situation, you can also use Tailwind’s <code>@apply</code> functionality, that allows you to combine multiple Tailwind classes into a single, custom class, and combine the benefits of Tailwind with a traditional CSS approach. This can be very handy for things like buttons or form elements that are used frequently but may not have the complexity necessary to extract to a component.</p>
<h2 id="heading-9-tailwind-styling-is-so-much-easier-to-maintain">9. Tailwind styling is so much easier to maintain!</h2>
<p>This may be the biggest benefit of Tailwind - if you have a project using Tailwind, changing styles is very straightforward most of the time. Especially if you’re making prolific use of styled components. You simply go into your component and change the classes, or change the classes in the code you want to adjust.</p>
<p>In a more typical CSS environment, you might have to make changes to a large CSS file, and when you do this, it is not clear where exactly those styles are used. Thus, testing and validating the changes is quite difficult and could have knock-on effects that aren’t obvious at first. Using a utility-first approach means you don’t have to dig around in a messy CSS file and hope you’re not breaking things or causing conflicts.</p>
<h1 id="heading-drawbacks-of-tailwind">Drawbacks of Tailwind</h1>
<p>To avoid seeming like a frothing-at-the-mouth Tailwind fanboy, I’ll concede that there are some drawbacks to using Tailwind. It’s not always perfect for all scenarios. Here are some of the major concerns.</p>
<h2 id="heading-1-a-build-step-is-required">1. A build step is required</h2>
<p>Although originally Tailwind was just a stylesheet you could pull in, newer versions require you to run a build process to generate the CSS. This means that there’s some extra overhead to the process of creating a web page. If you’re not familiar with front-end build processes, this can add complexity that could be overwhelming to a newer dev. Fortunately this process integrates well into the build processes used by many front-end frameworks already, and using TailwindCLI makes it fairly easy.</p>
<h2 id="heading-2-setup-and-learning-curve">2. Setup and learning curve</h2>
<p>If you already know CSS, Tailwind is another thing to learn. You have to install it, figure out how to configure it the way you want (if you can’t just accept the defaults), and then learn the conventions and class names and how different parts of Tailwind work.</p>
<p>Fortunately, Tailwind has excellent documentation that makes it pretty easy to find what you need, and there are lots of good tutorials and examples available. A couple days of study and practice is usually all that is required for basic proficiency.</p>
<h2 id="heading-3-larger-html-sizes">3. Larger HTML sizes</h2>
<p>Because the utility-first approach often involves adding a lot of classes into your HTML, the download size of the HTML may increase. The advantage of this is that this type of extra, repetitive data tends to compress very easily, so the net difference in user-experience may not be significant. Sarah Dayan from Algolia addressed this concern in her talk <a target="_blank" href="https://youtu.be/R50q4NES6Iw?t=785">“In Defense of Utility-First CSS”</a>. In it, she demonstrated the difference between a utility-first approach and a component approach with an existing page and found that the uncompressed size was about 20% larger with utility-first. But once compressed, the difference was only a few percent.</p>
<p>Furthermore, the CSS document was larger with the component approach, and it ends up being a more complex document that requires more processing for the browser to traverse and apply.</p>
<p>Still, if you’re trying to create some kind of very highly optimized content, it could potentially be more efficient to use bespoke classes with very short names.</p>
<h2 id="heading-4-tailwind-cant-do-everything">4. Tailwind can’t do everything</h2>
<p>While the capabilities of Tailwind are increasing all the time, there are some CSS properties and advanced techniques that are outside the scope of what it can do. This means that from time to time, you may have to add some inline styles or create some custom classes alongside Tailwind to get things done. Is this terrible? Not really, but it does mean that Tailwind isn’t a panacea for all needs.</p>
<h2 id="heading-5-it-could-stop-you-from-learning-css-properly">5. It could stop you from learning CSS properly</h2>
<p>If you are <em>very</em> new to CSS, I wouldn’t recommend you use Tailwind, because you should learn how CSS works first. You need to understand</p>
<ul>
<li><p>most of the selectors (id, class, element, spaces, adjacent element, and so-on)</p>
</li>
<li><p><a target="_blank" href="https://css-tricks.com/precedence-css-order-css-matters/">CSS precedence</a> - in what order does CSS apply?</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity">Specificity</a> - how the browser calculates which CSS applies (and which doesn’t)</p>
</li>
<li><p>How common, frequently used CSS properties work</p>
</li>
<li><p>How the <a target="_blank" href="https://www.w3schools.com/Css/css_boxmodel.asp">CSS box model</a> works</p>
</li>
<li><p>How flexbox works and how to use it</p>
</li>
<li><p>The basics of how CSS grid works</p>
</li>
</ul>
<p>All of this can take several years to master, so jumping straight into using a framework like Tailwind will leave you with some huge knowledge gaps that could affect your ability to do web design competently and compromise your ability to find work. Using Tailwind is convenient when you already understand CSS, but relying on it to compensate for a poor understanding of CSS is a bad idea.</p>
<h2 id="heading-6-it-reduces-the-separation-of-concerns-between-content-and-style">6. It reduces the separation of concerns between content and style</h2>
<p>The original philosophy of CSS was that you should separate your style (CSS) from your document structure and content (HTML). If you can do this, a designer can easily change the style of a document without touching any of the HTML and affecting the content.</p>
<p>The canonical example of this is <a target="_blank" href="http://www.csszengarden.com/">CSS Zen Garden</a>, a website that has many radically different appearances by virtue of a set of 3rd-party stylesheets.</p>
<p>In most modern ways of building web front-ends, having "separation of concerns" is still important, but is typically implemented across component boundaries, particularly when using React/Vue/etc., rather than simply delimiting by arbitrary language or technology boundaries.</p>
<p>The creator of Tailwind, Adam Wathan, <a target="_blank" href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/">wrote a document</a> explaining why separation of CSS and HTML concerns is a straw-man argument that doesn’t hold up in the real-world. In a project of any complexity, either your CSS depends on your HTML or your HTML depends on your CSS, and they are very hard to separate.</p>
<p>Have you ever had a large project for which you completely refactored the HTML without changing any CSS? Or one in which you were going to restyle the site completely without adjusting any HTML? Not likely!</p>
<p>In practice, this is not a strong argument against Tailwind, but if you think this is important for the way you work, it is worth considering.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If you were on the fence about trying Tailwind, or were a detractor of it for some reason, I hope this article shines some light on why its fans love it so much. It offers so many advantages for speed of development, maintainability, and efficiency. Besides all that, it has a great ecosystem around it of UI components and existing designs you can borrow, <a target="_blank" href="https://tailwindcss.com">excellent documentation</a>, and <a target="_blank" href="https://youtube.com/c/TailwindLabs">tutorial videos available for free on YouTube</a>.</p>
<p>If you enjoyed this article, please follow me <a target="_blank" href="https://twitter.com/jdlien">@jdlien on Twitter</a>.</p>
]]></content:encoded></item><item><title><![CDATA["You Belong in Tech" — Book Review and Summary]]></title><description><![CDATA[A career in technology can be amazing. And right now, there's no shortage of opportunity to enter the field, but competition can be fierce, and the complexity of going from "I've never written a Hello World program" to "I got my first full-time posit...]]></description><link>https://webartisan.info/you-belong-in-tech-book-review</link><guid isPermaLink="true">https://webartisan.info/you-belong-in-tech-book-review</guid><category><![CDATA[Technical interview]]></category><category><![CDATA[job search]]></category><category><![CDATA[Learning Journey]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Fri, 08 Jul 2022 16:12:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657296637949/bU57nMppW.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A career in technology can be amazing. And right now, there's no shortage of opportunity to enter the field, but competition can be fierce, and the complexity of going from "I've never written a Hello World program" to "I got my first full-time position as a software developer" is incredibly daunting.</p>
<p>I have experience going through both sides of this process — applying for and interviewing for positions, as well as interviewing candidates for web developer roles. Thus, I can confidently say that if you're trying to break into the software development profession, <a target="_blank" href="https://twitter.com/AnnaJMcDougall">Anna McDougall</a>'s new book "<a target="_blank" href="https://www.amazon.com/You-Belong-Tech-Programming-Knowledge-ebook/dp/B09WB6YR39/">You Belong in Tech</a>" has you well-covered.</p>
<p>The book is particularly focused on those who wish to become web developers, making it a good fit for the readers of this blog. That said, most of its advice will be relevant to job-seekers in related technical professions. As the cover suggests, it will walks you through the whole process of learning software development, looking for jobs, and applying and interviewing.</p>
<p>The first section on developing your technical skills covers:</p>
<ul>
<li>Technologies and skills you'll need to learn</li>
<li>Resources you can use to learn (both free and paid)</li>
<li>Creating a learning plan</li>
<li>Choosing an IDE for programming</li>
<li>What mentorship is and how to find a mentor</li>
</ul>
<p>Anna goes beyond just listing languages you ought to know, like HTML, CSS, JavaScript, Python, or you know, PHP. She also covers other critical skills for developers like using git and GitHub, Agile methodologies and Scrum, and briefly covers other IT disciplines beyond programming.</p>
<p>Areas the book goes into more depth than expected are personal branding, integrating yourself into a community, and using blogging and social media (Twitter, YouTube, LinkedIn, etc.) to connect with others in the field, promote yourself, and bolster your own expertise. It has often been said that you become like the people you spend the most time with, so the importance of this cannot be overlooked, particularly if you're making a career transition. There are great guidelines in the book for setting up your social media profiles and activity to help you integrate into a programming/developer community and leveraging this to make yourself more attractive to recruiters and potential employers.</p>
<p>Something that may not be obvious to someone on the job market is the concept of creating and curating a "personal brand" which the book covers extensively. By going through the exercises within, you will set yourself up to look significantly more professional and accelerate your learning. This is also a way to help compensate for a lack of real-world job experience.</p>
<p>I think readers will get considerable value from the section on applying and interviewing for jobs, as this can be daunting and, frankly, most people just do this poorly. When my organization gets job applications, we probably screen out 90%–95% of them immediately. Many people use a "spray and pray" approach to applications, which does not work. It's so easy to see through these sorts of bulk submissions.</p>
<p>"You Belong in Tech" spends considerable time discussing</p>
<ul>
<li>Where to look for jobs</li>
<li>Crafting an excellent résumé/CV using "Quotes, Names, and Numbers" to instill confidence in the reader</li>
<li>Compensating for a lack of job experience in the field you're applying for</li>
<li>Types of interviews to expect for technical positions</li>
<li>Preparing for interviews</li>
<li>Answering common interview questions</li>
<li>Following up after the interview</li>
</ul>
<p>One of the most useful pieces of advice in the whole book is probably this:</p>
<p>If you meet 50% of the requirements for a job that you want, <strong>apply</strong>.</p>
<p>After all, you'll never get a job you don't apply for, and most companies post a list of requirements they'd <em>like</em>, knowing full well that they are unlikely to find a candidate that actually starts with all those skills and and knowledge on their first day. In fact, if you do handily meet every single requirement, you likely are overqualified and some companies are wary of hiring candidates who are overqualified. (This is primarily because they won't usually stay for long, wasting everyone's time.)</p>
<p>Interviewing in particular is a nerve-wracking and stressful experience, and one surprise insight that the Anna has is that of using her experience as a singer and performer to advise on how to deal with nervousness and anxiety around the process. There are some great tips in the book. One of these is this:</p>
<p>Don't deny you feel nervous. Instead, <em>recognize</em> your nerves and prepare coping mechanisms in advance. Rather than thinking 'This is fine. I'll be fine. There's no need to be nervous', tell yourself something like 'I'm feeling nervous now. When I notice that, I'll clasp my hands together on my lap'. If you freeze up you can take a slow sip of water to allow yourself time to think.</p>
<p>The book also covers how to deal with rejection, how to weigh actual job offers that you do receive, and how to take your first steps when you start and begin to experience the dreaded "imposter syndrome".</p>
<p>If this is an adventure you are about to embark on, particularly if you are starting out or are still very junior, I highly recommend reading this book to give you great insight into the entire process of preparing to launch or improve your career as a software developer.</p>
<p>If you think you'll benefit from reading "You Belong in Tech", <a target="_blank" href="https://www.amazon.com/dp/B09WB6YR39/">you can find it on Amazon</a>.</p>
<p>Thanks for reading! If you like this type of content or enjoy learning about web development, <a target="_blank" href="https://twitter.com/jdlien">follow me on Twitter @jdlien</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Make Millions Deceiving Your Users with Dark Patterns]]></title><description><![CDATA[You could build your website to be easy for users to use. But why go through the effort of building an attractive UI that makes it easy for users to do what they want?
Instead, you just try these designs to trick users into giving you what you want!
...]]></description><link>https://webartisan.info/make-millions-deceiving-your-users-with-dark-patterns</link><guid isPermaLink="true">https://webartisan.info/make-millions-deceiving-your-users-with-dark-patterns</guid><category><![CDATA[UX]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Wed, 29 Jun 2022 20:37:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656534814921/QifGc_jLZ.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You could build your website to be easy for users to use. But why go through the effort of building an attractive UI that makes it easy for users to do what they want?</p>
<p>Instead, you just try these designs to trick users into giving you what <em>you</em> want!</p>
<p>Think of the benefits:</p>
<ul>
<li>Increase sales by having users purchase things they didn't even know they were buying!</li>
<li>Get loads of email addresses so you can directly market to people who stumbled upon your website by accident once.</li>
<li>Monetize content by tricking kids into filling out surveys for dubious rewards. (HEY KIDS! FREE ROBUX!)</li>
<li>Make your users feel bad if they don't accept your awesome offers.</li>
<li>Increase engagement by giving users no other choice.</li>
</ul>
<p>For the history of the Web, unscrupulous website owners have been designing sites to trick users into making purchases, hand over personal information, or view more ads. Obviously no user enjoys these experiences, but unfortunately these techniques do work — at least in the short-term.</p>
<p>The term 'dark pattern' was coined by Harry Brignull in 2010, who <a target="_blank" href="https://deceptive.design/">maintains a website</a> that keeps track of many of the worst offenders that use these types of designs.</p>
<p>Here are several examples of common deceptive designs.</p>
<h3 id="heading-sneak-into-basket">Sneak Into Basket</h3>
<p>Sometimes when you go to check out your shopping cart, an item you didn't actually want was there. Sometimes this seems good-intentioned, such as a small charity donation, but oftentimes this is used to add "extended warranties" or purchase protection. Many software stores used to sell you a "backup download" or some nonsense, pre-checking the option for an additional few dollars when you purchased downloadable software.</p>
<h3 id="heading-roach-motel">Roach Motel</h3>
<p>It's easy to get in, but good luck leaving!</p>
<p>Example: To create an account on Amazon, there's a very obvious "New Customer? Start here." link that takes you to a simple signup page that basically just requires your name, email, and a password. Easy peasy!
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656530266789/WMju9mZYP.png" alt="image.png" /></p>
<p>But what if you want to <em>delete</em> your account?</p>
<p>Amazon makes users scour the website and such a link is not located anywhere. Ultimately, you have to go three pages deep into a place where you can initiate a chat with customer support, who then try to talk you out of deleting your account. Finally, they give you a link that can initiate the procedure to delete your account. The whole process likely would take 20 minutes even if you knew exactly what you were doing.</p>
<p>Similarly, in the 1990s, AOL used to mail out floppies and CDs that made it dead-simple to sign up for a "free" AOL account. But if you want to cancel your service? Get prepared to call in and wait on hold for around an hour where an agent will try to talk you out of it.</p>
<h3 id="heading-confirmshaming">Confirmshaming</h3>
<p>Ever seen one of those links that has a giant button to sign up for something (like a newsletter or credit card) and then somewhere weird there's a <em>tiny</em> little link that says something like "No, I'm stupid and don't want to save money"? This pattern is called confirmshaming. It's a great way to make potential customers feel like the website-owner is a real dick.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656530823547/fim3mhXMc.jpeg" alt="confirmshame.jpeg" /></p>
<h3 id="heading-misdirection">Misdirection</h3>
<p>Have you ever installed software on your computer that had a pre-checked checkbox saying  "✅  Install FREE AdWare and Browser Toolbars"?</p>
<p>A lot of free (and even paid) software products make a good bit of their revenue by bundling in crap you don't want on your computer using this sort of technique. Less savvy users would be hesitant to change the default installer settings and would end up with some dreck on their PCs that clutters up their system, or worse, installs some sort of malware.</p>
<p>Often the same sites where this sort of software can be downloaded will have a wall of download buttons, and if you're lucky, maybe one of them will actually be the legitimate download link for the item you want. The rest download something you almost certainly don't want!</p>
<p>Another form of misdirection is when websites change the order of controls buttons on their website to mess with user's muscle memory. Take this form that most users would normally just go through and click "No" to every option on.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/jdlien/pen/qBoEEob">https://codepen.io/jdlien/pen/qBoEEob</a></div>
<p>It might be quite easy to find yourself accidentally signed up for an email list. Or worse!</p>
<h3 id="heading-fake-notifications">Fake Notifications</h3>
<p>Websites sometimes stick a red dot on something, or even use motion or banners alerting you that there's a notification. When you finally read it, you essentially end up finding a nuisance ad.</p>
<p>Here's a misleading notification from Tinder that is essentially just an ad tricking users into opening up the app and attempting to boost engagement.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656532811534/XDLU42BTV.png" alt="image.png" /></p>
<h3 id="heading-privacy-zuckering">Privacy Zuckering</h3>
<p>Detailed personal information about you is worth a lot to advertisers. Some websites are designed to coerce users into sharing more information about themselves than is really necessary. This is often achieved is by websites being unclear about what information they share or making it difficult to control privacy settings.</p>
<p>Guess who this pattern is named after?</p>
<h3 id="heading-disguised-ads">Disguised Ads</h3>
<p>Sometimes ads are designed to look like a legitimate part of application UI. Or even sneakier, ads will add a hair or bit of dust so that users attempt to brush it off their phone screen, accidentally triggering a click on the ad.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656533589259/JWyXlmq4Q.png" alt="image.png" /></p>
<h3 id="heading-bait-and-switch">Bait and Switch</h3>
<p>You think you're doing one thing, but something else happens. This application makes you think you're just getting a free download when in reality, you're signing up for a subscription to something you probably don't want.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656534010305/CxTYINJ_3.png" alt="image.png" /></p>
<h3 id="heading-forced-continuity">Forced Continuity</h3>
<p>Often services will sign you up for a "free" service, only to start charging you after you've forgotten what you've done.  Even worse, there are a category of scammy iPhone apps that bill you <em>weekly</em> so the price is more than 4x more expensive than you might have expected if you didn't read carefully.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656533829322/bQajqewJV.png" alt="image.png" /></p>
<h2 id="heading-should-you-actually-use-these-patterns">Should You Actually Use These Patterns?</h2>
<p>No.</p>
<p>As a web developer, you may be asked to implement stuff like these designs above. While they may work in the short-term to get some clicks, add subscribers, or increase sales a bit, using these sneaky tactics will only do your business reputational harm, and it will annoy users, perhaps alienating some of them.</p>
<p>I believe strongly that a business and a brand should be built on strong ethical principles of serving customers well and offering good quality products and helping customers get value from what you offer. Using these sorts of tricks almost exclusively do the opposite, and are not a long-term road to success.</p>
<p>Aside from that, there is legislation being proposed in the United States called the <a target="_blank" href="https://www.warner.senate.gov/public/index.cfm/2021/12/lawmakers-reintroduce-bipartisan-bicameral-legislation-to-ban-manipulative-dark-patterns">DETOUR Act</a> (Deceptive Experiences To Online Users Reduction). Although this law seems pretty squarely targeted at big tech, it's possible that at some point in the future, using deceptive design could land a company in legal hot water.</p>
<p>If you enjoyed this article, or have anything to add, please add a comment or <a target="_blank" href="https://twitter.com/jdlien">follow me on Twitter</a> for more web development tips.</p>
]]></content:encoded></item><item><title><![CDATA[How to Make the Perfect Dark Mode Switcher]]></title><description><![CDATA[Websites should match users' system settings for the best experience. One important setting is the system theme, which can be dark or light. Here's how to do that in the best way.

1. The Basics: prefers-color-scheme Media Query
Browsers now support ...]]></description><link>https://webartisan.info/how-to-make-the-perfect-dark-mode-switcher</link><guid isPermaLink="true">https://webartisan.info/how-to-make-the-perfect-dark-mode-switcher</guid><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Tue, 28 Jun 2022 06:56:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/WsEbnsnKbUE/upload/v1656395072224/pZYCzmNWu.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Websites should match users' system settings for the best experience. One important setting is the system theme, which can be dark or light. Here's how to do that in the best way.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656395579792/ISJPfsU-4.png" alt="image.png" /></p>
<h2 id="heading-1-the-basics-prefers-color-scheme-media-query">1. The Basics: prefers-color-scheme Media Query</h2>
<p>Browsers now support media queries to allow you to match the operating system theme using only CSS like so:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Adjust the theme to match the operating system */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme:</span> dark) {
  <span class="hljs-selector-tag">body</span> {
      <span class="hljs-attribute">background-color</span>: black;
      <span class="hljs-attribute">color</span>: white;
  }

  <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">color</span>: skyblue;
  }
}
</code></pre>
<p>This CSS will use the default colors unless the user's OS is in a dark theme, in which case the body will have a black background with white text and light blue links.</p>
<p>Great! But what if the user wants to switch to a theme different than their OS setting?</p>
<h2 id="heading-2-manual-switching-with-a-dark-class">2. Manual Switching with a 'dark' Class</h2>
<p>We can add a "dark" class to the html tag so that any descendants can inherit dark styles in our CSS  instead of using a media query. This makes it quite easy to toggle back and forth using JavaScript.</p>
<p>Let's make a basic webpage to demonstrate.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
      <span class="hljs-selector-class">.dark</span> <span class="hljs-selector-tag">body</span> {
        <span class="hljs-attribute">background-color</span>: black;
        <span class="hljs-attribute">color</span>: white;
      }      
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"toggleTheme()"</span>&gt;</span>Switch Theme<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello World!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toggleTheme</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">let</span> htmlClassList = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).classList

        <span class="hljs-keyword">if</span> (htmlClassList.contains(<span class="hljs-string">'dark'</span>)) {
          htmlClassList.remove(<span class="hljs-string">'dark'</span>)
        }
        <span class="hljs-keyword">else</span> {
          htmlClassList.add(<span class="hljs-string">'dark'</span>)
        }
      }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/jdlien/pen/bGvGNgK">https://codepen.io/jdlien/pen/bGvGNgK</a></div>
<p>Voilà — you can now switch the theme!  We have a <code>toggleTheme()</code> function that gets the classList of the <code>html</code> element, adding or removing the <code>dark</code> class from it, depending on whether it is present.</p>
<h2 id="heading-3-detecting-the-clients-theme-while-allowing-switching">3. Detecting The Client's Theme While Allowing Switching</h2>
<p>But all is not quite right... you see, when a user with a dark theme loads this page, it will load with default colors — usually light theme. A dark mode user will be blinded with a white screen.</p>
<p>Hmm... we didn't have that problem using the simple @media query approach. So what do we do?</p>
<p>We can add a little more JS to preemptively add the class in the html element depending on the user's theme. We use the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia">matchMedia</a> API to determine what the current theme is. We pass in <code>prefers-color-scheme: dark</code>, and get back a <code>MediaQueryList</code> object that contains a <code>matches</code> property. If <code>matches</code> is true, the user has a dark theme, and we should add the dark class.</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">'(prefers-color-scheme: dark)'</span>).matches) {
  <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).classList.add(<span class="hljs-string">'dark'</span>)
}
</code></pre>
<p>It's best to add this JS code into the <code>head</code> of the document — if you don't, there may be a "flash of unstyled content" when the page loads. Since we are only modifying the html element at the very beginning, we don't have to wait for it to load after the JS in the head element.</p>
<h2 id="heading-4-saving-and-restoring-the-users-chosen-theme">4. Saving And Restoring The User's Chosen Theme</h2>
<p>So where are we? We now allow the user to switch their theme while <em>also</em> respecting the user's default theme. But what if the user switches their theme and then reloads the page later?</p>
<p>Well, the theme will revert back to the system default. That would get annoying quickly for a frequent visitor who changed the theme.</p>
<p>Let's add more smarts by saving the user's preference into the browser's <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">localStorage</a>.</p>
<p>First let's revisit the <code>toggleTheme()</code> function from earlier and have it set a <code>theme</code> value in localStorage when the theme is switched.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toggleTheme</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">let</span> htmlClassList = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).classList
  <span class="hljs-keyword">if</span> (htmlClassList.contains(<span class="hljs-string">'dark'</span>)) {
    htmlClassList.remove(<span class="hljs-string">'dark'</span>)
    <span class="hljs-built_in">localStorage</span>.theme = <span class="hljs-string">'light'</span>
  }
  <span class="hljs-keyword">else</span> {
    htmlClassList.add(<span class="hljs-string">'dark'</span>)
    <span class="hljs-built_in">localStorage</span>.theme = <span class="hljs-string">'dark'</span>
  }
}
</code></pre>
<p>Now localStorage will contain the value, but we have to update our preemptive class-adding code in the head to respect this setting, instead of merely adding a dark mode based on the media query.</p>
<p>This gets a bit more complex. We need to check</p>
<ul>
<li>Has the user ever set a theme?</li>
<li>If not, does the user use a dark theme? If so, respect the media query.</li>
<li>If so, and it was dark, add the <code>dark</code> class</li>
<li>if so, and it was not dark (ie light), remove any dark class</li>
</ul>
<p>That is all handled by the if statement below.</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> (<span class="hljs-built_in">localStorage</span>.theme === <span class="hljs-string">'dark'</span> || (!(<span class="hljs-string">'theme'</span> <span class="hljs-keyword">in</span> <span class="hljs-built_in">localStorage</span>) &amp;&amp; <span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">'(prefers-color-scheme: dark)'</span>).matches)) {
  <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).classList.add(<span class="hljs-string">'dark'</span>)
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).classList.remove(<span class="hljs-string">'dark'</span>)
}
</code></pre>
<h2 id="heading-5-detect-changes-to-operating-system-theme">5. Detect Changes to Operating System Theme</h2>
<p>If the user doesn't ever select a dark mode, there's a possibility they may change the theme of the operating system. It may even change automatically at night time, so we'll want our webpage to adapt.</p>
<p>We can add an event listener to the matchMedia object to do this.</p>
<p>First we'll extract our theme updating code to a function so we can reuse it easily, then have the event listener call it. Note the replacement of the if/else block with a ternary to clean up the code a little.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> matchMediaDark = <span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">'(prefers-color-scheme: dark)'</span>)
<span class="hljs-keyword">const</span> htmlClassList = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).classList

<span class="hljs-comment">// Set the 'dark' class according to localStorage 'theme' setting.</span>
<span class="hljs-comment">// Else if there's no stored 'theme', set the class per the system.</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateTheme</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-built_in">localStorage</span>.theme === <span class="hljs-string">'dark'</span> || (!(<span class="hljs-string">'theme'</span> <span class="hljs-keyword">in</span> <span class="hljs-built_in">localStorage</span>) &amp;&amp; matchMediaDark.matches)
    ? htmlClassList.add(<span class="hljs-string">'dark'</span>)
    : htmlClassList.remove(<span class="hljs-string">'dark'</span>)
}

<span class="hljs-comment">// Apply theme when page loads.</span>
updateTheme()

<span class="hljs-comment">// Update 'dark' class when system theme is changed.</span>
matchMediaDark.addEventListener(<span class="hljs-string">'change'</span>, updateTheme)
</code></pre>
<h2 id="heading-6-putting-it-together">6. Putting It Together</h2>
<p>Put it all together and it looks like this.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width"</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-comment">/** Apply theme inline in head to avoid flash of unstyled content. */</span>
    <span class="hljs-keyword">const</span> matchMediaDark = <span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">'(prefers-color-scheme: dark)'</span>)
    <span class="hljs-keyword">const</span> htmlClassList = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).classList

    <span class="hljs-comment">// Set the 'dark' class according to localStorage 'theme' setting.</span>
    <span class="hljs-comment">// Else if there's no stored 'theme', set the class per the system.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateTheme</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-built_in">localStorage</span>.theme === <span class="hljs-string">'dark'</span> || (!(<span class="hljs-string">'theme'</span> <span class="hljs-keyword">in</span> <span class="hljs-built_in">localStorage</span>) &amp;&amp; matchMediaDark.matches)
        ? htmlClassList.add(<span class="hljs-string">'dark'</span>)
        : htmlClassList.remove(<span class="hljs-string">'dark'</span>)
    }

    <span class="hljs-comment">// Apply theme when page loads.</span>
    updateTheme()

    <span class="hljs-comment">// Update 'dark' class when system theme is changed.</span>
    matchMediaDark.addEventListener(<span class="hljs-string">'change'</span>, updateTheme)

    <span class="hljs-comment">// Toggle localStorage theme between 'dark' and 'light' then update the theme.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toggleTheme</span>(<span class="hljs-params"></span>) </span>{
      htmlClassList.contains(<span class="hljs-string">'dark'</span>)
        ? <span class="hljs-built_in">localStorage</span>.theme = <span class="hljs-string">'light'</span>
        : <span class="hljs-built_in">localStorage</span>.theme = <span class="hljs-string">'dark'</span>

      updateTheme()
    }
  </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
    <span class="hljs-selector-tag">body</span> {
      <span class="hljs-attribute">font-family</span>: sans-serif;
      <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">0deg</span> <span class="hljs-number">0%</span> <span class="hljs-number">80%</span>);
      <span class="hljs-attribute">color</span>: black;
    }

    <span class="hljs-selector-class">.dark</span> <span class="hljs-selector-tag">body</span> {
      <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">0deg</span> <span class="hljs-number">0%</span> <span class="hljs-number">10%</span>);
      <span class="hljs-attribute">color</span>: white;
    }

    <span class="hljs-selector-tag">a</span> {
      <span class="hljs-attribute">color</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">200deg</span> <span class="hljs-number">100%</span> <span class="hljs-number">30%</span>);
    }

    <span class="hljs-selector-class">.dark</span> <span class="hljs-selector-tag">a</span> {
      <span class="hljs-attribute">color</span>: skyblue;
    }

    <span class="hljs-selector-tag">button</span> {
      <span class="hljs-attribute">background-color</span>: transparent;
      <span class="hljs-attribute">color</span>: black;
      <span class="hljs-attribute">border-width</span>: <span class="hljs-number">1px</span>;
      <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">6px</span>;
      <span class="hljs-attribute">padding</span>:<span class="hljs-number">8px</span>;
    }

    <span class="hljs-selector-class">.dark</span> <span class="hljs-selector-tag">button</span> {
      <span class="hljs-attribute">color</span>: white;
    }
  </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"toggleTheme()"</span>&gt;</span>Toggle Theme<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Dark Mode Switcher Demo<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    Click the button above to switch themes!<span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
    All this really does is add or remove the 'dark' class from the parent HTML element.
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Link Example<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>You can try this out with the CodePen below, which has a few additional touches.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/jdlien/pen/PoRowyB">https://codepen.io/jdlien/pen/PoRowyB</a></div>
<p>I hope this tutorial has been useful. Feel free to ask questions below and <a target="_blank" href="https://twitter.com/jdlien">follow me on Twitter</a> for more PHP and web development tips!</p>
]]></content:encoded></item><item><title><![CDATA[Output From PHP]]></title><description><![CDATA[The first thing you want to know in any language is "How do I print stuff?". Here I'll show you how to write output from PHP.
1. The Basics
To generate output, PHP has a language construct called echo (and a similar one called print. Note that parent...]]></description><link>https://webartisan.info/output-from-php</link><guid isPermaLink="true">https://webartisan.info/output-from-php</guid><category><![CDATA[2Articles1Week]]></category><category><![CDATA[Hashnode]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Hello World]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Sun, 19 Jun 2022 23:43:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/67L18R4tW_w/upload/v1655681869961/8irqQ4-fK.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The first thing you want to know in any language is "How do I print stuff?". Here I'll show you how to write output from PHP.</p>
<h2 id="heading-1-the-basics">1. The Basics</h2>
<p>To generate output, PHP has a language construct called echo (and a similar one called print. Note that parentheses are optional.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello world!\n"</span>;

<span class="hljs-keyword">print</span> <span class="hljs-string">"Hello world!\n"</span>;
</code></pre>
<p>These work very similarly, with a few small differences:</p>
<ul>
<li><code>echo</code> returns void, <code>print</code> returns an int of 1</li>
<li>echo can take multiple arguments, eg (<code>echo $one, ' ', $two;</code>)</li>
</ul>
<h2 id="heading-2-formatted-strings">2. Formatted Strings</h2>
<p>PHP also supports a <a target="_blank" href="https://www.w3schools.com/PHP/func_string_printf.asp">printf function</a> that supports formatting. The most commonly used formatting values are:</p>
<ul>
<li>%d - signed decimal numbers</li>
<li>%u - unsigned decimal numbers (only greater than zero)</li>
<li>%f - floating point numbers (that use localization to show the decimal separator, like a comma or period).</li>
<li>%s - string</li>
</ul>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
$name = <span class="hljs-string">'JD Lien'</span>;
$country = <span class="hljs-string">'Canada'</span>;
$age = <span class="hljs-number">40</span>;

printf(
  <span class="hljs-string">"My name is %s from %s and I am %d years old."</span>,
  $name,
  $country,
  $age
);
</code></pre>
<p><code>My name is JD Lien from Canada and I am 40 years old.</code></p>
<h2 id="heading-3-echo-shortcut-syntax">3. Echo Shortcut Syntax</h2>
<p>When you need to output something in a block of plain HTML, PHP has a handy echo shortcut that greatly simplifies the output.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
$data = <span class="hljs-string">'some data'</span>;
<span class="hljs-meta">?&gt;</span>

&lt;p&gt;You can write <span class="hljs-meta">&lt;?=</span>$data<span class="hljs-meta">?&gt;</span> right in your HTML like this.&lt;/p&gt;
</code></pre>
<p><strong>Note:</strong> In the real world, any data output should be filtered or escaped to prevent cross-site scripting (XSS) attacks!</p>
<h2 id="heading-4-non-html-output">4. Non HTML Output</h2>
<p>Of course not all output from a webserver will be webpages. Sometimes you need a plaintext file, like CSV or something for data interchange, or JSON data for an API.</p>
<p>When doing this, you must ensure you set the correct header on the output. This is done simply by using the <code>header()</code> function like so:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
header(<span class="hljs-string">'Content-Type: application/json'</span>);
$data = [<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Here is a message!'</span>];
<span class="hljs-keyword">echo</span> json_encode($data);
</code></pre>
<p>Of course you can set any header values that are required in this way, calling the function multiple times to set more header values. By default it will replace any previously defined headers, unless a second 'false' argument is passed.</p>
<pre><code class="lang-php">header(<span class="hljs-string">'Content-Type: text/plain'</span>);
<span class="hljs-comment">// The Content-Type will be replaced here</span>
header(<span class="hljs-string">'Content-Type: application/json'</span>);

<span class="hljs-comment">// Here, both header values will be present</span>
header(<span class="hljs-string">'WWW-Authenticate: Negotiate'</span>);
header(<span class="hljs-string">'WWW-Authenticate: NTLM'</span>, <span class="hljs-literal">false</span>);
</code></pre>
<h2 id="heading-5-bonus-using-php-openclose-tags">5. Bonus: Using PHP Open/Close Tags</h2>
<p>PHP was originally used as a templating language, so you must start any PHP code with <code>&lt;?php</code>. Old versions of PHP also allowed you to leave out the "php" part, eg <code>&lt;?   ?&gt;</code>, but this is no longer supported.</p>
<p>It is recommended in the <a target="_blank" href="https://www.php-fig.org/psr/psr-2/#22-files">PSR-2 Style Guide</a> to never end a document with the closing <code>?&gt;</code> tag, so that you don't end up with extra whitespace at the end of a document that could cause issues. So don't add the closing tag unless you have to add something that is not PHP code at the very end of a document.</p>
<p>Thanks for reading! If you enjoy learning about PHP, please <a target="_blank" href="https://twitter.com/jdlien">follow me on Twitter</a> so you don't miss any of my other web development content!</p>
]]></content:encoded></item><item><title><![CDATA[The Complete Beginner’s Guide to How to Get Started as a PHP Developer]]></title><description><![CDATA[You want to create awesome websites that catch people’s attention, and get a great job in a thriving industry. But how do you get started? If you know nothing about web development, here’s how you can get started.
1. Learn HTML, CSS, and (optionally)...]]></description><link>https://webartisan.info/the-complete-beginners-guide-to-how-to-get-started-as-a-php-developer</link><guid isPermaLink="true">https://webartisan.info/the-complete-beginners-guide-to-how-to-get-started-as-a-php-developer</guid><category><![CDATA[PHP]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[Hashnode]]></category><dc:creator><![CDATA[JD Lien]]></dc:creator><pubDate>Thu, 16 Jun 2022 14:28:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/FHnnjk1Yj7Y/upload/v1655387501156/cEO5NBF5x.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You want to create awesome websites that catch people’s attention, and get a great job in a thriving industry. But how do you get started? If you know nothing about web development, here’s how you can get started.</p>
<h2 id="heading-1-learn-html-css-and-optionally-javascript">1. Learn HTML, CSS, and (optionally) JavaScript</h2>
<p>To be a web developer, you’ll have to know HTML (hypertext markup language) - this is the language that webpages are built with. It’s a fairly simple language to get started with - you’ll mostly need to learn about a couple dozen tags to be able to get started. Because it’s not a programming language, you don’t have to understand programming to learn this.</p>
<p>CSS, or cascading stylesheets are how webpages in HTML are styled. CSS is easy to get started with, but difficult to master. In the beginning you can learn how to change text size, colors, add borders, round corners, and space things using margin and padding.</p>
<p>JavaScript (not to be confused with Java, which is completely different), is the programming language that browsers use to make webpages interactive without needing to reload the page. It has become very important in today’s web, so a preliminary understanding of how to use it to change elements on a webpage is helpful, even if you are primarily a backend developer working in PHP. Fortunately the syntax is quite similar to PHP, so you can learn PHP and JavaScript together and apply some of what you learn in one language to the other.</p>
<h2 id="heading-2-learn-the-basics-of-programming-with-php">2. Learn the Basics of Programming with PHP</h2>
<p><a target="_blank" href="https://phpprotips.com/how-to-set-up-the-best-php-8-development-environment-in-windows-11">Get PHP Installed on your computer</a>.
PHP is a fairly easy programming language to get started with, but no matter which language you end up using, you’ll have to understand the fundamentals of programming. Conditional logic (if/else statements), loops, data structures, variables, arrays (a structure that contains many variables), and objects are important to understand. Learn how to get data from other websites and work with it.</p>
<h2 id="heading-3-build-yourself-a-website-using-what-you-have-learned">3. Build Yourself a Website Using What You Have Learned</h2>
<p>You don’t have to master steps 1 and 2 to start this step. If you have learned the very basics, you should start building a website for yourself. First, build it on your computer, then publish it to the web. You could use a platform like <a target="_blank" href="https://www.netlify.com/">Netlify</a> to host your code for free, or even run your own server fairly inexpensively over at <a target="_blank" href="https://digitalocean.com">DigitalOcean</a>.</p>
<p>You can use this website to market yourself and as a playground for building things as you learn. Whatever you do, you don’t be able to learn seriously until you have a reason for learning and there’s something you want to build with that knowledge.</p>
<h2 id="heading-4-learn-git-and-make-a-github-account">4. Learn Git And Make a GitHub Account</h2>
<p>You don’t need to know or use Git to be a developer, but in any serious programming environment, you’ll use some kind of version control system to keep track of changes to code and allow multiple people to work on a codebase. And that system is most often Git. Creating a GitHub account helps you connect with and contribute to other projects as well, which is a great way to learn. And even if you don’t use advanced features of Git, at least using it to push your code up to GitHub serves as a very effective backup in case the code you’ve been working on gets lost from your computer!</p>
<h2 id="heading-5-learn-basic-systems-administration">5. Learn Basic Systems Administration</h2>
<p>A competent developer knows how to work a command line, navigate a filesystem, change filesystem permissions, and run commands on a Linux server. These are concepts you will need to be comfortable with, particularly as a backend web developer.</p>
<h2 id="heading-6-learn-frameworks">6. Learn Frameworks</h2>
<p>Once you feel like you have a handle on the basics of HTML, CSS, JS, and PHP, you should learn a backend framework. For PHP, i’d suggest <a target="_blank" href="https://laravel.com">Laravel</a> because it’s very flexible and powerful and well-documented with a great community around it. But whatever you learn, it’ll help you to learn and adopt a set of best practices for how to organize your code (for example, using model, view, controller or MVC architecture), how to name things, and how to build things that will take a lot of painful and time-consuming decision making out of the mix and will rapidly accelerate your journey from beginner to master.</p>
<p>Once you have a cursory understanding of how to work with the backend, learn a CSS framework. I’d suggest <a target="_blank" href="https://tailwindcss.com">TailwindCSS</a> because of its flexibility and popularity with Laravel creators, but <a target="_blank" href="https://getbootstrap.com/">BootStrap</a> is also very popular and will make it faster to make great-looking websites.</p>
<h2 id="heading-7-put-your-skills-to-work">7. Put Your Skills To Work</h2>
<p>You need to put your skills to work to keep improving and to build credibility as a developer. You can volunteer for local organizations, friends who have small businesses, or make things for family members. You can also check out freelancing sites like Fiverr, Upwork, Freelancer, etc. In the beginning you might be building a “business card” webpage for $5 just to start building your reputation, but as your abilities improve and as you gain credibility, you can start charging serious money.</p>
<h2 id="heading-8-network-with-other-developers">8. Network With Other Developers</h2>
<p>Use social networking sites like Twitter, LinkedIn, etc. to meet and communicate with other people who share your interest in web development. Make friends who are into this stuff. Having a network of friends who are developers will kickstart your journey and give you additional motivation to keep improving.</p>
<p>If you get the chance, go to conferences and listen to talks and meet other people in person (wherever that’s still a thing in this post-COVID world!). Perhaps someday you can also give talks at such conferences yourself!</p>
<h2 id="heading-9-rinse-and-repeat">9. Rinse and Repeat</h2>
<p>You’re never finished with your journey as a web developer. Go back to step 1 and continue to learn new things about HTML, and CSS. Take courses in more advanced JavaScript. Learn Object Oriented PHP. Delve deeper into understanding how your framework of choice actually works. Web development is a field that changes rapidly so to stay on top you’ll have to be a lifelong learner.</p>
<p>Which of these steps do you think will be the most difficult for you? Is there anything in particular you would like help with?</p>
<p>If you found this interested, I’d appreciate a <a target="_blank" href="https://twitter.com/jdlien">follow on Twitter</a>!</p>
]]></content:encoded></item></channel></rss>