<?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[Witch Writes]]></title><description><![CDATA[Beginner-friendly guides to web fundamentals, accessibility, developer tooling and whatever I've just had to figure out. Written by a frontend dev who worked as]]></description><link>https://witchwrites.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 22 Oct 2026 06:15:12 GMT</lastBuildDate><atom:link href="https://witchwrites.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[CS50 web, Ubuntu, pyenv and submit50 - but I'm a Windows girl...]]></title><description><![CDATA[Setting up submit50 on Windows 11 using pyenv and WSL to follow CS50 for Web only to find out it doesn't work anymore and git is easier]]></description><link>https://witchwrites.hashnode.dev/cs50-web-ubuntu-pyenv-and-submit50-but-im-a-windows-girl</link><guid isPermaLink="true">https://witchwrites.hashnode.dev/cs50-web-ubuntu-pyenv-and-submit50-but-im-a-windows-girl</guid><category><![CDATA[WomenWhoTech]]></category><category><![CDATA[cs50]]></category><category><![CDATA[WSL]]></category><category><![CDATA[pyenv]]></category><category><![CDATA[pyenv-win]]></category><dc:creator><![CDATA[Dominika Wojewska]]></dc:creator><pubDate>Mon, 24 Mar 2025 21:31:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742851716742/413ca4b1-dcd8-4d42-9b3c-ed97101c4992.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I’ve recently started to follow CS50 for Web. Because why not, I have a week of free time between jobs, and I planned to do it for some time. Watching lectures is fun (while knitting, for example), but when I hit Django lecture I started to follow along. First of all, I’m a frontend dev using a Windows machine (Win11) and I try not to touch Linux until absolutely necessary. To follow a Django lecture, I had to install Django, and to install Django I had to install Python. But I didn’t want any Python, to me it’s logical that if there are multiple versions of something, there should be a version manager. Like for Node, there is <code>nvm</code>. After some googling it turned out there is <code>pyenv</code> and even a Windows version called <code>pyenv-win</code>. And there is a nice tutorial for that at <a target="_blank" href="https://pyenv-win.github.io/pyenv-win/docs/installation.html">https://pyenv-win.github.io/pyenv-win/docs/installation.html</a>.</p>
<p>I installed pyenv-win, I installed <a target="_blank" href="https://www.djangoproject.com/">Django</a>, I followed the lecture. Nice! Btw Django is awesome, it may look scary because it’s using templates (a concept in which I haven’t developed anything yet, but I am familiar with the syntax, I even wrote a bit about Twig - a template engine for PHP (I had to learn it for work, but haven’t used it in the end. The article/tutorial/introduction to Twig is <a target="_blank" href="https://witchwrites.hashnode.dev/what-is-twig-and-how-to-start-using-it">here</a>).</p>
<p>Watching lectures and coding along with a demonstration is nice, but at some point one has to work on a project. And if one wants to earn a verified certificate with an official stamp from Harvard University (I am one, I want such a cert), one has to submit the required projects that meet the required criteria. For this, after coding the <a target="_blank" href="https://cs50.harvard.edu/web/2020/projects/0/search/">Project 0</a> (which, btw, I pushed to <a target="_blank" href="https://github.com/WitchDevelops/web50">my GitHub repo</a> first), I had to figure out how to submit it for grading. The instructions told me to install <a target="_blank" href="https://cs50.readthedocs.io/submit50/"><code>submit50</code></a>, to make things easier. And what’s the first thing I see? To use Linux and install Ubuntu, like a proper human person, not a Windows normie… I’m both gaming and developing on my personal machine, I don’t want to ditch Windows… But WSL comes to the rescue. And with the newer WSL2 version (from Win11) it apparently has a dedicated Linux kernel (I don’t really know what it means, but it sounds professional). And I have already installed WSL on another machine, where I had to set up a dev environment for a big project at work. Seems easy, so let’s do it.</p>
<p><strong>Problem:</strong> Install Python, <code>pip</code> and <code>pyenv</code> on a Windows machine using WSL, while keeping Windows versions intact (<code>pyenv-win</code>, Python installed on Windows, <code>pip</code> installed on Windows) so that they don’t clash</p>
<p><strong>Starting point:</strong> Win11</p>
<p><strong>Goal:</strong> be able to use <code>submit50</code></p>
<p><strong>Steps:</strong></p>
<ol>
<li><h2 id="heading-installing-wsl-and-ubuntu">Installing WSL and Ubuntu</h2>
</li>
</ol>
<ul>
<li><p>First, install WSL and Ubuntu, following the official docs: <a target="_blank" href="https://learn.microsoft.com/en-us/windows/wsl/install">https://learn.microsoft.com/en-us/windows/wsl/install</a></p>
</li>
<li><p>Restart the system</p>
</li>
<li><p>Open Ubuntu: open terminal and enter <code>wsl -d Ubuntu</code></p>
</li>
</ul>
<ol start="2">
<li><h2 id="heading-installing-pyenv">Installing pyenv</h2>
</li>
</ol>
<ul>
<li><p>Install <code>pyenv</code> following this tutorial: <a target="_blank" href="https://realpython.com/intro-to-pyenv/#installing-pyenv">https://realpython.com/intro-to-pyenv/#installing-pyenv</a> (also make sure that your system is updated before doing that (<code>sudo apt update</code>)</p>
</li>
<li><p>Verify that <code>pyenv</code> was correctly installed by running <code>pyenv --version</code></p>
</li>
</ul>
<p>And if you have a pyenv-win installed, here is a potential issue. In my case <code>pyenv</code> was trying to execute the Windows version <code>pyenv-win</code> using Windows commands <code>cygpath</code> and <code>cmd</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742839338536/fed57acd-b3eb-4b95-b893-637f6edaf3a9.png" alt class="image--center mx-auto" /></p>
<p>And that’s not what I want.</p>
<p>By running <code>which pyenv</code> I verified that indeed it is looking in the Windows file system:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742839479540/fd2d9e2c-5920-4605-8b0a-c0bfe04870f3.png" alt class="image--center mx-auto" /></p>
<p>Steps to fix this and point my Linux <code>pyenv</code> to correct executable while keeping my Windows version of <code>pyenv-win</code> intact:</p>
<ul>
<li><p>Edit <code>~/.bashrc</code> by typing <code>nano ~/.bashrc</code> in the terminal</p>
</li>
<li><p>Add those lines at the end of the file:</p>
</li>
<li><pre><code class="lang-bash">  <span class="hljs-built_in">export</span> PYENV_ROOT=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.pyenv"</span>
  <span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">$PYENV_ROOT</span>/bin:<span class="hljs-variable">$PATH</span>"</span>
  <span class="hljs-built_in">eval</span> <span class="hljs-string">"<span class="hljs-subst">$(pyenv init --path)</span>"</span>
</code></pre>
</li>
<li><p>Save the file (<code>CTRL + X</code>, then <code>Y</code>, then <code>Enter</code>).</p>
</li>
<li><p>Reload your shell by typing <code>source ~/.bashrc</code></p>
</li>
<li><p>Verify the <code>pyenv</code> location and version again:</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742839754288/f00ff815-bfe3-4853-a2d2-6fb3226af0bb.png" alt /></p>
<ol start="3">
<li><h2 id="heading-installing-python-and-pip">Installing Python and <code>pip</code></h2>
</li>
</ol>
<p>Find what is the latest stable Pythin release at <a target="_blank" href="https://www.python.org/downloads/">https://www.python.org/downloads/</a>. Similarly, check what is the LTS for <code>pip</code> <a target="_blank" href="https://pypi.org/project/pip/">https://pypi.org/project/pip/</a>. Because I have both aleady installed, but on Windows, I run into a similar issue as with <code>pyenv</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742840387910/7e9ae585-269d-4d72-9e97-16d9a0a4fd10.png" alt class="image--center mx-auto" /></p>
<p>It is using the Windows version. Because my bashrc profile already contains the path for pyenv, it should be pointing to the correct pyenv version. But it isn’t. I found that adding another line at the end of the file solved the problem. Add this line to ensure that WSL does not point to Windows pyenv-win: <code>export PATH=$(echo "$PATH" | sed -e 's|/mnt/c/Users/wojew/.pyenv/pyenv-win[^:]*:||g')</code></p>
<p>To do this, edit your bash profile similarly to the steps above. So, to repeat for clarity:</p>
<ul>
<li><p>Type <code>nano ~/.bashrc</code> in the terminal</p>
</li>
<li><p>Scroll down and add this line to the end of the file: <code>export PATH=$(echo "$PATH" | sed -e 's|/mnt/c/Users/wojew/.pyenv/pyenv-win[^:]*:||g')</code></p>
</li>
<li><p>Save the file (<code>CTRL + X</code>, then <code>Y</code>, then <code>Enter</code>).</p>
</li>
<li><p>Reload your shell by typing <code>source ~/.bashrc</code></p>
</li>
<li><p>Now verify pip and python installation on WSL using the commands: <code>pyenv which pip</code> and <code>pyenv which python</code>. On my machine it is not finding the commands, meaning the Windsows installation is not being recognized anymore and I successfully separated them:</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742843083565/5d9977a3-538a-42b4-bd72-be71b199a2b8.png" alt class="image--center mx-auto" /></p>
<p>Now I need to install <code>pip</code> and Python on WSL using <code>pyenv</code>.</p>
<p>LTS for Python as I write it is 3.13.2 and for <code>pip</code> it is <strong>25.0.1. Let’s install them using</strong> <code>pyenv</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742843262771/5bb0732a-fc2b-4cb1-b942-f8aeec37be21.png" alt class="image--center mx-auto" /></p>
<p>Set it as global with <code>pyenv global 3.13.2</code>. Verify with <code>python --version</code> that this Python is actually being used and correctly installed. Now let’s check <code>pip</code>. When installing <code>pyenv</code>, <code>pip</code> should be installed automatically. Let’s verify which version is installed with <code>pip —version</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742844265779/2c37182a-9690-43c6-9c44-1f4aac047fda.png" alt class="image--center mx-auto" /></p>
<p>Since I haven’t worked on any projects yet, let’s upgrade <code>pip</code> to LTS, using <code>python -m pip install --upgrade pip==25.0.1</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742844313548/a0817e31-664d-400d-9e17-102ebb23d543.png" alt class="image--center mx-auto" /></p>
<p>Verify that it is currenlty using the desired <code>pip</code> version.</p>
<p>Now that everything is set up, I can finally submit my Project 0 code for grading using <code>submit50</code>! It took me only… 2 hours of faffing about ;)  </p>
<ol start="4">
<li><h2 id="heading-submitting-work-with-submit50-but-first-you-need-to-install-it">Submitting work with <code>submit50</code> - but first you need to install it</h2>
</li>
</ol>
<p>To install <code>submit50</code> using <code>pyenv</code>-managed version of Python, first check where <code>pip</code> is located using <code>which pip3</code>. If it points to <code>/usr/bin/pip3</code>, it’s installing system-wide, bypassing <code>pyenv</code>. If it’s pointing to <code>/home/[username]/.pyenv/</code>, it is using <code>pyenv</code>. The correct way of using <code>pyenv</code> to install packages is:</p>
<pre><code class="lang-bash">pyenv <span class="hljs-built_in">exec</span> pip install submit50
or
python -m pip install submit50
</code></pre>
<p>However, I run into issues. Rust and Cargo were needed to compile it… At this point I started to question all my choices because I don’t know what I’m doing anymore. Thankfully ChatGPT helped me figure out what to do about it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742844953070/2cb7760b-da27-4598-8a4a-7e84caf4f9ea.png" alt class="image--center mx-auto" /></p>
<p>Hint: See above for details. Eh. ChatGPT says “This means <strong>the package</strong> <code>jellyfish</code> (a dependency of <code>submit50</code>) requires Rust to compile, but Rust is missing in your WSL environment“. Fair. I’ll just provide a screenshot:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742845047297/1306f1f2-d238-4511-b7b2-ee9f0e58d938.png" alt class="image--center mx-auto" /></p>
<p>So I installed Rust and Cargo to be able to use submit50. Great. Re-running the installation was successfull. Hurray!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742845202702/eda2495a-1445-42ec-bafd-6d46933b148b.png" alt class="image--center mx-auto" /></p>
<p>Now I can finally follow the instructions at <a target="_blank" href="https://cs50.readthedocs.io/submit50/#submitting-with-submit50">https://cs50.readthedocs.io/submit50/#submitting-with-submit50</a> to submit my work.</p>
<p>…and I did all that, only to find out that <code>submit50</code> doesn’t work anymore and it is easier to just use git…</p>
<p>yay coding!</p>
]]></content:encoded></item><item><title><![CDATA[Don't interview when ill]]></title><description><![CDATA[Avoid interviewing when sick. Stress and illness can impair cognitive functions, leading to mistakes and missed opportunities]]></description><link>https://witchwrites.hashnode.dev/dont-interview-when-ill</link><guid isPermaLink="true">https://witchwrites.hashnode.dev/dont-interview-when-ill</guid><category><![CDATA[interview questions]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Junior developer ]]></category><dc:creator><![CDATA[Dominika Wojewska]]></dc:creator><pubDate>Wed, 26 Feb 2025 14:11:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740569078943/6d2f6813-919c-40d6-ae52-42b55569fe0e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I did and a technical interview while down with the flu and it cost me a job offer.</p>
<p>It was the final stage of the hiring process: the technical interview. Before this, I received a take-home coding task (it was easy, fetch data from an API, then sort and order it), and many questions were about the code in my solution. "This should be easy, right? I wrote this code. So what if I'm a bit ill," I thought. Wrong, when you're ill, you get penalties to your stats. In my case, INT saves always take a -2 penalty, even if it's "just a cold." My thinking is slowed and sluggish, and I miss incredibly obvious things and make awfully stupid mistakes (same when I'm tired, I miss obvious things and make silly mistakes then). Like trying to return a <code>&lt;p&gt;</code> tag inside another <code>&lt;p&gt;</code> tag because I didn't read the code a line above. The stress of the interview makes this even worse…</p>
<p>I’m currently a lvl 3 frontend dev (2 years of commercial exp + 1 year of learning) and in my current job I do lots of things: I write plenty of small projects in vanilla HTML, CSS (SCSS) and occasionally JavaScript for numerous clients of the company I’m working for, I do quality checks for the work supplied by freelancers (in terms of coding standards and adherence to strict technical requirements), and I write React components for a huge app we’re building. This app is a comprehensive solution for all things e-commerce-related, like inventory management, asset management, web scraper, newsletter, virtual workspaces, and many more. There are coding standards I have to adhere to that I didn’t come up with and had no chance to question (due to constant pressure of tight deadlines and a “work, work, work” mentality). It has a microfrontend architecture that I don’t fully grasp and wouldn’t be able to replicate in my own project (or maybe I would, I never tried, because I’ve never developer such a huge project on my own yet). Despite all that, I still contribute a lot to the project: I write reusable React components, I ensure code quality by participating in code reviews, and when the backend is lagging behind, I use dummy data, then connect them to the frontend once the API is ready. Sometimes it happens that dummy data have a different structure than live data from the API, so I need to correct the types in TypeScript to match those. I introduced a few features from scratch, refactored the code in lots of places, and fixed several bugs, so overall I'm feeling pretty confident in my current role, to the point that I'm trying to level up and get to that “mid” dev position. Also, that's why I didn't think much about revising theory before this interview (also, when sick all you want to do is sleep, not think) or checking my code twice - I wrote it, so I should know the What and the Why. This is what doomed me. Maybe I’m at the peak of mount stupid and this interview put me back in my place (that I know nothing).</p>
<p>When I was studying at the university, I always hated oral exams as they were much more stressful than written ones (for the record: I studied chemistry, so despite being science-oriented, I lack a traditional tech background). I should have treated this interview as an exam and prepared for it, but I didn’t, see the cover image.</p>
<p>The interview started with a simple data structures question: <strong>what is a Set?</strong> My thinking was so slow and I was so stressed that I was confused and thought the interviewer was asking about a state setter method. I used a Set in my code to extract all the company names that the API was returning to display them (without duplicates) in a company selector filter. So, what is a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set">Set</a>? It’s a built-in JavaScript object that lets you store a collection of values without duplicates. I don’t even remember what I answered. Another question: <strong>what is</strong> <a target="_blank" href="https://react.dev/reference/react/StrictMode"><strong>Strict Mode in React</strong></a> <strong>and why is it used?</strong> Well, Vite uses it as a default, and I used Vite to set up my take-home project. It helps to catch bugs like infinite loops and so on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740567101150/e60573b2-4eca-49f0-bdba-f4e90caa6f4d.png" alt class="image--center mx-auto" /></p>
<p>While the full answer should be: Strict Mode is used only during development to help you catch some common bugs early, like missing useEffect cleanups (that's why useEffect hooks are always run twice during development), missing ref cleanup (so ref callbacks are also run twice), and it highlights deprecated APIs.</p>
<p><strong>What are</strong> <code>keys</code> <strong>in React?</strong> Again, I struggled with the answer because I do like to give a proper definition, yet I struggle with properly naming things I’m unsure of. So I went with something along the lines that “<code>keys</code> are like ids for virtual DOM elements, but not really ids”. <a target="_blank" href="https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key">React <code>key</code></a> is an attribute used to uniquely identify an item, for example while mapping over an array. It should be either a unique string or a number. Ideally <code>keys</code> should be included in data rather than generated on the fly. They are needed for DOM updates when the array elements change order due to sorting, addition or deletion. They should be unique between siblings, but can be the same for different JSX nodes in two different arrays. Thing is, I add a <code>key</code> prop automatically when I map over an array - since I learned that it is necessary (or else React throws errors), I never think twice about it, especially at work, where each items in an array comes with a uniqid prop.</p>
<p><strong>What’s the</strong> <a target="_blank" href="https://www.geeksforgeeks.org/difference-between-foreach-and-map-loop-in-javascript/"><strong>difference between</strong> <code>forEach</code> <strong>and</strong> <code>map</code></a><strong>?</strong> That’s another one where I gave a half-good answer. Both are built-in JavaScript methods used to perform actions on each item in an array while iterating over it, but the main difference is that <code>forEach</code> does not modify the original array, while <code>map</code> creates a new array, leaving the original unchanged. There’s another example where it’s easy to fall into doing things automatically—I never needed to modify the original items in an array, so I always use <code>map</code>. Heck, I even get warnings from ESLint in my code when I accidentally use <code>forEach</code>.</p>
<p><strong>What do I use for debugging?</strong> Here my thoughts strayed towards proper debuggers, which I’ve never used because I don’t know how or why. I often use <code>console.log</code> or browser dev tools, check the browser console for errors, check the Network tab for errors in HTTP requests, and I also always check my terminal for TypeScript errors. I use <code>try… catch…</code> in my code, and I use a linter, but this wasn’t part of my answer due to impaired comprehension (-2 to INT). Even W3Schools teaches that <code>console.log</code> is a <a target="_blank" href="https://www.w3schools.com/js/js_debugging.asp">JS debugging technique</a>. Chrome for developers gives a nice overview of how to use the built-in <a target="_blank" href="https://developer.chrome.com/docs/devtools/javascript/">debugger in the browser</a>, and that’s something I need to learn to descend from the peak of my mount stupid.</p>
<p><strong>What is</strong> <a target="_blank" href="https://react.dev/reference/react/useEffect"><code>useEffect</code></a> <strong>used for in React?</strong> It’s used for executing a side effect, which is something that renders a component <a target="_blank" href="https://react.dev/learn/keeping-components-pure#side-effects-unintended-consequences">impure</a> (a pure function is a function that for a given input always returns the same output). A side effect in React is any operation that affects something outside of the component’s scope, such as data fetching, setting a timeout, updating the DOM manually, or interacting with browser storage. Junior devs like me often use useEffect for data fetching (unless they learn how to write their own hooks and/or how to use axios or Tanstack query) without thinking: why? What are we doing here? Fetching data from an API. Why this way, what are the consequences? What is the dependency array and why do we need it? What will happen if we don’t provide it? (The effect will run on every render, which may cause an infinite loop). What will happen if we leave the dependency array empty? (The effect will run only once).</p>
<p><strong>How do you cancel an HTTP request?</strong> By using an <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController">AbortController</a>, which works with the native <code>fetch()</code> method. It allows you to cancel a request before it completes. It is helpful for preventing race conditions between requests, avoiding memory leaks, handling rapid user inputs (it cancels old requests when a new one starts), and optimizing performance. It's a really helpful method.</p>
<p><strong>Why did I use TypeScript?</strong> Side note: there is a way to answer a question wrong. One of those is by being too honest. What I answered: “Out of habit, and because I wanted to show that I can use it.” (I probably added some more words to my answer, but the gist of it was as quoted above.) Luckily, the interviewer kept drilling with questions like: “Why not JavaScript, how is it better, etc.” There is this ongoing debate among developers, JS vs. TS. Some arguments against TS are that it slows you down (type errors everywhere), that you write more code (and a general rule of thumb is: more code means more bugs), and that it is less flexible than JS. To be honest, I lack the experience in JS to be able to tell whether the less flexibility of TS is a good or a bad thing. My main experience writing JS code is conditional logic or DOM manipulation to show and hide elements, some calculations on dates, comparison sliders (e.g., <a target="_blank" href="https://www.vichy.pl/artykul/cs16-feel-the-revolution/a88985.aspx">like here on this project that I recently developed for work</a>), and some fun exercises I did when I was learning <a target="_blank" href="https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/">JS with FreeCodeCamp (legacy course)</a>. I haven’t written a web app in vanilla JS, without using a framework or TypeScript (yet). It may be a good educational exercise to help me appreciate components and strict typing even more. But back to the question: why (how) is <a target="_blank" href="https://www.geeksforgeeks.org/8-reasons-why-you-should-pick-typescript-over-javascript/">TypeScript better than JavaScript</a>? Without going too deep into the whole debate, JS is a loosely typed language. It makes odd type coercions and other strange things. TypeScript prevents that and gives you more control over it. TS allows you to catch errors at compile time, it provides better code documentation (due to types), and it helps catch common errors that stem from <code>undefined</code> and <code>null</code> and write checks in the code to handle such cases.</p>
<p><strong>What is the difference between a type and an interface?</strong> I tend to use them pretty much interchangeably in my code. As a rule, I use an interface for props in a component and a type for data that interacts with the backend. Both are highly similar. According to <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces">TypeScript documentation</a>, almost all features of an interface are available in a type, with the key difference being that an interface is always extendable while a type cannot be reopened to add more properties. So it makes sense to use an interface for a component that may change and get new properties, and a type for data that interacts with an API.</p>
<p><strong>What’s the difference between localStorage and cookies?</strong> I had no idea how to answer this one, because I never read about those. All I know is that they exist and that I can save some user settings (e.g. theme) in localStorage. <a target="_blank" href="https://blog.shahednasser.com/localstorage-vs-cookies-whats-the-difference/">Both are mechanisms to store some data on the client</a> and are networking terms. They differ in amount of data they can hold, the time the data persists, and some other things. Below is a nice tabular overview of the differences:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740574810048/5b08f2c9-9116-461a-8f0e-c725a8aa77c1.png" alt class="image--center mx-auto" /></p>
<p>Do I know any <strong>CSS preprocessors (SASS, LESS) and what do they do that CSS cannot?</strong> Luckily, SASS is one of the very first things I learned on the job. It is used extensively at my current workplace, and compared with regular CSS, it is awesome. It offers plenty of <a target="_blank" href="https://sass-lang.com/documentation/">extra functionality</a>: <a target="_blank" href="https://sass-lang.com/documentation/values/functions/">functions</a>, <a target="_blank" href="https://sass-lang.com/documentation/at-rules/control/if/">conditional logic</a>, mixins, partials, and many more. It works well together with CSS frameworks like Bootstrap as it allows generating a class for every variable. Thanks to partials, you can split your style sheets, and it handles nesting differently than CSS. It also works well with BEM naming rules (also thanks to nesting), which allows writing more concise code. I happen to use it daily, and whenever I need to go back to basic CSS, I feel like there’s something missing.</p>
<p><strong>What is WCAG?</strong> This is an easy one, though I forgot the full name of the abbreviation. It's the <a target="_blank" href="https://www.w3.org/WAI/standards-guidelines/wcag/">Web Content Accessibility Guidelines</a>, which define a set of rules (or guidelines, but I like to think of them as rules) for creating web pages and apps that are accessible to people with disabilities. For example, to make your website easy to navigate using a keyboard or a screen reader, choose a color scheme with <a target="_blank" href="https://webaim.org/resources/contrastchecker/">recommended contrast</a>, use semantic HTML tags (as they help with screen reader navigation), always add descriptive <code>alt</code> attributes to images, include other attributes like labels for form fields, provide visual feedback for focused elements (<code>focus:visible</code>), and so on. Sometimes it means adding an <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA">aria attribute</a> to your HTML elements. The main goal is to make the web friendly and accessible to everyone, not just sighted users with a mouse.</p>
<p>There was also a question, or rather a live coding request, to open the code of my app, share my screen, and make every name show up in red if it has an even number of letters. This is when stress and being sick took over me (I’m also a terminal case of forgetting my own name when observed), and I wrote this monster and wondered why it didn’t work:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740577845888/4341ef8a-43b7-4f6d-891f-b5eb7270381d.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740578113456/112c6f1a-d5b9-402a-a1f3-af6524e7e1aa.png" alt class="image--center mx-auto" /></p>
<p>If I used Next.js for this project, it would probably steer me toward the correct answer by throwing a hydration error, and I would realize that I’m trying to render a <code>&lt;p&gt;</code> tag inside another <code>&lt;p&gt;</code> tag, instead of adding a class dynamically. I assume my interviewers now think I’m a moron and not even a junior.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740578231339/a1938d21-3a61-46d6-95bd-709fa0a1d5a2.png" alt class="image--center mx-auto" /></p>
<p>I also got some questions related to syntax, like the nullish coalescing operator and optional chaining. The thing is, sometimes it isn’t easy to explain what something with a very technical-sounding name is, but if you see the code, you know what it is and when to use it. For instance, I used the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing">nullish coalescing operator</a> (or: <code>??</code>) to set the default view to be a list (there is a switcher between list and grid view):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740576583812/aeeb0651-e398-4116-bc63-a1c2e0fc33ea.png" alt class="image--center mx-auto" /></p>
<p>it means that if there is no value (<code>null</code>) in localStorage with the key <code>isListView</code>, set it to <code>true</code>. It is similar to <code>||</code> (logical <code>OR</code> operator), but slightly different, as it is a special case of logical OR.</p>
<p>Logical or <code>||</code> returns the first truthy value (so it checks for <code>null</code>, <code>undefined</code>, <code>0</code>, empty string <code>““</code>, <code>NAN</code> or <code>false</code>)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740577050445/5661488c-f321-4c47-bd6c-c8873a11e3f0.png" alt class="image--center mx-auto" /></p>
<p>While nullish coalescing <code>??</code> returns the first defined (non-null) value, so it only checks for <code>null</code> or <code>undefined</code> values:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740576997582/9a97848f-7e9c-49bb-8f99-158479a8c906.png" alt class="image--center mx-auto" /></p>
<p>Another syntax-related question was about <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining">optional chaining</a>. I use it a lot in JSX when accessing properties of objects. Sometimes a property doesn’t exist, so it is always safer to access it with optional chaining <code>?.</code>. Then, if that property is not accessible, the expression short-circuits and returns undefined instead of throwing an error (in this case, availableCountries may be undefined because it comes from the data from the API).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740577419905/32c35fbd-68b1-418b-812f-01d89c1d8bb6.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740577538727/0a29e7ea-39c0-4784-a34d-45031ec2a8f0.png" alt class="image--center mx-auto" /></p>
<p>(here is also that Set that opened the interview. Why did I want a Set here? Because I didn’t want any duplicates in the dropdown options).</p>
<p>Learning to code is a long and perilous journey. You may feel like you've escaped Tutorial Hell by building your own projects, like me, but you might find yourself at the Peak of Mount Stupid. It’s only slightly better than Tutorial Hell, but if you’re here, it means you know how to do things, but you don’t know why those things are done. To descend from the Peak of Mount Stupid, start not only reading the docs of the technologies you’re using but also questioning the way things are done. Why are we doing things this way? Why is this way better than the other? What’s the difference between x and y? Why did you choose tech x over tech y for your project? Asking the “how?” questions will only give you knowledge, but the “why?” questions will give you an understanding of underlying principles. And this is what helps you get down from the Peak of Mount Stupid. That’s what I will try to do.</p>
<p><strong>References:</strong></p>
<p>Cover image: <a target="_blank" href="https://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Dunning%E2%80%93Kruger_Effect_01.svg/2462px-Dunning%E2%80%93Kruger_Effect_01.svg.png">https://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Dunning%E2%80%93Kruger_Effect_01.svg/2462px-Dunning%E2%80%93Kruger_Effect_01.svg.png</a></p>
<p><a target="_blank" href="https://hashnode.com/n/womenwhotech"><strong>#WomenWhoTech</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[How to add a PDF file preview to your (React) website and not lose your mind]]></title><description><![CDATA[Learn how to integrate PDF previews into your React website using iframes, libraries, and more without the hassle]]></description><link>https://witchwrites.hashnode.dev/how-to-add-pdf-file-preview-to-your-react-website-and-dont-lose-your-mind</link><guid isPermaLink="true">https://witchwrites.hashnode.dev/how-to-add-pdf-file-preview-to-your-react-website-and-dont-lose-your-mind</guid><category><![CDATA[pdf viewer]]></category><category><![CDATA[React]]></category><category><![CDATA[npm packages]]></category><dc:creator><![CDATA[Dominika Wojewska]]></dc:creator><pubDate>Fri, 14 Feb 2025 19:24:09 GMT</pubDate><content:encoded><![CDATA[<p>It should be easy, right?</p>
<p><em>tl;dr I wanted to add a PDF file preview inside my web page, and I regretted all my life choices</em></p>
<p>I’ve recently had this idea to add a <code>.pdf</code> file preview to my personal portfolio. Not to open it in a new tab, not to have it only available for a download, but to have the preview right there, sitting among other content of my web page.</p>
<p>The file is my CV, and the idea came from the fact that I started messing around with my portfolio, to expand and improve it. One of the ideas was to add more pages, one of them to be a dedicated page “For Recruiters” with my all skills and experiences, and to include a CV as a <code>.pdf</code> file as well. Google search returned some YouTube videos and some npm packages, I tried a few and decided to write about it.</p>
<h2 id="heading-the-way-i-didnt-want-nice-and-easy-using-an-tag">The way I didn’t want - nice and easy, using an <code>&lt;a&gt;</code> tag</h2>
<p>Using an anchor tag allows you to either open the file in a new tab, or to easily download it.</p>
<p>To open the file in a new tab:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/files/chicken.pdf"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>Open in a new tab<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>or to download it:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/files/chicken.pdf"</span> <span class="hljs-attr">download</span>&gt;</span>Download this file<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Problem is, this is not the behavior that I wanted.</p>
<h2 id="heading-the-other-easy-way-use-an-universal-and-simple-solution">The other easy way - use an <code>&lt;iframe&gt;</code> - universal and simple solution</h2>
<p>This brief and to the point video tutorial from <a target="_blank" href="https://www.youtube.com/watch?v=Ll7PA0dtpW0&amp;ab_channel=dcode">dcode</a> shows the simplest way to do this: add an <code>&lt;iframe&gt;</code> to your page and display the <code>.pdf</code> there. It’s easy enough and framework-agnostic, but it has a few drawbacks:</p>
<ul>
<li><p>some browsers have built-in <code>.pdf</code> previewers, others do not and force a file download</p>
</li>
<li><p>if the file is hosted externally, it may cause some CORS issues</p>
</li>
<li><p><code>&lt;iframe&gt;</code> has limited mobile support and may not work on some older mobile browsers (see: <a target="_blank" href="https://caniuse.com/?search=iframe">caniuse</a>)</p>
</li>
</ul>
<p>If you decide to use this method, simply add an <code>&lt;iframe&gt;</code> tag to your HTML markup:</p>
<pre><code class="lang-xml"><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> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</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">charset</span>=<span class="hljs-string">"UTF-8"</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, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</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">iframe</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"path/to/pdfFile.pdf"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">height:</span> "<span class="hljs-attr">100vh</span>", <span class="hljs-attr">width:</span> "<span class="hljs-attr">100</span>%" }}&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">iframe</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>This method also allows for searching the content of the file, which is neat. Here’s a Chrome preview (on the example of a timeless classic, the “chicken chicken” article):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739552479131/78e24dfd-295e-451c-ad1c-364f17ea46a0.png" alt class="image--center mx-auto" /></p>
<p>The main problem with this approach is that I have no control whatsover of how the file preview is gonna look like on different browsers. It works and is simple, but there are other ways to achieve this.</p>
<h2 id="heading-the-advanced-way-using-a-library">The advanced way - using a library</h2>
<p>There are several npm packages that handle <code>.pdf</code> files. I tried <a target="_blank" href="https://mozilla.github.io/pdf.js/"><strong>pdf.js</strong></a> from Mozilla, but it felt a bit too complex in use and documentation isn’t that great. I tried <a target="_blank" href="https://projects.wojtekmaj.pl/react-pdf/"><strong>react-pdf</strong></a> and it was a better, smoother experience. But still things didn’t work out of the box, there was an overflow on the page and the width of the file wasn’t responsive. So overall, I didn’t like it much. If you want to try it, here is some setup:</p>
<h3 id="heading-react-pdf">react-pdf</h3>
<ol>
<li><p><code>npm install react-pdf</code></p>
</li>
<li><p>in your <code>App.jsx</code> file, add those two lines:</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { pdfjs } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-pdf"</span>;
pdfjs.GlobalWorkerOptions.workerSrc = <span class="hljs-string">`//unpkg.com/pdfjs-dist@<span class="hljs-subst">${pdfjs.version}</span>/build/pdf.worker.min.mjs`</span>;
</code></pre>
<ol start="3">
<li>Then create your component, for example:</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-comment">// PDFViewer.jsx</span>

<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Document, Page } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-pdf"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'react-pdf/dist/Page/AnnotationLayer.css'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'react-pdf/dist/Page/TextLayer.css'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ReactPDFViewer = <span class="hljs-function">(<span class="hljs-params">{ src }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [numPages, setNumPages] = useState&lt;number&gt;();
  <span class="hljs-keyword">const</span> [pageNumber, setPageNumber] = useState&lt;number&gt;(<span class="hljs-number">1</span>);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onDocumentLoadSuccess</span>(<span class="hljs-params">{ numPages }: { numPages: number }</span>): <span class="hljs-title">void</span> </span>{
    setNumPages(numPages);
  }

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">nextPage</span>(<span class="hljs-params"></span>) </span>{
    setPageNumber(<span class="hljs-function">(<span class="hljs-params">v</span>) =&gt;</span> ++v);
  }

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">prevPage</span>(<span class="hljs-params"></span>) </span>{
    setPageNumber(<span class="hljs-function">(<span class="hljs-params">v</span>) =&gt;</span> --v);
  }
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">width:</span> "<span class="hljs-attr">100vw</span>", <span class="hljs-attr">height:</span> "<span class="hljs-attr">100</span>%" }}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{prevPage}</span> <span class="hljs-attr">disabled</span>=<span class="hljs-string">{pageNumber</span> &lt;= <span class="hljs-string">1}</span>&gt;</span>
        Previous
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{nextPage}</span> <span class="hljs-attr">disabled</span>=<span class="hljs-string">{pageNumber</span> &gt;</span>= (numPages ?? -1)}&gt;
        Next
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Document</span>
        <span class="hljs-attr">file</span>=<span class="hljs-string">{src}</span>
        <span class="hljs-attr">onLoadSuccess</span>=<span class="hljs-string">{onDocumentLoadSuccess}</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"my-react-pdf"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Page</span> <span class="hljs-attr">pageNumber</span>=<span class="hljs-string">{pageNumber}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Document</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        Page {pageNumber} of {numPages}
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
</code></pre>
<p>And here is the result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739555342460/8ed8978e-ef4b-4479-bf61-7ae4fc0c5aa0.png" alt class="image--center mx-auto" /></p>
<p><strong>Good things</strong>: it allows for viewing the pages with the buttons, which is nice, and the content of the file is searchable. But I failed to figure out where that overflow was coming from, and I don’t want any overflow on my portfolio. To fix the overflow, I run <code>npm uninstall react-pdf</code> and just like that, the overflow was gone. Nice. (well, not really, because this package has MIT license).</p>
<p>Another npm package with MIT license is <a target="_blank" href="https://www.npmjs.com/package/reactjs-pdf-reader"><strong>reactjs-pdf-reader</strong></a>. It looks easy enough to use from the docs, but…</p>
<p>Docs:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739556917728/1e2e5d84-c6a1-426f-a737-4b0dc767c9e2.png" alt class="image--center mx-auto" /></p>
<p>My project:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739556885304/920962f4-b004-459f-b500-690e484e8ec7.png" alt class="image--center mx-auto" /></p>
<p>Oh well. At least this package doesn’t cause a mysterious overflow, so there is that.</p>
<h3 id="heading-react-pdf-viewer">react-pdf-viewer</h3>
<p>That’s actually one of the first search results in google, but it’s paid, that’s why I wanted to be sure that I’ve exhausted all the other options before trying it. Its documentation is very clear and extensive, it has a ton of customization options, and costs <a target="_blank" href="https://react-pdf-viewer.dev/purchase/"><em>only</em> 50 bucks</a> (in my home currency that’s a lot… so I can’t afford it). Too bad, because it’s great, works out of the box and is open source. But weirdly enough - open source, but you need a licence to use it? no comprende. Anyway, here is how to <s>not</s> set it up in your react project.</p>
<ol>
<li><p>Go to <a target="_blank" href="https://react-pdf-viewer.dev/docs/getting-started/">https://react-pdf-viewer.dev/docs/getting-started/</a> and follow the steps.</p>
</li>
<li><p><code>npm install pdfjs-dist@3.4.120</code></p>
</li>
<li><p><code>npm install @react-pdf-viewer/core@3.12.0</code></p>
</li>
<li><p>Set up the Worker:</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Worker } <span class="hljs-keyword">from</span> <span class="hljs-string">'@react-pdf-viewer/core'</span>;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Worker</span> <span class="hljs-attr">workerUrl</span>=<span class="hljs-string">"https://unpkg.com/pdfjs-dist@3.4.120/build/pdf.worker.min.js"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- The viewer component will be put here --&gt;</span>
    ...
<span class="hljs-tag">&lt;/<span class="hljs-name">Worker</span>&gt;</span></span>
</code></pre>
<p>This can be done either at the root of your app, or in the component that will be rendering previews of pdfs.</p>
<ol start="5">
<li>Import the Viewer and styles and use it!</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Worker, Viewer } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-pdf-viewer/core"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@react-pdf-viewer/core/lib/styles/index.css"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> PDFViewer = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Worker</span> <span class="hljs-attr">workerUrl</span>=<span class="hljs-string">"https://unpkg.com/pdfjs-dist@3.4.120/build/pdf.worker.min.js"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Viewer</span> <span class="hljs-attr">fileUrl</span>=<span class="hljs-string">"path/to/pdfFile.pdf"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Worker</span>&gt;</span></span>
  );
};
</code></pre>
<p>This can be extended by a plethora of options, like loader, theme, default scale and many more. There’s also plenty of plugins. What’s really nice about this package is that it automatically resizes the <code>.pdf</code> viewbox to the size of the screen for better reading experience and that you can directly interact with the file content, as if you were using a standalone <code>.pdf</code> reader. Here is a preview:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739558832062/89a7b65b-7429-41ac-b9f7-0135a507459a.png" alt class="image--center mx-auto" /></p>
<p>And it also looks great in dark mode (Mozilla):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739561433384/4f28b920-fdd5-4804-bd01-91c7bea44a72.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-verdict">Verdict</h2>
<p>To sum up, the best <strong>FREE</strong> solution for my problem is to use an iframe. No hassle and it just works. If you have money, though, the best solution is the <strong>react-pdf-viewer</strong> package.</p>
<p>That’s it, happy coding!</p>
<p><strong>Bonus</strong><br />There is also a nice little library for creating pdfs online. <a target="_blank" href="https://react-pdf.org/">https://react-pdf.org/</a></p>
<p>There is some library that is under development, but for now it only has a shiny landing page and <a target="_blank" href="https://dev.to/9haroon/best-4-methods-to-build-a-pdf-viewer-in-reactjs-pdfjs-react-pdf-and-more-2024-guide-1613">an article on dev.to</a>, but - useless - I cannot use it yet. <a target="_blank" href="https://www.react-pdf.dev/">https://www.react-pdf.dev/</a></p>
]]></content:encoded></item><item><title><![CDATA[Preview Your Local Website on a Real Mobile Device]]></title><description><![CDATA[Learn to preview your local website on a real mobile device in a few simple steps]]></description><link>https://witchwrites.hashnode.dev/preview-your-local-website-on-a-real-mobile-device</link><guid isPermaLink="true">https://witchwrites.hashnode.dev/preview-your-local-website-on-a-real-mobile-device</guid><category><![CDATA[web dev]]></category><category><![CDATA[Responsive Web Design]]></category><category><![CDATA[ip address]]></category><dc:creator><![CDATA[Dominika Wojewska]]></dc:creator><pubDate>Sun, 09 Feb 2025 15:18:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739114254793/c7ada7d9-0e0e-4997-8e63-be8ab98d5596.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever wondered how your web project looks on a <strong>real</strong> mobile device while you're developing it? And why should you even care when browser developer tools are at your disposal?</p>
<p>Well, the thing is, some devices (<em>looking at you, Apple</em>) tend to handle certain CSS declarations in unexpected ways. That’s why it’s always a good practice to check how your site looks on an actual mobile device. I was recently reminded of this the hard way - my project looked perfect on my Windows machine, and everything seemed fine in the browser’s simulated device views. I typically test on a <strong>narrow mobile (360px wide), a larger mobile, iPhones, two tablet variants, a standard desktop (1920×1080), and a larger display (2560×1440)</strong>. But despite these checks, my client wasn’t happy with how certain parts of the project appeared on his <strong>iPhone</strong>.</p>
<p>So, if you’re new to web development or just need a quick refresher, here’s an <strong>easy way to preview your local web project on a real mobile device - without installing any extra software or emulators.</strong></p>
<h1 id="heading-step-by-step-guide">Step-by-step guide</h1>
<h2 id="heading-step-1">Step 1.</h2>
<p>Open up a local port and make sure you’re project is being served there. Let’s say, you’re working on a Next.js project. Next.js <code>npm run dev</code> opens a local port 3000:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739109933902/b9ca06fb-e2c0-40dd-a60d-454316d2b25d.png" alt /></p>
<p>So when you open your browser and enter <a target="_blank" href="http://localhost:3000/"><code>http://localhost:3000/</code></a>, you will see your project there.</p>
<h2 id="heading-step-2">Step 2.</h2>
<p>Make sure your mobile device is connected to the sam`e network as your PC/Mac.</p>
<h2 id="heading-step-3">Step 3.</h2>
<p>Find out what is your IPv4 address.</p>
<h3 id="heading-windows">Windows</h3>
<p>If you’re on a Windows machine, use the command <code>ipconfig</code> in your terminal. Then you will see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739110446806/93d5c952-3594-4554-bf4c-5ed50eaf450a.png" alt class="image--center mx-auto" /></p>
<p>Find the line that says ‘IPv4 address”. This is what you need. There are also other ways to find out your IP address on Windows (1).</p>
<h3 id="heading-mac">Mac</h3>
<p>If you’re on a Mac machine, the command is a bit different. Enter <code>ipconfig getifaddr en0</code> in your terminal. As the output you should get your IP address. There are also other ways to find out your IP address from a Mac (2).</p>
<h2 id="heading-step-4">Step 4.</h2>
<p>Once you have your local IP address, enter in in the browser of your mobile device, followed by the port at which your local project is running. So, in my previous example of a Next.js app, that would be <code>http://[yourIPv4Address]:3000</code>.</p>
<h2 id="heading-step-5">Step 5</h2>
<p>Enjoy! Now you can live preview your project on your other devices, like a pro.</p>
<p><mark>⚠ </mark> <strong><mark>Important Note:</mark></strong> Some firewalls or security settings may block access. If it doesn’t work, check your firewall settings and allow incoming connections for your development server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739112196801/5c78fb78-bfca-41bc-89f1-8ff5106e8987.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-explanation">Explanation</h1>
<p>This is neat, but why does it work?</p>
<p>It all comes down to basic networking. Your local IP address (formatted as <code>192.168.x.x</code>) is a unique identifier for your machine <strong>within your local network</strong>. When you run a development server (e.g., React, Next.js, or a local server like XAMPP), your computer <strong>listens for incoming connections on a specific port</strong> (e.g., 3000, 9001, etc.).</p>
<p>On your mobile device, entering your <strong>local IP address followed by the port number</strong> tells the browser to connect to your computer’s server over the same Wi-Fi network. This allows you to preview your locally hosted website on a real mobile device.</p>
<h1 id="heading-references">References</h1>
<p>(1) <a target="_blank" href="https://support.microsoft.com/en-us/windows/essential-network-settings-and-tasks-in-windows-f21a9bbc-c582-55cd-35e0-73431160a1b9">https://support.microsoft.com/en-us/windows/essential-network-settings-and-tasks-in-windows-f21a9bbc-c582-55cd-35e0-73431160a1b9</a> - IP address on Windows</p>
<p>(2) <a target="_blank" href="https://www.wikihow.com/Find-Your-IP-Address-on-a-Mac">https://www.wikihow.com/Find-Your-IP-Address-on-a-Mac</a> - IP address on Mac</p>
]]></content:encoded></item><item><title><![CDATA[What is Twig and how to start using it]]></title><description><![CDATA[Elevate your web development with Twig templates. Create efficient, SEO-friendly designs for high-performance web applications.]]></description><link>https://witchwrites.hashnode.dev/what-is-twig-and-how-to-start-using-it</link><guid isPermaLink="true">https://witchwrites.hashnode.dev/what-is-twig-and-how-to-start-using-it</guid><category><![CDATA[PHP]]></category><category><![CDATA[twig]]></category><category><![CDATA[Symfony]]></category><category><![CDATA[template-engine]]></category><dc:creator><![CDATA[Dominika Wojewska]]></dc:creator><pubDate>Fri, 06 Oct 2023 17:22:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1696609062349/f117f3d8-68d0-429b-ba50-4d71413ea850.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Prerequisites: PHP</em></p>
<h1 id="heading-what-is-twig">What is Twig?</h1>
<p><a target="_blank" href="https://twig.symfony.com/">Twig</a> is a widely used and popular <strong>template engine</strong> for PHP. It provides a flexible, efficient, and secure way to <strong>separate the presentation</strong> (HTML or other markup) <strong>from the application logic</strong> in PHP-based web applications. Twig was originally developed as a part of the <a target="_blank" href="https://symfony.com/">Symfony</a> web framework but has since gained popularity in its own right and is used independently in various PHP projects. If you're coming from the frontend side of things, you may think of the Twig templates as something similar to React components, because the main architectural principles are the same: you compose a web app from building blocks, which display some data and can be conditionally rendered. The main difference though is that Twig is more "basic", has simpler syntax and is based on PHP.</p>
<h2 id="heading-what-is-symfony">What is Symfony?</h2>
<p>Symfony is a high-performance <strong>PHP web application framework</strong> that facilitates the development of web applications and APIs. It follows the Model-View-Controller (MVC) architectural pattern and provides a wide range of reusable components and tools for building robust, scalable, and maintainable web applications. Symfony is open-source, well-documented, and has a large and active community of developers and contributors. You can learn more about it from this <a target="_blank" href="https://symfony.com/book">free ebook</a> written by its creator.</p>
<h2 id="heading-what-is-a-template-engine-anyway">What is a template engine anyway?</h2>
<p>A <strong>template engine</strong> is a software component or system that facilitates the s<strong>eparation of data and presentation</strong>, also called the <strong>separation of concerns.</strong> Its primary purpose is to generate dynamic content by combining <strong>data</strong> (often referred to as <strong><em>the model</em></strong>) with <strong>templates</strong> that define the structure and layout of the final output (often referred to as <strong><em>the view</em></strong>). <strong>Separation of concerns</strong> makes the code more maintainable and easier to work with, as changes to the presentation do not require modifications to the underlying logic. Template engines provide many more useful features, such as reusable templates, dynamic content rendering and improved performance. They are commonly used in web development but can be found in other types of software as well and are especially valuable when creating applications that display dynamic data retrieved from databases, APIs, or user input.</p>
<h2 id="heading-and-what-are-twig-templates">And what are Twig templates?</h2>
<p>Twig templates are essentially HTML files that include bits of Twig code. When the template is loaded, the placeholders in the Twig code are compiled to HTML and the resulting HTML is rendered in the browser.</p>
<h1 id="heading-twig-syntax">Twig syntax</h1>
<p>Twig has its own syntax that looks like a mix of PHP and HTML. The delimiters of Twig are curly braces <code>{ ... }</code> coupled with other characters that signify what sort of Twig code it is. In brief, three basic types of Twig syntax are as follows:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>syntax</td><td>meaning</td><td>purpose</td></tr>
</thead>
<tbody>
<tr>
<td><code>{{ ... }}</code></td><td>print something</td><td>used to display the content of a variable or the result of evaluating an expression</td></tr>
<tr>
<td><code>{% ... %}</code></td><td>do something</td><td>used to run some logic</td></tr>
<tr>
<td><code>{# ... #}</code></td><td>comment something</td><td>used to comment the code</td></tr>
</tbody>
</table>
</div><p>All the Twig's syntax is based on the above principle. Below are some more specific examples.</p>
<h2 id="heading-comments">Comments</h2>
<p>Twig supports single-line comments using <code>{# ... #}</code>. The main feature of Twig comments is that they are only visible in Twig files, they do not get parsed to HTML comments.</p>
<pre><code class="lang-php">{<span class="hljs-comment"># This is a comment #}</span>
</code></pre>
<h2 id="heading-output-variables">Output variables</h2>
<p>To output a variable's value in Twig, enclose it in double curly braces <code>{{ ... }}</code>. For example:</p>
<pre><code class="lang-php">&lt;p&gt;{{ username }}&lt;/p&gt;
</code></pre>
<h2 id="heading-filters">Filters</h2>
<p>Filters modify the output of a variable. You can apply filters using the pipe <code>|</code> symbol followed by the filter name.</p>
<pre><code class="lang-php">{{ variableName | filterName }}
</code></pre>
<p>Twig filters are functions that allow you to modify or manipulate data within your templates. Conceptually, they are similar to inbuilt functions or methods in other programming languages. For instance, Twig filter <code>upper</code> achieves the same output as JavaScript's string method <code>toUpperCase</code>. Twig filters take an input value, perform an operation on it, and return a modified value. Filters in Twig are used to format, filter, or transform variables or expressions within your templates. You can read more about Twig filters in <a target="_blank" href="https://twig.symfony.com/doc/3.x/filters/index.html">the docs</a>.</p>
<h2 id="heading-control-structures">Control Structures</h2>
<p>Twig provides control structures like <code>if</code>, <code>else</code>, <code>elseif</code>, <code>for</code>, and <code>block</code> for conditional rendering and looping. They require an opening <code>{%</code> and a closing <code>%}</code> tag, sometimes with a nested tag in between:</p>
<pre><code class="lang-php">{% <span class="hljs-keyword">if</span> condition %}
    Content to display when condition is <span class="hljs-literal">true</span>
{% <span class="hljs-keyword">else</span> %}
    Content to display when condition is <span class="hljs-literal">false</span>
{% <span class="hljs-keyword">endif</span> %}
</code></pre>
<h2 id="heading-loops">Loops</h2>
<p>Twig's <code>for</code> loop is used for iterating over arrays and other iterable objects. It uses the <code>for... in</code> syntax.</p>
<pre><code class="lang-php">{% <span class="hljs-keyword">for</span> item in items %}
    {{ item }}
{% <span class="hljs-keyword">endfor</span> %}
</code></pre>
<h2 id="heading-strings">Strings</h2>
<p>Strings are classically wrapped in either double or single quotation marks, but not the <code>`...` </code> backtics. Similar to HTML, if your string contains quotation marks, you can either escape the quotation mark with a backslash <code>\</code> or use the other type of quotation mark.</p>
<pre><code class="lang-php">{% set greeting = <span class="hljs-string">"Hello there"</span> %}
{% set heading = <span class="hljs-string">'Try the new 7" folding tablet'</span> %}
{% set subheading = <span class="hljs-string">"The original Microsoft Surface was 3.5' long."</span> %}
{% set alt_subheading = <span class="hljs-string">"The original Microsoft Surface was 3' 6\" long."</span> %}
</code></pre>
<p>To combine strings, you can either use string concatenation with tilde <code>~</code> or use string interpolation <code>#{...}</code>. Important! Interpolation only works with double-quoted strings.</p>
<pre><code class="lang-php">{<span class="hljs-comment"># Concatenation #}</span>
{% set greeting = <span class="hljs-string">"Hello, "</span> ~ currentUser.friendlyName %}

{<span class="hljs-comment"># Interpolation #}</span>
{% set greeting = <span class="hljs-string">"Hello, #{currentUser.friendlyName}"</span> %}
</code></pre>
<h1 id="heading-templates">Templates</h1>
<h2 id="heading-template-inheritance">Template inheritance</h2>
<p>The most powerful feature of Twig is template inheritance. It allows you to build a base "skeleton" of a page, that contains all the common elements of your site and defines blocks that child templates can override. Imagine a typical page (or open one, e.g. <a target="_blank" href="https://www.freecodecamp.org/">https://www.freecodecamp.org/</a>): it has three main parts (blocks): header/navigation bar, main content, and a footer. The navbar and the footer stay the same for all the subpages, while the main content differs for each page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696607061850/449242d5-b78e-49b5-aa2d-1f44ac51d2d4.png" alt /></p>
<p>To avoid repetition (the DRY principle), Twig allows us to create a 'master' template with those main blocks. Let's call it <code>base.html.twig</code>. Then based on this master template we can write child templates and populate them with content specific for each subpage, using the <code>extends</code> syntax.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696607826945/893a3d8d-51f3-4cb4-82a3-12ae08726d99.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-php">{<span class="hljs-comment"># In base.html.twig #}</span>
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;{% block title %} <span class="hljs-keyword">Default</span> Title {% endblock %}&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;header&gt;
        [header text, main navigation]
    &lt;/header&gt;
    &lt;main&gt;
        {% block content %}{% endblock %}
    &lt;/main&gt;
    &lt;footer&gt;
        [footer text]
    &lt;/footer&gt; 
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<pre><code class="lang-php">{<span class="hljs-comment"># In child.html.twig #}</span>
{% <span class="hljs-keyword">extends</span> <span class="hljs-string">'base.html.twig'</span> %}

{% block title %} The title of this page {% endblock %}

{% block content %}
    Content specific to this page
{% endblock %}
</code></pre>
<p>As you already noticed, to define a block of content in Twig we specify it's a block and give it a name <code>{% block [name of this block] %}</code> . Similarly to all other Twig code, it needs a closing tag: <code>{% endblock %}</code>. Now to override the content for a particular block, in the child template we need to specify a block element with the same name, so that Twig knows which block are we referring to, and specify the content for this particular page.</p>
<h2 id="heading-include">Include</h2>
<p>It is also possible to render a part of a template using the <code>include</code> syntax. It is used to specify reusable building blocks that can stand on it's own. You can create with it, for example, buttons, form fields, navigation elements, blocks of content or anything else that is a building block of your webpage. Twig templates that are loaded into other templates with the <code>include</code> syntax are independent of other templates. The main difference from the <code>block</code> elements is that the templates loaded with the <code>include</code> syntax have to be specified in separate files. For example, to define a base skeleton template from the previous example, with a navigation, main content and a footer, we can use code like this:</p>
<pre><code class="lang-php">{<span class="hljs-comment"># In base.html.twig #}</span>
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;{% block title %} <span class="hljs-keyword">Default</span> Title {% endblock %}&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    {{ <span class="hljs-keyword">include</span>(<span class="hljs-string">'nav.twig'</span>) }}
    &lt;main&gt;
        {% block content %}{% endblock %}
    &lt;/main&gt;
    {{ <span class="hljs-keyword">include</span>(<span class="hljs-string">'footer.twig'</span>) }}
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>then our <code>nav.twig</code> and <code>footer.twig</code> can look like this:</p>
<pre><code class="lang-xml">{# In nav.twig #}
<span class="hljs-tag">&lt;<span class="hljs-name">nav</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">li</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>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><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><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/variables"</span>&gt;</span>Variables<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><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><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/conditionals"</span>&gt;</span>Conditionals<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><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><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/loops"</span>&gt;</span>Loops<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><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><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/templates"</span>&gt;</span>Templates<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><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><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://twig.symfony.com/doc/3.x/"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>Docs<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><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">nav</span>&gt;</span>
</code></pre>
<pre><code class="lang-xml">{# In footer.twig #}
<span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
    Styled with ❤️ <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://simplecss.org/"</span>&gt;</span>simpleCSS<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</code></pre>
<h2 id="heading-block-extends-include-whats-the-difference">`Block, extends, include - what's the difference?</h2>
<p>Although at first it may seem confusing what are the differences and similarities between the <code>block</code>, <code>extends</code> and <code>include</code> syntax elements or when each of them should be used, it is rather simple.</p>
<ul>
<li><p><code>include</code> is used for reusing templates independently</p>
</li>
<li><p><code>extends</code> establishes template inheritance</p>
</li>
<li><p><code>block</code> defines and overrides content within templates following the inheritance hierarchy</p>
</li>
</ul>
<p>The choice between these syntax elements depends on your specific needs: reuse, layout consistency, and content customization within your Twig templates. In more details, here is an in-depth comparison.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><code>include</code></td><td><code>extends</code></td><td><code>block</code></td></tr>
</thead>
<tbody>
<tr>
<td>Purpose</td><td>Reusable templates: Include content from another template.</td><td>Template inheritance: Extend a base template and override specific blocks.</td><td>Define and override content within templates.</td></tr>
<tr>
<td>Syntax Example</td><td><code>{% include 'template.html.twig' %}</code></td><td><code>{% extends 'base.html.twig' %}</code></td><td><code>{% block block_name %}...{% endblock %}</code></td></tr>
<tr>
<td>Template Inheritance</td><td>Not used for template inheritance.</td><td>Used to specify the base template for inheritance.</td><td>Child templates override blocks in the base template.</td></tr>
<tr>
<td>Base Template Structure</td><td>No concept of a base template.</td><td>Serves as the base template for child templates.</td><td>Contains placeholders (blocks) for child templates to fill.</td></tr>
<tr>
<td>Child Template Structure</td><td>Independent of other templates.</td><td>Extends and overrides blocks from the base template.</td><td>Overrides blocks defined in the base template.</td></tr>
<tr>
<td>Extending Multiple Templates</td><td>Not applicable.</td><td>Can extend only one base template.</td><td>Not applicable.</td></tr>
<tr>
<td>Nesting Templates</td><td>Not applicable.</td><td>Can be extended further by other templates.</td><td>Not applicable.</td></tr>
<tr>
<td>Default Block Content</td><td>Not applicable.</td><td>Blocks in the base template can have default content.</td><td>Blocks in child templates can have default content.</td></tr>
<tr>
<td>Overriding Blocks</td><td>Not applicable.</td><td>Child templates can override blocks from the base template.</td><td>Blocks can be overridden by child templates.</td></tr>
<tr>
<td>Order of Rendering</td><td>Renders independently where included.</td><td>Child templates extend and modify the base template.</td><td>Blocks in child templates replace corresponding blocks in the base template.</td></tr>
<tr>
<td>Inclusion of Variables and Context</td><td>Variables can be passed when including a template.</td><td>Child templates share the same context as the base template.</td><td>Variables can be passed to child templates within blocks.</td></tr>
</tbody>
</table>
</div><h1 id="heading-installing-twig">Installing Twig</h1>
<p>To install Twig, you can use Composer, which is the recommended package manager for PHP projects. If you don't have Composer installed, follow the steps here: <a target="_blank" href="https://getcomposer.org/doc/00-intro.md">https://getcomposer.org/doc/00-intro.md</a>. First, make sure you have PHP installed by running:</p>
<pre><code class="lang-bash">php -v
</code></pre>
<p>in your terminal. If you don't have PHP installed, follow the steps here: <a target="_blank" href="https://www.php.net/manual/en/install.php">https://www.php.net/manual/en/install.php</a>.</p>
<p>Once you have installed both PHP and the Composer package you can install Twig for your project by following the steps here:</p>
<ol>
<li><strong>Create a New PHP Project</strong> (if not already done):</li>
</ol>
<p>If you don't have an existing PHP project, create a new directory for your project and navigate to it using your terminal or command prompt.</p>
<ol>
<li><p><strong>Install Symfony locally</strong>. Navigate to your project root directory and add Symfony using Composer:</p>
<pre><code class="lang-apache"> <span class="hljs-attribute">composer</span> require symfony/symfony
</code></pre>
<p> This will add a folder named <code>vendor</code> to your project, as well as two files: <code>composer.json</code> and <code>composer.lock</code>. Symfony will show up in dev dependencies in your <code>composer.json</code> file.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696419764980/d364839e-559b-4965-b2b7-f44b30e505d1.png" alt="composer.json file. { &quot;require-dev&quot;: { &quot;symfony/symfony&quot;: &quot;^6.3&quot; (v.6.3.5)}}" /></p>
</li>
<li><p><strong>Install Twig locally</strong>:</p>
<pre><code class="lang-apache"> <span class="hljs-attribute">composer</span> require twig/twig
</code></pre>
<p> This will add Twig to your dependencies in the <code>composer.json</code> file:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696419982824/8eab3048-5ae9-44f9-8f5b-4b4bf77cd615.png" alt="composer.json file. { &quot;require-dev&quot;: { &quot;symfony/symfony&quot;: &quot;^6.3&quot; (v.6.3.5)}, &quot;require&quot;: {&quot;twig/twig&quot;: &quot;^3.7&quot;}}" /></p>
</li>
<li><p><strong>You can now start using Twig in your PHP project.</strong> To do this, create Twig templates and use the Twig library to render them in your PHP code.</p>
<p> Here's an example of how to render a Twig template. First, create an <code>index.php</code> file, then:</p>
<pre><code class="lang-php"> <span class="hljs-meta">&lt;?php</span>
 <span class="hljs-keyword">require</span> <span class="hljs-string">'vendor/autoload.php'</span>; <span class="hljs-comment">// Composer's autoloader</span>

 <span class="hljs-comment">// Create a Twig environment</span>
 $loader = <span class="hljs-keyword">new</span> \Twig\Loader\FilesystemLoader(<span class="hljs-string">'/path/to/templates'</span>);
 $twig = <span class="hljs-keyword">new</span> \Twig\Environment($loader);

 <span class="hljs-comment">// Render a template</span>
 <span class="hljs-keyword">echo</span> $twig-&gt;render(<span class="hljs-string">'template.twig'</span>, [<span class="hljs-string">'variable'</span> =&gt; <span class="hljs-string">'Hello, Twig!'</span>]);
</code></pre>
<p> Replace <code>/path/to/templates</code> with the actual path to your Twig templates directory and <code>template.twig</code> with the name of your Twig template.</p>
</li>
<li><p><strong>Create Twig Templates:</strong></p>
<p> Create your Twig templates with the <code>.twig</code> file extension and place them in the specified templates directory. You can use these templates to separate your HTML or other markup from your PHP logic.</p>
</li>
</ol>
<p>That's it! You have now successfully installed and can use Twig in your PHP project.</p>
<h1 id="heading-example">Example</h1>
<p>Let's render a simple string with two variables just to demonstrate how to use Twig. In the root directory of your project, create a folder named <code>views</code>. It is not a strict requirement to have a folder with this name, but it's a convention often used to organize Twig templates and is a recommended practice for maintaining a structured and organized project.</p>
<p>In your <code>views</code> folder create an HTML file. Let's call it <code>hello.twig</code>.</p>
<p>In <code>index.php</code> we'll echo two variables: <code>name</code> and <code>age</code>. In the <code>hello.twig</code> we will access those variables using the double curly braces syntax.</p>
<pre><code class="lang-php">index.php
<span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'vendor/autoload.php'</span>; <span class="hljs-comment">// Composer's autoloader</span>

<span class="hljs-comment">// Create a Twig environment</span>
$loader = <span class="hljs-keyword">new</span> \Twig\Loader\FilesystemLoader(<span class="hljs-string">'views'</span>);
$twig = <span class="hljs-keyword">new</span> \Twig\Environment($loader);

<span class="hljs-comment">// Render a template</span>
<span class="hljs-keyword">echo</span> $twig-&gt;render(<span class="hljs-string">'hello.twig'</span>, <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">'John Doe'</span>,
    <span class="hljs-string">'age'</span> =&gt; <span class="hljs-number">25</span>
));
</code></pre>
<pre><code class="lang-xml">views&gt;hello.twig

<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> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</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">charset</span>=<span class="hljs-string">"UTF-8"</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, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Let's learn Twig<span class="hljs-tag">&lt;/<span class="hljs-name">title</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">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello, {{ name }}! You are {{ age }} years old.<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</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>Now to see how it is rendered in the browser you need to run a PHP server.</p>
<h2 id="heading-how-to-set-up-a-server-to-run-php-locally">How to set up a server to run PHP locally</h2>
<p>In a terminal, run the command:</p>
<pre><code class="lang-bash">php -S localhost:8000
</code></pre>
<p>This command opens a local server at port 8000 (PHP command <code>-S</code> : <code>Run with built-in web server</code>). Now you can navigate to your browser at localhost:8000 and see what is being rendered. If you followed the above steps you should see:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696426356178/b00cd1cb-12a8-4228-b30d-f32149cf185b.png" alt /></p>
<p>In case the PHP server won't run for you for some mysterious reason, there's also this neat VS code extension called <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=brapifra.phpserver">PHP server from brapifra</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696595133694/ead7c617-9a2a-4187-ab76-85d02a09d671.png" alt /></p>
<p>And that's it! You can find the code I used for the above example in <a target="_blank" href="https://github.com/WitchDevelops/Twig-tutorial/tree/main">this repo</a>, with more examples in <code>hello.html.twig</code> file.</p>
<p>Happy coding!</p>
<h1 id="heading-resources">Resources</h1>
<ol>
<li><p>Twig documentation: <a target="_blank" href="https://twig.symfony.com/doc/3.x/">https://twig.symfony.com/doc/3.x/</a> (official documentation)</p>
</li>
<li><p>Symfony documentation on templates: <a target="_blank" href="https://symfony.com/doc/current/templates.html">https://symfony.com/doc/current/templates.html</a> (official documentation)</p>
</li>
<li><p>Introduction to templating with Twig: <a target="_blank" href="https://craftcms.com/docs/4.x/dev/twig-primer.html">https://craftcms.com/docs/4.x/dev/twig-primer.html</a> (article)</p>
</li>
<li><p>Course on templating in Twig for Drupal <a target="_blank" href="https://symfonycasts.com/screencast/twig/basics">https://symfonycasts.com/screencast/twig/basics</a> (video course, paid)</p>
</li>
<li><p>Introduction to Twig and its syntax: <a target="_blank" href="https://www.youtube.com/watch?v=50RBUmPOYig">https://www.youtube.com/watch?v=50RBUmPOYig</a> (YT video)</p>
</li>
<li><p>Intro to Twig and templating: <a target="_blank" href="https://www.youtube.com/playlist?list=PLo5h1Mro0GtGoNDhF8KUWsiDdSbpnl7xr">https://www.youtube.com/playlist?list=PLo5h1Mro0GtGoNDhF8KUWsiDdSbpnl7xr</a> (YT playlist)</p>
</li>
<li><p>Twig blocks: <a target="_blank" href="https://www.youtube.com/watch?v=1rHvEq3XoRU">https://www.youtube.com/watch?v=1rHvEq3XoRU</a> (YT video)</p>
</li>
<li><p>Twig include, embed, and extends <a target="_blank" href="https://www.youtube.com/watch?v=XWhQku4QBD8">https://www.youtube.com/watch?v=XWhQku4QBD8</a> (YT video)</p>
</li>
<li><p>Source code with examples: <a target="_blank" href="https://github.com/WitchDevelops/Twig-tutorial/tree/main">https://github.com/WitchDevelops/Twig-tutorial/tree/main</a> (GitHub repository)</p>
</li>
<li><p>VS code extension for syntax highlighting: <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Stanislav.vscode-twig">https://marketplace.visualstudio.com/items?itemName=Stanislav.vscode-twig</a> (VS code extension)</p>
</li>
<li><p>VS code extension with Twig snippets, auto-completion, emmet and syntax <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=bajdzis.vscode-twig-pack">https://marketplace.visualstudio.com/items?itemName=bajdzis.vscode-twig-pack</a> (VS code extension)</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Four git commands you should know]]></title><description><![CDATA[A brief overview of git commands: log, revert, reset and rebase with examples.]]></description><link>https://witchwrites.hashnode.dev/four-git-commands-you-should-know</link><guid isPermaLink="true">https://witchwrites.hashnode.dev/four-git-commands-you-should-know</guid><category><![CDATA[Git]]></category><category><![CDATA[Collaboration]]></category><category><![CDATA[version control]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Dominika Wojewska]]></dc:creator><pubDate>Mon, 02 Oct 2023 13:51:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1696244041485/1c1f9a22-083e-4020-b2b1-03cf12a9d095.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the fast-paced world of software development, version control is the unsung hero that keeps chaos at bay. Whether you're working on a personal project or collaborating with a team, the ability to track changes, manage code efficiently, and roll back to the previous state is essential. This is where git, a powerful version control system, comes into play.</p>
<p>As a front-end developer, your codebase is your canvas, and git is your trusted brush. With git, you gain the ability to document your project's history, collaborate seamlessly with teammates, and ensure the stability and reliability of your web applications. Besides the essential git commands, like <code>push</code>, <code>clone</code>, <code>pull</code> and <code>commit</code>, there are a couple more incredibly useful commands. Let's dive in.</p>
<h2 id="heading-log">log</h2>
<h3 id="heading-when-to-use">When to use</h3>
<p>Whenever you want to see the commit history! Especially useful when combined with <code>--oneline</code>. This command only displays the first 7 characters of the SHA key of each commit, improving readability.</p>
<h3 id="heading-how-to-use-it">How to use it</h3>
<p>Type <code>git log --oneline</code>. That's it. You can combine it with <code>--graph</code> to visualize the branches. It's not the prettiest and there are other ways to achieve a similar effect, but it's good to know that git has this as an inbuilt command. The effects may look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696246432860/70c36e37-3bfe-4c42-9a60-6dd2e827d877.png" alt /></p>
<h3 id="heading-read-more">Read more</h3>
<p><a target="_blank" href="https://git-scm.com/docs/git-log">https://git-scm.com/docs/git-log</a></p>
<h2 id="heading-revert">revert</h2>
<h3 id="heading-when-to-use-1">When to use</h3>
<p>Say, you made a commit and the commit messed up your codebase. Or you committed something twice and you want to undo it. This is where <code>git revert</code> comes in handy: you give it the SHA key of the commit you want to undo and it removes the changes by creating a new commit with rolled back changes.</p>
<h3 id="heading-how-to-use-it-1">How to use it</h3>
<p>You specify the SHA key (or the first 7 characters) of the commit you want to undo:</p>
<p><code>git revert [insert the SHA key of the commit you want to remove]</code></p>
<h3 id="heading-example">Example</h3>
<p>for a git log like this:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">log</span> --oneline
cb76ee4 bad commit
01b56c6 good commit
2e407ce first commit
</code></pre>
<p>a command <code>git revert cb76ee4</code> will undo the changes and remove the last commit. It will create a new commit with the changes that are rolled back.</p>
<h3 id="heading-read-more-1">Read more</h3>
<p><a target="_blank" href="https://git-scm.com/docs/git-revert">https://git-scm.com/docs/git-revert</a></p>
<p><a target="_blank" href="https://stackoverflow.com/questions/19032296/how-to-use-git-revert">https://stackoverflow.com/questions/19032296/how-to-use-git-revert</a></p>
<h2 id="heading-reset">reset</h2>
<h3 id="heading-when-to-use-2">When to use</h3>
<p>For bigger fuckups, used to revert several commits and reset the current HEAD to the specified state. It requires the SHA key of the last commit you want to keep and removes anything that was committed after it.</p>
<h3 id="heading-how-to-use-it-2">How to use it</h3>
<p>You specify the SHA key (or the first 7 characters) of the last commit you want to keep:</p>
<p><code>git reset [insert the SHA key of the last commit you want to keep]</code></p>
<h3 id="heading-example-1">Example</h3>
<p>for a git log like this (same as previously):</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">log</span> --oneline
cb76ee4 bad commit
01b56c6 good commit
2e407ce first commit
</code></pre>
<p>command <code>git reset 2e407ce</code> will remove the good commit, the bad commit, and keep the code from the first commit only.</p>
<h3 id="heading-read-more-2">Read more</h3>
<p><a target="_blank" href="https://git-scm.com/docs/git-reset">https://git-scm.com/docs/git-reset</a></p>
<h2 id="heading-squash">squash</h2>
<h3 id="heading-when-to-use-3">When to use</h3>
<p>It happens that you were in a hurry, changed some code, pushed it, and then realized there are some more changes to be made with the same commit message. Or another example: you have a git history full of small commits with meaningless messages. To have a clear commit history with comprehensive messages it's best to squash some commits into one, with a single, meaningful message.</p>
<h3 id="heading-how-to-use-it-3">How to use it</h3>
<p>Run <code>git rebase</code> in interactive mode. Select which commits you want to squash. Then select a commit message and push the changes.</p>
<h3 id="heading-example-2">Example</h3>
<p>Let's say you have 4 commits you want to squash into one. For a git log like this:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">log</span> --oneline
80801e4 more changes
e3873ff update styles
92c2f17 update css
186d599 changes
2e407ce first commit
</code></pre>
<ol>
<li><p>run git rebase in interactive mode <code>git rebase -i HEAD~4</code></p>
</li>
<li><p>this opens the editor window. Change "pick" to "squash" for every commit that you want to be squashed into one, except for the first one. Save changes and close the editor.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696245326367/2e07a18f-9edd-4a3d-9382-71923385b622.png" alt /></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696245382920/f55be5a5-23ef-4351-aa1d-5938b80a8b42.png" alt /></p>
</li>
<li><p>choose a commit message, save changes and close the editor.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696245729973/3f18827e-7ff2-469d-9152-9a8ffa7ce1fd.png" alt /></p>
</li>
<li><p>push changes with <code>git push --force origin HEAD</code></p>
</li>
</ol>
<h3 id="heading-read-more-3">Read more</h3>
<p><a target="_blank" href="https://git-scm.com/docs/git-rebase">https://git-scm.com/docs/git-rebase</a></p>
<p><a target="_blank" href="https://prasad-k-pawar.medium.com/how-to-combine-multiple-git-commits-into-one-c04c67367a36">https://prasad-k-pawar.medium.com/how-to-combine-multiple-git-commits-into-one-c04c67367a36</a></p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we explored four incredibly useful commands: <code>log</code>, <code>revert</code>, <code>reset</code>, and <code>rebase</code>. These commands are indispensable tools in a developer's git toolkit, providing the ability to visualize commit histories, undo changes, reset branches, and streamline branch management.</p>
<ul>
<li><p><code>git log</code> offers valuable insights into your project's commit history, helping you understand the evolution of your codebase and facilitating collaboration.</p>
</li>
<li><p><code>git revert</code> empowers you to gracefully undo specific commits, creating a new commit to reverse changes without altering history.</p>
</li>
<li><p><code>git reset</code> allows you to reset your branch to a specific commit, whether to unstage changes or start anew.</p>
</li>
<li><p><code>git rebase</code> provides an advanced way to rewrite and reorganize your commit history, promoting a linear and clean history that's easier to maintain and understand.</p>
</li>
</ul>
<p>Mastering these commands not only enhances your efficiency as a git user but also empowers you to navigate the complexities of version control with confidence. By learning how to properly use git to its full potential, you'll be better equipped to manage your repositories and collaborate effectively with your team. By no means it's an exhaustive list as git offeres so much more. But we'll talk about it some other time. Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[The importance of alt texts in web accessibility and SEO]]></title><description><![CDATA[Importance of Alt Texts for SEO & Accessibility. Learn best practices to optimize descriptive alt texts for images. WCAG guidelines included.]]></description><link>https://witchwrites.hashnode.dev/the-importance-of-alt-texts-in-web-accessibility-and-seo</link><guid isPermaLink="true">https://witchwrites.hashnode.dev/the-importance-of-alt-texts-in-web-accessibility-and-seo</guid><category><![CDATA[SEO for Developers]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[Web Accessibility]]></category><category><![CDATA[alt text]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Dominika Wojewska]]></dc:creator><pubDate>Tue, 25 Jul 2023 15:05:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4MoIpDcSlr4/upload/4446bdabd69039753564f7f222d3ef29.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a junior front-end developer working in an e-commerce creative agency, I am mainly tasked with transforming designs into code. Ideally, the code should not only be responsive, which is a standard nowadays, but also semantic and accessible. Working for an agency, however, often means dealing with clients and their demands. The Client is always right (except when they're not, which is quite often) so The Client thinks they can demand things done their way. And all would be well unless The Client wouldn't demand breaking some ground rules in modern web standards.</p>
<p>I've been tasked recently with coding a landing page, which included a quiz as a slider, one question per slide. There was a background image (same for each slide, purely decorative). Each slide was generated with JavaScript, so that I could use an array of objects containing information such as question, answer, and which button is correct (it was a yes/no type thing). Then came The Client with their list of alt texts. The Client wanted to include an alt text to the background image of each slide. The alt texts were meaningless and purely sales-oriented, not to mention way too long and not descriptive at all. As I'm merely a junior developer at the very end of the chain of command, and The Client is of the "I'm always right, do as I say" type, I had to figure out a way to satisfy those ridiculous wishes in the best possible way.</p>
<p>I never paid too much attention to alt texts or best practices, I usually wrote some short description of an image if it's inserted with HTML and never paid any attention to those inserted with CSS (as they are usually only decoration with no meaning). So I thought it's good to learn more about alt texts, their importance and best practices. In this article, we will explore the proper use of alt texts according to W3C standards, their relevance for SEO and best accessibility practices.</p>
<h1 id="heading-what-are-alt-texts">What are alt texts?</h1>
<p>Alt texts are short, descriptive texts added to image elements in HTML that serve as substitutes for images when they cannot be displayed. These texts are essential for users with visual impairments who rely on screen readers to access web content. When an image lacks an alt text, screen readers may read out the filename or provide no information at all, creating a confusing and frustrating experience for users.</p>
<p>According to the Web Content Accessibility Guidelines (WCAG) by the World Wide Web Consortium (W3C), alt texts should be <strong>concise, meaningful, and provide an accurate description of the image's content and function</strong>. Avoid using redundant phrases like "image" or "picture" and instead describe the purpose and information conveyed by the image.</p>
<h2 id="heading-image-types-based-on-content-and-meaning">Image types based on content and meaning</h2>
<p>Not all images are equal so not all of them need an alt text. Based on their meaning, we can distinguish several image types.</p>
<ul>
<li><p><strong>Decorative Images:</strong> For purely decorative images that do not convey any meaningful information, it's essential to provide an <strong>empty alt attribute</strong> or use <code>aria-hidden="true"</code> to indicate to screen readers that these images are not significant for content comprehension. From <a target="_blank" href="https://www.w3.org/TR/WCAG21/#text-alternatives">WCAG</a>: "If non-text content is pure decoration, is used only for visual formatting, or is not presented to users, then it is implemented in a way that it can be ignored by assistive technology".</p>
</li>
<li><p><strong>Informative Images:</strong> Images that contain valuable information must have descriptive alt texts that convey the same information to users who cannot see the image. From <a target="_blank" href="https://www.w3.org/TR/WCAG21/#text-alternatives">WCAG</a>, again: "All non-text content that is presented to the user has a text alternative that serves <strong>the equivalent purpose</strong>".</p>
</li>
<li><p><strong>Complex Images and Graphics</strong>: Complex images or graphics, such as charts or diagrams, may need alternative ways of conveying their content, such as using descriptive text near the image or providing a link to a separate page with an in-depth explanation. It is a great use case for a <code>longdesc</code> attribute (or was, as it's now deprecated. Current recommendation is to encapsulate an image in a link using the <code>a</code> element or to use <code>aria-details</code> attribute).</p>
</li>
</ul>
<h3 id="heading-brief-aside-longdesc-and-aria-details">brief aside: <code>longdesc</code> and <code>aria-details</code></h3>
<p>Initially, <code>longdesc</code> attribute was meant to provide a link to a longer description of information-dense images, like flowcharts, schematics and so on (think highly technical type of graphics, like <a target="_blank" href="https://www.researchgate.net/figure/mpact-of-a-low-concentration-of-Ba-2-on-the-effect-of-sildenafil-on-the-human-Kir22_fig4_368232651">this</a> one). The description was meant to be used both by visually-impaired users, as well as those needing more explanation and could have either been on the same page, on another page, or included in data. <a target="_blank" href="https://www.w3schools.com/tags/att_img_longdesc.asp">W3Schools provides some examples of that</a>. <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/longDesc">This attribute has been deprecated</a> and the current recommendation is to wrap the image in the <code>a</code> element instead. Alternatively, <code>aria-details</code> attribute can be used to provide additional information related to the object. It has a broader use, it's not exclusive to images. Its main use is to provide additional information, complex descriptions or navigable content related to the object. More information regarding the best practices can be found <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-details">here</a>.</p>
<h1 id="heading-why-are-alt-texts-important-for-seo">Why are alt texts important for SEO?</h1>
<p>Beyond their primary role in web accessibility, alt texts also play a crucial role in Search Engine Optimization (SEO). Search engines, such as Google, use alt texts to understand and index images on web pages. properly optimized alt texts can have a direct impact on boosting the SEO performance of websites.</p>
<p>Here's why alt texts are vital for SEO:</p>
<ul>
<li><p><strong>Image Search Ranking:</strong> When users perform image searches, search engines rely on alt texts to understand what the images are about. By providing relevant and descriptive alt texts, we increase the likelihood of our images appearing in search results, potentially driving more organic traffic to our website.</p>
</li>
<li><p><strong>Keyword Relevance</strong>: Alt texts provide an opportunity to include relevant keywords that are related to the content of the image and the webpage. However, it's crucial to use these keywords naturally and avoid stuffing alt texts with excessive keywords, as search engines frown upon such practices and may penalize websites.</p>
</li>
<li><p><strong>Improved Page Relevance:</strong> Search engines consider alt texts as part of the broader context of a webpage. By crafting descriptive alt texts that closely relate to the page's content, we can boost the overall relevance of the page for specific search queries, ultimately contributing to better SEO performance.</p>
</li>
<li><p><strong>Accessibility and User Experience</strong>: SEO and web accessibility go hand in hand. By providing meaningful alt texts, we not only make our websites accessible to users with disabilities but also enhance the user experience for all visitors. A positive user experience can indirectly impact our SEO rankings, creating a win-win situation.</p>
</li>
<li><p><strong>Screen Reader SEO</strong>: Alt texts are crucial for users who navigate the web using screen readers. These assistive technologies rely on alt texts to provide relevant search results to visually impaired users. Crafting well-thought-out alt texts can significantly improve our website's visibility to users using assistive technologies.</p>
</li>
</ul>
<h2 id="heading-best-practices-for-optimizing-alt-texts-for-seo">Best practices for optimizing alt texts for SEO</h2>
<p>Some essential best practices regarding alt texts optimized for SEO are as follows:</p>
<ul>
<li><p><strong>Be Descriptive</strong>: Crafting concise and descriptive alt texts is crucial. These texts should precisely convey the image's content and context, leaving no room for ambiguity. Avoid using generic or vague descriptions that might not provide meaningful information to users while staying concise.</p>
</li>
<li><p><strong>Use Relevant Keywords</strong>: Including relevant keywords in alt texts can contribute to better SEO performance. Align these keywords with the image's content and the overall page, but ensure they are naturally integrated into the alt text. Avoid keyword stuffing, as it can harm SEO efforts.</p>
</li>
<li><p><strong>Avoid Keyword Stuffing</strong>: Maintain a balance with keyword usage. Overloading alt texts with excessive keywords is not only penalized by search engines but also can negatively impact user experience. Focus on providing useful information to users.</p>
</li>
<li><p><strong>Decorative Images</strong>: For purely decorative images that do not add valuable information, use empty alt attributes (<code>alt=""</code>) or <code>aria-hidden="true"</code> to prevent them from being indexed and unnecessarily cluttering search results.</p>
</li>
<li><p><strong>Context Matters</strong>: Al texts should align seamlessly with the surrounding content and the image's purpose on the page. Relevant and contextually appropriate alt texts enhance the overall user experience and improve page relevance for search engines.</p>
</li>
<li><p><strong>Screen Reader Testing</strong>: One way I ensure that alt texts are serving their purpose is by testing them with screen readers. Verifying that screen readers read the alt texts accurately and provide meaningful information to visually impaired users is essential for creating an inclusive web experience.</p>
</li>
</ul>
<h1 id="heading-alt-texts-on-background-images">Alt texts on background images</h1>
<p>(why tho)</p>
<p>In general, background images rarely convey meaning and are typically added with CSS for the whole container. This prevents using the traditional way of adding an <code>alt</code> attribute to an <code>img</code> element (and according to WCAG <code>alt</code> texts on decorative images should not be used anyway, and what are background images if not decoration?). However, some workarounds adhere to W3C standards (workaround, as the name implies, is not the most straightforward or the best way to achieve your goal. So thread with caution).</p>
<h2 id="heading-adding-alt-texts-to-css-background-images-using-aria">Adding alt texts to CSS background images using ARIA</h2>
<p>To provide <code>alt</code> text for background images, we can use ARIA roles and <code>aria-label</code> attribute. By applying the <code>role="img"</code> and <code>aria-label</code> to a container element, we inform screen readers that the container represents an image with the specified label. Here's an example of how to do it:</p>
<p><code>&lt;div id="leftdiv" class="bkg_img" role="img" aria-label="A view of the woods with its magnificent autumn colors"&gt;&lt;/div&gt;</code></p>
<p>The caveat of this solution though is in case the div contains any content, it will be hidden from screen readers, as the whole element will be conveyed as an image. ARIA <code>role="img"</code> changes the semantics of the code and is meant to use for a container that identifies a collection of elements that together form a meaningful image. For example, consider this code snippet:</p>
<p><code>&lt;div role="img" aria-label="scared face"&gt;(ㆆ _ ㆆ)&lt;/div&gt;</code></p>
<p>So in cases when the CSS background image is used on a container that provides some meaningful information, a better solution is to either add an empty <code>span</code> element with <code>role="img"</code> and <code>aria-label</code>, or an empty <code>img</code> element with an <code>alt</code> tag at the end of the container. But again, it's a misuse of <code>alt</code> tags and such practices should be discouraged.</p>
<h2 id="heading-adding-alt-texts-to-images-inserted-with-javascript">Adding alt texts to images inserted with JavaScript</h2>
<p>While images added via CSS cannot have direct <code>alt</code> texts, JavaScript can be used to dynamically set the <code>alt</code> text attribute using the <code>setAttribute</code> method. For example:</p>
<p><code>const image = document.getElementById("leftdiv");</code></p>
<p><code>image.setAttribute("alt", "A view of the woods with its magnificent autumn colors");</code></p>
<p>This method dynamically sets the alt attribute on the element with the specified <code>alt</code> text, making it accessible to screen readers.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In conclusion, understanding the significance of alt texts has given me a new perspective on web development. Web accessibility is an essential aspect of modern web development, and providing accurate and descriptive alt texts for images is a fundamental practice. As I continue to learn and grow in my role, I'm committed to creating websites that not only meet clients' demands but also adhere to best practices in web accessibility and SEO. Good SEO practices should always complement and not compromise web accessibility, ensuring that all users can benefit from a fully inclusive online experience. </p>
<h1 id="heading-references">References</h1>
<p>WCAG guidelines <a target="_blank" href="https://www.w3.org/TR/WCAG21/#text-alternatives">https://www.w3.org/TR/WCAG21/#text-alternatives</a></p>
<p>ARIA roles <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles">https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles</a></p>
<p>role="img" <a target="_blank" href="https://accessibilityinsights.io/info-examples/web/role-img-alt/">https://accessibilityinsights.io/info-examples/web/role-img-alt/</a></p>
<p>role="img" and aria-label (proper use) <a target="_blank" href="https://www.w3.org/WAI/GL/wiki/ARIATechnique_usingImgRole_with_aria-label_forCSS-backgroundImage">https://www.w3.org/WAI/GL/wiki/ARIATechnique_usingImgRole_with_aria-label_forCSS-backgroundImage</a></p>
<p>aria-details <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-details">https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-details</a></p>
<p>image SEO best practices <a target="_blank" href="https://developers.google.com/search/docs/appearance/google-images">https://developers.google.com/search/docs/appearance/google-images</a></p>
<p>image alt text optimization for SEO <a target="_blank" href="https://yoast.com/image-seo-alt-tag-and-title-tag-optimization/">https://yoast.com/image-seo-alt-tag-and-title-tag-optimization/</a></p>
]]></content:encoded></item></channel></rss>